spring的声明式的事物管理和编程事务管理的区别

本文内容纲要:

- 编程式事务:编码方式实现事务管理(代码演示为JDBC事务管理)

- 3.声明式事务:可知编程式事务每次实现都要单独实现,但业务量大功能复杂时,使用编程式事务无疑是痛苦的,而声明式事务不同,声明式事务属于无侵入式,不会影响业务逻辑的实现。

- 2.编程式事务:编码方式实现事务管理(代码演示为JDBC事务管理)

- 3.声明式事务:可知编程式事务每次实现都要单独实现,但业务量大功能复杂时,使用编程式事务无疑是痛苦的,而声明式事务不同,声明式事务属于无侵入式,不会影响业务逻辑的实现。

一.Spring对编程式事务的支持

Spring中的事务分为物理事务和逻辑事务;

物理事务:就是底层数据库提供的事务支持,如JDBC或JTA提供的事务;

逻辑事务:是Spring管理的事务,不同于物理事务,逻辑事务提供更丰富的控制,而且如果想得到Spring事务管理的好处,必须使用逻辑事务,因此在Spring中如果没特别强调一般就是逻辑事务;

逻辑事务即支持非常低级别的控制,也有高级别解决方案:

**低级别解决方案:**使用工具类获取连接(会话)和释放连接(会话),如Spring中使用DataSourceUtils ,Hibernate中使用SessionFactoryUtils,JPA中使用EntityManagerFactoryUtils

**高级别解决方案:**使用Spring提供的模板类,如JdbcTemplate、HibernateTemplate和JpaTemplate模板类等,而这些模板类内部其实是使用了低级别解决方案中的工具类来管理连接或会话

二.使用PlatformTransactionManager

  • applicationContext.xml

    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

    <property name="locations">

    <list>

    <value>classpath:jdbc.properties</value>

    </list>

    </property>

    </bean>

    <!-- 数据源配置, 使用DBCP数据库连接池 -->

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

    <!-- Connection Info -->

    <property name="driverClassName" value="${jdbc.driver}"/>

    <property name="url" value="${jdbc.url}"/>

    <property name="username" value="${jdbc.username}"/>

    <property name="password" value="${jdbc.password}"/>

    <!-- Connection Pooling Info -->

    <property name="maxActive" value="3"/>

    <property name="defaultAutoCommit" value="false"/>

    <!-- 连接Idle一个小时后超时 -->

    <property name="timeBetweenEvictionRunsMillis" value="3600000"/>

    <property name="minEvictableIdleTimeMillis" value="3600000"/>

    </bean>

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

    <property name="dataSource" ref="dataSource"/>

    </bean>

  • 使用低级别解决方案来进行事务管理器测试:

    @Test

    public void testPlatformTransactionManager() {

    DefaultTransactionDefinition def = new DefaultTransactionDefinition();

    def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);

    def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

    TransactionStatus status = txManager.getTransaction(def);

    Connection connection = DataSourceUtils.getConnection(dataSource);

    try{

    connection.prepareStatement(CREATE_TABLE_SQL).execute();

    PreparedStatement pstmt = connection.prepareStatement(INSERT_SQL);

    pstmt.setString(1, "test");

    pstmt.execute();

    connection.prepareStatement(DROP_TABLE_SQL).execute();

    txManager.commit(status);

    }catch(Exception ex){

    status.setRollbackOnly();

    txManager.rollback(status);

    }finally{

    DataSourceUtils.releaseConnection(connection, dataSource);

    }

    }

  • 使用高级别方案JdbcTemplate****来进行事务管理器测试

    @Test

    public void testPlatformTransactionManager() {

    DefaultTransactionDefinition def = new DefaultTransactionDefinition();

    def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);

    def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

    TransactionStatus status = txManager.getTransaction(def);

    try{

    jdbcTemplate.execute(CREATE_TABLE_SQL);

    jdbcTemplate.update(INSERT_SQL, "test");

    }catch(Exception ex){

    txManager.rollback(status);

    }

    txManager.commit(status);

    }

三.使用TransactionTemplate

TransactionTemplate模板类用于简化事务管理,事务管理由模板类定义,而具体操作需要通过TransactionCallback回调接口或TransactionCallbackWithoutResult回调接口指定,通过调用模板类的参数类型为TransactionCallback或TransactionCallbackWithoutResult的execute方法来自动享受事务管理。

TransactionTemplate模板类使用的回调接口:

