jpa entitymanager rollback not working - java

In my spring project i am using hibernate entitymanager. I have done transaction management in the project like below.
applicationContext.xml
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:#xxxx:1527:selva" />
<property name="username" value="xxx" />
<property name="password" value="yyy" />
</bean>
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan">
<list>
<value>com.selva.entity</value>
</list>
</property>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="ORACLE" />
<property name="showSql" value="true" />
</bean>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
TestTranService.java
#Transactional
public String checkTranWork() {
try {
testTranDao.method1();
int i=0;
if(i==0)
throw new ValidationException();
testTranDao.method2();
testTranDao.method3();
} catch(Exception e) {
e.printStackTrace();
}
return null;
}
TestTranDaoImpl.java
public class TestTranDaoImpl implements TestTranDao {
public static final Logger LOGGER = Logger
.getLogger(TestTranDaoImpl.class);
#PersistenceContext
private EntityManager entityManager;
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
public EntityManager getEntityManager() {
return entityManager;
}
#Override
public void method1() {
try {
String sql = "INSERT INTO CUST(MOBNUM,CUST_NAME) values ('1','dd')";
Query query = entityManager.createNativeQuery(sql);
query.executeUpdate();
} catch(Exception e) {
e.printStackTrace();
}
}
#Override
public void method2() {
try {
String sql = "INSERT INTO CUST(MOBNUM,CUST_NAME) values ('1','dd')";
Query query = entityManager.createNativeQuery(sql);
query.executeUpdate();
} catch(Exception e) {
e.printStackTrace();
}
}
}
In my code after method 1 executing the insert statement i am throwing the exception so the first statement should be rolled back.But in my case rollback is not happened.Is their any other configuration required for rollback.
Any help will be greatly appreciated!!!

You Catch the Exceptions to log them, but then, you drop them. Don't do that!
An Exception has a meaning in a software, more than for logging, but also for all the framework around it.
Remove the try catch blocks so the #Transactional Aspect around your method can detect there's an error and rollback your transaction.

As explained by Gillaume for #Transactional to work, the method has to throw an exception then only the framework will know that rollback has to be performed.
One solution already suggested is to remove the try/catch and if there is a need to catch the exception and log in application error log file then make sure the exception is propagated not consumed.
So the updated code would look like:
#Transactional
public String checkTranWork() {
try {
testTranDao.method1();
int i=0;
if(i==0)
throw new ValidationException();
testTranDao.method2();
testTranDao.method3();
} catch(Exception e) {
e.printStackTrace();
throw e;
}
return null;
}

Related

Deadlock caused by EntityManager using dependency injection

