Questions about spring+hibernate transaction control

Recently, the project has built the framework of springMVC+spring+hibernate, and the transaction uses spring's annotation @Transactional
spring and hibernate configuration files
<?xml version="1.0" encoding="UTF-8"?>
<!--
	  - Middle tier application context definition for the image database.
	  -->
<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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
	http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"
	default-autowire="byName" default-lazy-init="false">
	<!-- If you don't add default-autowire="byName", you will not get hibernateTemplate -->
	<!-- Configure the scope of spring scan annotations-->
	<context:component-scan base-package="com.holpe.dao.*"  />

	<!-- Define the file where the placeholder is located -->
	<context:property-placeholder location="classpath:jdbc.properties"/>
	
	<!-- Configure the data source, using placeholders -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
		<property name="driverClass" value="${driverClassName}"/>
		<property name="jdbcUrl" value="${jdbc_url}"/>
		<property name="user" value="${jdbc_username}"/>
		<property name="password" value="${jdbc_password}"/>
	</bean>


	<!-- Configure sessionFactory, using placeholders -->
	<bean id="sessionFactory"
		class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
		<property name="dataSource">
			<ref bean="dataSource" />			
		</property>
		<!-- Load the entity class in the form of hibernate's jpa annotation -->
		<!-- <property name="packagesToScan" value="com.holpe.entity*" /> -->
		
	   <!-- Automatically scan hibernate files and .hbm files configured in hbm mode -->
		<property name="mappingLocations">
			<list><value>classpath*:hibernate/*.hbm.xml</value></list>
		</property>
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl}</prop>
				<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
				<prop key="hibernate.dialect">${hibernate.dialect}</prop>
				<prop key="hibernate.query.factory_class">org.hibernate.hql.ast.ASTQueryTranslatorFactory</prop>
				<prop key="connection.useUnicode">true</prop>
				<prop key="connection.characterEncoding">UTF-8</prop>
				<!-- <prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate3.SpringSessionContext</prop> -->
			</props>
		</property>
	</bean>
	

		
</beans>


Then run the test to create an exception in the service and test the transaction rollback function
Test case 1
	public void delRole(String roleIds) {
		String roleSql="DELETE r from t_role r where r.role_id in("+roleIds+")";
		String roleRelationSql="DELETE r from t_role_menu_relation r where r.role_id  in("+roleIds+")";
		String userRelationSql="DELETE r from t_user_role_relation r where r.role_id  in("+roleIds+")";
		dao.execSql(roleSql);
		dao.execSql(roleRelationSql);
		dao.execSql(userRelationSql);
		int a=10;
	}

Test result: there is no rollback,
and other tests are done. Test
case 2
public void saveRole(TRole role) {
		dao.saveOrUpdate(role);
		int a=10;
	}

Test result: rollback
Combining above two test cases, we can know that the transaction configuration is working, where is the problem? Then I entered the dao layer for step-by-step troubleshooting and found
execSql
public boolean execSql(String sql) {
		String strsql = sql;
		SessionFactory sf = null;
		Session session = null;
		Transaction tx = null;
		try {
			sf = getHibernateTemplate().getSessionFactory();
			session = sf.openSession();
			tx = session.beginTransaction();
			Query query = session.createSQLQuery(strsql);
			query.executeUpdate();
			tx.commit();
		} catch (Exception e) {
			tx.rollback();
			e.printStackTrace ();
			return false;
		} finally {
			if (session != null && session.isOpen()) {
				session.close();
			}
		}
		return true;
	}


saveOrUpdate
	/**
	 * Save entities including additions and modifications
	 *
	 * @param t
	 * Entity object
	 */
	@Override
	public <T> void saveOrUpdate(T t) {
		getHibernateTemplate().saveOrUpdate(t);

	}


After a comparison, I found that using the method provided by getHibernateTemplate() can roll back the transaction, while session.createSQLQuery does not roll back the transaction using native sql or Hql. Knowing the reason, then Baidu went to find the answer, and the answer was: openSession (), the difference between getCurrentSession()
1. What is the difference between getCurrentSession() and openSession()?
When the SessionFactory is started, Hibernate will create the corresponding CurrentSessionContext according to the configuration. When getCurrentSession() is called, the method that is actually executed is CurrentSessionContext.currentSession(). When currentSession() is executed, if the current Session is empty, currentSession will call openSession of SessionFactory. So getCurrentSession() is a better way to get a Session for Java EE.
* The session created with getCurrentSession() will be bound to the current thread, but the session created with openSession() will not
* The session created with getCurrentSession() will be automatically closed when commit or rollback, while the session created with openSession() The session must be closed manually
2. To use getCurrentSession(), you need to add the following configuration to the hibernate.cfg.xml file:
* If you are using a local transaction (jdbc transaction)
<property name="hibernate.current_session_context_class">thread</property>
* If using a global transaction (jta transaction)
<property name="hibernate.current_session_context_class">jta</property>
* If using the session management mechanism (less commonly used)
<property name="hibernate.current_session_context_class">managed</property>




1 The session created by getCurrentSession will be bound to the current thread, while openSession will not.

2 The thread created by getCurrentSession will be automatically closed after the transaction is rolled back or the transaction is committed, and the openSession must be closed manually.





Here getCurrentSession local transaction (local transaction: jdbc), the following settings should be made in the configuration file

    * If using local transaction (jdbc) transaction)
<property name="hibernate.current_session_context_class">thread</property>
* if using global transaction (jta transaction)
<property name="hibernate.current_session_context_class">