TransactionCallback:通过实现该接口的“T doInTransaction(TransactionStatus status) ”方法来定义需要事务管理的操作代码;

TransactionCallbackWithoutResult:继承TransactionCallback接口,提供“void doInTransactionWithoutResult(TransactionStatus status)”便利接口用于方便那些不需要返回值的事务操作代码。

  • TransactionTemplate 模板类使用

    @Test

    public void testTransactionTemplate(){

    TransactionTemplate transactionTemplate = new TransactionTemplate(txManager);

    transactionTemplate.execute(new TransactionCallbackWithoutResult() {

    @Override

    protected void doInTransactionWithoutResult(TransactionStatus arg0) {

    jdbcTemplate.execute(CREATE_TABLE_SQL);

    jdbcTemplate.update(INSERT_SQL, "test");

    }

    });

    String COUNT_ALL = "select count(*) from test";

    Number count = jdbcTemplate.queryForInt(COUNT_ALL);

    Assert.assertEquals(1, count.intValue());

        jdbcTemplate.execute(DROP_TABLE_SQL);

    }

三.实际应用

  • 模型对象

    public class User implements java.io.Serializable{

    /**

    *

    */

    private static final long serialVersionUID = 1L;

    private Address address;

    private Integer id;

    private String name;

    //省略get,set

    }

    public class Address implements java.io.Serializable{

    /**

    *

    */

    private static final long serialVersionUID = 1L;

    private String city;

    private Integer id;

    private String province;

    private String street;

    private Integer userId;

    //省略get,set

    }

  • Dao层接口及实现

    public interface UserDao {

    public void save(User user);

    public int countAll();

    }

    package transaction.dao;

    import java.util.HashMap;

    import java.util.Map;

    import org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport;

    import transaction.bean.User;

    public class UserDaoImp extends NamedParameterJdbcDaoSupport implements UserDao{

    private final String INSERT_SQL = "insert into user(name) values(:name)";

    private final String COUNT_ALL_SQL = "select count(*) from user";

    @Override

    public void save(final User user) {

    Map<String,String> para = new HashMap<String,String>();

    para.put("name", user.getName());

    getNamedParameterJdbcTemplate().update(INSERT_SQL, para);

    }

    @Override

    public int countAll() {

    return getJdbcTemplate().queryForInt(COUNT_ALL_SQL);

    }

    }

    public interface AddressDao {

    public void save(Address address);

    public int countAll();

    }

    public class AddressDaoImp extends NamedParameterJdbcDaoSupport implements AddressDao{

    private final String INSERT_SQL = "insert into address(province, city, street, user_id)" + "values(:province, :city, :street, :userId)";

    private final String COUNT_ALL_SQL = "select count(*) from address";

    @Override

    public void save(Address address){

    KeyHolder generatedKeyHolder = new GeneratedKeyHolder();

    /SqlParameterSource source = new BeanPropertySqlParameterSource(address);

    getNamedParameterJdbcTemplate().update(INSERT_SQL, source, generatedKeyHolder);/

    Map<String,Object> para = new HashMap<String,Object>();

    para.put("province", address.getProvince());

    para.put("city", address.getCity());

    para.put("street", address.getStreet());

    para.put("userId", address.getUserId());

    getNamedParameterJdbcTemplate().update(INSERT_SQL, para);

    }

    @Override

    public int countAll() {

    return getJdbcTemplate().queryForInt(COUNT_ALL_SQL);

    }

    }

  • Service****层接口及实现

    public interface UserService {

    public void save(User user);

    public int countAll();

    }

    @Override

    public void save(final User user){

    TransactionTemplate transactionTemplate = TransactionTemplateUtils.getDefaultTransactionTemplate(txManager);

    transactionTemplate.execute(new TransactionCallbackWithoutResult(){

    @Override

    protected void doInTransactionWithoutResult(TransactionStatus arg0) {

    userDao.save(user);

    user.getAddress().setUserId(user.getId());

    try {

    addressService.save(user.getAddress());

    } catch (Exception e) {

    // TODO Auto-generated catch block

    e.printStackTrace();

    }

    }

    });

    }

    @Override

    public int countAll() {

    return userDao.countAll();

    }

    public interface AddressService {

    public void save(Address address) throws Exception;

    public int countAll();

    }

    public class AddressServiceImp implements AddressService{

    private AddressDao addressDao;

    private PlatformTransactionManager txManager;

    public void setAddressDao(AddressDao addressDao) {

    this.addressDao = addressDao;

    }

    public void setTxManager(PlatformTransactionManager txManager) {

    this.txManager = txManager;

    }

    @Override

    public void save(final Address address){

    TransactionTemplate transactionTemplate = TransactionTemplateUtils.getDefaultTransactionTemplate(txManager);

    transactionTemplate.execute(new TransactionCallbackWithoutResult(){

    @Override

    protected void doInTransactionWithoutResult(TransactionStatus arg0) {

    addressDao.save(address);

    }

    });

    }

    @Override

    public int countAll() {

    return addressDao.countAll();

    }

    }

  • applicationContext.xml

    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

    <property name="locations">

    <list>

    <value>classpath:jdbc.properties</value>

    </list>

    </property>

    </bean>

    <!-- 数据源配置, 使用DBCP数据库连接池 -->

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

    <!-- Connection Info -->

    <property name="driverClassName" value="${jdbc.driver}"/>

    <property name="url" value="${jdbc.url}"/>

    <property name="username" value="${jdbc.username}"/>

    <property name="password" value="${jdbc.password}"/>

    <!-- Connection Pooling Info -->

    <property name="maxActive" value="3"/>

    <property name="defaultAutoCommit" value="false"/>

    <!-- 连接Idle一个小时后超时 -->

    <property name="timeBetweenEvictionRunsMillis" value="3600000"/>

    <property name="minEvictableIdleTimeMillis" value="3600000"/>

    </bean>

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

    <property name="dataSource" ref="dataSource"/>

    </bean>

    <bean id="abstractDao" abstract="true">

    <property name="dataSource" ref="dataSource"/>

    </bean>

    <bean id="userDao" class="transaction.dao.UserDaoImp" parent="abstractDao"/>

    <bean id="addressDao" class="transaction.dao.AddressDaoImp" parent="abstractDao"/>

    <bean id="userService" class="transaction.service.UserServiceImp">

    <property name="addressService" ref="addressService"/>

    <property name="userDao" ref="userDao"/>

    <property name="txManager" ref="transactionManager"/>

    </bean>

    <bean id="addressService" class="transaction.service.AddressServiceImp">

    <property name="addressDao" ref="addressDao"/>

    <property name="txManager" ref="transactionManager"/>

    </bean>

  • TransactionTemplateUtils类

    public class TransactionTemplateUtils {

    private static TransactionTemplate getTransactionTemplate(

    PlatformTransactionManager txManager, int propagationBehavior,

    int isolationLevel) {

    TransactionTemplate transactionTemplate = new TransactionTemplate(txManager);

    transactionTemplate.setPropagationBehavior(propagationBehavior);

    transactionTemplate.setIsolationLevel(isolationLevel);

    return transactionTemplate;

    }

    public static TransactionTemplate getDefaultTransactionTemplate(PlatformTransactionManager txManager) {

    return getTransactionTemplate(txManager,

    TransactionDefinition.PROPAGATION_REQUIRED,

    TransactionDefinition.ISOLATION_READ_COMMITTED);

    }

    }

