小白新手web开发简单总结(十二)-数据库连接的相关优化(事务管理)

目录

前言

一 什么是事务

1.事务分类

2.事务四个原则

3.事务管理方式

二 编程式事务管理

1.TransactionDefinition

2.TransactionStatus

3.PlatformTransactionManager

(1)作用

(2)实现类

(3)实例

 4.TransactionTemplate

5.简单总结编程式事务管理

三 声明式事务管理

1.xml配置

(1)通过在xml配置来定义事务通知

(2)增加切入点

(3)实例验证

2.通过注解@Transactional

(1)在xml中配置 

(2)在相应需要事务管理的方法上添加 @Transactional

四 总结


前言

小白新手web开发简单总结(十一)-数据库连接的相关优化(数据源DataSource)主要总结了DataSource的一些概念,这次在总结下数据库的事务管理。

一 什么是事务

小白新手web开发简单总结(十)-数据库HSQLDB实例问题总结中也简单的提了下什么是事务。所谓的事务就是一系列的动作,这些动作必须全部完成,若有一个失败,则事务回滚到初始状态,那么事务其实就是对应一系列SQL语句的操作,这些SQL语句要么全都执行,要不全不执行。

1.事务分类

在Java EE中提供了两种事务:

  • (1)JDBC(Java DataBase Connectivity)事务:即本地事务,也就是一个普通事务,对应独立的一个数据库,通过Connection来控制管理;

所以在白新手web开发简单总结(十一)-数据库连接的相关优化(数据源DataSource)中的也可以看到我们配置了那么多的DataSource,通过DataSource来获取到Connection

  • (2)JTA(Java Transcation API)事务:即分布式事务,涉及到两个或多个数据源的事务,使事务可以跨越多个数据源,在Java EE中只提供了事务管理接口,具体的实现是由应用程序的服务厂商(如WebSphere Application Server、Tomcat等)提供,JTA事务要比JDBC事务更强大。

另外还有一种对JDBC和JTA事务进行轻量封装的Hibernate,Mybatis 等框架,最终连接数据库还是用到Connection。

在Hibernate中,底层将JDBCTransaction或JTATransaction进行封装,通过委托底层的JDBC或JTA来实现事务的处理。里面有事务工厂类可以设置成JDBCTransactionFactory或者JTATransactionFactory,默认的事务是JDBC事务,该工厂类是线程安全对象,在web应用启动的时候创建,一旦创建将不会轻易关闭,直到web应该关闭的时候,才会关闭。

遗留问题:Hibernate的一些简单的内容后面继续补充。

遗留问题:Mybatis使用的SqlSession对象的一些简单的内容后面继续补充。

2.事务四个原则

事务要遵循四个原则ACID:

  • 原子性:事务开始的时候,要不全部做完,要不全部不做;若出现中止,必须全部回滚到初始状态;
  • 一致性:在事务执行的时候处于正确状态,在事务执行完之后还是处于正确状态,例如A向B转钱,不可能出现A转了钱,但是B没有收到;
  • 隔离性:并发事务执行互不影响,每次只能一个事务对数据进行操作,例如A从一张银行卡取钱的时候,不能向银行卡里面存钱;
  • 持久性:事务完成后,所有的数据都要保存到数据库中

3.事务管理方式

