Declarative spring transactions not being committed - java

I have the following service classes:
public class UserLoginServiceImpl implements UserLoginService {
private UserDAO dao;
public UserLoginServiceImpl(UserDAO dao) {this.dao = dao; }
public User login(String userName, String password) {
User u = dao.findUserByCredentials(userName, password);
if (u == null) {
throw new UserNotFoundException();
}
return u;
}
public class UserManagementServiceImpl implements UserManagementService {
private UserDAO dao;
public UserManagementServiceImpl(UserDAO dao) { this.dao = dao; }
public void createUser(User u) {
dao.save(u);
}
}
My service interfaces reside under com.mysystem.services package and my service implementations under com.mysystem.services.impl package.
I am using hibernate as my jpa implementation and declarative transactions in spring. My configuration file is the following:
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="serviceMethod" expression="execution(* com.mysystem.services.impl.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod"/>
</aop:config>
<!--
Beans
-->
<bean id="userDao" class="com.mysystem.dao.UserDAOImpl">
<constructor-arg ref="entityManagerFactory" />
</bean>
<bean id="userLoginService" class="com.mysystem.services.impl.UserLoginServiceImpl">
<constructor-arg ref="userDao" />
</bean>
<bean id="userManagementService" class="com.mysystem.services.impl.UserManagementServiceImpl">
<constructor-arg ref="userDao" />
</bean>
Lastly, somewhere in my code i run the following:
UserManagementService userManagementService = context.getBean(UserManagementService.class);
userManagementService.createUser(new User("test", "test"));
UserLoginService userLogginService = context.getBean(UserLoginService.class);
User user = userLogginService.login("test", "test");
which results in the login() method throwing an UserNotFoundException because it cannot find the user inserted by createUser() previously which means that hibernate will not flush the session between method calls. I can verify this form the console output (i can see hibernate's create and select statements but not insert).
Why isn't the transaction committed soon after the call to createUser()? What am i doing wrong here?
EDIT:
Using the #Transactional annotation instead of declarative transactions works. Using #Transactional however is not an option for me. I need to implement declarative transactions.
Below is the spring's debug output:
13:03:18,174 DEBUG [main] jpa.JpaTransactionManager - Creating new transaction with name [com.mysystem.services.impl.UserManagementServiceImpl.createUser]: PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT
13:03:18,175 DEBUG [main] jpa.JpaTransactionManager - Opened new EntityManager [org.hibernate.ejb.EntityManagerImpl#1af6a711] for JPA transaction
Hibernate:
select
nextval ('hibernate_sequence')
13:03:18,255 DEBUG [main] jpa.JpaTransactionManager - Initiating transaction commit
13:03:18,255 DEBUG [main] jpa.JpaTransactionManager - Committing JPA transaction on EntityManager [org.hibernate.ejb.EntityManagerImpl#1af6a711]
13:03:18,256 DEBUG [main] jpa.JpaTransactionManager - Closing JPA EntityManager [org.hibernate.ejb.EntityManagerImpl#1af6a711] after transaction
13:03:18,263 DEBUG [main] jpa.JpaTransactionManager - Creating new transaction with name [com.mysystem.services.impl.UserLoginServiceImpl.login]: PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT
13:03:18,263 DEBUG [main] jpa.JpaTransactionManager - Opened new EntityManager [org.hibernate.ejb.EntityManagerImpl#1533badd] for JPA transaction
Hibernate:
/*
from
com.mysystem.domain.User u
where
u.userName = :userName
and u.password = :password */ select
user0_.id as id0_,
user0_.password as password0_,
user0_.userName as userName0_
from
Users user0_
where
user0_.userName=?
and user0_.password=? limit ?
13:03:18,374 TRACE [main] sql.BasicBinder - binding parameter [1] as [VARCHAR] - test
13:03:18,374 TRACE [main] sql.BasicBinder - binding parameter [2] as [VARCHAR] - test
13:03:18,379 DEBUG [main] jpa.JpaTransactionManager - Initiating transaction commit
13:03:18,379 DEBUG [main] jpa.JpaTransactionManager - Committing JPA transaction on EntityManager [org.hibernate.ejb.EntityManagerImpl#1533badd]
13:03:18,379 DEBUG [main] jpa.JpaTransactionManager - Closing JPA EntityManager [org.hibernate.ejb.EntityManagerImpl#1533badd] after transaction

Can you try this one.
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="createUser" propagation="REQUIRES_NEW" />
</tx:attributes>
</tx:advice>

Related

Could not retrieve pre-bound Hibernate session - Could not obtain transaction-synchronized Session for current thread

I am creating a web application with spring-mvc, spring-security, spring-core, and hibernate.
But I always get this non-error from hibernate session factory.
It is actually throwing HibernateException, but I see on the log it is in debug level, and my application actually continues its execution with no problem.
But I am very curious to understand the reason of the exception raised.
The log shows the following lines.
2016-08-29 13:34:55,310 DEBUG [sg.com.diamond.express.base.dao.impl.HibernateDAOImpl].[doExecute]([328]) [http-nio-8888-exec-4] - Could not retrieve pre-bound Hibernate session
org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:134)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1014)
at org.springframework.orm.hibernate4.HibernateTemplate.doExecute(HibernateTemplate.java:325)
at org.springframework.orm.hibernate4.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:308)
at org.springframework.orm.hibernate4.HibernateTemplate.get(HibernateTemplate.java:418)
at org.springframework.orm.hibernate4.HibernateTemplate.get(HibernateTemplate.java:411)
at sg.com.diamond.express.base.dao.impl.BaseHibernateDaoImpl.findById(BaseHibernateDaoImpl.java:160)
at sg.com.diamond.express.webadmin.service.impl.ClientProfileServiceImpl.searchByName(ClientProfileServiceImpl.java:48)
at sg.com.diamond.express.webadmin.controller.impl.ClientController.home(ClientController.java:48)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.web.servlet.mvc.multiaction.MultiActionController.invokeNamedMethod(MultiActionController.java:472)
at org.springframework.web.servlet.mvc.multiaction.MultiActionController.handleRequestInternal(MultiActionController.java:409)
at org.springframework.web.servlet.mvc.AbstractController.handleRequest(AbstractController.java:146)
at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:50)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:943)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
This is the code that I have.
DAO Level
This is an abstraction level that I created to minimise the number of boiler-plate codes for DAO, no need to implement the basic c-r-u-d.
BaseDAO Interface
public interface BaseDAO <ID extends Serializable, M extends BaseModel> {
Session getCurrentSession();
M findById(ID id) throws DAOException;
List<M> findByCriteria(M criteria) throws DAOException;
List<M> listAll() throws DAOException;
ID saveObject(M object) throws DAOException;
void saveOrUpdate(M object) throws DAOException;
Query createQuery(String hql) throws DAOException;
Query createSQLQuery(String sql) throws DAOException;
Criteria createCriteria(Class clazz) throws DAOException;
List<M> runQuery(Query query) throws DAOException;
List<M> runQuery(String sql) throws DAOException;
}
BaseDAO Implementation
public class BaseHibernateDaoImpl<ID extends Serializable, M extends BaseModel> extends HibernateTemplate implements BaseDAO<ID, M> {
protected Class<M> modelClass;
#Override
public Session getCurrentSession() {
return this.getSessionFactory().getCurrentSession();
}
public ID saveObject(M object) throws DAOException {
try {
object.setCreator("Batch");
object.setCreatedAt(new Timestamp(new Date().getTime()));
object.setUpdater("Batch");
object.setUpdatedAt(new Timestamp(new Date().getTime()));
return (ID) super.save(object);
} catch (Exception e) {
logger.error(ExceptionUtil.getStackTraces(e), e);
throw new DAOException(e);
}
}
/** all other implementation methods **/
Spring XML Configuration
<!--|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Data Source Configuration
|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<bean id="targetDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:#192.168.170.36:1521:EXPT2" />
<property name="username" value="EXP_USER" />
<property name="password" value="passw0rd" />
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<property name="targetDataSource" ref="targetDataSource"/>
</bean>
<!--|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Hibernate abstract session factory parent
|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<bean id="baseSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" abstract="true">
<property name="hibernateProperties">
<props>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.jdbc.batch_size">10</prop>
</props>
</property>
</bean>
<!--|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Hibernate entity configurations
|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<bean id="expressSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" parent="baseSessionFactory">
<property name="hibernateProperties">
<props merge="true">
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
</props>
</property>
<property name="dataSource" ref="dataSource"/>
<property name="mappingLocations" value="classpath*:hbm/express/*.hbm.xml"/>
</bean>
<!--|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Transaction Management
|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="expressSessionFactory"/>
</bean>
<aop:config>
<aop:pointcut id="servicePointcut" expression="execution(* sg.com.diamond.express.webadmin.service.*.*.*(..))"/>
<aop:advisor id="serviceTx" advice-ref="txAdvice" pointcut-ref="servicePointcut"/>
</aop:config>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" propagation="REQUIRES_NEW" isolation="READ_COMMITTED" read-only="true" />
<tx:method name="search*" propagation="REQUIRES_NEW" isolation="READ_COMMITTED" read-only="true" />
<tx:method name="list*" propagation="REQUIRES_NEW" isolation="READ_COMMITTED" read-only="true" />
<tx:method name="is*" propagation="REQUIRES_NEW" isolation="READ_COMMITTED" read-only="true" />
<tx:method name="*" propagation="REQUIRES_NEW" isolation="READ_COMMITTED" read-only="false"/>
</tx:attributes>
</tx:advice>
<!--|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Base Dao Definitions
|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<bean id="baseExpressDAO" class="sg.com.diamond.express.base.dao.impl.BaseHibernateDaoImpl" abstract="true">
<property name="sessionFactory" ref="expressSessionFactory"/>
</bean>
Question
Why does the error happen? and it doesn't break my app, the log level is DEBUG, not ERROR, my app is able to continue properly after that.
Is my configuration correct?
Note
Don't mind the full-xml config, for some reason, I need to use full-xml and none-annotations.
Answer
After mucking around with the aop pointcut expression, I noticed that on a simple batch program that I wrote, where I only have 1 context, I do not have this problem.
This leads me to the answer.
I originally structured my spring xml config as multiple layers.
At the top level, at the application context, I declare all the data sources, and the transaction managements.
At the second level, the servlet level, each servlet has its own context. At this level, I put all the controller-service-dao configurations.
There should be multiple of this "second-level" context, for example, 1 servlet context should handle all RESTful services, where another one handles web-ui.
I've structured it assuming it will take on the transaction management setup in the application context. Sort of like "all servlet context should be able to use the same data-source and transaction-management the same way"
But as it turns out, it doesn't.
When I moved the transaction management piece, including the data-source config, into the servlet context configuration, it all works fine, and the service is now wrapped correctly under transaction.
Thanks.
The error happens because your aop transaction configuration is not correct. It does not cover the service class from the stack trace. Tweak with the mapping until you stop getting the stack trace. Or maybe better use annotated transaction configuration if possible.
The answer to your second question is in the sources of hibernate template:
try {
325 session = getSessionFactory().getCurrentSession();
326 }
327 catch (HibernateException ex) {
328 logger.debug("Could not retrieve pre-bound Hibernate session", ex);
329 }
330 if (session == null) {
331 session = getSessionFactory().openSession();
332 session.setFlushMode(FlushMode.MANUAL);
333 isNew = true;
334 }

Hibernate and Spring DataSourceTransactionManager

I'm trying to work with Hibernate and Spring DataSourceTransactionManager to handle commit and rollback functions, but probably I dind't get something.
Before using Spring DataSourceTransactionManager, this was one of my DAO class
package com.springgestioneerrori.DAO;
public class UtenteDAO extends DAOBase{
public void salvaUtente(Utente utenteIn) throws DAOException{
Session session = getHibernateSession(); //from this method I get Hibernate SessionFactory
try{
session.beginTransaction();
session.saveOrUpdate(Object);
session.getTransaction().commit();
}
catch(Exception e){
session.getTransaction().rollback()
}
}
}
This is the class that give me the sessionFactory
private static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
return new AnnotationConfiguration().configure().buildSessionFactory();
}
catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static void shutdown() {
getSessionFactory().close();
}
public Session getHibernateSession (){
Session session = HibernateUtil.getSessionFactory().openSession();
return session;
}
Now i'm trying to use DataSourceTransactionManager in a declarative way. Following some examples on the internet I wrote this:
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager" />
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="salvaUtente"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="userDaoTxPointcut" expression="execution(* com.springgestioneerrori.DAO.UtenteDAO.salvaUtente(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="userDaoTxPointcut" />
</aop:config>
Now, what should I write inside the method salvaUtente() to perform many inserts, for example like these
session.saveOrUpdate(User);
session.saveOrUpdate(UserCredentials);
session.saveOrUpdate(UserOtherDetails);
and making Spring handle commint and rollback?
First you are using the wrong transaction manager. The DataSourceTransactionManager isn't for Hibernate but for plain JDBC. If you are using plain Hibernate use the HibernateTransactionManager. (Assuming that you are using Hibernate 4 here!).
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
Now I would also suggest to use #Transactional instead of a <tx:advice /> and <aop:config /> block. Makes your configuration easier. Simply remove the 2 mentioned blocks and replace with <tx:annotation-driven />.
<tx:annotation-driven />
Now your code is wrong as you are starting transactions yourself. Annotate your method with #Transactional (after adding the thing above) and remove your transaction handling code.
#Transactional
public void salvaUtente(Utente utenteIn) throws DAOException{
Session session = getHibernateSession(); //from this method I get Hibernate SessionFactory
session.saveOrUpdate(Object);
}
Now I don't know what your getHibenateSession method does but make sure you don't use openSession on the SessionFactory to obtain a session. Use getCurrentSession instead.
protected Session getHibernateSession() {
return sessionFactory.getCurrentSession();
}
Your current BaseDAO class is flawed. Delete your buildSessionFactory and remove the static final. Let Spring configure and inject the SessionFactory.
public abstract class BaseDAO {
#Autowired
private SessionFactory sessionFactory;
protected Session getHibernateSession() {
return sessionFactory.getCurrentSession();
}
}
In your configuration add the configuration for the LocalSessionFactoryBean.
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
... Other Hibernate Properties
</bean>
That is basically all that is needed. This is also all explained in detail in the Spring Reference guide. I strongly suggest a read.
I would suggest
#Transaction(rollbackFor = {if you need to rollback on certain checked exception})
salvaUtente()
The transaction is only roll backed for unchecked exception until mentioned otherwise.
if there is an exception at any point salvaUtente in the transaction will be roll backed else the transaction will be committed once the method is existed
HibernateTransactionManager can also manage the transactions on the DataSource. We need to inject the DataSource in the HibernateTransactionManager and it should work for both, Hibernate and plain old SQL.
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager>
<property name="sessionFactory" ref="sessionFactory"/>
<property name="dataSource" ref="dataSource"/>
</bean>
Hope this works for you.
(I know this is pretty late, but may help someone landing to this post)

unable to persist entity in spring tests with JpaTransactionManager

I'm trying to persist an entity using. My test case works (data are persisted in db) when I handle my transaction myself.
public User saveUser(User user) {
getEm().getTransaction().begin();
getEm().persist(user);
getEm().getTransaction().commit();
return user;
}
(entity manager comes from a template class which my DAO class extends)
It stops persisiting data when I configure spring to handle transactions.
test-context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="domainPU" />
<property name="loadTimeWeaver">
<bean
class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://192.168.5.111/testdb" />
<property name="username" value="sdgdsgs" />
<property name="password" value="sdfgdsg" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory">
<ref local="entityManagerFactory" />
</property>
</bean>
</beans>
UsersRepositoryTest:
#ContextConfiguration(locations = { "classpath:test-context.xml", "classpath:module-context.xml" })
#TransactionConfiguration(defaultRollback=false)
public class UsersRepositoryTest extends AbstractTransactionalTestNGSpringContextTests {
#Autowired
private UsersRepository usersRepository;
#Test(dependsOnMethods = {"findUserByLogin"})
public void registerUser() throws DataAccessException {
User myUser = getUserHomer();
usersRepository.saveUser(myUser);
User returnedUser = usersRepository.getUserByLogin(myUser.getLogin());
assertNotNull(returnedUser);
...
}
}
stacktrace:
2012-10-22 08:26:26 org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean createNativeEntityManagerFactory
INFO: Building JPA container EntityManagerFactory for persistence unit 'domainPU'
2012-10-22 08:26:26 org.hibernate.annotations.common.Version <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {4.0.1.Final}
2012-10-22 08:26:26 org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {4.1.6.Final}
2012-10-22 08:26:26 org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
2012-10-22 08:26:26 org.hibernate.cfg.Environment buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : javassist
2012-10-22 08:26:26 org.hibernate.ejb.Ejb3Configuration configure
INFO: HHH000204: Processing PersistenceUnitInfo [
name: domainPU
...]
2012-10-22 08:26:26 org.hibernate.service.jdbc.connections.internal.ConnectionProviderInitiator instantiateExplicitConnectionProvider
INFO: HHH000130: Instantiating explicit connection provider: org.hibernate.ejb.connection.InjectedDataSourceConnectionProvider
2012-10-22 08:26:27 org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
2012-10-22 08:26:27 org.hibernate.engine.jdbc.internal.LobCreatorBuilder useContextualLobCreation
INFO: HHH000424: Disabling contextual LOB creation as createClob() method threw error : java.lang.reflect.InvocationTargetException
2012-10-22 08:26:27 org.hibernate.engine.transaction.internal.TransactionFactoryInitiator initiateService
INFO: HHH000268: Transaction strategy: org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory
2012-10-22 08:26:27 org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory <init>
INFO: HHH000397: Using ASTQueryTranslatorFactory
2012-10-22 08:26:27 org.springframework.context.support.AbstractApplicationContext$BeanPostProcessorChecker postProcessAfterInitialization
INFO: Bean 'entityManagerFactory' of type [class org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2012-10-22 08:26:27 org.springframework.context.support.AbstractApplicationContext$BeanPostProcessorChecker postProcessAfterInitialization
INFO: Bean 'hibernateTranslator' of type [class org.springframework.orm.hibernate4.HibernateExceptionTranslator] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2012-10-22 08:26:27 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory#5c28305d: defining beans [org.springframework.aop.config.internalAutoProxyCreator,org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0,org.springframework.transaction.interceptor.TransactionInterceptor#0,org.springframework.transaction.config.internalTransactionAdvisor,entityManagerFactory,dataSource,transactionManager,org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor#0,hibernateTranslator,usersDao,zoneDao,usersRepository,zoneRepository,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor,org.springframework.context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor#0]; root of factory hierarchy
2012-10-22 08:26:27 org.springframework.test.context.transaction.TransactionalTestExecutionListener startNewTransaction
INFO: Began transaction (1): transaction manager [org.springframework.orm.jpa.JpaTransactionManager#3341b736]; rollback [false]
Hibernate: select user0_.id as id0_, user0_.active as active0_, user0_.creation_date as creation3_0_, user0_.email as email0_, user0_.last_login_date as last5_0_, user0_.login as login0_, user0_.password as password0_, user0_1_.AVATAR as AVATAR1_, user0_1_.FIRST_NAME as FIRST2_1_, user0_1_.ID as ID1_, user0_1_.LAST_NAME as LAST4_1_, case when user0_1_.id is not null then 1 when user0_.id is not null then 0 end as clazz_ from user user0_ left outer join client user0_1_ on user0_.id=user0_1_.id where user0_.login=?
Hibernate: select phone0_.id as id2_0_, phone0_.phone_number as phone2_2_0_, phone0_.user_id as user3_2_0_ from phone phone0_ where phone0_.id=?
Hibernate: select address0_.ID as ID6_0_, address0_.apartment_number as apartment2_6_0_, address0_.city as city6_0_, address0_.country as country6_0_, address0_.house_number as house5_6_0_, address0_.post_code as post6_6_0_, address0_.street as street6_0_ from address address0_ where address0_.ID=?
Hibernate: select user0_.id as id0_, user0_.active as active0_, user0_.creation_date as creation3_0_, user0_.email as email0_, user0_.last_login_date as last5_0_, user0_.login as login0_, user0_.password as password0_, user0_1_.AVATAR as AVATAR1_, user0_1_.FIRST_NAME as FIRST2_1_, user0_1_.ID as ID1_, user0_1_.LAST_NAME as LAST4_1_, case when user0_1_.id is not null then 1 when user0_.id is not null then 0 end as clazz_ from user user0_ left outer join client user0_1_ on user0_.id=user0_1_.id where user0_.login=?
2012-10-22 08:26:27 org.springframework.test.context.transaction.TransactionalTestExecutionListener endTransaction
INFO: Committed transaction after test execution for test context [[TestContext#63edf84f testClass = UsersRepositoryTest, testInstance = pl.eports.zonen.user.repository.UsersRepositoryTest#7e3bc473, testMethod = findUserByLogin#UsersRepositoryTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration#1c493dca testClass = UsersRepositoryTest, locations = '{classpath:test-context.xml, classpath:module-context.xml}', classes = '{}', activeProfiles = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader']]]
2012-10-22 08:26:27 org.springframework.test.context.transaction.TransactionalTestExecutionListener startNewTransaction
INFO: Began transaction (2): transaction manager [org.springframework.orm.jpa.JpaTransactionManager#3341b736]; rollback [false]
Hibernate: select user0_.id as id0_, user0_.active as active0_, user0_.creation_date as creation3_0_, user0_.email as email0_, user0_.last_login_date as last5_0_, user0_.login as login0_, user0_.password as password0_, user0_1_.AVATAR as AVATAR1_, user0_1_.FIRST_NAME as FIRST2_1_, user0_1_.ID as ID1_, user0_1_.LAST_NAME as LAST4_1_, case when user0_1_.id is not null then 1 when user0_.id is not null then 0 end as clazz_ from user user0_ left outer join client user0_1_ on user0_.id=user0_1_.id where user0_.login=?
2012-10-22 08:26:27 org.springframework.test.context.transaction.TransactionalTestExecutionListener endTransaction
INFO: Committed transaction after test execution for test context [[TestContext#63edf84f testClass = UsersRepositoryTest, testInstance = pl.eports.zonen.user.repository.UsersRepositoryTest#7e3bc473, testMethod = registerUser#UsersRepositoryTest, testException = java.lang.AssertionError: expected object to not be null, mergedContextConfiguration = [MergedContextConfiguration#1c493dca testClass = UsersRepositoryTest, locations = '{classpath:test-context.xml, classpath:module-context.xml}', classes = '{}', activeProfiles = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader']]]
PASSED: findUserByLogin
FAILED: registerUser
java.lang.AssertionError: expected object to not be null
at org.testng.Assert.fail(Assert.java:94)
...
As you can see from the stacktrace, transaction seems to be started and finished, but no sql inserts are produced and no data are inserted into db (data rollback is set to false)
I've tried force flushing data after persist() but spring managed transaction seems to be finished by then.
I've looked for a similar issue on forums but none works.
I can provide some more code if necessary.
I'm using Spring 3.1.2 and Hibernate 4.1.6.Final
update:
DAO: (#Transactional annotation is put here instead on service layer temporarily)
#Repository
public class JpaUsersDAO extends JpaDAOTemplate<User> implements UsersDAO {
public JpaUsersDAO(EntityManagerFactory factory) {
super(factory.createEntityManager(), User.class);
}
#Transactional
public User saveUser(User user) {
getEm().persist(user);
return user;
}
}
DAOTemplate:
public abstract class JpaDAOTemplate<T extends Serializable> {
private EntityManager em;
private Class<T> clazz;
public JpaDAOTemplate(EntityManager em, final Class<T> clazz) {
this.em = em;
this.clazz = clazz;
}
public EntityManager getEm() {
return em;
}
public T getById(Integer id) {...}
...
}
bean definition:
<bean id="usersDao"
class="pl.package.user.dao.impl.JpaUsersDAO">
<constructor-arg ref="entityManagerFactory" />
</bean>
It seems that the transaction is lost somewhere after selects and before calling getEm().persist() - org.hibernate.ejb.TransactionImpl#tx is null when calling persist() but it was org.hibernate.engine.transaction.internal.jdbc.JDBCTransaction before the call (when sql select statements calls were made)
update 2
debug level stacktrace:
[2012-10-23 11:15:14,546]DEBUG 38629[main] - org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:365) - Creating new transaction with name [registerUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
[2012-10-23 11:15:33,252]DEBUG 57335[main] - org.hibernate.internal.SessionImpl.<init>(SessionImpl.java:316) - Opened session at timestamp: 13509837332
[2012-10-23 11:15:33,252]DEBUG 57335[main] - org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:368) - Opened new EntityManager [org.hibernate.ejb.EntityManagerImpl#18025c5c] for JPA transaction
[2012-10-23 11:15:40,085]DEBUG 64168[main] - org.hibernate.engine.transaction.spi.AbstractTransactionImpl.begin(AbstractTransactionImpl.java:158) - begin
[2012-10-23 11:15:40,101]DEBUG 64184[main] - org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:295) - Obtaining JDBC connection
[2012-10-23 11:15:40,101]DEBUG 64184[main] - org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:301) - Obtained JDBC connection
[2012-10-23 11:15:40,101]DEBUG 64184[main] - org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doBegin(JdbcTransaction.java:69) - initial autocommit status: true
[2012-10-23 11:15:40,101]DEBUG 64184[main] - org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doBegin(JdbcTransaction.java:71) - disabling autocommit
[2012-10-23 11:16:06,934]DEBUG 91017[main] - org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:407) - Not exposing JPA transaction [org.hibernate.ejb.EntityManagerImpl#18025c5c] as JDBC transaction because JpaDialect [org.springframework.orm.jpa.DefaultJpaDialect#5f343722] does not support JDBC Connection retrieval
[2012-10-23 11:16:06,934]DEBUG 91017[main] - org.springframework.test.context.transaction.TransactionalTestExecutionListener.isRollback(TransactionalTestExecutionListener.java:357) - No method-level #Rollback override: using default rollback [false] for test context [[TestContext#5809fdee testClass = UsersRepositoryTest, testInstance = pl.eports.zonen.user.repository.UsersRepositoryTest#3fde891b, testMethod = registerUser#UsersRepositoryTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration#77fe4169 testClass = UsersRepositoryTest, locations = '{classpath:test-context.xml, classpath:module-context.xml}', classes = '{}', activeProfiles = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader']]]
[2012-10-23 11:16:06,934] INFO 91017[main] - org.springframework.test.context.transaction.TransactionalTestExecutionListener.startNewTransaction(TransactionalTestExecutionListener.java:275) - Began transaction (1): transaction manager [org.springframework.orm.jpa.JpaTransactionManager#37977909]; rollback [false]
[2012-10-23 11:17:08,103]DEBUG152186[main] - org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:245) - Returning cached instance of singleton bean 'org.springframework.transaction.interceptor.TransactionInterceptor#0'
[2012-10-23 11:17:08,118]DEBUG152201[main] - org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource.getTransactionAttribute(AbstractFallbackTransactionAttributeSource.java:106) - Adding transactional method 'saveUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
[2012-10-23 11:17:08,118]DEBUG152201[main] - org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:245) - Returning cached instance of singleton bean 'txManager'
[2012-10-23 11:17:27,073]DEBUG171156[main] - org.springframework.orm.jpa.JpaTransactionManager.doGetTransaction(JpaTransactionManager.java:331) - Found thread-bound EntityManager [org.hibernate.ejb.EntityManagerImpl#18025c5c] for JPA transaction
[2012-10-23 11:18:04,031]DEBUG208114[main] - org.springframework.transaction.support.AbstractPlatformTransactionManager.handleExistingTransaction(AbstractPlatformTransactionManager.java:470) - Participating in existing transaction
Final update:
Problem was in the way I injected EntityManagerFactory to my DAO. I used applicationContext.xml configuration file to inject it (this way EMF does not participate in transaction). To have it working I changed my DAO class to:
#Repository
public class JpaUsersDAO extends JpaDAOTemplate<User> implements UsersDAO {
#PersistenceContext
private EntityManager em;
...
}
You are using Spring 3.0.
I am not clear your coding public User saveUser(User user) method. Even If you use Spring 3.0, you handel the transaction your self(Bean Management Transaction).
It just need to use #Transactional(propagation = Propagation.REQUIRED) annotation. Change propagation type based on your process.
Example DAO :
GroupDAO.java
#Repository("GroupDAO")
public class GroupDAO {
#PersistenceContext
protected EntityManager em;
#Transactional(propagation = Propagation.REQUIRED)
public void insert(Group group) {
try {
em.persist(group);
em.flush();
} catch (PersistenceException pe) {
}
}
}
spring-bean.xml with EclipseLink JPA <-- just for reference.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:faces="http://www.springframework.org/schema/faces"
xmlns:int-security="http://www.springframework.org/schema/integration/security"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:sec="http://www.springframework.org/schema/security"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/integration/security http://www.springframework.org/schema/integration/security/spring-integration-security-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/faces http://www.springframework.org/schema/faces/spring-faces-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<context:annotation-config/>
<context:component-scan base-package="your-package"/>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://192.168.5.111/testdb"/>
<property name="username" value="your-username"/>
<property name="password" value="your-password"/>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!-- <property name="dataSource" ref="dataSource"/>-->
<property name="persistenceUnitName" value="domainPU"/>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect"/>
</property>
<property name="jpaPropertyMap">
<props>
<prop key="eclipselink.weaving">false</prop>
</props>
</property>
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver">
</bean>
</property>
</bean>
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
<property name="databasePlatform" value="org.eclipse.persistence.platform.database.MySQLPlatform"/>
<!-- <property name="databasePlatform" value="org.eclipse.persistence.platform.database.OraclePlatform" />-->
<property name="generateDdl" value="false"/>
<property name="showSql" value="true"/>
</bean>
</beans>

Spring Transaction - automatic rollback of previous db updates when one db update failes

I am writing a simple application (Spring + Hibernate + PostgreSql db). I am just trying to construct a sample object and persist in db.
I run a simple java class main method where i have loaded the applicationContext and have got reference to the service class as below
TestService srv = (TestService)factory.getBean("testService");
Application Context - context :
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactoryVsm" />
</bean>
<bean id="testService" class="com.test.service.TestServiceImpl">
<property name="testDao" ref="testDao"/>
</bean>
<bean id="testDao" class="com.test.dao.TestDaoImpl>
<property name="sessionFactory" ref="sessionFactoryVsm"/>
</bean>
In TestService I have injected TestDao. In test service method I have constructed to employee objects
emp1 and emp2 and calling dao twice to update.
TestDaoImpl code:
public void saveOrUpdate(BaseDomainModel baseObject) {
Session session = null;
try {
session = getHibernateTemplate().getSessionFactory().openSession();
session.saveOrUpdate(baseObject);
session.flush();
} catch (Exception e) {
logger.error("Generic DAO:saveOrUpdate::" + e);
e.printStackTrace();
} finally {
if (session != null) {
session.close();
}
}
}
When emp2 update fails emp1 should also fail. How do I do that. Please advice
Thanks in advance
Updated :
Thanks Nanda. I tried Declarative transaction. But it is not working. emp1 gets persisted and not rolled back eveb second dao call fails. I have added transaction advice to the method.
to test if the transaction advice is applied or not i changed the propagation to "NOT_SUPPORTED". but still emp1 gets persisted. the expectation is we should have got Transaction Not Supported type of exception. please advice .
UPDATED
#seanizer - Thanks for the update. I have even tried adding
#Transactional(propagation=Propagation.NOT_SUPPORTED)
public void saveEmp(Employee emp)
to that service method. But it didn't work. Moreover iterating the collection<BaseDomainModel> hold good only if i need to call one dao. If in case i have to call two different dao to persist obj1 and obj2- this may not help. Just to check if the transaction is getting applied I get
#Transactional(propagation=Propagation.NOT_SUPPORTED).
But still obj1 got persisted.
I just doubt if the xml configuration/ annotation given is correct. please check
<bean id="txManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactoryVsm" />
</bean>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="saveEmp" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="testServiceOperation" expression="execution(*com.test.service.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="testServiceOperation"/>
</aop:config>
I am using org.springframework.orm.hibernate3.HibernateTransactionManager for the transactionManager. Is this correct ?
Updated
I have created my exception class myRuntimeExp extending from RuntimeException and throwing the same from Dao method to the service method. but still the rollback is not happening. I just doubt if I have correctly given the configurations in the applnContext.xml. Can someone help me how to check if the transaction advice / annotation is being applied to the method or not? is there any way of running it in a debugging mode and check
Issue :
I was using
session = getHibernateTemplate().getSessionFactory().openSession();
But it should be current session and it is working fine.
session = getHibernateTemplate().getSessionFactory().getCurrentSession();
If you use declarative transaction management, you can lose most of this boilerplate:
TestDaoImpl:
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory f){
this.sessionFactory = f;
}
public void saveOrUpdate(BaseDomainModel baseObject) {
Session session = sessionFactory.getCurrentSession();
session.saveOrUpdate(baseObject);
}
And you can control the transaction handling from the service layer using #Transactional (or xml configuration)
TestServiceImpl:
private TestDao testDao;
public void setTestDao(TestDao d){
this.testDao = d;
}
#Transactional // one transaction for multiple operations
public void someServiceMethod(Collection<BaseDomainModel> data){
for(BaseDomainModel baseObject : data)
testDao.saveOrUpdate(baseObject);
}
Reference:
Implementing DAOs based on plain
Hibernate 3 API
By default, Spring only rolls back for unchecked exception. You have to provide the rollback-for attribute and specify what exception you are trying to catch.
From the Spring documentation:
However, please note that the Spring Framework's transaction
infrastructure code will, by default, only mark a transaction for
rollback in the case of runtime, unchecked exceptions; that is, when
the thrown exception is an instance or subclass of RuntimeException.
(Errors will also - by default - result in a rollback.) Checked
exceptions that are thrown from a transactional method will not result
in the transaction being rolled back.
Here, get these snippets and hope they will help you:
<bean id="abstractService"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
abstract="true">
<property name="transactionManager" ref="transactionManager" />
<property name="transactionAttributes">
<props>
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="add*">PROPAGATION_REQUIRED, -Exception</prop>
<prop key="update*">PROPAGATION_REQUIRED, -Exception</prop>
<prop key="modify*">PROPAGATION_REQUIRED, -Exception</prop>
<prop key="delete*">PROPAGATION_REQUIRED, -Exception</prop>
<prop key="save*">PROPAGATION_REQUIRED, -Exception</prop>
</props>
</property>
</bean>
<bean id="persistenceServiceTarget" class="com.blahblah.server.service.impl.PersistenceServiceImpl">
<property name="persistenceDAO" ref="persistenceDAO" />
</bean>
<bean id="persistenceService" parent="abstractService">
<property name="target" ref="persistenceServiceTarget" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="abstractDAO"
class="org.springframework.orm.hibernate3.support.HibernateDaoSupport"
abstract="true">
<property name="sessionFactory">
<ref bean="webSessionFactory" />
</property>
</bean>
<bean id="persistenceDAO" class="com.blahblah.server.dao.impl.PersistenceDAOImpl"
parent="abstractDAO">
</bean>
These should be the things that you need. They don't have to be in the same file, maybe split them between services and daos.
You can put the sessionFactory in TestServiceImpl and open the session there.
another point is to check if the database support the transaction

EclipseLink with Spring - Can't persist into Db

I am using Spring + EclipseLink 2 to manage entity on a Derby database. Select object from db works fine but when I try to persist one, nothing happens. Program executes correctly and no exception are thrown. I probably did something wrong, as I'm not familiar with Spring, thanks for your comments and suggestions :)
The ServerDaoDb method :
#Transactional
public void addServer(Server server) {
EntityManager em = emf.createEntityManager();
emf.createEntityManager().persist(server);
em.close();
}
Application context is :
...
<tx:annotation-driven />
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="SpringPratiquePU" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
Persistence.xml :
<persistence-unit name="SpringPratiquePU" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>net.athom.spring.examples.models.eas.Server</class>
<class>net.athom.spring.examples.models.eas.Node</class>
<properties>
<property name="eclipselink.target-database" value="DERBY"/>
<property name="javax.persistence.jdbc.url" value="jdbc:derby://localhost:1527/SpringPratique"/>
<property name="javax.persistence.jdbc.password" value="clem"/>
<property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.ClientDriver"/>
<property name="javax.persistence.jdbc.user" value="clem"/>
</properties>
</persistence-unit>
</persistence>
The Debug Trace :
DEBUG JpaTransactionManager:365 - Creating new transaction with name [net.athom.spring.examples.service.impl.ServerManagerImpl.addServer]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
[EL Info]: 2010-10-29 15:33:27.443--ServerSession(14894886)--EclipseLink, version: Eclipse Persistence Services - 2.0.2.v20100323-r6872
[EL Info]: 2010-10-29 15:33:28.606--ServerSession(14894886)--file:/C:/netbeanProject/SpringPratique/src/_SpringPratiquePU login successful
15:33:28,893 DEBUG JpaTransactionManager:323 - Opened new EntityManager [org.eclipse.persistence.internal.jpa.EntityManagerImpl#1779885] for JPA transaction
15:33:28,951 DEBUG DefaultListableBeanFactory:242 - Returning cached instance of singleton bean 'transactionManager'
15:33:28,952 DEBUG JpaTransactionManager:286 - Found thread-bound EntityManager [org.eclipse.persistence.internal.jpa.EntityManagerImpl#1779885] for JPA transaction
15:33:28,953 DEBUG JpaTransactionManager:470 - Participating in existing transaction
15:33:29,266 DEBUG JpaTransactionManager:752 - Initiating transaction commit
15:33:29,267 DEBUG JpaTransactionManager:462 - Committing JPA transaction on EntityManager [org.eclipse.persistence.internal.jpa.EntityManagerImpl#1779885]
15:33:29,268 DEBUG JpaTransactionManager:548 - Closing JPA EntityManager [org.eclipse.persistence.internal.jpa.EntityManagerImpl#1779885] after transaction
15:33:29,308 DEBUG EntityManagerFactoryUtils:328 - Closing JPA EntityManager
Your error is here:
#Transactional
public void addServer(Server server) {
EntityManager em = emf.createEntityManager();
emf.createEntityManager().persist(server);
em.close();
}
Your are creating two different EntityManager instances, the em, which you are closing, and a new one at the next line with emf.createEntityManager() which you are using directly to persist your changes.
Try this:
#Transactional
public void addServer(Server server) {
EntityManager em = emf.createEntityManager();
em.persist(server);
em.close();
}
I guess that when you close your em instance, your changes are written to the DB, but, in case that they are not, you have to add em.flush(); right before closing the em instance.
I am not sure what exactly is wrong with your configuration (I guess EntityManager created manually can't participate in #Transactional transactions), but the typical JPA configuration in Spring looks like this, so you don't need to create EntityManager manually (also note the classname of the factory bean):
#PersistenceContext
private EntityManager em;
#Transactional
public void addServer(Server server) {
em.persist(server);
}
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="SpringPratiquePU" />
</bean>
Thx for your answers, both were very helpful. I ended up using LocalContainerEntityManagerFactoryBean, so I could inject the entity manager into my dao.
I added servlet-agent-2.5.6.jar on the lib folder and and pass VM options :
-javaagent:path/to/lib/ervlet-agent-2.5.6.jar
Then I modified ApplicationContext.xml :
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="SpringPratiquePU" />
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>
</bean>
And my dao:
...
#PersistenceContext
private EntityManager em;
#Transactional
public void addServer(Server server) {
em.persist(server);
}
Persistence and transactions work like a charm now !

Categories