三.事务属性

事务属性通过TransactionDefinition接口实现定义,主要有事务隔离级别、事务传播行为、事务超时时间、事务是否只读。

Spring提供TransactionDefinition接口默认实现DefaultTransactionDefinition,可以通过该实现类指定这些事务属性

  • 事务隔离级别:用来解决并发事务时出现的问题,其使用TransactionDefinition中的静态变量来指定:

    ISOLATION_DEFAULT:默认隔离级别,即使用底层数据库默认的隔离级别;

    ISOLATION_READ_UNCOMMITTED:未提交读;

    ISOLATION_READ_COMMITTED:提交读,一般情况下我们使用这个;

    ISOLATION_REPEATABLE_READ:可重复读;

    ISOLATION_SERIALIZABLE:序列化

  • 事务传播行为:Spring管理的事务是逻辑事务,而且物理事务和逻辑事务最大差别就在于事务传播行为,事务传播行为用于指定在多个事务方法间调用时,事务是如何在这些方法间传播的,Spring共支持7种传播行为

    Required:必须有逻辑事务,否则新建一个事务,使用PROPAGATION_REQUIRED指定,表示如果当前存在一个逻辑事务,则加入该逻辑事务,否则将新建一个逻辑事务

    RequiresNew:创建新的逻辑事务,使用PROPAGATION_REQUIRES_NEW指定,表示每次都创建新的逻辑事务(物理事务也是不同的)

    Supports:支持当前事务,使用PROPAGATION_SUPPORTS指定,指如果当前存在逻辑事务,就加入到该逻辑事务,如果当前没有逻辑事务,就以非事务方式执行

    NotSupported:不支持事务,如果当前存在事务则暂停该事务,使用PROPAGATION_NOT_SUPPORTED指定,即以非事务方式执行,如果当前存在逻辑事务,就把当前事务暂停,以非事务方式执行

    Mandatory:必须有事务,否则抛出异常,使用PROPAGATION_MANDATORY指定,使用当前事务执行,如果当前没有事务

    Never:不支持事务,如果当前存在是事务则抛出异常,使用PROPAGATION_NEVER指定,即以非事务方式执行,如果当前存在事务,则抛出异常(IllegalTransactionStateException)

    Nested:嵌套事务支持,使用PROPAGATION_NESTED指定,如果当前存在事务,则在嵌套事务内执行,如果当前不存在事务,则创建一个新的事务,嵌套事务使用数据库中的保存点来实现,即嵌套事务回滚不影响外部事务,但外部事务回滚将导致嵌套事务回滚