小白新手web开发简单总结(八)-数据库HSQLDB实例之前用到的JdbcTemplate来操作数据库(其代码对应com.wj.hsqldb.controller.BookManagerJdbcService#addBook())的时候,可以不关心Connection的创建和释放,直接调用已有的方法来操作数据库,但是默认的是AutoCommit模式,单纯的依赖JdbcTemplate是无法保证事务的原子性、一致性、隔离性和持久性。所以就有了事务管理的概念,而事务管理就是通过封装出一些类来保证事务四个原则。

在Spring中提供了两种事务管理的方式:

  • (1)编程式事务管理:在业务逻辑需要的时候通过代码的方式实现,粒度小;
  • (2)声明式事务管理:通过XML配置或注解实现,粒度比编程式事务管理大。

二 编程式事务管理

编程式事务管理主要就是在业务逻辑需要的时候加入相应的代码来实现事务的管理。主要依赖于PlatformTransactionManager和模版类TransactionTemplate。

在Java EE中提供三个接口类来管理事务:

  • org.springframework.transaction.PlatformTransactionManager:事务管理器,所有的事务都有该类负责;
  • org.springframework.transaction.TransactionDefinition:事务的一些基础信息,如超时时间、隔离级别、传播属性等;
  • org.springframework.transaction.TransactionStatus:事务的一些状态,如是否为一个新的事务,是否标记为回滚。

1.TransactionDefinition

主要就是事务的一些基本基础属性。

(1)隔离级别

一个事务可能受其他并发事务影响的程度。

并发可能引起的问题:

  • 脏读(Dirty reads):一个事务读取了另外一个事务改写但尚未提交的数据时,如果另外一个事务回滚,那么第一个事务读取的数据是无效的。
  • 不可重复性(Nonrepeatable read):一个事务执行相同的查询两次或两次以上,但每次都到的内容不同。通常是在另外一次查询的时候进行了更新。重点在于修改
  • 幻读(Phantom read):于不可重复性类似,发生在一个事务读取了几行数据,另一个并发事务插入了一些数据,那么在第一个事务就会多了一些不存在的记录。重点在于增加或删除

在TransactionDefinition中提供了Spring的五大隔离级别:

  • int ISOLATION_DEFAULT = -1; 默认的隔离级别;
  • int ISOLATION_READ_UNCOMMITTED = 1;未提交读:最低隔离级别,事务未提交前,可被其他事务读取;可能会导致脏读、不可重复性、或幻读;
  • int ISOLATION_READ_COMMITTED = 2;提交读:一个事务提交之后,才可被其他事务读取;可阻止脏读,但可能发生不可重复性或幻读;
  • int ISOLATION_REPEATABLE_READ = 4;可重复读:多次读取同一数据时,都和事务开始时候的内容是一致的,禁止读到其他事务未提交的数据;可阻止脏读和不可重复性,但是可能发生幻读;
  • int ISOLATION_SERIALIZABLE = 8;序列化:代价最高,最靠谱的级别,可阻止脏读、不可重复性、或幻读,但也是最慢的事务隔离级别。

(2)传播行为

当事务方法被另外一个事务调用时,必须指定事务如何传播。也就是方法有可能在现有的事务中执行,也有可能开启一个新事务,在自己的事务中运行。

在TransactionDefinition还提供了七大传播行为:

  • int PROPAGATION_REQUIRED = 0;当前方法必须运行在事务中。若当前事务存在,方法在当前事务中执行;若没有当前事务,则新建一个事务;
  • int PROPAGATION_SUPPORTS = 1;当前方法不需要事务上下文。若当前事务存在,方法则在当前事务中执行;若没有当前事务,则以非事务性执行;
  • int PROPAGATION_MANDATORY = 2;该方法必须在事务中运行。若没有当前事务,则抛出异常;
  • int PROPAGATION_REQUIRES_NEW = 3;当前方法必须运行在自己的事务中。始终创建一个新的事务,若原来有事务,则在执行该方法的时候,把当前事务挂起;
  • int PROPAGATION_NOT_SUPPORTED = 4;该方法不运行在事务中,始终以非事务性方式执行,若有当前事务,挂起该事务;
  • int PROPAGATION_NEVER = 5;该方法不运行事务中;若有当前事务存在,则引发异常;
  • int PROPAGATION_NESTED = 6;若当前事务存在,则该方法将在嵌套事务(可以独立于当前事务进行单独的提交或回滚)中执行。如果没有当前事务,则新建一个

(3)只读

如果是只读事务,那么数据库就可以应用它认为合适的优化措施。

    //返回事务是否为只读
    default boolean isReadOnly() {
        return false;
    }

(4)事务超时 

事务执行的定时器,如果超时则自动回滚,而不需要一直等待

    //返回事务超时时间
    default int getTimeout() {
        return -1;
    }

(5)回滚规则 

默认的只会在运行异常的时候回滚,也可以设置在遇到特定的检查型异常的时候进行回滚。

在Spring中默认实现类为 org.springframework.transaction.support.DefaultTransactionDefinition,里面提供了一些默认的TransactionDefinition的配置项,当然也可以调用相应的设置方法进行设置。

2.TransactionStatus

事务的状态信息。提供简单的事务执行和查询事务状态的方法。在回滚或提交的时候需要应用对应的事务状态。

在Spring中默认实现类为org.springframework.transaction.support.DefaultTransactionStatus。

3.PlatformTransactionManager

(1)作用

事务管理的核心。

为了兼容不同框架在处理事务的时候用到的对象不同,特意定义了PlatformTransactionManager接口来统一标准,对不同的框架都要实现该类来进行事务管理。

在Spring中内置的事务管理都继承了org.springframework.transaction.support.AbstractPlatformTransactionManager,而AbstractPlatformTransactionManager该抽象类实现了PlatformTransactionManager接口。

 在PlatformTransactionManager中接口内容如下:

public interface PlatformTransactionManager extends TransactionManager {
    TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
    void commit(TransactionStatus var1) throws TransactionException;
    void rollback(TransactionStatus var1) throws TransactionException;
}

主要就是包含一个提交、回滚、以及获取事务状态。

(2)实现类

在Spring中常用的内置事务管理器,也就是PlatformTransactionManager的实现类有下面三种:

  • org.springframework.jdbc.datasource.DataSourceTransactionManager:主要用于处理JDBC事务;
  • org.springframework.transaction.jta.JtaTransactionManager:主要用于处理JTA事务;
  • org.springframework.orm.hibernate5.HibernateTransactionManager:主要用于对单个org.hibernate.SessionFactory事务支持,用于集成Hibernate框架时的事务管理

(3)实例

在优化之前实例代码之前,先了解下在使用DataSourceTransactionManager来管理事务的一个基本流程就是:

  • 1)定义一个事务TransactionDefinition,设置里面的基本属性
  • 2)通过platformTransactionManager.getTransaction(transactionDefinition)来获取到TransactionStatus,同时开启事务
  • 3)执行增删改查数据库的代码
  • 4)如果正常执行完代码,则将该事务通过platformTransactionManager.commit(transactionStatus)
  • 5)如果在执行代码的时候,抛出异常,则将该此事务通过platformTransactionManager.rollback(transactionStatus)进行回滚