I am developing an application with REST services. I am using dependency injection to, among other things, inject EntityManagerFactory. I have a heavy process where I want to use threads, but it is giving me a deadlock when using EntityManager. The code:
Note: All code has been simplified and renamed classes to explain the problem
persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="mypersistenceunit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!-- Entities -->
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.event.merge.entity_copy_observer" value="allow" />
<property name="hibernate.connection.autocommit" value="false"/>
</properties>
</persistence-unit>
</persistence>
applicationContext.xml:
<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: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-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
"
default-autowire="byName">
<context:property-placeholder location="classpath:database.properties"/>
<bean id="logDao" class="my.package.example.dao.LogDao">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean id="extractDataDao" class="my.package.example.source.ExtractDataDao">
<property name="completeDatabaseModel" ref="completeDatabaseModel"/>
</bean>
<bean id="completeDatabaseModel" class="my.package.example.source.CompleteDatabaseModel">
<property name="logDao" ref="logDao" />
</bean>
<bean id="fill" class="my.package.example.services.impl.FillDatabaseServiceImpl">
<property name="extractDataDao" ref="extractDataDao" />
</bean>
<!-- Configuracion de acceso a base de datos -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="myDataSource" />
<property name="persistenceUnitName" value="mypersistenceunit"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="false" />
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
</bean>
</property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
</bean>
<bean id="myDataSource" parent="dataSource">
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
FillDatabaseServiceImpl.java:
public class FillDatabaseServiceImpl implements FillDatabaseService {
private ExtractDataDao extractDataDao;
private final static Gson gson = new Gson();
#Override
public Response start() {
ResultDto resultDto = new ResultDto();
Thread thread = new Thread(this.extractDataDao);
thread.start();
resultDto.setData("Process has started");
return Response.ok(gson.toJson(resultDto)).build();
}
public void setExtractDataDao(ExtractDataDao extractDataDao) {
this.extractDataDao = extractDataDao;
}
}
ExtractDataDao.java:
public class ExtractDataDao implements Runnable {
private CompleteDatabaseModel completeDatabaseModel;
#Override
public void run() {
// Very simplifed class
int numThreads = 5;
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
for (int j = 0; j < numThreads; j++) {
executor.execute(this.completeDatabaseModel);
}
}
public void setCompleteDatabaseModel(CompleteDatabaseModel completeDatabaseModel) {
this.completeDatabaseModel = completeDatabaseModel;
}
}
CompleteDatabaseModel.java
public class CompleteDatabaseModel implements Runnable {
private EntityManagerFactory entityManagerFactory;
private LogDao logDao;
#Override
public void run() {
// Very simplified class
EntityManager entityManager = null;
try {
entityManager = this.entityManagerFactory.createEntityManager();
...
this.logDao.insertLog("Test message", CATEGORY);
...
this.getCountry(entityManager, "country")
} finally {
entityManager.close();
}
}
private synchronized Country getCountry(final EntityManager entityManager, String strCountry) {
Country country = Country.getCountryByCountry(entityManager, strCountry);
if (country == null) {
country = ImdbToMovieDatabase.convert(strCountry, Country.class);
entityManager.getTransaction().begin();
entityManager.persist(country);
entityManager.getTransaction().commit();
country = Country.getCountryByCountry(entityManager, strCountry);
}
return country;
}
public void setLogDao(final LogDao logDao) {
this.logDao = logDao;
}
}
And the classes that provoke deadlock:
LogDao.java:
public class LogDao {
private EntityManagerFactory entityManagerFactory;
public void insertLog(final String message, final Category.CategoryName categoryName) {
Log log = new Log();
EntityManager entityManager = this.entityManagerFactory.createEntityManager();
try {
entityManager.getTransaction().begin();
Category category = Category.getCategoryByName(entityManager, categoryName.name());
log.setCategory(category);
log.setLog(message);
entityManager.persist(log); // // Cause of deadlock in Thread-1 (for example)
entityManager.getTransaction().commit();
} catch (Exception e) {
logger.error("Error persisting log [" + message + "]", e);
entityManager.getTransaction().rollback();
} finally {
if (entityManager != null) {
entityManager.close();
}
}
}
public void setEntityManagerFactory(final EntityManagerFactory entityManagerFactory) {
this.entityManagerFactory = entityManagerFactory;
}
}
Entity.java:
public static Country getCountryByCountry(final EntityManager entityManager, final String strCountry) {
Country country = null;
try {
TypedQuery<Country> typedQuery =
entityManager.createQuery(
"SELECT c FROM Country c WHERE country = ?",
Country.class);
typedQuery.setParameter(1, strCountry);
country = typedQuery.getSingleResult(); // Cause of deadlock in Thread-2 (for example)
} catch (NoResultException e) {
System.out.println("No data found for " + strCountry);
} catch (Exception e) {
e.printStackTrace();
}
return country;
}
Maybe private synchronized Country getCountry is provoking the deadlock? I don't think I should, since logDao has injected its own EntityManagerFactory
Can I use a connection pool?
Thanks in advance.

Spring Transactional Rollback does not work

I have following Thread and transactional method, I have get thrown an exception to test rollback of insertion of DB but notthing is changed. What am I missing?
public class CleaningThread extends Thread {
public void run() {
try {
doJob();
} catch (Exception e) {
e.printStackTrace();
}
}
#Transactional(rollbackFor=Exception.class)
private void doJob() throws Exception {
//INSERT OPERATION
final BatchSqlUpdate bs = new BatchSqlUpdate
bs.flush()
throw new Exception("Custom exception")
//UPDATE
}
}
Application Context:
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>file:conf/offclear.properties</value>
</list>
</property>
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
</bean>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="cleaningThread" class="CleaningThread" scope="prototype"/>
Using Spring 3.1
You're invoking method doJob() from method run() of the same class. That's why you're working with real method, not proxied one.
Actually, this question was covered in this topic: One Service method invoke inner multiple method for Spring transaction

When to create a new SessionFactory?