Nested和RequiresNew的区别:

1、 RequiresNew每次都创建新的独立的物理事务,而Nested只有一个物理事务;

2、 Nested嵌套事务回滚或提交不会导致外部事务回滚或提交,但外部事务回滚将导致嵌套事务回滚,而 RequiresNew由于都是全新的事务,所以之间是无关联的;

3、 Nested使用JDBC 3的保存点实现,即如果使用低版本驱动将导致不支持嵌套事务。

使用嵌套事务,必须确保具体事务管理器实现的nestedTransactionAllowed属性为true,否则不支持嵌套事务,如DataSourceTransactionManager默认支持,而HibernateTransactionManager默认不支持,需要我们来开启

  • 事务超时:设置事务的超时时间,单位为秒,默认为-1表示使用底层事务的超时时间;
  • 事务只读:将事务标识为只读,只读事务不修改任何数据;

编程式事务:编码方式实现事务管理(代码演示为JDBC事务管理)

Spring实现编程式事务,依赖于2大类,分别是上篇文章提到的PlatformTransactionManager,与模版类TransactionTemplate(推荐使用)。下面分别详细介绍Spring是如何通过该类实现事务管理。

1)PlatformTransactionManager,上篇文章已经详情解说了该类所拥有的方法,不记得可以回看上篇文章。

事务管理器配置

[java] view plaincopy

  1. <bean id="dataSource"class="com.mchange.v2.c3p0.ComboPooledDataSource">
  2. 5
  3. 30
  4. 10
  5. 60
  6. 5
  7. 0
  8. 60
  9. 30
  10. true
  11. false
  12. <bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

业务中使用代码(以测试类展示)

[java] view plaincopy

  1. import java.util.Map;
  2. import javax.annotation.Resource;
  3. import javax.sql.DataSource;
  4. import org.apache.log4j.Logger;
  5. import org.junit.Test;
  6. import org.junit.runner.RunWith;
  7. import org.springframework.jdbc.core.JdbcTemplate;
  8. import org.springframework.test.context.ContextConfiguration;
  9. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  10. import org.springframework.transaction.PlatformTransactionManager;
  11. import org.springframework.transaction.TransactionDefinition;
  12. import org.springframework.transaction.TransactionStatus;
  13. import org.springframework.transaction.support.DefaultTransactionDefinition;
  14. @RunWith(SpringJUnit4ClassRunner.class)
  15. @ContextConfiguration(locations = { "classpath:spring-public.xml" })
  16. public****class test {
  17. @Resource
  18. private PlatformTransactionManager txManager;
  19. @Resource
  20. private DataSource dataSource;
  21. private****static JdbcTemplate jdbcTemplate;
  22. Logger logger=Logger.getLogger(test.class);
  23. privatestaticfinal String INSERT_SQL = "insert into testtranstation(sd) values(?)";
  24. privatestaticfinal String COUNT_SQL = "select count(*) from testtranstation";
  25. @Test
  26. public****void testdelivery(){
  27. //定义事务隔离级别,传播行为,
  28. DefaultTransactionDefinition def = new DefaultTransactionDefinition();
  29. def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
  30. def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
  31. //事务状态类,通过PlatformTransactionManager的getTransaction方法根据事务定义获取;获取事务状态后,Spring根据传播行为来决定如何开启事务
  32. TransactionStatus status = txManager.getTransaction(def);
  33. jdbcTemplate = new JdbcTemplate(dataSource);
  34. int i = jdbcTemplate.queryForInt(COUNT_SQL);
  35. System.out.println("表中记录总数:"+i);
  36. try {
  37. jdbcTemplate.update(INSERT_SQL, "1");
  38. txManager.commit(status); //提交status中绑定的事务
  39. } catch (RuntimeException e) {
  40. txManager.rollback(status); //回滚
  41. }
  42. i = jdbcTemplate.queryForInt(COUNT_SQL);
  43. System.out.println("表中记录总数:"+i);
  44. }
  45. }

