Run SpringLiquibase BEFORE Hibernate - java

I'm using SpringLiquibase to apply my liquibase update automatically during the application startup. In general this works fine, but when I set hibernate.hbm2ddl.auto to "validate" then hibernate starts to complain about the database scheme before liquibase seems to have the chance to apply the updates.
My configuration looks like this:
#Configuration
#EnableTransactionManagement
#ComponentScan(basePackages = "com.myapp")
#PropertySource(value = {"classpath:myapp.properties"})
#EnableJpaRepositories("com.myapp")
public class MyappConfig {
#Resource
private Environment env;
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getRequiredProperty("jdbc.driver"));
dataSource.setUrl(env.getRequiredProperty("jdbc.url"));
dataSource.setUsername(env.getRequiredProperty("jdbc.username"));
dataSource.setPassword(env.getRequiredProperty("jdbc.password"));
return dataSource;
}
#Bean
public SpringLiquibase liquibase() {
SpringLiquibase liquibase = new SpringLiquibase();
liquibase.setDataSource(dataSource());
liquibase.setChangeLog("classpath:liquibase/liquibase-master-changelog.xml");
return liquibase;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
entityManagerFactoryBean.setPackagesToScan("com.myapp");
entityManagerFactoryBean.setJpaProperties(hibernateProperties());
return entityManagerFactoryBean;
}
private Properties hibernateProperties() {
Properties properties = new Properties();
String[] propertyNames = new String[]{"hibernate.dialect", "hibernate.show_sql", "hibernate.hbm2ddl.auto"};
for (String propertyName : propertyNames) {
properties.put(propertyName, env.getRequiredProperty(propertyName));
}
return properties;
}
#Bean
public JpaTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
}
Is there any way to get liquibase to apply its updates BEFORE hibernate tries to validate the schema?

Thanks to M. Deinum I was able to solve this by using
#Bean
#DependsOn("liquibase")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
[...]
}
The #DependsOn makes sure that liquibase is run before Hibernates schema validation.

Related

How can I read object from one datasource and write to another with spring data?

I have a config class:
#SpringBootConfiguration
#ComponentScan(basePackages = "vap")
public class AppConfig {
Logger logger = LoggerFactory.getLogger(this.getClass());
public AppConfig() {
}
#Bean
public ServerRuntime runtime() {
ServerRuntime runtime = ServerRuntime.builder().addConfig("cayenne-project.xml").build();
return runtime;
}
#Bean
public ObjectContext getContext(#Autowired ServerRuntime serverRuntime) {
return serverRuntime.newContext();
}
#Bean(name = "pgDataSource")
public DataSource getDataSource() {
Properties props = new Properties();
props.setProperty("user", "postgres");
props.setProperty("password", "");
PoolConfiguration configuration = new PoolProperties();
configuration.setDbProperties(props);
configuration.setUrl("jdbc:postgresql://localhost/mikro00");
configuration.setDriverClassName("org.postgresql.Driver");
DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource(configuration);
return dataSource;
}
#Bean(name = "hsqldbDataSource")
public DataSource getHSQLDataSource() {
Properties props = new Properties();
props.setProperty("user", "sa");
props.setProperty("password", "");
PoolConfiguration configuration = new PoolProperties();
configuration.setDbProperties(props);
configuration.setUrl("jdbc:h2:file:./outbase");
configuration.setDriverClassName("org.h2.Driver");
DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource(configuration);
return dataSource;
}
}
my PGConfig.java
#Configuration
#EnableTransactionManagement
public class PGConfig {
#Primary
#Bean(name = "entityManagerFactoryPG")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder, #Qualifier(value = "pgDataSource") DataSource dataSource) {
LocalContainerEntityManagerFactoryBean vap = builder.dataSource(dataSource)
.packages("vap")
.build();
JpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
vap.setJpaVendorAdapter(jpaVendorAdapter);
return vap;
}
#Primary
#Bean(name = "transactionManagerPG")
public PlatformTransactionManager transactionManager(
#Qualifier("entityManagerFactory") EntityManagerFactory
entityManagerFactory
) {
return new JpaTransactionManager(entityManagerFactory);
}
}
My H2Config.java
#Configuration
#EnableTransactionManagement
public class H2Config {
#Primary
#Bean(name = "dataSource")
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource(){
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder, #Qualifier(value = "hsqldbDataSource") DataSource dataSource){
LocalContainerEntityManagerFactoryBean vap = builder.dataSource(dataSource)
.packages("vap")
.build();
JpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
vap.setJpaVendorAdapter(jpaVendorAdapter);
return vap;
}
#Primary
#Bean(name = "transactionManagerH2")
public PlatformTransactionManager transactionManager(
#Qualifier("entityManagerFactory") EntityManagerFactory
entityManagerFactory
) {
return new JpaTransactionManager(entityManagerFactory);
}
}
KlientRepository
#Repository
public interface KlientRepository extends CrudRepository<Klient,Integer> {
}
How can I read Klient from one repository and write to another. I need to read from PG, work with data and save to h2. I can't find how two object of repository with different datasource, or simply create repository object with concrete datasource
You have pretty much everything out there in your code, you only need to do a bit of fine tuning thats all
Create two configuration classes with bean declaration for Datasource, EntityManagerFactory and TransactionManager
Mark one of the two as primary
Create two model classes(one for each database model)
Create two Repository classes in two different package**(very Important)**
In your service class Autowire both Repositories, read from one DB, manipulate and save to other.
Only thing missing in your code is you need to tell Spring which Repository class should use which EntityManager /Datasource(Since you have two). This can be done by Annotation #EnableJpaRepositories(basepackages=...). Use this annotation on each configuration classes, with basePackages indicating your repository classes