In my app I've been passing my SessionFatory in my method parameters when I need to access my database within those methods. It's instantiated in my Controller using:
#Autowired
private SessionFactory sessionFactory;
And when I need to access my database I use lines like this:
sessionFactory.getCurrentSession().save()
// or
List products = sessionFactory.getCurrentSession().createSQLQuery("SELECT * FROM PRODUCTS").list();
Should I be creating new sessionFactories to get my current session or should is it okay to pass it as a parameter?
edit [added for clarity]:
From HomeController.java:
#Controller
public class HomeController {
#Autowired
private SessionFactory sessionFactory;
//Correlations Insert Handler
//DOCUMENT CREATE
#RequestMapping(value="/documents", method = RequestMethod.PUT)
public #ResponseBody String insertDocument(HttpServletRequest request, HttpServletResponse response){
DocumentControl documentControl = new DocumentControl();
documentControl.insertDocument(request, response, sessionFactory);
return "went through just find";
}
//DOCUMENT READ
#RequestMapping(value="/documents", method = RequestMethod.GET)
public #ResponseBody List<Documents> getAllDocuments(){
//List documents = sessionFactory.getCurrentSession().createQuery("from Documents").list();
DocumentControl documentControl = new DocumentControl();
List documents = documentControl.getAllDocuments(sessionFactory);
return documents;
}
From DocumentControl.java:
public class DocumentControl {
private static Logger logger = Logger.getLogger(DocumentControl.class.getName());
public DocumentControl(){};
//CREATE
public void insertDocument(HttpServletRequest request, HttpServletResponse response, SessionFactory sessionFactory){
Documents document = new Documents(request)
sessionFactory.getCurrentSession().save(document);
}
//READ
public List<DocumentReturn> getAllDocuments(SessionFactory sessionFactory){
List documents = sessionFactory.getCurrentSession().createQuery("from Documents").list();
return documents;
}
private boolean ifProductExists (String productCode, SessionFactory sessionFactory) {
boolean exists = false;
List<Products> productList = sessionFactory.getCurrentSession().createCriteria(Products.class).add( Restrictions.eq("productCode", productCode)).list();
if ( !productList.isEmpty() && productList.size() > 0 ) {
exists = true;
}
return exists;
}
private boolean ifStandardExists (String standardCode, SessionFactory sessionFactory) {
boolean exists = false;
List<Standards> standardList = sessionFactory.getCurrentSession().createCriteria(Standards.class).add( Restrictions.eq("standardCode", standardCode)).list();
if ( !standardList.isEmpty() && standardList.size() > 0 ) {
exists = true;
}
return exists;
}
}
hibernate.cfg.xml:
hibernate-configuration>
<session-factory>
<property name="hibernate.c3p0.acquire_increment">1</property>
<property name="hibernate.c3p0.idle_test_period">100</property>
<property name="hibernate.c3p0.max_size">10</property>
<property name="hibernate.c3p0.max_statements">10</property>
<property name="hibernate.c3p0.min_size">10</property>
<property name="hibernate.c3p0.timeout">100</property>
<mapping class="***.*****.********.model.Documents"/>
</session-factory>
</hibernate-configuration>
persistence-context.xml:
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key=" hibernate.use_sql_comments">true</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<
property name="sessionFactory" ref="sessionFactory" />
</bean>
SessionFactory is a Singleton. Therefore, you should get it from the ApplicationContext (i.e. using the #Autowired annotation). Passing it as a parameter will only complicate your API.
I think that you should leave creation of SessionFactory to the framework you are using and inject it as a resource just as you are doing by now. Besides you are not creating it in your code at all. Good practice is to create separate session for every chunk of data manipulation, if it is not done by a framework ofc. Session should be your main unit of work.

Spring, Hibernate, transaction management, not committed

So, I try to use Spring for transaction management. Finally my application had worked, but deleting do not commits. Before using Spring I managed transactions in DAO, and I thought that after I can remove this code. But now deleting do not commit to DB.
My DAO:
protected Session getSession() {
Session session = sessionFactory.openSession();
ThreadLocalSessionContext.bind(session);
return session;
}
public void delete(T t) {
Session session = getSession();
// **this approach I used before**
// session.beginTransaction();
// try {
// session.delete(t);
// session.getTransaction().commit();
// } catch (Exception e) {
// session.getTransaction().rollback();
// }
session.delete(t);
}
My applicationContext:
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation=" http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://ngs-java-srv.synapse.com:3306/mybase" />
<property name="username" value="user" />
<property name="password" value="password" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- <property name="configLocation" value="hibernate.cfg.xml" /> -->
<property name="packagesToScan" value="todolist.entity" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<!-- dao beans -->
<bean id="userDao"
class="todolist.dao.hibernate.UserDaoImpl">
<constructor-arg>
<value>todolist.entity.User</value>
</constructor-arg>
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="itemDao"
class="todolist.dao.hibernate.ItemDaoImpl">
<constructor-arg>
<value>todolist.entity.Item</value>
</constructor-arg>
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- service bean -->
<bean id="userService" class="todolist.service.UserServiceImpl">
<property name="userDao" ref="userDao" />
<property name="itemDao" ref="itemDao" />
</bean>
<!-- transaction manager -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" mode="aspectj"/>
<!-- <tx:annotation-driven/> -->
<!-- <aop:config>
<aop:pointcut id="serviceMethods"
expression="execution(* todolist.service.UserService.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods" />
</aop:config>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRES_NEW" />
<tx:method name="deleteItem" no-rollback-for="Exception" />
</tx:attributes>
</tx:advice> -->
<!-- backbeans -->
<bean id="userLogin" class="todolist.jsf.UserLogin"
scope="request">
<property name="userService" ref="userService" />
</bean>
<bean id="userLogged" class="todolist.jsf.UserLogged"
scope="session">
<aop:scoped-proxy />
</bean>
<bean id="userRegister" class="todolist.jsf.UserRegister"
scope="request">
<property name="userService" ref="userService" />
</bean>
<bean id="createItem" class="todolist.jsf.CreateItem"
scope="request">
<property name="userService" ref="userService" />
</bean>
<bean id="todoList" class="todolist.jsf.TodoList"
scope="request">
<property name="userService" ref="userService" />
</bean>
</beans>
UserServiceImpl class
package todolist.service;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.log4j.Logger;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import todolist.exception.AuthorizationError;
import todolist.exception.DuplicateLoginsException;
import todolist.service.StringToHashTool;
import todolist.dao.ItemDao;
import todolist.dao.UserDao;
import todolist.entity.Item;
import todolist.entity.User;
public class UserServiceImpl implements UserService {
//private static final Logger log = Logger.getLogger(UserServiceImpl.class);
private UserDao userDao;
private ItemDao itemDao;
public void setItemDao(ItemDao itemDao) {
this.itemDao = itemDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
#Override
#Transactional(propagation = Propagation.REQUIRES_NEW)
public boolean isUserExists(String login) {
return (userDao.getUserByLogin(login) != null);
}
#Override
#Transactional(propagation = Propagation.REQUIRES_NEW)
public boolean isPasswordCorrect(String login, String password) {
if (isUserExists(login)) {
return userDao.getUserByLogin(login).getPassword()
.equals(StringToHashTool.getHash(password));
} else {
return false;
}
}
#Override
#Transactional(propagation = Propagation.REQUIRES_NEW)
public User login(String login, String password) {
if (isPasswordCorrect(login, password)) {
return userDao.getUserByLogin(login);
} else {
throw new AuthorizationError("Incorrect password");
}
}
#Override
#Transactional(propagation = Propagation.REQUIRES_NEW)
public boolean register(String login, String password) {
if (isUserExists(login)) {
throw new DuplicateLoginsException("Login " + login + " is already used.");
} else {
User user = new User();
user.setLogin(login);
user.setPassword(StringToHashTool.getHash(password));
userDao.save(user);
return true;
}
}
#Override
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void logout() {
}
#Override
#Transactional(propagation = Propagation.REQUIRES_NEW)
public Item createItem(Long creator_id, Long performer_id,
String description, Date dueDate) {
Item item = new Item();
User user = userDao.getById(creator_id);
item.setCreator(user);
user = userDao.getById(performer_id);
item.setPerformer(user);
item.setDescription(description);
item.setStartDate(new Date());
item.setDueDate(dueDate);
itemDao.save(item);
return item;
}
#Override
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void deleteItem(Long item_id) {
Item item = itemDao.getById(item_id);
itemDao.delete(item);
}
#Override
#Transactional(propagation = Propagation.REQUIRES_NEW)
public List<Item> getUserItems(String login) {
User user = userDao.getUserByLogin(login);
return itemDao.getItemsByPerformer(user.getId());
}
#Override
#Transactional(propagation = Propagation.REQUIRES_NEW)
public List<User> getUsers() {
return userDao.getUsers();
}
}
<aop:pointcut
id="serviceMethods"
expression="execution(*todolist.service.UserService.*(..))"
/>
First Step:
Change this to:
<aop:pointcut
id="serviceMethods"
expression="todolist.service.UserServiceImpl.delete()"
/>
See if delete's start committing. I just want to make sure you flush out any typos and what have you. If this explicit config fails then there must be something wrong with the config.
I have had success using the Spring HibernateTransactionManager object and annotation driven configuration. In my application-context, I simply declare the following two beans:
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="session_factory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
Then in my service layer, your UserServiceImpl for example:
public class UserServiceImpl implements UserService {
private final UserDao userDao;
private final ItemDao itemDao;
public UserServiceImpl(UserDao userDao, ItemDao itemDao) {
this.userDao = userDao;
this.itemDao = itemDao;
}
#Override
#Transactional(propagation = Propagation.REQUIRES_NEW)
public boolean isUserExists(String login) {
return (userDao.getUserByLogin(login) != null);
}
...
It's important to move you transaction annotations as close to the view (in MVC) as possible. The reason is 1) performance and 2) atomic transactions. If the call calling the UserService calls the isUserExists, isPasswordCorrect and login methods in a single function call, you would want those to execute in a single transaction. To do so, remove the #Transactional annotation from the UserServiceImpl and move it to the calling class. This ensures that a single transaction is used for all three method calls. For atomic execution, this also works. Think of the classic bank money transfer example. If you had two separate transactions on the UserService, the first two method call can succeed and the last one fail. The firt two transactions would be committed because they are in separate transaction blocks which would leave you database in an inconsistent state. Of course, atomic transactions are mostly relevant for transactions that write data.
Also, don't add the transactional annotations to the interfaces, keep them on the implementations.
Then in my DAO, I simply do a sessionFactory.getCurrentSession() and go from there. getCurrentSession() is thread safe but keep it private to your DAOs.
Use getCurrentSession instead of openSession in your DAO, so that the getSession method looks like:
protected Session getSession() {
return sessionFactory.getCurrentSession();
}
You're creating a new session that uses a different database connection so the transaction set up for the service doesn't apply.
Also as you have them configured the DAOs will have a default scope of singleton; passing an entity as a constructor-argument doesn't make sense. A singleton shouldn't have instance state specific to some individual transaction.

Spring Transaction management

In java Spring, I am facing an issue regarding transaction rollback.
Example:
I have 3 DAOs in my code (A, B, C). All of them extend JDBCTemplate:
#Transaction(propagation=new_required)
public void serviceClassProc() throws Exception {
A.db1();
B.db2();
C.db3();
}
Now with the above code if I throw an exception in B.db2(), nothing gets rolled back.
Now if I modify B.db2 as following:
#Transaction(propagation=nested,rollbackon=Exception.class)
public void db2() throws Exception{
...
throw new Exception();
}
And then call serviceClassProc(), only the transaction in B.db2, gets rolled back.
I want an implementation where all transactions inside serviceClassProc() get rolled back.
Here are the 2 configurations I am using:
<bean id="bonddao" class="com.marki.bonds.staticdata.dao.MuniStaticDataDaoImpl"> <property name="dataSource" ref="c3p0DataSource" /> </bean> <bean id="dcldao" class="com.bonds.staticdata.dao.DclSettingsDaoImpl"> <constructor-arg ref="c3p0DataSource" /> </bean> <bean id="batchlogdao" class="com.bonds.staticdata.dao.MuniFeedHandlerBatchLogDaoImpl"> <constructor-arg ref="c3p0DataSource" /> </bean>
<bean id="bondsApplication" class="com.markit.bonds.staticdata.service.MuniRefDataSyncApp"> <property name="refdataService" ref="refDataSynchService" /> <property name="mailService" ref="mailSender"></property> <property name="batchLogger" ref="batchlogdao"></property> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="c3p0DataSource" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" />
Where I am going wrong? Is it wrong to have 3 DAOs all extending JDBC template? Should all of them share same JDBCTemplate?
You should add rollbackon=Exception.class to the annotation of your service method and remove the transaction annotation entirely from the DAO methods. It is a bad idea to have transaction control at DAO level.
you can use also the : **org.springframework.transaction.interceptor.TransactionAspectSupport;**
here is an example that you can handle it:
#Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, transactionManager = "transactionManager")
public void messageHandler() {
try {
//TODO your own code
} catch (Exception ex) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
} finally {
}
}

Categories

Resources