从这个基本流程中我们可以看到其实PlatformTransactionManager在控制着事务的提交或者回滚来满足事务的原子性。

那么现在就使用DataSourceTransactionManager来优化在小白新手web开发简单总结(八)-数据库HSQLDB实例中的com.wj.hsqldb.controller.BookManagerJdbcService中的事务处理的逻辑,优化之后的代码如下:

@Configuration
@PropertySource("classpath:/config/jdbc.properties")
public class BookManagerPlatformTransactionService {
    @Value("${jdbc.table}")
    private String jdbcTable = "book";
    //1.从xml文件中获取DataSourceTransactionManager
    @Resource
    private DataSourceTransactionManager platformTransactionManager;
    private TransactionStatus transactionStatus;
    private DefaultTransactionDefinition transactionDefinition;
    private JdbcTemplate jdbcTemplate;

    @PostConstruct
    public void createTransactionManager() {
        //2.定义事务的传播行为以及隔离级别
        transactionDefinition = new DefaultTransactionDefinition();
        transactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        //3.获取到TransactionStatus,并开启事务
        transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
        //4.执行业务逻辑代码
        try {
            createBookTableSQL(platformTransactionManager.getDataSource());
            //5.根据业务逻辑正常,则将该次事务进行提交
            platformTransactionManager.commit(transactionStatus);
        } catch (RuntimeException e) {
            e.printStackTrace();
            //6.出现了异常,则将该次事务进行回滚
            platformTransactionManager.rollback(transactionStatus);
        }
    }