Spring Annotation Config with Multiple JPA Persistence Units Doesn't Persist

I started out with this guide, to migrate our xml config to an annotation config.
The current problem is, that my test a persist seems to not actually write the data (= no transaction commit). This results in the next check to fail. The environment currently has five persistence units and according entityManagerFactories and transactionManagers.
MyTest.java
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {PersistenceJPAConfig.class}, loader = AnnotationConfigContextLoader.class)
public class MyTest {
#Autowired
private MyDao testable;
#Transactional(transactionManager = "tm1") // tried with name= and without
#Test
public void crudTest() throws Exception {
// assert that the table is empty
List<MyDO> all = testable.getAll();
assertTrue(all.isEmpty());
// write one entity
MyDO anEntity = new MyDO();
testable.persist(anEntity);
// load all entities and assert that the details match
List<MyDO> allAfterInsert = testable.getAll();
// THIS FAILS
assertFalse("The database result should not be empty.", allAfterInsert.isEmpty());
}
}
PersistenceJPAConfig.java
#Configuration
#EnableTransactionManagement
#ComponentScan("my.package")
public class PersistenceJPAConfig {
#PersistenceContext(unitName = "myPersistenceUnit") // also tried
#Bean(name = "myEntityManager")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] {"my.package"});
em.setPersistenceUnitName("myPersistenceUnit");
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
return em;
}
// ... the same for the other persistenceUnits with increasing names
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
#Bean(name = "tm1")
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getNativeEntityManagerFactory());
transactionManager.setPersistenceUnitName("myPersistenceUnit");
transactionManager.afterPropertiesSet();
return transactionManager;
}
// ... the same for the other persistenceUnits with increasing names
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
#Bean
public PersistenceAnnotationBeanPostProcessor persistenceAnnotationBeanPostProcessor() {
return new PersistenceAnnotationBeanPostProcessor();
}
Properties additionalProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.hbm2ddl.auto", "update");
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
return properties;
}
}
MyDao.java
public class MyDao {
#PersistenceContext(unitName = "myPersistenceUnit")
EntityManager em;
#Override
#Transactional(transactionManager = "tm1") // also tried with different variations like above
public Long persist(final MyDO entity) {
em.persist(entity); // tried to add an em.flush(), but that throws a TransactionRequiredException: no transaction is in progress
// handling transactions would throw IllegalStateException: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead
return entity.getId();
}
}
The persistence units are located in the file META-INF/persistence.xml.
The tests worked with the xml configuration, but don't work with my current annotation config. Is there something I forgot? What more information could I provide?
I found a solution: Instead of entityManagerFactory().getNativeEntityManagerFactory() I used entityManagerFactory().getObject() and everything works as expected.
#Bean(name = "tm1")
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
transactionManager.setPersistenceUnitName("myPersistenceUnit");
transactionManager.afterPropertiesSet();
return transactionManager;
}
I am not sure why though.

