Spring Advice object [null] using JdkDynamicAopProxy - java

Using an injected spring service service.listAll() i have the exception below :
Caused by: org.springframework.aop.framework.adapter.UnknownAdviceTypeException:
Advice object [null] is neither a supported subinterface of
[org.aopalliance.aop.Advice] nor an [org.springframework.aop.Advisor]
at org.springframework.aop.framework.adapter.DefaultAdvisorAdapterRegistry.getInterceptors(DefaultAdvisorAdapterRegistry.java:88)
at org.springframework.aop.framework.DefaultAdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(DefaultAdvisorChainFactory.java:61)
at org.springframework.aop.framework.AdvisedSupport.getInterceptorsAndDynamicInterceptionAdvice(AdvisedSupport.java:482)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:188)
The Configuration Class
#Configuration
#EnableTransactionManagement
#PropertySource("classpath:system/db.properties")
#ComponentScan("com.agitech.agitech.service")
public class ContextConfig {
protected static Log log = LogFactory.getLog(ContextConfig.class);
#Resource
private Environment env;
public void setEnv(Environment env) {
this.env = env;
}
private final Properties hibProperties() {
Properties properties = new Properties();
properties.put( HIBERNATE_DIALECT, env.getRequiredProperty(HIBERNATE_DIALECT) );
properties.put( HIBERNATE_SHOW_SQL, env.getRequiredProperty(HIBERNATE_SHOW_SQL) );
properties.put( "hibernate.bytecode.provider", "javassist" );
properties.put( "hibernate.listeners.envers.autoRegister", false );
return properties;
}
#Bean(name ="dataSource")
public DataSource dataSource() {
return dataSource( env.getRequiredProperty(DATABASE_URL) );
}
#Bean(name ="mgrJPA")
public JpaTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
public DataSource dataSource(String url) {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getRequiredProperty(DATABASE_DRIVER));
dataSource.setUrl(url);
dataSource.setUsername(env.getRequiredProperty(DATABASE_USERNAME));
dataSource.setPassword(env.getRequiredProperty(DATABASE_PASSWORD));
return dataSource;
}
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
DataSource dataSource, String unitName, String... packages) {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean =
new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource( dataSource );
entityManagerFactoryBean.setPersistenceProviderClass( HibernatePersistence.class );
entityManagerFactoryBean.setPackagesToScan( packages );
entityManagerFactoryBean.setJpaProperties( hibProperties() );
entityManagerFactoryBean.setPersistenceUnitName( unitName );
return entityManagerFactoryBean;
}
#Bean(name ="mgrEMF")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
return entityManagerFactory(dataSource(), "mgrUnit", Contract.class.getPackage().getName());
}
}

The problem was that i didn't annotade the listAll procedure of the Service with the #Transactional annotation, but i thought that given that its just select query i dont need a transaction

Most probably you are missing <context:annotation-config/> in your Spring configuration file therefore interceptor doesn't get injected into PointcutAdvisor

Related

Can I apply #Bean annotation to the functions as well?

I am new to Spring Java and a little bit confused about where to use #Bean annotation.
See the following code for instance:
#Configuration
#EnableTransactionManagement
public class HibernateConfig {
#Autowired
private Environment env;
private final Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
properties.put("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
properties.put("hibernate.format_sql", env.getProperty("hibernate.format_sql"));
properties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
properties.put("hibernate.max_fetch_depth", env.getProperty("hibernate.max_fetch_depth"));
properties.put("hibernate.cache.use_second_level_cache",env.getProperty("hibernate.cache.use_second_level_cache"));
properties.put("hibernate.cache.use_minimal_puts", env.getProperty("hibernate.cache.use_minimal_puts"));
properties.put("hibernate.connection.release_mode", env.getProperty("hibernate.connection.release_mode"));
properties.put("hibernate.cache.use_query_cache",env.getProperty("hibernate.cache.use_query_cache"));
return properties;
}
#Bean
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(env.getProperty("spring.datasource.driver-class-name"));
dataSource.setUrl(env.getProperty("spring.datasource.url"));
dataSource.setUsername(env.getProperty("spring.datasource.username"));
dataSource.setPassword(env.getProperty("spring.datasource.password"));
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean lcemfb = new LocalContainerEntityManagerFactoryBean();
lcemfb.setJpaVendorAdapter(getJpaVendorAdapter());
lcemfb.setDataSource(dataSource());
lcemfb.setPersistenceUnitName("entityManagerFactory");
lcemfb.setPackagesToScan("com.sha.microservicecoursemanagement.model");
lcemfb.setJpaProperties(hibernateProperties());
return lcemfb;
}
#Bean
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager(entityManagerFactory().getObject());
}
#Bean
public JpaVendorAdapter getJpaVendorAdapter() {
JpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
return adapter;
}
}
Now in the above code, dataSource, LocalContainerEntityManagerFactoryBean, PlatformTransactionManager and JpaVendorAdapter these functions have attribute as #Bean.
Now, what I have read on the internet is #Bean can only be given to the class types, not to the functions.
Can someone clear my confusion, please?
The #Bean can be applied to method, if the class is annotated with #Configuration. Refer this link
Following example where I am using #Bean
#Configuration
public class ClientDataSourceConfig {
#Value("${spring.datasource.driver}")
private String driver;
#Value("${spring.datasource.url}")
private String url;
#Value("${spring.datasource.username}")
private String username;
#Value("${spring.datasource.password}")
private String password;
public ClientDataSourceRouter dataSource;
#Bean(name = "getDataSource")
public ClientDataSourceRouter getDataSource() throws Exception {
dataSource = new ClientDataSourceRouter();
dataSource.init(driver, url, username, password);
return dataSource;
}
}
Yes #Bean can only be given to the Class types. In your functions you can see that you are returning class instances such as DataSource, LocalContainerEntityManagerFactoryBean etc. You are not adding #Bean annotation to the function. what you are doing is, telling Spring to return a bean from that class. for an example,
#Bean
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(env.getProperty("spring.datasource.driver-class-name"));
dataSource.setUrl(env.getProperty("spring.datasource.url"));
dataSource.setUsername(env.getProperty("spring.datasource.username"));
dataSource.setPassword(env.getProperty("spring.datasource.password"));
return dataSource;
}
In here you are telling Spring to return a DataSource Object. Unless if you have #Bean annocation , you can't autowire the datasource object. Therefor #Bean allow you to use #Autowired DataSource dataSource;. These beans are managed by Spring IoC container.

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

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;
}
}

Run SpringLiquibase BEFORE Hibernate

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.

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