    /**
     * 原创建table的业务逻辑
     *
     * @param source
     */
    private void createBookTableSQL(DataSource source) {
        jdbcTemplate = new JdbcTemplate(source);
        System.out.println("BookManagerJdbcService 初始化 自动创建表!!!");
        String sql = String.format("CREATE TABLE IF NOT EXISTS %s " +
                "(id INTEGER PRIMARY KEY , name VARCHAR(50) NOT NULL, price DOUBLE, online DATE)", jdbcTable);
        jdbcTemplate.execute(sql);
        System.out.println(String.format("BookManagerJdbcService 创建 %s 表", jdbcTable));
    }

}

在相比较于之前单纯用 JdbcTemplate来进行SQL操作,现在多增加了DataSourceTransactionManager来进行事务管理,最后根据事务是否正常执行来决定是否提交或回滚该次事务。在BookManagerJdbcService中的其他方法的优化,可查看代码com.wj.hsqldb.controller.service.BookManagerPlatformTransactionService中的相关逻辑,代码已经更新到github,地址为https://github.com/wenjing-bonnie/sql-web.git,对应的代码的tag为example12-1(因为项目在一直更新,所以通过打tag的方式来标记这次代码)

注意一个问题:当对该次事务执行了commit()操作之后,在执行commit()操作会抛出下面的异常:

org.springframework.transaction.IllegalTransactionStateException: Transaction is already completed - do not call commit or rollback more than once per transaction
	org.springframework.transaction.support.AbstractPlatformTransactionManager.rollback(AbstractPlatformTransactionManager.java:804)
	com.wj.hsqldb.controller.BookManagerPlatformTransactionService.getBook(BookManagerPlatformTransactionService.java:95)
	com.wj.hsqldb.controller.BookListController.doGet(BookListController.java:34)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:626)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)

所以目前的解决方案就是在执行一次新的事务的时候,在调用一下开启事务的方法。

transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);

遗留问题:目前不知道这种方式是否正确,后面参照一些像Hibernate,Mybatis等框架的源码来看下该问题的解决方式。

解决方案:通过 去查看TransactionTemplate的excute()的源码,我发现这种处理方式应该是正确的。

 4.TransactionTemplate

在3.PlatformTransactionManager中提到的DataSourceTransactionManager来管理事务的方式,比较复杂,需要开发者自行根据正常或异常执行来提交和回滚事务,所以在Spring中提供了 org.springframework.transaction.support.TransactionTemplate继承DefaultTransactionDefinition并实现TransactionDefinition接口,用于简化事务管理。可以直接通过调用里面的excute()方法来自动进行提交和回滚事务。提供了两个回调接口来处理业务代码,而不需要在关系什么时候提交事务,什么时候回退事务:

  • org.springframework.transaction.support.TransactionCallback:返回业务方法的返回值,那么此时excute()的返回值就是业务方法的返回值。
  • org.springframework.transaction.support.TransactionCallbackWithoutResult:实现TransactionCallback的抽象类,便于那些不需要返回值的业务逻辑。

进到execute()的源码中可以看到,其实就是将上述流程中DataSourceTransactionManager中来处理事务的5个步骤中的2)、4)、5)封装到该方法中。

    @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 {
        //1.开启事务
            TransactionStatus status = this.transactionManager.getTransaction(this);

            Object result;
         //2.在该事务中增加业务逻辑代码,也就是我们的回调方法
            try {
                result = action.doInTransaction(status);
            } catch (Error | RuntimeException var5) {
         //3.异常之后,回滚事务
                this.rollbackOnException(status, var5);
                throw var5;
            } catch (Throwable var6) {
         //3.异常之后,回滚事务
                this.rollbackOnException(status, var6);
                throw new UndeclaredThrowableException(var6, "TransactionCallback threw undeclared checked exception");
            }
         //4.正常的话,提交事务
            this.transactionManager.commit(status);
            return result;
        }
    }

