一、编程式事物流程
编程式事物实现主要有两种方法,一种是使用TransactionTemplate,另一中就是使用PlatformTransactionManager.这里我主要介绍前者的使用方式。
1、 准备jdbc.properties配置数据库需要的信息,将配置属性注入com.mchange.v2.c3p0.ComboPooledDataSourc,
得到我们需要的数据源
2、 将DataSource数据源注入到org.springframework.orm.hibernate5.LocalSessionFactoryBean中
3、 将org.springframework.orm.hibernate5.LocalSessionFactoryBean注入到
org.springframework.orm.hibernate5.HibernateTransactionManager中,使用Spring管理事务
4、 将org.springframework.orm.hibernate5.HibernateTransactionManager注入到
org.springframework.transaction.support.TransactionTemplate中
5、 将org.springframework.orm.hibernate5.LocalSessionFactoryBean注入到DAO层
6、 将org.springframework.transaction.support.TransactionTemplate注入到Service层
两个与事务模板相关的类
TransactionCallBack<T>//如果方法执行没有返回值则覆写此类的方法
TransactionCallBackWithoutResult<T>//如果方法执行有返回值则覆写此类的方法
二、编程式事物例子
Student.java 使用注解的方式实现Object和数据库表的映射
package com.zbt;
import javax.persistence.*;
@Entity
@Table(name="stu")
public class Student {
@Id
@Column(name="id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column(name="first_name")
private String firstName;
@Column(name="last_name")
private String lastName;
@Column(name="address")
private String address;
@Column(name="phone")
private String phone;
//省略了getters 和 setters方法
上下文配置文件
第一步 :配置数据源
<!--使用c3p0配置 使用配置文件的方式需要指明配置文件的位置-->
<context:property-placeholder location="classpath:/jdbc.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
第二步:使用Spring注入org.springframework.orm.hibernate5.LocalSessionFactoryBean相关属性
<!--使用hibernateProperties + DataSource的方式取代hibernate.cfg.xml文件-->
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="annotatedClasses">
<list>
<value>com.zbt.Student</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">false</prop>
</props>
</property>
</bean>
第三步: Spring 注入org.springframework.orm.hibernate5.HibernateTransactionManager相关属性
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
第四步:Spring 注入org.springframework.transaction.support.TransactionTemplate相关属性
<bean id = "transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>//注意这里需要将HibernateTransactionManager注入进来
<property name="isolationLevelName" value="ISOLATION_READ_COMMITTED"/>//设置隔离级别
<property name="timeout" value="30"/>//设置事务运行时间
</bean>
第五步:StudentDao.java 实现与数据库的交互,需要注入SessionFactory
package com.zbt;
import org.hibernate.SessionFactory;
import org.hibernate.query.Query;
public class StudentDao {
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory){
this.sessionFactory = sessionFactory;
}
public int saveStudent(Student stu){
return (int) this.sessionFactory.getCurrentSession().save(stu);
}
public void save(Student stu){
this.sessionFactory.getCurrentSession().save(stu);
}
public Integer update(int id,String phone){
String hql = "update Student set phone =:phone where id = :id";
Query query = this.sessionFactory.getCurrentSession().createQuery(hql);
query.setParameter("phone",phone);
query.setParameter("id",id);
return query.executeUpdate();
}
}
上下文中的配置
<bean id="studentDao" class="com.zbt.StudentDao">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
第六步:在StudentService中注入DAO和org.springframework.transaction.support.TransactionTemplate
package com.zbt;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
public class StudentService {
private TransactionTemplate transactionTemplate;
private StudentDao studentDao;
public void setTransactionTemplate(TransactionTemplate transactionTemplate){
this.transactionTemplate = transactionTemplate;
}
public void setStudentDao(StudentDao studentDao){
this.studentDao = studentDao;
}
public void save(Student stu){
this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {//方法执行无返回,所以覆盖此类的方法
@Override
public void doInTransactionWithoutResult(TransactionStatus status) {
studentDao.save(stu);
}
});
}
public Integer update(int id, String phone){
return this.transactionTemplate.execute(new TransactionCallback<Integer>() {//方法执行有返回,所以覆盖此类的方法
@Override
public Integer doInTransaction(TransactionStatus status) {
try{
return studentDao.update(id,phone);
}catch(Exception e){
status.setRollbackOnly();
}
return null;
}
});
}
}
最后我们可以看一下全部的上下文配置方案
<?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: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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd ">
<!--使用c3p0配置 使用配置文件的方式需要指明配置文件的位置-->
<context:property-placeholder location="classpath:/jdbc.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--使用hibernateProperties + DataSource的方式取代hibernate.cfg.xml文件-->
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="annotatedClasses">
<list>
<value>com.zbt.Student</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">false</prop>
</props>
</property>
</bean>
<!--将事务交给Spring管理-->
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id = "transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
<property name="isolationLevelName" value="ISOLATION_READ_COMMITTED"/>
<property name="timeout" value="30"/>
</bean>
<bean id="studentDao" class="com.zbt.StudentDao">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="studentService" class="com.zbt.StudentService">
<property name="transactionTemplate" ref="transactionTemplate"/>
<property name="studentDao" ref ="studentDao"/>
</bean>
</beans>
三、TransactionTemplate源码
package org.springframework.transaction.support;
@SuppressWarnings("serial")
public class TransactionTemplate extends DefaultTransactionDefinition
implements TransactionOperations, InitializingBean {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
@Nullable
private PlatformTransactionManager transactionManager;
/**
* Construct a new TransactionTemplate for bean usage.
* <p>Note: The PlatformTransactionManager needs to be set before
* any {@code execute} calls.
* @see #setTransactionManager
*/
public TransactionTemplate() {
}
/**
* Construct a new TransactionTemplate using the given transaction manager.
* @param transactionManager the transaction management strategy to be used
*/
public TransactionTemplate(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
/**
* Construct a new TransactionTemplate using the given transaction manager,
* taking its default settings from the given transaction definition.
* @param transactionManager the transaction management strategy to be used
* @param transactionDefinition the transaction definition to copy the
* default settings from. Local properties can still be set to change values.
*/
public TransactionTemplate(PlatformTransactionManager transactionManager,
TransactionDefinition transactionDefinition) {
super(transactionDefinition);
this.transactionManager = transactionManager;
}
/**
* Set the transaction management strategy to be used.
*/
public void setTransactionManager(@Nullable PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
/**
* Return the transaction management strategy to be used.
*/
@Nullable
public PlatformTransactionManager getTransactionManager() {
return this.transactionManager;
}
@Override
public void afterPropertiesSet() {
if (this.transactionManager == null) {
throw new IllegalArgumentException("Property 'transactionManager' is required");
}
}
@Override
@Nullable
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");
if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
}
else {
TransactionStatus status = this.transactionManager.getTransaction(this);
T result;
try {
result = action.doInTransaction(status);
}
catch (RuntimeException | Error ex) {
// Transactional code threw application exception -> rollback
rollbackOnException(status, ex);
throw ex;
}
catch (Throwable ex) {
// Transactional code threw unexpected exception -> rollback
rollbackOnException(status, ex);
throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
}
this.transactionManager.commit(status);
return result;
}
}
/**
* Perform a rollback, handling rollback exceptions properly.
* @param status object representing the transaction
* @param ex the thrown application exception or error
* @throws TransactionException in case of a rollback error
*/
private void rollbackOnException(TransactionStatus status, Throwable ex) throws TransactionException {
Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");
logger.debug("Initiating transaction rollback on application exception", ex);
try {
this.transactionManager.rollback(status);
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
}