I have an entity User, a Repository/Dao class UserDao (using Spring Data JPA) and a Service class UserService with a method addUser annotated as #Transactional:
#Service
public class UserService {
#Autowired
private UserDao userDao;
#Transactional
public void addUser() throws Exception {
User user = new User();
user.setUsername("aaa");
// Save the user, but since this method have the #Transactional
// annotation it should not be committed....
userDao.save(user);
// Forcing an error here I expected that the previous operation
// were rolled back.. Instead the user is saved in the db.
if ("".equals("")) {
throw new Exception("something fails");
}
// Other operations (never executed in this example)
user.setUsername("bbb");
userDao.save(user);
return;
} // method addUser
} // class UserService
The UserDao is simply this:
#Transactional
public interface UserDao extends CrudRepository<User, Long> { }
Reading the Spring Data JPA documentation and other questions on the same argument (1, 2) my expectations were that each operations inside a method marked with #Transactional will be rolled back if some error occurs..
What am I doing wrong?
Is there a way for rollback the save operation in the previous example if an error occurs?
Your understanding is correct however automatic rollback only occurs for runtime, unchecked exceptions.
So, assuming your transaction manager is configured correctly, to rollback on a non-runtime, checked exception add the rollbackFor attribute to your transactional annotation:
#Transactional(rollbackFor=Exception.class)
public void addUser() throws Exception {
}
You need to add things to your Xml configuration file.. you need to add trnasaction manager.
<tx:annotation-driven transaction-manager="txMgrDataSource" />
<!-- Creating TransactionManager Bean, since JDBC we are creating of type
DataSourceTransactionManager -->
<bean id="txMgrDataSource"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="DataSource" />
</bean>
Assuming your data source is:
<bean id="DataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="####" />
<property name="url"
value="jdbc:sqlConnection" />
<property name="username" ref="user" />
<property name="password" ref="pass" />
</bean>
Related
Assuming I have the next code:
#Autowired
private IManager1 manager1;
#Autowired
private IManager2 manager2;
#Autowired
private IManager3 manager3;
#Transactional
public void run() {
manager1.doStuff();
manager2.registerStuffDone();
manager3.doStuff();
manager2.registerStuffDone();
manager1.doMoreStuff();
manager2.registerStuffDone();
}
If any exception is launched I want to rollback everything done by the "doStuff()" methods, but I don't want to rollback the data recorded by the "registerStuffDone()" method.
I've been reading the propagation options for #Transactional annotation, but I don't understand how to use them properly.
Every manager internally uses hiberante to commit the changes:
#Autowired
private IManager1Dao manager1Dao;
#Transactional
public void doStuff() {
manager1Dao.doStuff();
}
Where the dao looks like this:
#PersistenceContext
protected EntityManager entityManager;
public void doStuff() {
MyObject whatever = doThings();
entityManager.merge(whatever);
}
This is my applicationContext configuration:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSourcePool" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
</property>
</bean>
<bean id="entityManager" class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
Ideas?
You need 2 transactions, one for the stuff to be committed and one for the stuff to be rolled back.
#Transactional(propagation = Propagation.REQUIRES_NEW, noRollbackFor={Exception1.class, Exception2.class})
public void registerStuffDone()() {
//code
}
Your run method will then use the first transaction and that will be rolled back, but the registerStuffDone method will start a second transaction which will be commited.
You are using declarative transaction and want to control like program sense. For this reason, you need more practice and deep understanding about Spring transaction definition such as PROPAGATION, ISOLATION etc...
Programmatic transaction management: This means that you have manage the transaction with the help of programming. That gives you extreme flexibility, but it is difficult to maintain.
VsDeclarative transaction management: This means you separate transaction management from the business code. You only use annotations or XML based configuration to manage the transactions.
Perhaps, alternative way for your questions by Programmatic transaction management.
/** DataSourceTransactionManager */
#Autowired
private PlatformTransactionManager txManager;
public void run() {
try {
// Start a manual transaction.
TransactionStatus status = getTransactionStatus();
manager1.doStuff();
manager2.registerStuffDone();
manager3.doStuff();
manager2.registerStuffDone();
manager1.doMoreStuff();
manager2.registerStuffDone();
//your condition
txManager.commit(status);
//your condition
txManager.rollback(status);
} catch (YourException e) {
}
}
/**
* getTransactionStatus
*
* #return TransactionStatus
*/
private TransactionStatus getTransactionStatus() {
DefaultTransactionDefinition dtd = new DefaultTransactionDefinition();
dtd.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
dtd.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
dtd.setReadOnly(false);
return txManager.getTransaction(dtd);
}
Note: It does not mean you need to always use one approach like Programmatic transaction management. I prefer mixed approach. Please use easy way like Declarative transaction for simple database services, otherwise, just control with Programmatic transaction in your services will save your logic easily.
1. Spring MVC application-context.xml
<tx:annotation-driven/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="username" value="test"/>
<property name="password" value="test"/>
<property name="url" value="jdbc:mysql://localhost:13306/test"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
2. Service impl class
#Override
#Transactional
public void deleteCommentAndFiles(int commentId) {
int deletedCommentCount = commentDAO.deleteComment(commentId);
int deletedFileCount = fileDAO.deleteFiles(commentId);
if (deletedCommentCount != 1) {
throw new IncorrectUpdateSemanticsDataAccessException("Deleted comment not 1 [deletedCount : " + deletedCommentCount);
}
if (deletedFileCount != 1) {
throw new IncorrectUpdateSemanticsDataAccessException("Deleted file not 1 [deletedCount : " + deletedCommentCount);
}
}
3. Test Case
#Test
public void rollbackT() {
boolean hasException = false;
int sizeBeforDelete = commentDAO.selectCountByArticle(1);
try {
commentService.deleteCommentAndFiles(1);
} catch (RuntimeException e) {
hasException = true;
}
Assert.assertTrue(hasException);
Assert.assertEquals(sizeBeforDelete, commentDAO.selectCountByArticle(1));
}
in Test case
first Assert.assertTrue(hasException); is passed but
Assert.assertEquals(sizeBeforDelete, commentDAO.selectCountByArticle(1)) this case fail Expected : 1 but Actual : 0
this second TC fail mean Exception is occur but doesn't rollback delete comment
deleteCommentAndFiles method throw exception but doesn't rollback
Im trying to use #Transactional(propagation=Propagation.REQUIRED, rollbackFor={IncorrectUpdateSemanticsDataAccessException.class})
but same doesn't work
why #Transactional annotaion doesn't work?
I've also had the same issue. I moved the #transactional to the controller in order to works.
#EnableTransactionManagement and only looks for #Transactional on beans in the same application context they are defined in. This means that, if you put annotation driven configuration in a WebApplicationContext for a DispatcherServlet, it only checks for #Transactional beans in your controllers, and not your services. See Section 21.2, “The DispatcherServlet” for more information.
The database has to support transactions. In case of MySQL you need to create a table with type of InnoDB, BDB or Genini.
Per default myisam does not support transactions.
I didn't see your main test class, but i assume you use the default spring configuration for junit :
By default, a test is launched on his own implicit transaction. when you call your service, it start a "sub" logical transaction (which is a part of the test transaction, because you didn't use require_new for the propagation). when this transaction failed, the main transaction is marked for rollback, but the rollback is not done until the main transaction has finished, when the test end
If you want to test a rollback, you shouldn't use this implicit transaction, launched by the test framework, or you can use TestTransaction.end() before your assertions to force this transaction to be committed or rollback.
I am using Spring and Hibernate in my application and using Spring Transaction.
So I have a service layer with annotation #Transaction on methods and DAO layer having methods for database query.
#Transactional(readOnly = false)
public void get(){
}
The issue is when I want to save an object in the database,then I have to use session.flush() at the end of DAO layer method. Why?
I think if I have annotated #Transaction, then Spring should automatically commit the transaction on completion of the service method.
DAO layer :
public BaseEntity saveEntity(BaseEntity entity) throws Exception {
try {
Session session = sessionFactory.getCurrentSession();
session.saveOrUpdate(entity);
session.flush();
} catch (HibernateException he) {
throw new Exception("Failed to save entity " + entity);
}
return entity;
}
Service layer :
#Transactional(readOnly = false)
public BaseEntity saveEntity(BaseEntity entity) throws Exception {
return dao.saveEntity(entity);
}
spring config :
<context:property-placeholder properties-ref="deployProperties" />
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- Activate Spring Data JPA repository support -->
<jpa:repositories base-package="com" />
<!-- Declare a datasource that has pooling capabilities-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close"
p:driverClass="${app.jdbc.driverClassName}"
p:jdbcUrl="${app.jdbc.url}"
p:user="${app.jdbc.username}"
p:password="${app.jdbc.password}"
p:acquireIncrement="5"
p:idleConnectionTestPeriod="60"
p:maxPoolSize="100"
p:maxStatements="50"
p:minPoolSize="10" />
<!-- Declare a JPA entityManagerFactory -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:persistenceXmlLocation="classpath*:META-INF/persistence.xml"
p:persistenceUnitName="hibernatePersistenceUnit"
p:dataSource-ref="dataSource"
p:jpaVendorAdapter-ref="hibernateVendor"/>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
p:dataSource-ref="dataSource" p:configLocation="${hibernate.config}"
p:packagesToScan="com" />
<!-- Specify our ORM vendor -->
<bean id="hibernateVendor" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
p:showSql="false"/>
<!-- Declare a transaction manager-->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory"/>
Yes, if you have #Transactional for your DAO method then you need not flush the session manually, hibernate will take care of flushing the session as part of committing the transaction if the operations in the method are successful.
Check this link to know on how #Transactional works - Spring - #Transactional - What happens in background?
By default, hibernate stacks its queries so they can be optimized when they are finally executed onto the database.
The whole point of flush is to flush this stack and execute it in your transaction onto the database. Your leaving the "safe" house of the JVM and execute your query on a big strange database.
This is why you can't select something you've just saved without a flush. It's simply not in the database yet.
The meaning of commit is to end the transaction and make changes of the database visible for others. Once commit has been executed there's no return possible anymore.
Frankly I'm not exactly sure if it is a best practice but for normal CRUD operations you should be able to add flush into your DAO layer.
This way you don't need to worry about it into the service layer.
If you want java to optimize your transaction then you'll have to add it into your service layer. But remember that you don't need to solve performance issues when there aren't any! Flushes all over your code into the service layer is not good for the code readability. Keep it simple and stupid ;)
I´m new to JPA. I´m developing an application which uses JPA (Hibernate implementation) and Spring. I´ve declared a persistence unit in my persistence.xml and configuration about EntityManagerFactory in my Spring config files. Something like this:
<bean id="myEmf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="my.package" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">create-drop</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
</props>
</property>
Then I have some DAOs where I inject the entityManager with the #PersistenceContext annotation:
public MyDaoImpl implements MyDao{
private EntityManager entityManager;
#PersistenceContext
private void setEntityManager(EntityManager em){
this.entityManager = em;
}
}
And finally, I have some services where DAOs are injected (by #Autowired Spring's annotation):
public MyServiceImpl implements MyService{
#Autowired
private MyDao myDao;
public List<MyEntity> readOperation(){
//
return myDAo.searchAll();
}
}
As its a read only operation I thought it wasn´t needed the #Transactional annotation, but without it, there is an exception:
java.lang.IllegalStateException: No transactional EntityManager available
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:223)
at $Proxy121.unwrap(Unknown Source)
I´ve read some other posts like this: java.lang.IllegalStateException: No transactional EntityManager available
And all is said is that the transactional annotation is needed. It´s true that it works with it, but I´d like to know (and why) if all methods (even read only operations) must be transactional.
A JPA Transaction is needed for all your methods - essentially a transaction is what opens a Hibernate session, and you need an open session to interact with it.
You can annotate the transactions as readonly or readwrite, and you can also annotate at the class level to save you annotating each method. For example:
#Transactional(readOnly = true)
public MyDaoImpl implements MyDao{
private EntityManager entityManager;
#PersistenceContext
private void setEntityManager(EntityManager em){
this.entityManager = em;
}
#Transactional(readOnly = false)
public void saveItem(MyEntity entity) {
}
public List<MyEntity> searchAll() {
}
}
You a need a transaction for all operations that change anything in DB (the only exception is SELECT queries, without locking). Check this answer.
I'm having and issue with Hibernate where I'm trying to save and entity through and Abstract Dao class and it seems to not want to save or update however my delete functionality seems to work flawlessly. So I was wondering if someone could point out where I seem to be going wrong.
I did debug to see that the entity is in-fact fully loaded into the variable when it goes to update or save.
Thanks in advance for any ideas.
The class where I'm pulling all the data in:
try{
accountDao.add(newAccount);
return SUCCESS;
}
catch(Exception e){
this.addActionError("And unknown error has occurred please try refreshing the page");
return INPUT;
}
Which goes to this hibernate utility class, the entity data is in the parameter being passed into the add function being called but it just seems that hibernate won't save it:
public abstract class AbstractDao<Entity> extends HibernateDaoSupport {
public void add(Entity entity) {
getHibernateTemplate().save(entity);
}
public void delete(Entity entity) {
getHibernateTemplate().delete(entity);
}
public abstract List<Entity> findAll();
public abstract List<Entity> findById(Long id);
public void update(Entity entity) {
getHibernateTemplate().update(entity);
}
}
Bean definition
<bean id="accountDao" class="com.dao.AccountDao">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="utilityDao" class="com.dao.UtilityDao">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="account_usageDao" class="com.dao.Account_UsageDao">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="addCustomerInfo" class="com.action.customer.AddAction" scope="prototype">
<property name="accountDao" ref="accountDao"/>
<property name="utilityDao" ref="utilityDao"/>
<property name="account_usageDao" ref="account_usageDao"/>
</bean>
Update 1: Bean definition
I can't tell without seeing more of the implementation. It appears you are using spring. This problem usually happens to me when I forget to define a transaction context.
Have a look at: Spring Transaction management