How to ensure Spring Data JPA does not do DDLs (without Spring-Boot)?

I have the following database config
#Configuration
#EnableJpaRepositories("com.mycompany.databaseutilities.repo")
#ImportResource("classpath:data_source.xml")
public class DataConfig {
#Autowired
DataSource dataSource;
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean ans =
new LocalContainerEntityManagerFactoryBean();
ans.setDataSource(dataSource);
ans.setJpaVendorAdapter(jpaVendorAdapter());
ans.setPackagesToScan("com.mycompany.databaseutilities.model");
return ans;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter ans = new HibernateJpaVendorAdapter();
ans.setShowSql(false);
ans.setGenerateDdl(false); // is this sufficient?
ans.setDatabase(Database.MYSQL);
return ans;
}
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager ans = new JpaTransactionManager();
ans.setEntityManagerFactory(entityManagerFactory().getObject());
return ans;
}
}
Package com.mycompany.databaseutilities.model contains classes, annotated by #Entity
Can I be sure, that it won't execute any DDL statements? I don't wish to damage existing database.
You can either specify the spring.jpa.hibernate.ddl-auto property in your spring boot application.properties or application.yaml file.
You can also specify this at the time of creating your LocalContainerEntityManagerFactoryBean by supplying properties:
final Properties jpaProperties = new Properties();
jpaProperties.put( AvailableSettings.HBM2DDL_AUTO, "false" );
entityManagerFactoryBean.setJpaProperties( jpaProperties );

How to include Spring JPA in the existing Spring Hibernate application