那么我们上述的 BookManagerPlatformTransactionService类又可优化成为com.wj.hsqldb.controller.service.BookManageTransactionTemplateService的方式:

Configuration
@PropertySource("classpath:/config/jdbc.properties")
public class BookManageTransactionTemplateService {
    @Value("${jdbc.table}")
    private String jdbcTable = "book";
    //1.从xml文件中获取DataSourceTransactionManager
    @Resource
    private DataSourceTransactionManager platformTransactionManager;
    private TransactionTemplate transactionTemplate;
    private JdbcTemplate jdbcTemplate;

    @PostConstruct
    public void createTransactionManager() {
        //2.定义事务的传播行为以及隔离级别
        transactionTemplate = new TransactionTemplate(platformTransactionManager);
        transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        //3.调用 transactionTemplate.execute执行业务逻辑
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                createBookTableSQL(platformTransactionManager.getDataSource());
            }
        });
    }
}

相比较于使用DataSourceTransactionManager来管理事务,使用TransactionTemplate直接将DataSourceTransactionManager通过构造函数的方式传入到TransactionTemplate,只要执行transactionTemplate.execute()可轻松管理事务了。

在BookManagerPlatformTransactionService中的其他方法的优化,可查看代码com.wj.hsqldb.controller.service.BookManageTransactionTemplateService中的相关逻辑,代码已经更新到github,地址为https://github.com/wenjing-bonnie/sql-web.git,对应的代码的tag为example12-2(因为项目在一直更新,所以通过打tag的方式来标记这次代码)

5.简单总结编程式事务管理

通过上面的四个小标题学习了什么是编程式管理。可以有两种实现方式:

(1)通过PlatformTransactionManager来实现事务的管理,自行去根据业务代码的执行成功和失败来进行提交或回滚事务;

(2)通过TransactionTemplate来将(1)的方式进行封装,开发者不需要关心什么时候提交事务,什么时候回滚事务,只需要在处理业务逻辑代码的回调方法来实现相关逻辑即可。要注意要将实例化的PlatformTransactionManager对象传入到TransactionTemplate中。

另外我们看到我们在上述的这些xxxService类中,都会引入 JdbcTemplate 来操作数据库的时候,其实在Spring中提供了org.springframework.jdbc.core.support.JdbcDaoSupport类来访问数据库:

public abstract class JdbcDaoSupport extends DaoSupport {
    @Nullable
    private JdbcTemplate jdbcTemplate;

    public JdbcDaoSupport() {
    }

    public final void setDataSource(DataSource dataSource) {
        if (this.jdbcTemplate == null || dataSource != this.jdbcTemplate.getDataSource()) {
            this.jdbcTemplate = this.createJdbcTemplate(dataSource);
            this.initTemplateConfig();
        }
    }

    protected JdbcTemplate createJdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    public final void setJdbcTemplate(@Nullable JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
        this.initTemplateConfig();
    }

    @Nullable
    public final JdbcTemplate getJdbcTemplate() {
        return this.jdbcTemplate;
    }

    。。。。。。

}

其实只要子类继承 JdbcDaoSupport就可以拥有JdbcTemplate。但是我们看到该JdbcDaoSupport中的jdbcTemplate并没有用@Autowired进行标记,所以如果通过注解来加载实例的方式,需要对该类进行改造;而如果直接通过xml配置JavaBean的实例方式,则可以直接将子类注册到xml即可。

三 声明式事务管理

通过编程式事务管理每次事务都需要单独实现,在实际的项目中,业务逻辑都会非常复杂,所以这种方式无疑会造成代码臃肿,那么就有了声明式事务管理。对于声明使事务管理主要有两种方式:

  • (1)通过xml配置<tx:advice>
  • (2)通过注解@Transactional

