I just have a new project to maintain, and using Hibernate+Spring. I wrote a DeliveryInfoServiceImpl which have a method to query for some entity but not have any update or save operation, but I have an error:
Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
Unless I add #Transactional on the method or the class.
My questions are :
Why I have to add #Transactional though I am only executing select query?
Does adding #Transactional means enable transaction support ,which may have more unnecessary overhead when I am only using "select" query.
Below is my code snippet:
#Override
public List<UnavailableRestaurantBean> getUnavailableRestaurantBean(String custAddress, List<Long> dishesId) {
List<Dish> dishes = getDishByIds(dishesId);//exception here
....
}
private List<Dish> getDishByIds(List<Long> ids){
return deliveryInfoDao.findByIds(Dish.class,ids);
}
And I have below transaction manager config:
<tx:annotation-driven transaction-manager="myTxManager" />
<bean id="myTxManager" name="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="mySessionFactory">
</property>
</bean>
Related
I'm using Spring and Hibernate trying to update a value of the database. It is needed that, in case of exceptions, save the error message into a table. I'm facing some errors because when I'm able to save the message into the database, the transaction doesn't rollback. Here is the pseudo-code
#Transactional
public class ObjectTable(){
//instanciate other objects
RelatedObject relatedObject = relatedObjectController.getObjectById(primaryKey)
Object object = objectController.getObjectByRelatedObject(relatedObject.getPrimaryKey())
#Transactional(rollbackFor = Exception.class)
public updateObject(Object object) throws MyCustomException{
try{
getHibernateTemplate().getSessionFactory.getCurrentSession().evict(object);
getHibernateTemplate().getSessionFactory.getCurrentSession().saveOrUpdate(object);
getSession.flush();
}catch(Exception ex){
saveErrorMessageIntoDatabase(ex.getMessage, this.relatedObject);
throw new MyCustomException(ex.getMessage)
}
}
public saveErrorMessageIntoDatabase(String message, RelatedObject relatedObject){
relatedObject.setErrorMessage(message);
getHibernateTemplate().getSessionFactory.getCurrentSession().evict(relatedObject);
getHibernateTemplate().getSessionFactory.getCurrentSession().saveOrUpdate(relatedObject);
getSession.flush();
}
}
With this kind of tought, I'm not being able to save the message in relatedObject and rollback the changes in object. Making a few variations, such as putting a propagation = Propagation.REQUIRES_NEW or propagation = Propagation.REQUIRED or removing the Excpection.class for rollback, I get some other behaviours, like save the error message but also writes the changes of object when there is an exception, or rollin back the changes but also not writing the error message into relatedObject.
I also tried with merge instead of saveOrUpdate, but with no success.
Could someone help me write a way to rollback changes in case of error but also save the error message into the database?
Thank you.
*I don't post the actual code because this is not a personal project.
EDIT:
My transactionManager is configured as a XML bean where first I create the objectTableTarget setting the sessionFactory property and bellow that I set another bean objectTable refering to the methods I want to set as transactional.
<bean id="objectTableTarget" class="br.com.classes.ObjectTable" singleton="true">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="objectTable" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager" />
<property name="target" ref="objectTableTarget" />
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
<prop key="updateObject*">PROPAGATION_REQUIRED,-br.com.package.exception.MyCustomException</prop>
</props>
</property>
</bean>
move your log method into separate service and annotate it with
#Transactional(propagation = Propagation.REQUIRES_NEW)
#Service
public class ErrorLoggerService(){
#Transactional(propagation = Propagation.REQUIRES_NEW)
public saveErrorMessageIntoDatabase(String message, RelatedObject relatedObject){
relatedObject.setErrorMessage(message);
getHibernateTemplate().getSessionFactory.getCurrentSession().evict(relatedObject);
getHibernateTemplate().getSessionFactory.getCurrentSession().saveOrUpdate(relatedObject);
getSession.flush();
}
}
Then spring can create proxy on your method and error should be write in new transaction
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.
I am migrating from iBatis 2.3 to MyBatis 3.2.8. Everything is going well, but I do have a question when it comes to transactions and batch operations. Towards the bottom of https://mybatis.github.io/spring/sqlsession.html, it says:
The caveat to this form is that there cannot be an existing transaction running >with a different ExecutorType when this method is called. Either ensure that >calls to SqlSessionTemplates with different executor types run in a separate >transaction (e.g. with PROPAGATION_REQUIRES_NEW) or completely outside of a >transaction.
I have a service call that is annotated with #Transactional that makes multiple Dao Calls:
#Override
#Transactional
public void saveMassUploadOrder(List<IndividualOrder> orders) {
List<IndividualOrderItem> ioItems = new ArrayList<IndividualOrderItem>();
for (IndividualOrder order : orders) {
ioItems.addAll(order.getIoItems());
}
individualOrderDao.insertBatch(orders);
individualOrderItemDao.insertBatch(ioItems);
}
The DAOs would look like:
public class IndividualOrderDAOImpl implements IndividualOrderDAO{
private SqlSession sqlSession;
public void setSqlSession(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
public void insertBatch(final List<IndividualOrder> orders) {
for (IndividualOrder order: orders){
sqlSession.insert("namespace.insert", order);
}
}
}
The config looks like:
<bean id="batchSqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="doeSqlSessionFactory" />
<constructor-arg index="1" value="BATCH" />
</bean>
<bean id="individualOrderDao" class="com.doe.mybatis.jdoe.dao.IndividualOrderDAOImpl">
<property name="sqlSession" ref="batchSqlSession" />
</bean>
<bean id="individualOrderItemDao" class="com.doe.mybatis.jdoe.dao.IndividualOrderItemDAOImpl">
<property name="sqlSession" ref="batchSqlSession" />
</bean>
So - would the multiple DAO calls within this service call be OK since the service is opening a transaction and its not working in a batch mode and making calls to these DAOs? Obviously, I am wanting the call to this service to not actually insert any orders or order items if there is a failure somewhere during the execution.
I haven't migrated these DAOs yet, and I can probably set up a catastrophic event during a test batch insert and check the data, I was just wanting to get some other opinions before I started to see if I have to set this up differently or not.
Any help appreciated!
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 ;)
My application is based on Hibernate 3.2 and Spring 2.5. Here is the transaction management related snippet from the application context:
<tx:annotation-driven transaction-manager="txManager"/>
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
<property name="nestedTransactionAllowed" value="true"/>
</bean>
<bean id="transactionTemplate" classs="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="txManager"/>
</bean>
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="configLocation" value="classpath:/hibernate.cfg.xml"></property>
</bean>
For all the DAO's there are relevant Service class and the transactions are handled there using #Transactional on each method in the service layer. However there is a scenario now that a method in DAO say "parse()" is called from the service layer. In the service layer I specified #Transactional(readOnly=false). This parse method in the DAO calls another method say "save()" in the same DAO which stores a large number of rows (around 5000) in the database. Now the save method is called in a loop from the parse function. Now the issue is that after around 100 calls to the "save" method.. i sometimes get a OutOfMemory Exception or sometimes the program stops responding.
For now these are the changes which I have made to the save method:
Session session = getHibernateTemplate().getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
int counter = 0;
if(books!=null && !books.isEmpty()){
for (Iterator iterator = books.iterator(); iterator
.hasNext();) {
Book book = (Book) iterator.next();
session.save(book);
counter++;
if(counter % 20==0) {
session.flush();
session.clear();
}
}
}
tx.commit();
session.close();
This is the only method in my application where I start a transaction like this and commit it at the end of method. Otherwise I normally just call getHibernateTemplate.save(). I am not sure whether I should perform transaction management for this save method separately in the DAO by placing #Transactional(readOnly=false, PROPOGATION=NEW) on save(), or is this approach okay?
Also I have updated the hibernate.jdbc.batch_size to 20 in the hibernate.cfg configuration file.
Any suggestions?
For the batch insertion with hibernate, the best practice is StatelessSession, it doesn`t cache any states of your entity, you will not encounter OutOfMemory, the code like:
if (books == null || books.isEmpty) {
return;
}
StatelessSession session = getHibernateTemplate().getSessionFactory().openStatelessSession();
Transaction tx = session.beginTransaction();
for (Book each : books) {
session.insert(book);
}
tx.commit();
session.close();
And the Transaction of StatelessSession is independent from the current transaction context.
You only need the bit with flushing and clearing the session. Leave transaction management to Spring. Use sessionFactory.getCurrentSession() to reach the session that Spring has already opened for you. Also, Spring's recent recommmendation is to avoid HibernateTemplate and work directly with Hibernate's API. Inject SessionFactory to your dao-bean.
I would refactor parse in a way it doesn't call save directly but takes some callback from service layer. Service layer would pass its transactional method with save call as this callback.
It may not work exactly as decribed in your case but from this short description this would be something I'd try.