2)使用TransactionTemplate,该类继承了接口DefaultTransactionDefinition,用于简化事务管理,事务管理由模板类定义,主要是通过TransactionCallback回调接口或TransactionCallbackWithoutResult回调接口指定,通过调用模板类的参数类型为TransactionCallback或TransactionCallbackWithoutResult的execute方法来自动享受事务管理。

TransactionTemplate模板类使用的回调接口:

  • TransactionCallback:通过实现该接口的“T doInTransaction(TransactionStatus status) ”方法来定义需要事务管理的操作代码;

  • TransactionCallbackWithoutResult:继承TransactionCallback接口,提供“void doInTransactionWithoutResult(TransactionStatus status)”便利接口用于方便那些不需要返回值的事务操作代码。

    还是以测试类方式展示如何实现

[java] view plaincopy

  1. @Test
  2. public****void testTransactionTemplate(){
  3. jdbcTemplate = new JdbcTemplate(dataSource);
  4. int i = jdbcTemplate.queryForInt(COUNT_SQL);
  5. System.out.println("表中记录总数:"+i);
  6. //构造函数初始化TransactionTemplate
  7. TransactionTemplate template = new TransactionTemplate(txManager);
  8. template.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
  9. //重写execute方法实现事务管理
  10. template.execute(new TransactionCallbackWithoutResult() {
  11. @Override
  12. protected****void doInTransactionWithoutResult(TransactionStatus status) {
  13. jdbcTemplate.update(INSERT_SQL, "饿死"); //字段sd为int型,所以插入肯定失败报异常,自动回滚,代表TransactionTemplate自动管理事务
  14. }}
  15. );
  16. i = jdbcTemplate.queryForInt(COUNT_SQL);
  17. System.out.println("表中记录总数:"+i);
  18. }

3.声明式事务:可知编程式事务每次实现都要单独实现,但业务量大功能复杂时,使用编程式事务无疑是痛苦的,而声明式事务不同,声明式事务属于无侵入式,不会影响业务逻辑的实现。

声明式事务实现方式主要有2种,一种为通过使用Spring的tx:advice定义事务通知与AOP相关配置实现,另为一种通过@Transactional实现事务管理实现,下面详细说明2种方法如何配置,已经相关注意点

1)方式一,配置文件如下

[java] view plaincopy

  1. <tx:method name="save*" propagation="REQUIRED" isolation="READ_COMMITTED" timeout="" read-only="false" no-rollback-for="" rollback-for=""/>
  2. <tx:method name="*" propagation="SUPPORTS"/>
  3. </tx:attributes>
  4. </tx:advice>
  5. aop:config
  6. <aop:pointcut expression="execution(* com.kaizhi.*.service.impl.*.*(..))" id="pointcut"/>
  7. <aop:advisor advice-ref="advice" pointcut-ref="pointcut"/>
  8. </aop:config>

关于事务传播行为与隔离级别,可参考http://blog.csdn.net/liaohaojian/article/details/68488150

注意点:

  1. 事务回滚异常只能为RuntimeException异常,而Checked Exception异常不回滚,捕获异常不抛出也不会回滚,但可以强制事务回滚:TransactionAspectSupport.currentTransactionStatus().isRollbackOnly();
  2. 解决“自我调用”而导致的不能设置正确的事务属性问题,可参考http://www.iteye.com/topic/1122740

2)方式二通过@Transactional实现事务管理

[java] view plaincopy

  1. <bean id="txManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  2. <tx:annotation-driven transaction-manager="txManager"/> //开启事务注解

@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.READ_COMMITTED),具体参数跟上面tx:method中一样