1.xml配置<tx:advice>

需要经过两个过程来完成通过xml来声明事务:

(1)通过在xml配置<tx:advice>来定义事务通知

在这配置<tx:advice>之前,仍然要像编程式事务的一样,在xml配置DataSource、PlatformTransactionManager。然后再就是通过<tx:advice>来配置事务属性,其中transaction-manager为指定的事务管理器;

通过<tx:attributes>来指定需要拦截的方法(这些方法都是在项目中定义的需要进行加事务的方法)。

    <tx:advice id="advice" transaction-manager="xmlTransactionManager">
        <tx:attributes>
            <!--拦截下面的方法,即我们在业务逻辑中的相关调用数据库的方法-->
            <tx:method name="createBookTable" propagation="REQUIRED" isolation="READ_COMMITTED"/>
            <tx:method name="*SQL" propagation="REQUIRED" isolation="READ_COMMITTED"/>
            <tx:method name="verifyTransaction" propagation="REQUIRED" isolation="READ_COMMITTED"/>
            <tx:method name="*" propagation="SUPPORTS" read-only="true"/>
        </tx:attributes>
    </tx:advice>

<tx:method>里面的每个属性还是比较好理解的:

  • name:就是直接我们在业务逻辑中操作数据库的需要增加事务的相关方法,支持通配符*;
  • propagation、isolation、timeout、read-only :就是和上面的TransactionDefinition提到的属性的概念一样;
  • rollback-for:需要触发回滚的异常,可以定义多个,用","分开,默认为任何RuntimeException,而Checked Exception将不回滚事务;
  • no-rollback-for:不被触发回滚的异常,使用同rollback-for

(2)增加切入点

通过<aop:config> 定义哪些包或者哪些类需要切入事务。AOP本质是一种代理模式的实现。

    <aop:config>                                 <!--任意返回类型  com.wj.hsqldb.controller.service下面类中的任意方法 方法可以有任意多的参数-->
        <aop:pointcut id="pointcut"
                      expression="execution(* com.wj.hsqldb.controller.service.BookManagerXmlTransactionService.*(..))"/>
        <aop:advisor advice-ref="advice" pointcut-ref="pointcut"/>
    </aop:config>

重点说下expression的里面的这个如下图:

 也就是说将 com.wj.hsqldb.controller.service.BookManagerXmlTransactionService下的所有方法都要切入事务。

注意在使用该装配方式,需要在pom.xml添加AspectJ的依赖包:

      <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.5.RELEASE</version>
        </dependency>

通过上面两步之后,就可以将 com.wj.hsqldb.controller.service.BookManagerXmlTransactionService下的所有方法添加了事务。

(3)实例验证

通过在https://github.com/wenjing-bonnie/sql-web.git中的项目添加相应的代码验证这个里面类里面的方法已经添加了事务。对于事务有个很重要的准则就是要么全部执行,要么全不执行,那么我们就可以借助这个特点来验证事务是否添加成功。

首先需要将项目启动,之后点击http://localhost:8080/booklist下列表中的任意一行,都会进入到显示该书的详细信息的页面,也就是进入的com.wj.hsqldb.controller.BookDetailController。在该页面中会调用了com.wj.hsqldb.controller.service.BookManagerXmlTransactionService#verifyTransaction(Book, String)来验证是否添加成功。

 // @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
    public void verifyTransaction(Book book, String id) {
        //在插入之前先查询下每个表中的数据
        System.out.println("====异常数据之前====");
        System.out.println(getTableRowCount(jdbcTable, getBookSQL(jdbcTable).size()));
        System.out.println(getTableRowCount(xmlTransTable, getBookSQL(xmlTransTable).size()));
        //将这个数据加入到原xbook表中,这个时候该操作会抛出异常
        //将这个数据插入到hot表中,若增加了事务,则这个操作应该是操作失败的.
        addBookSQL(xmlTransTable, book);
        System.out.println("====异常数据报错之前插入hot表的数据====");
        System.out.println(getTableRowCount(xmlTransTable, getBookSQL(xmlTransTable).size()));
        System.out.println("====产生异常数据报错====");
        String insert = String.format("INSERT INTO %s \n" +
                "(id, name, price, online)\n" +
                "VALUES (%d , '%s', %f,'%s' )", jdbcTable, Integer.parseInt(id), book.name, book.price, book.online);
        jdbcTemplate.update(insert);
    }