I am working on one enhancement in my application. This application is Spring Hibernate implementation.But the enhancement now I am working is Spring-JPA.
Without disturbing the existing implementation I need to include JPA
Below is my DBconfiguration class.
#Configuration
#EnableJpaRepositories(basePackages = {"packagename","package name" })
#EnableTransactionManagement
public class DataBaseConfiguration {
#Value("${postgres.driver}")
private String postgresDriver;
#Value("${postgres.url}")
private String postgresUrl;
#Value("${postgres.username}")
private String postgresUsername;
#Value("${postgres.password}")
private String postgresPassword;
#Value("${hibernate.dialect}")
private String hibernateDialect;
#Value("${hibernate.dialect.property}")
private String dialectProperty;
#Value("${hibernate.show-sql}")
private String hibernateShowSql;
#Value("${boolean.true}")
private String booleanTrue;
**\\ existing hibernate flow**
#Autowired
#Bean(name = "name")
public SessionFactory getSessionFactory(DataSource dataSource) {
LocalSessionFactoryBuilder sessionBuilder = new LocalSessionFactoryBuilder(
dataSource);
sessionBuilder.addAnnotatedClasses(entity.class);
sessionBuilder.setProperty(hibernateDialect, dialectProperty);
sessionBuilder.setProperty(postgresDriver, postgresUrl);
sessionBuilder.setProperty(hibernateShowSql, booleanTrue);
return sessionBuilder.buildSessionFactory();
}
**\\ existing hibernate flow**
#Bean(name = "name")
public DataSource getDataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(postgresDriver);
dataSource.setUrl(postgresUrl);
dataSource.setUsername(postgresUsername);
dataSource.setPassword(postgresPassword);
return dataSource;
}
**\\ existing hibernate flow**
#Autowired
#Bean(name = "manager")
public HibernateTransactionManager getTransactionManager(
SessionFactory sessionFactory) {
HibernateTransactionManager transactionManager = null;
transactionManager = new HibernateTransactionManager(sessionFactory);
return transactionManager;
}
**\\included newly for JPA**
#Bean
public org.springframework.orm.jpa.JpaTransactionManager transactionManager() {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setDataSource(getDataSource());
jpaTransactionManager
.setJpaDialect(new org.springframework.orm.jpa.vendor.HibernateJpaDialect());
return jpaTransactionManager;
}
**\\included newly for JPA**
#Bean
public EntityManager entityManger() {
return entityManagerFactory().getObject().createEntityManager();
}
\\included newly for JPA
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setDataSource(getDataSource());
entityManagerFactory.setPackagesToScan(
"some package",
"some package");
Map<String, Object> jpaProperty = entityManagerFactory
.getJpaPropertyMap();
jpaProperty.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
HibernateJpaVendorAdapter va = new HibernateJpaVendorAdapter();
va.setShowSql(Boolean.valueOf("true"));
entityManagerFactory.setJpaVendorAdapter(va);
return entityManagerFactory;
}
}
When I deployed this application i am getting below exception
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class ------
Caused by: java.lang.NullPointerException
at org.hibernate.hql.internal.antlr.HqlBaseParser.identPrimary(HqlBaseParser.java:4285)
at org.hibernate.hql.internal.antlr.HqlBaseParser.primaryExpression(HqlBaseParser.java:980)
at org.hibernate.hql.internal.antlr.HqlBaseParser.atom(HqlBaseParser.java:3609)
at org.hibernate.hql.internal.antlr.HqlBaseParser.unaryExpression(HqlBaseParser.java:3387)
at org.hibernate.hql.internal.antlr.HqlBaseParser.multiplyExpression(HqlBaseParser.java:3259)
at org.hibernate.hql.internal.antlr.HqlBaseParser.additiveExpression(HqlBaseParser.java:2964)
at org.hibernate.hql.internal.antlr.HqlBaseParser.concatenation(HqlBaseParser.java:597)
at org.hibernate.hql.internal.antlr.HqlBaseParser.relationalExpression(HqlBaseParser.java:2730)
at org.hibernate.hql.internal.antlr.HqlBaseParser.equalityExpression(HqlBaseParser.java:2591)
at org.hibernate.hql.internal.antlr.HqlBaseParser.negatedExpression(HqlBaseParser.java:2555)
at org.hibernate.hql.internal.antlr.HqlBaseParser.logicalAndExpression(HqlBaseParser.java:2471)
at org.hibernate.hql.internal.antlr.HqlBaseParser.logicalOrExpression(HqlBaseParser.java:2436)
at org.hibernate.hql.internal.antlr.HqlBaseParser.expression(HqlBaseParser.java:2146)
at org.hibernate.hql.internal.antlr.HqlBaseParser.exprList(HqlBaseParser.java:3994)
at org.hibernate.hql.internal.antlr.HqlBaseParser.identPrimary(HqlBaseParser.java:4278)
I would suggest another approach, which could be simpler. Make JPA leading and use the LocalContainerEntityManagerFactoryBean to configure everything (including what you use now for hibernate). Next use the HibernateJpaSessionFactoryBean to expose the underlying SessionFactory.
This would save you duplicate configuration and would allow you to use a single JpaTransactionManager for all your transactions, however you still would be able to use a SessionFactory or HibernateTemplate if you would.
#Configuration
#EnableJpaRepositories(basePackages = {"packagename","package name" })
#EnableTransactionManagement
public class DataBaseConfiguration {
#Autowired
private Environment env;
#Autowired
#Bean(name = "name")
public FactoryBean<SessionFactory> getSessionFactory(EntityManagerFactory emf) {
HibernateJpaSessionFactoryBean factory = new HibernateJpaSessionFactoryBean();
factory.setEntityManagerFactory(emf);
return factory;
}
**\\ existing hibernate flow**
#Bean(name = "name")
public DataSource getDataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(env.getRequiredProperty("postgres.driver"));
dataSource.setUrl(env.getRequiredProperty("postgres.url);
dataSource.setUsername(env.getRequiredProperty("postgres.username");
dataSource.setPassword(env.getRequiredProperty("postgres.password");
return dataSource;
}
#Bean
public JpaTransactionManager transactionManager() {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setDataSource(getDataSource());
jpaTransactionManager.setEntityManagerFactory(entityManagerFactory());
return jpaTransactionManager;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setDataSource(getDataSource());
Map<String, String> properties = new HashMap<String, String>();
properties.put("hibernate.current_session_context_class", SpringSessionContext.class.getName());
entitytManagerFactory.setJpaPropertyMap(properties);
entityManagerFactory.setPackagesToScan(
"some package",
"some package");
HibernateJpaVendorAdapter va = new HibernateJpaVendorAdapter();
va.setShowSql(env.getProperty("hibernate.show-sql", Boolean.class, true);
va.setDatabasePlatform(env.getProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
entityManagerFactory.setJpaVendorAdapter(va);
return entityManagerFactory;
}
}

working with two transactions, one seem to work but the other does not. why?

I'm working with two transactions on spring which has reference to two entitymanagers and hence two datasources, while using #Transactional("transaction1") it works fine but #Transactional("transaction2") throws out an error saying no active transactions found. following is the piece of code:
#Bean(name = "transaction1")
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactoryBean2()
.getObject());
return transactionManager;
}
#Bean(name = "transaction2")
public PlatformTransactionManager sermaTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager
.setEntityManagerFactory(entityManagerFactoryBean1()
.getObject());
return transactionManager;
}
respective entitymanagers:
#Bean(name = "entitymanager1")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean1() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource1());
entityManagerFactoryBean
.setPackagesToScan(new String[] { this.environment
.getProperty("db.packagesToScan") });
entityManagerFactoryBean
.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter());
Map<String, Object> jpaProperties = new HashMap<String, Object>();
jpaProperties.put("eclipselink.weaving", "false");
jpaProperties.put("eclipselink.logging.level", "INFO");
jpaProperties.put("eclipselink.logging.parameters", "true");
entityManagerFactoryBean.setJpaPropertyMap(jpaProperties);
return entityManagerFactoryBean;
}
#Bean(name = "entitymanager2")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean2() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource2());
entityManagerFactoryBean
.setPackagesToScan(new String[] { this.environment
.getProperty("db.packagesToScan") });
entityManagerFactoryBean
.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter());
Map<String, Object> jpaProperties = new HashMap<String, Object>();
jpaProperties.put("eclipselink.weaving", "false");
jpaProperties.put("eclipselink.logging.parameters", "true");
jpaProperties.put("eclipselink.logging.level", "INFO");
entityManagerFactoryBean.setJpaPropertyMap(jpaProperties);
return entityManagerFactoryBean;
}
respective datasources:
#Bean("datasource1")
public DriverManagerDataSource dataSource1() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource .setDriverClassName(this.environment.getProperty("db.driver"));
dataSource.setUrl(this.environment.getProperty("db.url"));
dataSource.setUsername(this.environment
.getProperty("db.username.abc"));
dataSource.setPassword(this.environment
.getProperty("db.password.abc"));
return dataSource;
}
#Bean("datasource2")
public DriverManagerDataSource dataSource2() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource
.setDriverClassName(this.environment.getProperty("db.driver"));
dataSource.setUrl(this.environment.getProperty("db.url"));
dataSource.setUsername(this.environment.getProperty("db.username"));
dataSource.setPassword(this.environment.getProperty("db.password"));
return dataSource;
}
My genericDaoImpl has the following to link respective entitymanagers:
#PersistenceContext(unitName = "entitymanager1")
private EntityManager em1;
#PersistenceContext(unitName = "entitymanager2")
private EntityManager em2;
when i do #Transactional("transaction1") it works fine, but when i do #Transactional("transaction2") it says no active transactions. and when i dont mention the qualifier for the transactions, error says two transactions found (transaction1, transaction2). any help would be great.
There is an issue with the config:
The problem is that when the beans are initialized like this:
transactionManager.setEntityManagerFactory(entityManagerFactoryBean2().getObject());
The call to entityManagerFactoryBean2() will create a new entity manager factory, and then the #Bean annotation will trigger the creation of another entity manager factory, with the same config.
When you inject the entity manager factory with #Autowiredin a bean, you are injecting the instance created with #Bean, and not the instance passed to the transaction manager.
One warning concerning the config:
The config above allows to do transactions in two separate datasources, but it's not possible with this config to do transactions that span the two databases. For example:
#Service
public class PlanesService {
#PersistenceContext(unitName = "entityManagerFactory1")
private EntityManager em1;
#PersistenceContext(unitName = "entityManagerFactory2")
private EntityManager em2;
#Transactional("transactionManager1")
public Plane savePlanes() {
F14 f14 = new F14("F14","f14");
F16 f16 = new F16("F16","f16");
em1.persist(f14);
em2.persist(f16);
return f14;
}
}
This code will only persist f14, because the only entity manager that has an ongoing transaction is em1 (due to #Transactional("transactionManager1")). The call to em2.persist() will be ignored (em2 can still do reads though).
If you want to do transactions that include both databases you need a JTA transaction manager.
An example of a working config:
This an example of a configuration that fixes the above injection issues:
#Configuration
public class DataSourcesConfig {
#Bean(name = "datasource1")
public DriverManagerDataSource dataSource1() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
...
return dataSource;
}
#Bean(name = "datasource2")
public DriverManagerDataSource dataSource2() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
...
return dataSource;
}
}
#Configuration
public class EntityManagerFactoriesConfiguration {
#Autowired
#Qualifier("datasource1")
private DataSource dataSource1;
#Autowired
#Qualifier("datasource2")
private DataSource dataSource2;
#Bean(name = "entityManagerFactory1")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource1);
entityManagerFactoryBean.setPackagesToScan(new String[] { "your.package.here" });
entityManagerFactoryBean.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
Map<String, Object> jpaProperties = new HashMap<String, Object>();
...
entityManagerFactoryBean.setJpaPropertyMap(jpaProperties);
return entityManagerFactoryBean;
}
#Bean(name = "entityManagerFactory2")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean2() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource2);
entityManagerFactoryBean.setPackagesToScan(new String[] { "your.package.here" });
entityManagerFactoryBean.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
Map<String, Object> jpaProperties = new HashMap<String, Object>();
entityManagerFactoryBean.setJpaPropertyMap(jpaProperties);
...
return entityManagerFactoryBean;
}
}
#Configuration
#EnableTransactionManagement
public class TransactionManagersConfig {
#Autowired
#Qualifier("entityManagerFactory1")
EntityManagerFactory entityManagerFactory1;
#Autowired
#Qualifier("entityManagerFactory2")
EntityManagerFactory entityManagerFactory2;
#Autowired
#Qualifier("datasource1")
private DataSource dataSource1;
#Autowired
#Qualifier("datasource2")
private DataSource dataSource2;
#Bean(name = "transactionManager1")
public PlatformTransactionManager transactionManager1() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory1);
transactionManager.setDataSource(dataSource1);
return transactionManager;
}
#Bean(name = "transactionManager2")
public PlatformTransactionManager transactionManager2() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory2);
transactionManager.setDataSource(dataSource2);
return transactionManager;
}
}
By splitting the config in several #Configuration classes and autowiring them, we ensure that it's always the same beans being used (singletons).
Notice the #EnableTransactionManagement annotation, that enables #Transactional.

Categories

Resources