Spring提供的@Transaction注解事务管理,内部同样是利用环绕通知TransactionInterceptor实现事务的开启及关闭。

使用@Transactional注意点:

  1. 如果在接口、实现类或方法上都指定了@Transactional 注解,则优先级顺序为方法>实现类>接口;
  2. 建议只在实现类或实现类的方法上使用@Transactional,而不要在接口上使用,这是因为如果使用JDK代理机制(基于接口的代理)是没问题;而使用使用CGLIB代理(继承)机制时就会遇到问题,因为其使用基于类的代理而不是接口,这是因为接口上的@Transactional注解是“不能继承的”;

2.编程式事务:编码方式实现事务管理(代码演示为JDBC事务管理)

Spring实现编程式事务,依赖于2大类,分别是上篇文章提到的PlatformTransactionManager,与模版类TransactionTemplate(推荐使用)。下面分别详细介绍Spring是如何通过该类实现事务管理。

1)PlatformTransactionManager,上篇文章已经详情解说了该类所拥有的方法,不记得可以回看上篇文章。

事务管理器配置

[java] view plaincopy

  1. <bean id="dataSource"class="com.mchange.v2.c3p0.ComboPooledDataSource">
  2. 5
  3. 30
  4. 10
  5. 60
  6. 5
  7. 0
  8. 60
  9. 30
  10. true
  11. false
  12. <bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

业务中使用代码(以测试类展示)

[java] view plaincopy

  1. import java.util.Map;
  2. import javax.annotation.Resource;
  3. import javax.sql.DataSource;
  4. import org.apache.log4j.Logger;
  5. import org.junit.Test;
  6. import org.junit.runner.RunWith;
  7. import org.springframework.jdbc.core.JdbcTemplate;
  8. import org.springframework.test.context.ContextConfiguration;
  9. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  10. import org.springframework.transaction.PlatformTransactionManager;
  11. import org.springframework.transaction.TransactionDefinition;
  12. import org.springframework.transaction.TransactionStatus;
  13. import org.springframework.transaction.support.DefaultTransactionDefinition;
  14. @RunWith(SpringJUnit4ClassRunner.class)
  15. @ContextConfiguration(locations = { "classpath:spring-public.xml" })
  16. public****class test {
  17. @Resource
  18. private PlatformTransactionManager txManager;
  19. @Resource
  20. private DataSource dataSource;
  21. private****static JdbcTemplate jdbcTemplate;
  22. Logger logger=Logger.getLogger(test.class);
  23. privatestaticfinal String INSERT_SQL = "insert into testtranstation(sd) values(?)";
  24. privatestaticfinal String COUNT_SQL = "select count(*) from testtranstation";
  25. @Test
  26. public****void testdelivery(){
  27. //定义事务隔离级别,传播行为,
  28. DefaultTransactionDefinition def = new DefaultTransactionDefinition();
  29. def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
  30. def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
  31. //事务状态类,通过PlatformTransactionManager的getTransaction方法根据事务定义获取;获取事务状态后,Spring根据传播行为来决定如何开启事务
  32. TransactionStatus status = txManager.getTransaction(def);
  33. jdbcTemplate = new JdbcTemplate(dataSource);
  34. int i = jdbcTemplate.queryForInt(COUNT_SQL);
  35. System.out.println("表中记录总数:"+i);
  36. try {
  37. jdbcTemplate.update(INSERT_SQL, "1");
  38. txManager.commit(status); //提交status中绑定的事务
  39. } catch (RuntimeException e) {
  40. txManager.rollback(status); //回滚
  41. }
  42. i = jdbcTemplate.queryForInt(COUNT_SQL);
  43. System.out.println("表中记录总数:"+i);
  44. }
  45. }

2)使用TransactionTemplate,该类继承了接口DefaultTransactionDefinition,用于简化事务管理,事务管理由模板类定义,主要是通过TransactionCallback回调接口或TransactionCallbackWithoutResult回调接口指定,通过调用模板类的参数类型为TransactionCallback或TransactionCallbackWithoutResult的execute方法来自动享受事务管理。

TransactionTemplate模板类使用的回调接口:

  • TransactionCallback:通过实现该接口的“T doInTransaction(TransactionStatus status) ”方法来定义需要事务管理的操作代码;
  • TransactionCallbackWithoutResult:继承TransactionCallback接口,提供“void doInTransactionWithoutResult(TransactionStatus status)”便利接口用于方便那些不需要返回值的事务操作代码。

