<tx:advice id="serviceAdvice"> <tx:attributes> <tx:method name="save*" read-only="false" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="servicePointCut" expression="execution(* com.example.service.*ServiceImp.*(..))"/> <aop:advisor advice-ref="serviceAdvice" pointcut-ref="servicePointCut"/> </aop:config>
<!--DataSource--> <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"> <property name="driverClassName" value="${dataSource.driverName}"/> <property name="username" value="${dataSource.username}"/> <property name="url" value="${dataSource.url}"/> <property name="password" value="${dataSource.password}"/> </bean> <!--SessionFactory--> <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="configLocation" value="classpath:hibernate.cfg.xml"/> </bean>
SessionFactory sessionFactory = (SessionFactory) applicationContext.getBean("sessionFactory"); //HibernateTemplate也可以在applicationContext.xml中配置,随意发挥 HibernateTemplate hibernateTemplate = new HibernateTemplate(sessionFactory); //自定义事务属性、规则 //这里想在applicationContext.xml中配置bean也可以,随意发挥 TransactionDefinition transactionDefinition = new TransactionDefinition() { public int getPropagationBehavior() { return PROPAGATION_REQUIRED; } public int getIsolationLevel() { return ISOLATION_REPEATABLE_READ; } public int getTimeout() { return TIMEOUT_DEFAULT; } public boolean isReadOnly() { //设置readOnly为false //解决文章开头提到的异常 return false; } public String getName() { return "thomas-transaction"; } }; //因为使用的是Hibernate,所以就使用HibernateTransactionManager //也可以使用其他的,如DataSourceTransactionManager(针对JDBC) PlatformTransactionManager txManager = new HibernateTransactionManager(sessionFactory); //开始事务 TransactionStatus txStatus = txManager.getTransaction(transactionDefinition); try{ hibernateTemplate.save(new User()); //提交事务 txManager.commit(txStatus); }catch (Exception e) { e.printStackTrace(); //事务回滚 txManager.rollback(txStatus); }
public interface TransactionDefinition { int getPropagationBehavior(); int getIsolationLevel(); int getTimeout(); boolean isReadOnly(); String getName(); }
public interface PlatformTransactionManager { TransactionStatus getTransaction (TransactionDefinition definition) throws TransactionException; void commit(TransactionStatus status) throws TransactionException; void rollback(TransactionStatus status) throws TransactionException; }
默认情况下,对于RuntimeException会回滚,而对于CheckedException不会回滚。
public interface TransactionStatus extends SavepointManager { boolean isNewTransaction(); boolean hasSavepoint(); void setRollbackOnly(); boolean isRollbackOnly(); void flush(); boolean isCompleted(); }
<!--DataSource--> <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"> <property name="driverClassName" value="${dataSource.driverName}"/> <property name="username" value="${dataSource.username}"/> <property name="url" value="${dataSource.url}"/> <property name="password" value="${dataSource.password}"/> </bean> <!--SessionFactory--> <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="configLocation" value="classpath:hibernate.cfg.xml"/> </bean> <!--Transaction Manager--> <bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"/> </bean>
HibernateTransactionManager transactionManager = (HibernateTransactionManager) applicationContext.getBean("transactionManager"); final HibernateTemplate hibernateTemplate = new HibernateTemplate(transactionManager.getSessionFactory()); TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); //解决文章开头异常的关键 transactionTemplate.setReadOnly(false); Serializable id = transactionTemplate.execute(new TransactionCallback<Serializable>() { public Serializable doInTransaction(TransactionStatus status) { Serializable id = null; try{ User user = new User(username, password, UserType.CUSTOMER, email, null); id = hibernateTemplate.save(user); }catch (Exception e) { e.printStackTrace(); status.setRollbackOnly(); } return id; } }); System.out.println(id);
@Override public Serializable save(final Object entity) throws DataAccessException { return executeWithNativeSession(new HibernateCallback<Serializable>() { @Override public Serializable doInHibernate(Session session) throws HibernateException { checkWriteOperationAllowed(session); return session.save(entity); } }); } 追踪executeWithNativeSession方法: public <T> T executeWithNativeSession(HibernateCallback<T> action) { return doExecute(action, true); } 追踪doExecute()方法: protected <T> T doExecute(HibernateCallback<T> action, boolean enforceNativeSession) throws DataAccessException { Assert.notNull(action, "Callback object must not be null"); Session session = null; boolean isNew = false; try { //如果没有使用TransactionManager,则不会有随TransactionManager //的初始化而创建的Session实例 session = getSessionFactory().getCurrentSession(); } catch (HibernateException ex) { logger.debug("Could not retrieve pre-bound Hibernate session", ex); } //此处session为null,Spring调用Hibernate的API新建一个 //这里是关键之一,新建的Session的FlushMode是MANUAL,只读不可写。 if (session == null) { session = getSessionFactory().openSession(); session.setFlushMode(FlushMode.MANUAL); isNew = true; } try { enableFilters(session); Session sessionToExpose = (enforceNativeSession || isExposeNativeSession() ? session : createSessionProxy(session)); //如果开启了事务,则跳过if(session == null)执行到这里 //doInHibernate在save()方法中,以匿名内部类的方式定义 return action.doInHibernate(sessionToExpose); } catch (HibernateException ex) { throw SessionFactoryUtils.convertHibernateAccessException(ex); } catch (RuntimeException ex) { // Callback code threw application exception... throw ex; } finally { if (isNew) { SessionFactoryUtils.closeSession(session); } else { disableFilters(session); } } } 回到一开始的HibernateTemplate的save()方法: @Override public Serializable save(final Object entity) throws DataAccessException { return executeWithNativeSession(new HibernateCallback<Serializable>() { @Override public Serializable doInHibernate(Session session) throws HibernateException { checkWriteOperationAllowed(session); return session.save(entity); } }); } 追踪checkWriteOperationAllowed()方法 protected void checkWriteOperationAllowed(Session session) throws InvalidDataAccessApiUsageException { if (isCheckWriteOperations() && session.getFlushMode().lessThan(FlushMode.COMMIT)) { throw new InvalidDataAccessApiUsageException( "Write operations are not allowed in read-only mode (FlushMode.MANUAL): "+ "Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition."); } }这里检查是否允许写入操作,通过查看session的FlushMode是否小于FlushMode.COMMIT。
if (definition.isReadOnly() && txObject.isNewSession()) { // Just set to MANUAL in case of a new Session for this transaction. session.setFlushMode(FlushMode.MANUAL); } if (!definition.isReadOnly() && !txObject.isNewSession()) { // We need AUTO or COMMIT for a non-read-only transaction. FlushMode flushMode = session.getFlushMode(); if (session.getFlushMode().equals(FlushMode.MANUAL)) { session.setFlushMode(FlushMode.AUTO); txObject.getSessionHolder().setPreviousFlushMode(flushMode); } }可以看到,随着HibernateTransactionManager的初始化,会产生一个Session实例,