Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add JPA components only on single DataSource #6560

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration.HibernateEntityManagerCondition;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.orm.jpa.hibernate.SpringJtaPlatform;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
Expand All @@ -55,13 +57,15 @@
* @author Josh Long
* @author Manuel Doninger
* @author Andy Wilkinson
* @author Kazuki Shimizu
*/
@Configuration
@ConditionalOnClass({ LocalContainerEntityManagerFactoryBean.class,
EnableTransactionManagement.class, EntityManager.class })
@Conditional(HibernateEntityManagerCondition.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class })
public class HibernateJpaAutoConfiguration extends JpaBaseConfiguration {
@EnableConfigurationProperties(JpaProperties.class)
public class HibernateJpaAutoConfiguration {

private static final Log logger = LogFactory
.getLog(HibernateJpaAutoConfiguration.class);
Expand All @@ -83,110 +87,117 @@ public class HibernateJpaAutoConfiguration extends JpaBaseConfiguration {
"org.hibernate.engine.transaction.jta.platform.internal.WebSphereExtendedJtaPlatform",
"org.hibernate.service.jta.platform.internal.WebSphereExtendedJtaPlatform", };

public HibernateJpaAutoConfiguration(DataSource dataSource,
JpaProperties jpaProperties,
ObjectProvider<JtaTransactionManager> jtaTransactionManagerProvider) {
super(dataSource, jpaProperties, jtaTransactionManagerProvider);
}

@Override
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
return new HibernateJpaVendorAdapter();
}
@Configuration
@ConditionalOnSingleCandidate(DataSource.class)
static class HibernateJpaConfiguration extends JpaBaseConfiguration {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a bold move but I think I like it. I have a feeling it's going to break user's app, wdyt @wilkinsona ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might too. I don't think such a change belongs in 1.4.1.


@Override
protected Map<String, Object> getVendorProperties() {
Map<String, Object> vendorProperties = new LinkedHashMap<String, Object>();
vendorProperties.putAll(getProperties().getHibernateProperties(getDataSource()));
return vendorProperties;
}
HibernateJpaConfiguration(DataSource dataSource,
JpaProperties jpaProperties,
ObjectProvider<JtaTransactionManager> jtaTransactionManagerProvider) {
super(dataSource, jpaProperties, jtaTransactionManagerProvider);
}

@Override
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
return new HibernateJpaVendorAdapter();
}

@Override
protected void customizeVendorProperties(Map<String, Object> vendorProperties) {
super.customizeVendorProperties(vendorProperties);
if (!vendorProperties.containsKey(JTA_PLATFORM)) {
configureJtaPlatform(vendorProperties);
@Override
protected Map<String, Object> getVendorProperties() {
Map<String, Object> vendorProperties = new LinkedHashMap<String, Object>();
vendorProperties.putAll(getProperties().getHibernateProperties(getDataSource()));
return vendorProperties;
}

@Override
protected void customizeVendorProperties(Map<String, Object> vendorProperties) {
super.customizeVendorProperties(vendorProperties);
if (!vendorProperties.containsKey(JTA_PLATFORM)) {
configureJtaPlatform(vendorProperties);
}
}
}

private void configureJtaPlatform(Map<String, Object> vendorProperties)
throws LinkageError {
JtaTransactionManager jtaTransactionManager = getJtaTransactionManager();
if (jtaTransactionManager != null) {
if (runningOnWebSphere()) {
// We can never use SpringJtaPlatform on WebSphere as
// WebSphereUowTransactionManager has a null TransactionManager
// which will cause Hibernate to NPE
configureWebSphereTransactionPlatform(vendorProperties);
private void configureJtaPlatform(Map<String, Object> vendorProperties)
throws LinkageError {
JtaTransactionManager jtaTransactionManager = getJtaTransactionManager();
if (jtaTransactionManager != null) {
if (runningOnWebSphere()) {
// We can never use SpringJtaPlatform on WebSphere as
// WebSphereUowTransactionManager has a null TransactionManager
// which will cause Hibernate to NPE
configureWebSphereTransactionPlatform(vendorProperties);
}
else {
configureSpringJtaPlatform(vendorProperties, jtaTransactionManager);
}
}
else {
configureSpringJtaPlatform(vendorProperties, jtaTransactionManager);
vendorProperties.put(JTA_PLATFORM, getNoJtaPlatformManager());
}
}
else {
vendorProperties.put(JTA_PLATFORM, getNoJtaPlatformManager());
}
}

private boolean runningOnWebSphere() {
return ClassUtils.isPresent(
"com.ibm.websphere.jtaextensions." + "ExtendedJTATransaction",
getClass().getClassLoader());
}

private void configureWebSphereTransactionPlatform(
Map<String, Object> vendorProperties) {
vendorProperties.put(JTA_PLATFORM, getWebSphereJtaPlatformManager());
}
private boolean runningOnWebSphere() {
return ClassUtils.isPresent(
"com.ibm.websphere.jtaextensions." + "ExtendedJTATransaction",
getClass().getClassLoader());
}

private Object getWebSphereJtaPlatformManager() {
return getJtaPlatformManager(WEBSPHERE_JTA_PLATFORM_CLASSES);
}
private void configureWebSphereTransactionPlatform(
Map<String, Object> vendorProperties) {
vendorProperties.put(JTA_PLATFORM, getWebSphereJtaPlatformManager());
}

private void configureSpringJtaPlatform(Map<String, Object> vendorProperties,
JtaTransactionManager jtaTransactionManager) {
try {
vendorProperties.put(JTA_PLATFORM,
new SpringJtaPlatform(jtaTransactionManager));
private Object getWebSphereJtaPlatformManager() {
return getJtaPlatformManager(WEBSPHERE_JTA_PLATFORM_CLASSES);
}
catch (LinkageError ex) {
// NoClassDefFoundError can happen if Hibernate 4.2 is used and some
// containers (e.g. JBoss EAP 6) wraps it in the superclass LinkageError
if (!isUsingJndi()) {
throw new IllegalStateException("Unable to set Hibernate JTA "
+ "platform, are you using the correct "
+ "version of Hibernate?", ex);

private void configureSpringJtaPlatform(Map<String, Object> vendorProperties,
JtaTransactionManager jtaTransactionManager) {
try {
vendorProperties.put(JTA_PLATFORM,
new SpringJtaPlatform(jtaTransactionManager));
}
// Assume that Hibernate will use JNDI
if (logger.isDebugEnabled()) {
logger.debug("Unable to set Hibernate JTA platform : " + ex.getMessage());
catch (LinkageError ex) {
// NoClassDefFoundError can happen if Hibernate 4.2 is used and some
// containers (e.g. JBoss EAP 6) wraps it in the superclass LinkageError
if (!isUsingJndi()) {
throw new IllegalStateException("Unable to set Hibernate JTA "
+ "platform, are you using the correct "
+ "version of Hibernate?", ex);
}
// Assume that Hibernate will use JNDI
if (logger.isDebugEnabled()) {
logger.debug("Unable to set Hibernate JTA platform : " + ex.getMessage());
}
}
}
}

private boolean isUsingJndi() {
try {
return JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable();
}
catch (Error ex) {
return false;
private boolean isUsingJndi() {
try {
return JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable();
}
catch (Error ex) {
return false;
}
}
}

private Object getNoJtaPlatformManager() {
return getJtaPlatformManager(NO_JTA_PLATFORM_CLASSES);
}
private Object getNoJtaPlatformManager() {
return getJtaPlatformManager(NO_JTA_PLATFORM_CLASSES);
}

private Object getJtaPlatformManager(String[] candidates) {
for (String candidate : candidates) {
try {
return Class.forName(candidate).newInstance();
}
catch (Exception ex) {
// Continue searching
private Object getJtaPlatformManager(String[] candidates) {
for (String candidate : candidates) {
try {
return Class.forName(candidate).newInstance();
}
catch (Exception ex) {
// Continue searching
}
}
throw new IllegalStateException("Could not configure JTA platform");
}
throw new IllegalStateException("Could not configure JTA platform");

}

@Order(Ordered.HIGHEST_PRECEDENCE + 20)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.domain.EntityScanPackages;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand All @@ -59,8 +58,8 @@
* @author Dave Syer
* @author Oliver Gierke
* @author Andy Wilkinson
* @author Kazuki Shimizu
*/
@EnableConfigurationProperties(JpaProperties.class)
@Import(DataSourceInitializedPublisher.Registrar.class)
public abstract class JpaBaseConfiguration implements BeanFactoryAware {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
* @author Kazuki Shimizu
*/
@Configuration
class MultiDataSourceConfiguration {
public class MultiDataSourceConfiguration {

@Bean
public DataSource test1DataSource() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
* @author Kazuki Shimizu
*/
@Configuration
class MultiDataSourceUsingPrimaryConfiguration {
public class MultiDataSourceUsingPrimaryConfiguration {

@Bean
@Primary
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@
import org.junit.Test;
import org.junit.rules.ExpectedException;

import org.springframework.beans.factory.BeanCreationException;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.boot.autoconfigure.jdbc.MultiDataSourceConfiguration;
import org.springframework.boot.autoconfigure.jdbc.MultiDataSourceUsingPrimaryConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.test.City;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
Expand All @@ -58,6 +60,7 @@
*
* @author Phillip Webb
* @author Dave Syer
* @author Kazuki Shimizu
*/
public abstract class AbstractJpaAutoConfigurationTests {

Expand All @@ -77,10 +80,40 @@ public void close() {
public void testNoDataSource() throws Exception {
this.context.register(PropertyPlaceholderAutoConfiguration.class,
getAutoConfigureClass());
this.expected.expect(BeanCreationException.class);
this.expected.expectMessage("No qualifying bean");
this.expected.expectMessage("DataSource");
this.context.refresh();
assertThat(this.context.getBeansOfType(JpaProperties.class).size()).isEqualTo(1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I like that, you're changing the semantic a lot (and it's wider than the intent of this PR IMO).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @snicoll , i have a question for your opinion.

Should i change to assertThat(this.context.getBeansOfType(JpaProperties.class)).isEmpty(); ? or other ?

Copy link
Member

@snicoll snicoll Aug 17, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. I am not sure I understand what triggers this change but previoulsy if you had no data source at all you'd get a BeanCreationException. Now you don't get any exception. That's the thing we're discussing here.

assertThat(this.context.getBeansOfType(JpaVendorAdapter.class)).isEmpty();
assertThat(this.context.getBeansOfType(EntityManagerFactoryBuilder.class)).isEmpty();
assertThat(this.context.getBeansOfType(LocalContainerEntityManagerFactoryBean.class)).isEmpty();
assertThat(this.context.getBeansOfType(PlatformTransactionManager.class)).isEmpty();
}

@Test
public void testMultiDataSource() throws Exception {
this.context.register(PropertyPlaceholderAutoConfiguration.class,
MultiDataSourceConfiguration.class,
getAutoConfigureClass());
this.context.refresh();
assertThat(this.context.getBeansOfType(DataSource.class).size()).isEqualTo(2);
assertThat(this.context.getBeansOfType(JpaProperties.class).size()).isEqualTo(1);
assertThat(this.context.getBeansOfType(JpaVendorAdapter.class)).isEmpty();
assertThat(this.context.getBeansOfType(EntityManagerFactoryBuilder.class)).isEmpty();
assertThat(this.context.getBeansOfType(LocalContainerEntityManagerFactoryBean.class)).isEmpty();
assertThat(this.context.getBeansOfType(PlatformTransactionManager.class)).isEmpty();
}

@Test
public void testMultiDataSourceUsingPrimary() throws Exception {
this.context.register(PropertyPlaceholderAutoConfiguration.class,
MultiDataSourceUsingPrimaryConfiguration.class,
getAutoConfigureClass());
this.context.refresh();
assertThat(this.context.getBeansOfType(DataSource.class).size()).isEqualTo(2);
assertThat(this.context.getBean(JpaProperties.class)).isNotNull();
assertThat(this.context.getBean(JpaVendorAdapter.class)).isNotNull();
assertThat(this.context.getBean(EntityManagerFactoryBuilder.class)).isNotNull();
assertThat(this.context.getBean(LocalContainerEntityManagerFactoryBean.class)).isNotNull();
assertThat(this.context.getBean(JpaTransactionManager.class)).isNotNull();
}

@Test
Expand Down