还是以测试类方式展示如何实现

[java] view plaincopy

  1. @Test
  2. public****void testTransactionTemplate(){
  3. jdbcTemplate = new JdbcTemplate(dataSource);
  4. int i = jdbcTemplate.queryForInt(COUNT_SQL);
  5. System.out.println("表中记录总数:"+i);
  6. //构造函数初始化TransactionTemplate
  7. TransactionTemplate template = new TransactionTemplate(txManager);
  8. template.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
  9. //重写execute方法实现事务管理
  10. template.execute(new TransactionCallbackWithoutResult() {
  11. @Override
  12. protected****void doInTransactionWithoutResult(TransactionStatus status) {
  13. jdbcTemplate.update(INSERT_SQL, "饿死"); //字段sd为int型,所以插入肯定失败报异常,自动回滚,代表TransactionTemplate自动管理事务
  14. }}
  15. );
  16. i = jdbcTemplate.queryForInt(COUNT_SQL);
  17. System.out.println("表中记录总数:"+i);
  18. }

3.声明式事务:可知编程式事务每次实现都要单独实现,但业务量大功能复杂时,使用编程式事务无疑是痛苦的,而声明式事务不同,声明式事务属于无侵入式,不会影响业务逻辑的实现。

声明式事务实现方式主要有2种,一种为通过使用Spring的tx:advice定义事务通知与AOP相关配置实现,另为一种通过@Transactional实现事务管理实现,下面详细说明2种方法如何配置,已经相关注意点

1)方式一,配置文件如下

[java] view plaincopy

  1. <tx:method name="save*" propagation="REQUIRED" isolation="READ_COMMITTED" timeout="" read-only="false" no-rollback-for="" rollback-for=""/>
  2. <tx:method name="*" propagation="SUPPORTS"/>
  3. </tx:attributes>
  4. </tx:advice>
  5. aop:config
  6. <aop:pointcut expression="execution(* com.kaizhi.*.service.impl.*.*(..))" id="pointcut"/>
  7. <aop:advisor advice-ref="advice" pointcut-ref="pointcut"/>
  8. </aop:config>

关于事务传播行为与隔离级别,可参考http://blog.csdn.net/liaohaojian/article/details/68488150

注意点:

  1. 事务回滚异常只能为RuntimeException异常,而Checked Exception异常不回滚,捕获异常不抛出也不会回滚,但可以强制事务回滚:TransactionAspectSupport.currentTransactionStatus().isRollbackOnly();
  2. 解决“自我调用”而导致的不能设置正确的事务属性问题,可参考http://www.iteye.com/topic/1122740

2)方式二通过@Transactional实现事务管理

[java] view plaincopy

  1. <bean id="txManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  2. <tx:annotation-driven transaction-manager="txManager"/> //开启事务注解

@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.READ_COMMITTED),具体参数跟上面tx:method中一样

Spring提供的@Transaction注解事务管理,内部同样是利用环绕通知TransactionInterceptor实现事务的开启及关闭。

使用@Transactional注意点:

  1. 如果在接口、实现类或方法上都指定了@Transactional 注解,则优先级顺序为方法>实现类>接口;
  2. 建议只在实现类或实现类的方法上使用@Transactional,而不要在接口上使用,这是因为如果使用JDK代理机制(基于接口的代理)是没问题;而使用使用CGLIB代理(继承)机制时就会遇到问题,因为其使用基于类的代理而不是接口,这是因为接口上的@Transactional注解是“不能继承的”;

本文内容总结:编程式事务:编码方式实现事务管理(代码演示为JDBC事务管理),3.声明式事务:可知编程式事务每次实现都要单独实现,但业务量大功能复杂时,使用编程式事务无疑是痛苦的,而声明式事务不同,声明式事务属于无侵入式,不会影响业务逻辑的实现。,2.编程式事务:编码方式实现事务管理(代码演示为JDBC事务管理),3.声明式事务:可知编程式事务每次实现都要单独实现,但业务量大功能复杂时,使用编程式事务无疑是痛苦的,而声明式事务不同,声明式事务属于无侵入式,不会影响业务逻辑的实现。,

原文链接:https://www.cnblogs.com/wchxj/p/8138047.html

以上是 spring的声明式的事物管理和编程事务管理的区别 的全部内容, 来源链接: utcz.com/z/362786.html

回到顶部