openSession() re-establishes a new session

In an application, if the DAO layer uses spring's hibernate template to control the session's life cycle through Spring, getCurrentSession() is preferred.

Combined with this project, I use spring for transaction control, and I must choose getCurrentSession ()
to modify execSql
public boolean execSql(String sql, Object... params) {
		String strsql = sql;// "delete from TPrintPaper where id = ?";
		SessionFactory sf = null;
		Session session = null;
		Transaction tx = null;
		try {
			sf = getHibernateTemplate().getSessionFactory();
			session=sf.getCurrentSession();
//			session = sf.openSession();
//			tx = session.beginTransaction();
			Query query = session.createSQLQuery(strsql);
			for (int position = 0; position < params.length; position++) {
				query.setParameter(position, params[position]);
			}
			query.executeUpdate();
//			tx.commit();
		} catch (Exception e) {
			
//			tx.rollback();
			e.printStackTrace ();
			return false;
		} finally {
			if (session != null && session.isOpen()) {
				session.close();
			}
		}
		return true;
	}


Online information says that using getCurrentSession() requires adding the following configuration to the hibernate.cfg.xml file:
* If you are using a local transaction (jdbc transaction)
<property name="hibernate.current_session_context_class">thread</property>

* If using a global transaction (jta transaction)
<property name="hibernate.current_session_context_class">jta</property>

* If you are using the session management mechanism (less commonly used)
<property name="hibernate.current_session_context_class">managed</property>


Modify the configuration files of spring and hibernate
<!-- Configure sessionFactory, use placeholders -->
	<bean id="sessionFactory"
		class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
		<property name="dataSource">
			<ref bean="dataSource" />			
		</property>
		<!-- Load the entity class in the form of hibernate's jpa annotation -->
		<!-- <property name="packagesToScan" value="com.holpe.entity*" /> -->
		
	   <!-- Automatically scan hibernate files and .hbm files configured in hbm mode -->
		<property name="mappingLocations">
			<list><value>classpath*:hibernate/*.hbm.xml</value></list>
		</property>
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl}</prop>
				<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
				<prop key="hibernate.dialect">${hibernate.dialect}</prop>
				<prop key="hibernate.query.factory_class">org.hibernate.hql.ast.ASTQueryTranslatorFactory</prop>
				<prop key="connection.useUnicode">true</prop>
				<prop key="connection.characterEncoding">UTF-8</prop>
				 <prop key="hibernate.current_session_context_class">jta</prop>
			</props>
		</property>
	</bean>


Then use the above test cases to test, the result is

	org.hibernate.HibernateException: No TransactionManagerLookup specified
	at org.hibernate.context.JTASessionContext.currentSession(JTASessionContext.java:54)
	at org.hibernate.impl.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:544)
	at com.holpe.dao.impl.BaseDaoImpl.execSql(BaseDaoImpl.java:683)
	at com.holpe.service.impl.UserServiceImpl.updatePwd(UserServiceImpl.java:123)
	at com.holpe.service.impl.UserServiceImpl$$FastClassByCGLIB$$a90d3b56.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:698)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
	at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:96)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631)
	at com.holpe.service.impl.UserServiceImpl$$EnhancerByCGLIB$$31e15f26.updatePwd(<generated>)
	at com.holpe.action.UserAction.updatePwd(UserAction.java:117)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:219)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:745)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:686)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:936)
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:838)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:812)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
	at com.holpe.comom.SessionListenerFilter.doFilter(SessionListenerFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
	at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
	at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1070)
	at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:611)
	at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:745)


Online query information

found that it was configured in the configuration of spring and hibernate
<prop key="hibernate.current_session_context_class">jta</prop>

When using spring to manage transactions, hibernate.current_session_context_class will be set to org.springframework.orm.hibernate3.SpringSessionContext by default,
so I commented out this line, and then I did not report an error, and reported another error
session is closed

and checked my own writing dao layer code discovery
	public boolean execSql(String sql) {
		String strsql = sql;// "delete from TPrintPaper where id = ?";
		SessionFactory sf = null;
		Session session = null;
//		Transaction tx = null;
		try {
			sf = getHibernateTemplate().getSessionFactory();
			session=sf.getCurrentSession();
//			session = sf.openSession();
//			tx = session.beginTransaction();
			Query query = session.createSQLQuery(strsql);
			query.executeUpdate();
//			tx.commit();
		} catch (Exception e) {
//			tx.rollback();
			e.printStackTrace ();
			return false;
		} finally {
			[color=red]if (session != null && session.isOpen()) {
				session.close();
			}[/color]
		}
		return true;
	}

There is a problem in the place marked in red. Using getCurrentSession() will close the session by itself. You don't need to close the session manually. Delete this and everything is normal.
Summary:
1. The transaction does not work, how to troubleshoot
1. Check whether the configuration file is correct
2. Whether a try catch is added to the code to catch the exception, but it is not thrown
3. Whether the mysql database is used, because some engines in the mysql database do not support transactions
Reference : http://www.baike369.com/content/?id=5456
4. Find your own underlying dao code
2. Pay attention to the difference between openSession() and getCurrentSession()

(1) openSession is a new Session every time it is opened , so the Session instances obtained multiple times are different, and the close method needs to be manually called to close the Session.
(2) getCurrentSession obtains the Session from the current context and binds it to the current thread. When it is called for the first time, a Session instance will be created. If the Session is not closed, the same Session instance will be obtained multiple times; transaction submission or Sesison is automatically closed when rolling back, no manual shutdown is required.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326564239&siteId=291194637