代码的具体逻辑其实通过该方法往两种表book和hot里面添加新数据:

  • 在插入新数据之前先打印出book表和hot表中的数据数量
  • 向hot表添加数据:通过调用 addBookSQL(xmlTransTable, book)来添加新数据;
  • 向book表添加数据:因为该book对象是从book表中读出来的,所以用原来的id插入到book表中的时候,会抛出“ ava.sql.SQLIntegrityConstraintViolationException: integrity constraint violation: unique constraint or index violation”

那么对于现在这个现象就是:由于向book表添加数据引起该次操作终止,所以因为verifyTransaction()已经交给事务进行管理,那么如果发生这次中断之后,hot表的数据不会发生变化。

通过点击http://localhost:8080/booklist下列表中的任意一行,进入到下一个页面,报错,然后在返回到http://localhost:8080/booklist,在点击一次,再次进入到该页面,我们看下打出来的日志内容如下:

从日志中可以看出来,由于加入了事务管理,所以在插入book表的数据报错的时候,会将hot表的数据回滚。

2.通过注解@Transactional

通过注解@Transactional的方式要比xml配置的方式,xml配置的内容少点,代替配置<tx:advice>和<aop:config> ,只需要下面的两步内容:

(1)在xml中配置 <tx:annotation-driven>

 <tx:annotation-driven transaction-manager="xmlTransactionManager"/>

(2)在相应需要事务管理的方法上添加 @Transactional

     @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
    public void verifyTransaction(Book book, String id) {

    }

其中  @Transactional里面的一些设置,同上的含义一致。

具体的可参照https://github.com/wenjing-bonnie/sql-web.git下的相应代码,对应的代码的tag为example12-3(因为项目在一直更新,所以通过打tag的方式来标记这次代码)。

四 总结

终于把事务管理的基本内容给看完了。当然这只是基本的,后面还需要在去看下什么是JTA事务,那些Hibernate,Mybatis等比较好的框架还需要继续学习下。

1.所谓的事务简单的说就是一些动作,要么全部执行,要么全部不执行,若执行了一半,被中止,那么全部还原到初始状态;

2.Java EE中提供了两种事务:一种是JDBC事务,一种是JTA事务。针对这两种事务有一些轻量封装的框架如Hibernate,Mybatis等;

3.事务的四个原则:原子性、隔离性、一致性和持久性;

4.事务管理方式有两种:编程式事务管理和声明式事务管理;

5.在Spring中为了兼容不同框架对事务的处理,定义了PlatformTransactionManager来统一接口,不同的框架提供的事务管理需要实现该接口;

6.在Spring默认的有两种实现类来处理JDBC事务和JTA事务;

7.在一个事务管理过程主要分为:

  • 定义一个事务TransactionDefinition
  • 通过PlatformTransactionManager获取事务的状态TransactionStatus,同时开启事务
  • 在该事务中执行业务代码逻辑
  • 如果该业务逻辑没有异常,则提交事务;如果出现异常,则回滚事务

8.当然Spring也提供了一个对事务管理的封装类TransactionTemplate,通过该类只需要将实例化的PlatformTransactionManager传入即可,就可以直接通过execute()方法来执行业务逻辑代码;

9.声明式事务又分为两种:一种通过xml配置;一种通过@Transactional;

10.使用xml配置事务的事务时,要添加AspectJ的依赖。

下一篇去总结一下 Hibernate,Mybatis的使用。加油!!!

猜你喜欢

转载自blog.csdn.net/nihaomabmt/article/details/114536479