I'm injecting #PersistenceContext into my DAO classes like
#PersistenceContext
private EntityManager em;
#PersistenceContext is configured in my project via spring xml configuration. I'm trying to convert the following spring xml configuration to annotations
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:packagesToScan="com.company.myagentmonitor.model"
p:dataSource-ref="companyDataSource"
>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="false" />
<property name="showSql" value="true" />
</bean>
</property>
</bean>
<!-- Transactions -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
I'm trying to convert this config to #Beans I can autowire in my DAO classes. What I have currently is not working
#Bean
public EntityManagerFactory entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setDataSource(companyDataSource());
entityManagerFactory.setPackagesToScan(new String[] { "com.company.myagentmonitor.model" });
entityManagerFactory.setJpaVendorAdapter(jpaVendorAdapter());
return entityManagerFactory.getObject();
}
#Bean
JpaTransactionManager transactionManager(final EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory());
return transactionManager;
}
#Bean
HibernateJpaVendorAdapter jpaVendorAdapter(){
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setGenerateDdl(false);
jpaVendorAdapter.setShowSql(true);
return jpaVendorAdapter;
}
I basically get a null pointer exception at runtime. I think it has something to do with the way I'm creating or using EntityManagerFactory.
Caused by: java.lang.NullPointerException: null
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.initProxyClassLoa
der(SharedEntityManagerCreator.java:199) ~[spring-orm-4.3.6.RELEASE.jar!/:4.3.6.RELEASE]
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.<init>(SharedEnti
tyManagerCreator.java:191) ~[spring-orm-4.3.6.RELEASE.jar!/:4.3.6.RELEASE]
at org.springframework.orm.jpa.SharedEntityManagerCreator.createSharedEntityManager(SharedEntityManagerCreator.j
ava:163) ~[spring-orm-4.3.6.RELEASE.jar!/:4.3.6.RELEASE]
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.resolveEntityMa
nager(PersistenceAnnotationBeanPostProcessor.java:719) ~[spring-orm-4.3.6.RELEASE.jar!/:4.3.6.RELEASE]
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.getResourceToIn
ject(PersistenceAnnotationBeanPostProcessor.java:680) ~[spring-orm-4.3.6.RELEASE.jar!/:4.3.6.RELEASE]
at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:
169) ~[spring-beans-4.3.6.RELEASE.jar!/:4.3.6.RELEASE]
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) ~[spring-bea
ns-4.3.6.RELEASE.jar!/:4.3.6.RELEASE]
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.postProcessPropertyValues(Persiste
nceAnnotationBeanPostProcessor.java:354) ~[spring-orm-4.3.6.RELEASE.jar!/:4.3.6.RELEASE]
... 64 common frames omitted
Can someone take a look? Not sure what I'm doing wrong. Thank you!
change this
#Bean
public EntityManagerFactory entityManagerFactory() {
return entityManagerFactory.getObject();
}
to this
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
return entityManagerFactory;
}
and in your transactionManager you also change
transactionManager.setEntityManagerFactory(entityManagerFactory());
to
jpaTransactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
First off all you shouldn't be calling getObject in your entityManagerMethod() let Spring handle that for you. Just return the LocalContainerEntityManagerFactoryBean.
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setDataSource(companyDataSource());
entityManagerFactory.setPackagesToScan(new String[] { "com.company.myagentmonitor.model" });
entityManagerFactory.setJpaVendorAdapter(jpaVendorAdapter());
return entityManagerFactory;
}
The LocalContainerEntityManagerFactoryBean implements several interfaces which spring detects and as such will register callbacks for (init and destruction) and also inject some needed dependencies.
Next where you need the EntityManagerFactory just inject it into your method instead of calling entityManagerFactory().
#Bean
JpaTransactionManager transactionManager(final EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
Related
In our Spring 4 application, we currently configure database connection in applicationContext.xml :
<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
<property name="poolName" value="springHikariCP" />
<property name="dataSourceClassName" value="org.postgresql.ds.PGSimpleDataSource" />
<property name="maximumPoolSize" value="10" />
<property name="idleTimeout" value="30000" />
<property name="dataSourceProperties">
<props>
<prop key="url">jdbc:postgresql://google/mydb?cloudSqlInstance=project:region:myinstance&socketFactory=com.google.cloud.sql.postgres.SocketFactory</prop>
<prop key="user">postgres</prop>
<prop key="password">password</prop>
</props>
</property>
</bean>
Instead on defining this in applicationContext.xml, can I define the database configuration in a class such as the following:
HikariConfig config = new HikariConfig();
config.setJdbcUrl(JDBC_URL);
config.setUsername(DB_USER);
config.setPassword(DB_PASS);
....
Is it possible to do this?
You can define properties on HikariDataSource
public DataSource getDataSource(){
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(JDBC_URL);
dataSource.setUsername(DB_USER);
dataSource.setPassword("DB_PASS);
return dataSource;
}
public class HikariDataSource extends HikariConfig implements DataSource, Closeable
Then declare your method as a #Bean in #Configuration class
#Bean
public DataSource dataSource() {
return DataSourceClass.getDataSource();
}
This is a king of more general solution, not specific for your DataSource but it can be useful:
#Configuration
public class DataSourceConfig {
#Bean
public DataSource getDataSource() {
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.driverClassName("org.h2.Driver");
dataSourceBuilder.url("jdbc:h2:mem:test");
dataSourceBuilder.username("SA");
dataSourceBuilder.password("");
return dataSourceBuilder.build();
}
}
What you actually need is a DataSource bean (in XML you define a bean right?) and also put it into #Configuration class so the Spring could pick it up.
What you put in that method should return pre-configured for your specific case data source.
Also, you could try something like this:
#Bean
public DataSource dataSource() throws SQLException {
HikariConfig config = new HikariConfig("/hikari.properties");
HikariDataSource dataSource = new HikariDataSource(config);
return dataSource;
}
But then you need hikari.properties file in the classpath. Example:
driverClassName=com.mysql.jdbc.Driver
jdbcUrl=jdbc:mysql://localhost:3306/myDb
connectionTestQuery=SELECT 1
maximumPoolSize=20
username=...
password=...
Remember to keep #Bean in #Configuration :)
Simply annotate a class with #Configuration, making sure that this class is scanned by Spring when the application starts.
Within this class declare a datasource as #Bean in this way:
#Configuration
public class DataSourceConfig {
// More code...
#Bean(name="datasource")
public DataSource dataSource() {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setDriverClassName(org.postgresql.Driver.class.getName());
hikariConfig.setJdbcUrl("jdbc:postgresql://....");
hikariConfig.setUsername("postgres");
hikariConfig.setPassword("password");
hikariConfig.setMaximumPoolSize(10);
hikariConfig.setIdleTimeout(30000);
hikariConfig.setPoolName("springHikariCP");
HikariDataSource hikariDataSource = new HikariDataSource(hikariConfig);
return hikariDataSource;
}
// More code...
}
And that's all.
I am fairly new to the Spring Framework and have been having some trouble setting up the project I am currently working on. I need to be able to connect to two different databases one being MongoDB and the other being MSSQL. I am using JPA to connect to the MSSQL.
The problem that I am encountering is that it appears to be trying to make calls to the Mongo database when I want it to make calls to the MSSQL and I'm not really sure how to tell it what to read from. I have seen the posts advising to use the #Qualifier annotation to direct it to the correct implementation, but I don't think that that will work for my case.
#RestController
#RequestMapping("/software")
public class SoftwareEndpoint {
#Autowired
SoftwareRepository repo;
/**********************************************************************************
********************************MSSQL calls****************************************
***********************************************************************************/
#RequestMapping(value="/all",method=RequestMethod.GET,produces=MediaType.APPLICATION_JSON)
String getAllSoftware(){
System.out.println("Here1");
List<Software> allSoftware = (List<Software>) repo.findAll();
System.out.println("Here2");
//rest of method and class
Above shows a snippet of my controller class that has an instance of my SoftwareRepository. I also print to the out stream before and after the db call.
The out stream only shows "Here1", goes on to print out this line:
2016-10-04 07:35:39.810 INFO 4236 --- [nio-8080-exec-2] org.mongodb.driver.cluster : No server chosen by ReadPreferenceServerSelector{readPreference=primary} from cluster description ClusterDescription{type=UNKNOWN, connectionMode=SINGLE, all=[ServerDescription{address=localhost:27017, type=UNKNOWN, state=CONNECTING, exception={com.mongodb.MongoSocketOpenException: Exception opening socket}, caused by {java.net.ConnectException: Connection refused: connect}}]}. Waiting for 30000 ms before timing out
and then throws an exception on timeout.
I do not have a mongo instance running locally, however there will be on where the application is being deployed, but I don't believe that this is the problem because on hitting that endpoint, it shouldn't be making a call to the Mongo database, it should be trying to reach out to MSSQL.
TLDR: How do I specify which database implementation for Spring to use for a specific repository or database call?
You can connect to different databases in spring based on the configuration in context.
The below code is for connecting to MySql and Mongo DB. You can substitute MySql with MSSQL provided you have the JDBC for it. Check http://jdbforms.sourceforge.net/UsersGuide/html/ch20s02.html for what the properties for JDBC connection mean.
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg ref="mySqldataSource" /> <!-- Change the datasource to MSSQL-->
</bean>
<bean id="mySqldataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="removeAbandoned">
<value>true</value>
</property>
<property name="removeAbandonedTimeout">
<value>30</value>
</property>
<property name="driverClassName">
<value>MSSQL_DRIVER_CLASS_NAME</value>
</property>
<property name="url">
<value>MSSQL_DATABASE_URL</value>
</property>
<property name="username">
<value>MSSQL_DB_USER_NAME</value>
</property>
<property name="password">
<value>MSSQL_DB_PASSWORD</value>
</property>
<property name="maxIdle">
<value>10</value>
</property>
<property name="maxActive">
<value>10</value>
</property>
<property name="maxWait">
<value>100000</value>
</property>
<property name="testOnBorrow">
<value>false</value>
</property>
<property name="testWhileIdle">
<value>false</value>
</property>
<property name="timeBetweenEvictionRunsMillis">
<value>60000</value>
</property>
<property name="minEvictableIdleTimeMillis">
<value>60000</value>
</property>
<property name="numTestsPerEvictionRun">
<value>1</value>
</property>
<property name="defaultTransactionIsolation" value="1" />
<property name="poolPreparedStatements" value="true" />
<property name="maxOpenPreparedStatements" value="1" />
</bean>
<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"></bean>
Below is for connecting to mongodb
<mongo:db-factory dbname="mongoDbName" host="mongoServer" port="mongoPort"/>
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
</bean>
<mongo:repositories base-package="com.test.repoPackage"/> <!-- Package containing the mongo repository interfaces -->
Now you can use the repositories provided by spring.
EDIT 1: Suppose name of config is springConfig.properties. In the above example for the properties dbname, host and port in mongo:db-factory, you would want the values to be configured in springConfig.properties. So lets name them below:
mongoServer = xxx.xx.xxx.xxx
mongoPort = 27017
mongoDb = testDb
Now the context file needs to be modified to import the springConfig.properties. this is done as below in the context file:
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" >
<property name="locations" >
<list>
<value>classpath:/log4j.properties</value>
<value>classpath:/springConfig.properties</value>
</list>
</property>
</bean>
The bean mongo:db-factory would now look like:
<mongo:db-factory dbname="${mongoDb}" host="${mongoServer}" port="${mongoPort}"/>
Notice that the "keys" from config (dbname, host and port) are represented insde ${}. This will replace with values in config for the keys.
You need to have two separated config for JPA. Don't forget to disable JPA auto configuration.
#SpringBootApplication(
exclude={
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class
}
)
Below is example for two different sql database. Could be easily adapted for your needs (when second datasource is mongo).
First one:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "sellitEntityManagerFactory",
transactionManagerRef = "sellitTransactionManager",
basePackages = { "co.sellit.core.api.repository.sellit" }
)
public class JpaSellitConfig {
#Bean
#ConfigurationProperties(prefix="spring.datasource.sellit")
public DataSourceProperties sellitDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#ConfigurationProperties(prefix="spring.hikaricp.sellit")
public HikariConfig sellitHikariConfig() {
HikariConfig hikariConfig = new HikariConfig();
return hikariConfig;
}
#Bean
public DataSource sellitDataSource(
#Qualifier("sellitHikariConfig") HikariConfig sellitHikariConfig,
#Qualifier("sellitDataSourceProperties") DataSourceProperties sellitDataSourceProperties) {
sellitHikariConfig.setDriverClassName(sellitDataSourceProperties.getDriverClassName());
sellitHikariConfig.setJdbcUrl(sellitDataSourceProperties.getUrl());
sellitHikariConfig.setUsername(sellitDataSourceProperties.getUsername());
sellitHikariConfig.setPassword(sellitDataSourceProperties.getPassword());
sellitHikariConfig.setConnectionTestQuery("SELECT 1");
HikariDataSource hikariDataSource = new HikariDataSource(sellitHikariConfig);
return hikariDataSource;
}
#Bean
#ConfigurationProperties(prefix="spring.jpa.sellit")
public JpaVendorAdapter sellitJpaVendorAdapter() {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
return jpaVendorAdapter;
}
#Bean
#Autowired
public EntityManagerFactory sellitEntityManagerFactory(
#Qualifier("sellitDataSource") DataSource sellitDataSource,
#Qualifier("sellitJpaVendorAdapter") JpaVendorAdapter sellitJpaVendorAdapter) {
LocalContainerEntityManagerFactoryBean lemf = new LocalContainerEntityManagerFactoryBean();
lemf.setDataSource(sellitDataSource);
lemf.setJpaVendorAdapter(sellitJpaVendorAdapter);
lemf.setPackagesToScan("co.sellit.core.api.entity.sellit");
lemf.setPersistenceUnitName("sellitPersistenceUnit");
lemf.afterPropertiesSet();
return lemf.getObject();
}
#Bean
#Autowired
public EntityManager sellitDataSourceEntityManager(
#Qualifier("sellitEntityManagerFactory") EntityManagerFactory sellitEntityManagerFactory) {
return sellitEntityManagerFactory.createEntityManager();
}
#Bean
#Autowired
#Qualifier("sellitTransactionManager")
public PlatformTransactionManager sellitTransactionManager(
#Qualifier("sellitEntityManagerFactory") EntityManagerFactory sellitEntityManagerFactory) {
return new JpaTransactionManager(sellitEntityManagerFactory);
}
}
Second one:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "ofEntityManagerFactory",
transactionManagerRef = "ofTransactionManager",
basePackages = { "co.sellit.core.api.repository.openfire" }
)
public class JpaOpenfireConfig {
#Bean
#ConfigurationProperties(prefix="spring.datasource.openfire")
public DataSourceProperties ofDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#ConfigurationProperties(prefix="spring.hikaricp.openfire")
public HikariConfig ofHikariConfig() {
HikariConfig hikariConfig = new HikariConfig();
return hikariConfig;
}
#Bean
public DataSource ofDataSource(
#Qualifier("ofHikariConfig") HikariConfig ofHikariConfig,
#Qualifier("ofDataSourceProperties") DataSourceProperties ofDataSourceProperties) {
ofHikariConfig.setDriverClassName(ofDataSourceProperties.getDriverClassName());
ofHikariConfig.setJdbcUrl(ofDataSourceProperties.getUrl());
ofHikariConfig.setUsername(ofDataSourceProperties.getUsername());
ofHikariConfig.setPassword(ofDataSourceProperties.getPassword());
ofHikariConfig.setConnectionTestQuery("SELECT 1");
HikariDataSource hikariDataSource = new HikariDataSource(ofHikariConfig);
return hikariDataSource;
}
#Bean
#ConfigurationProperties(prefix="spring.jpa.openfire")
public JpaVendorAdapter ofJpaVendorAdapter() {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
return jpaVendorAdapter;
}
#Bean
#Autowired
public EntityManagerFactory ofEntityManagerFactory(
#Qualifier("ofDataSource") DataSource ofDataSource,
#Qualifier("ofJpaVendorAdapter") JpaVendorAdapter ofJpaVendorAdapter) {
LocalContainerEntityManagerFactoryBean lemf = new LocalContainerEntityManagerFactoryBean();
lemf.setDataSource(ofDataSource);
lemf.setJpaVendorAdapter(ofJpaVendorAdapter);
lemf.setPackagesToScan("co.sellit.core.api.entity.openfire");
lemf.setPersistenceUnitName("ofPersistenceUnit");
lemf.afterPropertiesSet();
return lemf.getObject();
}
#Bean
#Autowired
public EntityManager ofDataSourceEntityManager(
#Qualifier("ofEntityManagerFactory") EntityManagerFactory ofEntityManagerFactory) {
return ofEntityManagerFactory.createEntityManager();
}
#Bean
#Autowired
#Qualifier("ofTransactionManager")
public PlatformTransactionManager ofTransactionManager(
#Qualifier("ofEntityManagerFactory") EntityManagerFactory ofEntityManagerFactory) {
return new JpaTransactionManager(ofEntityManagerFactory);
}
}
So repositories from package co.sellit.core.api.repository.sellit will use sellit db
But repositories from package co.sellit.core.api.repository.openfire will use openfire database.
UPDATE (xml config version)
<bean id="openfireDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/openfire?characterEncoding=UTF-8" />
</bean>
<bean id="sellitDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/sellit?characterEncoding=UTF-8" />
</bean>
<bean id="openfireSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="openfireDataSource" />
<property name="packagesToScan" value="co.sellit.core.api.repository.openfire" />
<property name="hibernateProperties">
<props>
...
</props>
</property>
</bean>
<bean id="sellitSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="sellitDataSource" />
<property name="packagesToScan" value="co.sellit.core.api.repository.sellit" />
<property name="hibernateProperties">
<props>
...
</props>
</property>
</bean>
<bean id="openfireTxnManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="openfireSessionFactory" />
</bean>
<bean id="sellitTxnManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sellitSessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="openfireTxnManager" />
<tx:annotation-driven transaction-manager="sellitTxnManager" />
I upgrade Hibernate from 3.6 to 4.2.21 in a Spring 3.2.15 based application.
This my configuration
#Configuration
#ComponentScan(basePackageClasses = { myClass.class })
#EnableAspectJAutoProxy(proxyTargetClass = true)
#EnableTransactionManagement
#EnableCaching
public class JavaConfigSpring {
// ############ SESSION FACTORY ############ //
#Bean(name = "sessionFactory")
#Autowired
#Primary
public LocalSessionFactoryBean localSessionFactoryBean() {
LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
localSessionFactoryBean.setConfigLocation(new ClassPathResource("hibernate.cfg.xml"));
return localSessionFactoryBean;
}
// ############ TRANSACTION MANAGER ############ //
#Bean(name = "transactionManager")
#Autowired
#Primary
public HibernateTransactionManager hibernateTransactionManager(#Qualifier("sessionFactory") SessionFactory sessionFactory) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory);
txManager.setRollbackOnCommitFailure(true);
return txManager;
}
#Bean
public BeanPostProcessor persistenceTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
}
and my hibernate configuration file :
<hibernate-configuration>
<session-factory>
<property name="show_sql">false</property>
<property name="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</property>
<property name="hibernate.connection.driver_class">oracle.jdbc.OracleDriver</property>
<property name="hibernate.connection.username">user</property>
<property name="hibernate.connection.password">pass</property>
<property name="hibernate.connection.url">jdbc:oracle:thin:#xxx.xx.xxx.xx:xxx:MyBase</property>
<property name="hibernate.default_batch_fetch_size">10</property>
<property name="hibernate.current_session_context_class">thread</property>
<property name="hibernate.connection.release_mode">after_transaction</property>
<!--Pool C3PO-->
<property name="hibernate.c3p0.acquire_increment">2</property>
<property name="hibernate.c3p0.idle_test_period">120</property>
<property name="hibernate.c3p0.timeout">1800</property>
<property name="hibernate.c3p0.max_size">2</property>
<property name="hibernate.c3p0.max_statements">0</property>
<property name="hibernate.c3p0.min_size">2</property>
<mapping resource="com/toto/persistence/hibernate/hbm/Profiles.hbm.xml"/>
<mapping resource="com/toto/persistence/hibernate/hbm/Role.hbm.xml"/>
</session-factory>
And also I put #Transactional in the right places, this is where the error happens
#Component
#Transactional
public class LabelsFactory {
#Autowired
SessionFactory sessionFactory;
public List<Labels> getLabels() {
Criteria crit = sessionFactory.getCurrentSession().createCriteria(Labels.class);
return crit.list();
}
}
But when I start the application I got this error :
org.hibernate.HibernateException: createCriteria is not valid without active transaction
So apparently the transaction manager is not working
I tried to remove :
<property name="hibernate.current_session_context_class">thread</property>
But then I got this new error :
org.hibernate.HibernateException: No Session found for current thread
What is wrong with my configuration ?
Hi I have a legacy code which gets its jdbc connections through
DataSource.getConnection()
The DataSource is bounded to Jndi namespace.,
Suppose that I have a function which gets its connections like that:
foo(){
...
Connection con = DataSource.getConnection()
...
}
And I want to run this foo method into a well defined spring transaction. How would I do that ?
I have used TransactionAwareDataSourceProxy and It worked quite well before I move onto something like JPA
At first I could synronize foo's transaction with my spring transaction with this configuration.
<tx:annotation-driven transaction-manager="txManager"/>
<bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations"><list><value>classpath:/db.properties</value></list></property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<constructor-arg ref="dbcpDataSource"/>
</bean>
<bean id="dbcpDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName"><value>oracle.jdbc.driver.OracleDriver</value></property>
<property name="url"><value>jdbc:oracle:thin:#${jdbc.url}:1521:${jdbc.db}</value></property>
<property name="username"><value>${jdbc.username}</value></property>
<property name="password"><value>${jdbc.password}</value></property>
<!-- <property name="defaultAutoCommit"><value>true</value></property> -->
</bean>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
After that I move onto JPA using LocalContainerEntityManagerFactoryBean and JpaTransactionManager.
package setup;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
#Configuration
#EnableJpaRepositories
public class SpringContextConfiguration {
#Bean
public TestsSetup testSetup(){
return new TestsSetup();
}
#Bean
public TransactionAwareDataSourceProxy dataSource(){
TransactionAwareDataSourceProxy tp = new TransactionAwareDataSourceProxy();
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("oracle.jdbc.driver.OracleDriver");
ds.setUrl("jdbc:oracle:thin:#a.a.a.a:port:some");
ds.setUsername("user");
ds.setPassword("paswd");
ds.setDefaultAutoCommit(true);
tp.setTargetDataSource(ds);
return tp;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, JpaVendorAdapter jpaVendorAdapter) {
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(dataSource);
lef.setJpaVendorAdapter(jpaVendorAdapter);
lef.setPackagesToScan("setup");
return lef;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(true);
hibernateJpaVendorAdapter.setGenerateDdl(false);
hibernateJpaVendorAdapter.setDatabase(Database.ORACLE);
return hibernateJpaVendorAdapter;
}
#Bean
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager();
}
}
Now my legacy code blocks do not synchronize with my managed transactions. How can I overcome this problem. Any tools or comments is greatly appreciated.
EDIT
Inject the DataSource and EntityManagerFactory into the JpaTransactionManager bean. See class-level comment # http://docs.spring.io/spring/docs/3.2.5.RELEASE/javadoc-api/org/springframework/orm/jpa/JpaTransactionManager.html for more info.
The above configuration is actually correct and the underlying legacy functions is being called inside the created spring transaction. What I miss is that on the upper layer I needed to flush the underlying session to the datastore. When I did that the legacy transaction begins to aware of the changed data and everything works perfect and also the transactions can be rolled back.
I flush my session to the datastore with this :
#Autowired
PlatformTransactionManager pt;
TransactionDefinition td = new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_MANDATORY);
pt.getTransaction(td).flush();
I Moving from Spring MVC XML files to javaconfig. I am really at a lost with my database XML file. I don't know how to get Hibernate4 working and my JBoss JNDI Datasource working. Can someone please tell me how to make the javaconfig class work like this XML..
Here is my database.xml:
?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-3.0.xsd">
<context:property-placeholder location="classpath:app.properties" />
<context:component-scan base-package="org.uftwf" />
<tx:annotation-driven transaction-manager="hibernateTransactionManager" />
<jee:jndi-lookup id="dataSource" jndi-name="java:jboss/datasources/mySQLDB"
expected-type="javax.sql.DataSource" />
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="annotatedClasses">
<list>
<value>org.uftwf.inquiry.model.MemberInquiryInformation</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.use_sql_comments">${hibernate.use_sql_comments}</prop>
<prop key="format_sql">${format_sql}</prop>
</props>
</property>
</bean>
<bean id="hibernateTransactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>
Here is my javaconfig class:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages= {"org.uftwf.inquiry"})
#ImportResource("/WEB-INF/spring/root-config.xml")
public class WebMVCConfig extends WebMvcConfigurerAdapter {
private static final String MESSAGE_SOURCE = "/WEB-INF/classes/messages";
private static final Logger logger = LoggerFactory.getLogger(WebMVCConfig.class);
#Value("${jdbc.driverClassName}")
private String driverClassName;
#Value("${jdbc.url}")
private String url;
#Value("${jdbc.username}")
private String username;
#Value("${jdbc.password}")
private String password;
#Value("${hibernate.dialect}")
private String hibernateDialect;
#Value("${hibernate.show_sql}")
private String hibernateShowSql;
#Value("${hibernate.hbm2ddl.auto}")
private String hibernateHbm2ddlAuto;
#Bean
public PropertyPlaceholderConfigurer getPropertyPlaceholderConfigurer()
{
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
ppc.setLocation(new ClassPathResource("application.properties"));
ppc.setIgnoreUnresolvablePlaceholders(true);
return ppc;
}
#Bean()
public DataSource getDataSource()
{
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName(driverClassName);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
return ds;
}
#Bean
public LocalSessionFactoryBean sessionFactory()
{
LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean();
factoryBean.setDataSource(getDataSource());
factoryBean.setHibernateProperties(getHibernateProperties());
factoryBean.setPackagesToScan("org.uftwf.inquiry.model");
return factoryBean;
}
#Bean
public Properties getHibernateProperties()
{
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.dialect", hibernateDialect);
//hibernateProperties.setProperty("hibernate.show_sql", "true");
//hibernateProperties.setProperty("hibernate.format_sql", "true");
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", "update");
hibernateProperties.setProperty("javax.persistence.validation.mode", "none");
//Audit History flags
hibernateProperties.setProperty("org.hibernate.envers.store_data_at_delete", "true");
hibernateProperties.setProperty("org.hibernate.envers.global_with_modified_flag", "true");
return hibernateProperties;
}
#Bean
#Autowired
public HibernateTransactionManager transactionManager(SessionFactory sessionFactory)
{
HibernateTransactionManager htm = new HibernateTransactionManager();
htm.setSessionFactory(sessionFactory);
return htm;
}
#Bean
public ViewResolver resolver() {
UrlBasedViewResolver url = new UrlBasedViewResolver();
url.setPrefix("/WEB-INF/view/");
url.setViewClass(JstlView.class);
url.setSuffix(".jsp");
return url;
}
#Bean(name = "messageSource")
public MessageSource configureMessageSource() {
logger.debug("setting up message source");
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename(MESSAGE_SOURCE);
messageSource.setCacheSeconds(5);
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
#Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver lr = new SessionLocaleResolver();
lr.setDefaultLocale(Locale.ENGLISH);
return lr;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
logger.debug("setting up resource handlers");
registry.addResourceHandler("/resources/").addResourceLocations("/resources/**");
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
logger.debug("configureDefaultServletHandling");
configurer.enable();
}
#Override
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(new LocaleChangeInterceptor());
}
#Bean
public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
SimpleMappingExceptionResolver b = new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.put("org.springframework.web.servlet.PageNotFound", "p404");
mappings.put("org.springframework.dao.DataAccessException", "dataAccessFailure");
mappings.put("org.springframework.transaction.TransactionException", "dataAccessFailure");
b.setExceptionMappings(mappings);
return b;
}
#Bean
public RequestTrackerConfig requestTrackerConfig()
{
RequestTrackerConfig tr = new RequestTrackerConfig();
tr.setPassword("Waiting#$");
tr.setUrl("https://uftwfrt01-dev.uftmasterad.org/REST/1.0");
tr.setUser("root");
return tr;
}
}
I think the parts I am missing are the following but please overcheck my class
<context:property-placeholder location="classpath:app.properties" />
<context:component-scan base-package="org.uftwf" />
<tx:annotation-driven transaction-manager="hibernateTransactionManager" />
<jee:jndi-lookup id="dataSource" jndi-name="java:jboss/datasources/mySQLDB"
expected-type="javax.sql.DataSource" />
For
<tx:annotation-driven transaction-manager="hibernateTransactionManager" />
annotate your Configuration class, WebMVCConfig, with
#EnableTransactionManagement
For
<context:component-scan base-package="org.uftwf" />
Add the package String to your #ComponentScan field basePackages
For
<context:property-placeholder location="classpath:app.properties" />
annotate your Configuration class with
#PropertySource(value = "classpath:app.properties")
and make your PropertyPlaceholderConfigurer #Bean method static.
For
<jee:jndi-lookup id="dataSource" jndi-name="java:jboss/datasources/mySQLDB"
expected-type="javax.sql.DataSource" />
I think you can do
#Bean
public DataSource dataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:jboss/datasources/mySQLDB");
}
Instead of autowiring your session factory, just call your #Bean method
#Bean
public HibernateTransactionManager transactionManager()
{
HibernateTransactionManager htm = new HibernateTransactionManager();
htm.setSessionFactory(sessionFactory());
return htm;
}