spring 事务

学习目标

  • 能够说出事务的ACID原则

  • 能够说出spring的事务管理方式

  • 能够说出spring事务管理常用接口

  • 能够理解事务的隔离级别

  • 能够理解事务的传播行为

  • 能够通过xml实现声明式事务控制

  • 能够通过注解实现声明式事务控制

  • 了解spring编程式事务控制

spring中声明式事务控制【掌握】

事务回顾

事务定义

事务(transaction),一般是指要做的或者所做的事情。在程序中,尤其是在操作数据库的程序中,指的是访问并且可能更新数据库中数据项的一个执行单元(unit),这个执行单元由事务开始(begin transaction)和事务结束(end transaction)之间执行的全部操作组成。

事务特性(ACID原则)【记住】

事务具有4个基本特性:原子性、一致性、隔离性、持久性。也就是我们常说的ACID原则。

  • 原子性(Atomicity):一个事务已经是一个不可再分割的工作单位。事务中的全部操作要么都做;要么都不做。

  • 一致性(Consistency):事务必须是使得数据库状态从一个一致性状态,转变到另外一个一致性状态。也就是说在事务前,和事务后,被操作的目标资源状态一致。比如银行转账案例中,转账前和转账后,总账不变。

  • 隔离性(Isolation):一个事务的执行不能被其他事务的影响。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,多个并发事务之间不能相互干扰。

  • 持久性(Durability):一个事务一旦提交,它对数据库中数据的改变会永久存储起来。其他操作不会对它产生影响。

事务案例:账户名单插入

案例需求

使用JdbcTemplate+druid操作账户表(account),模拟实现银行转账操作。

准备数据

create table account(
	id int primary key auto_increment,
	name varchar(40),
	money float
)ENGINE=InnoDB character set utf8 collate utf8_general_ci;

insert into account(name,money) values('小明',1000);
insert into account(name,money) values('小花',1000);
insert into account(name,money) values('小王',1000);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uaAJL4HG-1582267033349)(media/e1e7a6739c6b8a28ebf1b7e8165aab33.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gnTFXIk7-1582267033350)(media/03a404c56dde7018d23e05fbc254ccfc.png)]

案例需求准备

创建项目

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hMBD6QUu-1582267033351)(media/1540467765890.png)]

配置pom.xml,加入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>Spring04</groupId>
    <artifactId>tx_xml</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!--spring核心包-->
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!--spring AOP包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!--aspectj依赖导入-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.1</version>
        </dependency>
        <!--spring事务包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!--jdbctemplate包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!--mysql驱动包-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!--数据库连接池依赖-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!--spring测试包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!--junit测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

    </dependencies>


</project>
编写账户实体类对象
package com.po;

public class Account {
    private int id;
    private String name;
    private double money;

    public Account() {
    }

    public Account(int id, String name, double money) {
        this.id = id;
        this.name = name;
        this.money = money;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}

编写账户持久层对象
编写账户dao接口
package com.dao;

import com.po.Account;

public interface AccountDao {
    void insert(Account account);

}

编写账户dao实现类
package com.dao.impl;

import com.dao.AccountDao;
import com.po.Account;
import org.springframework.jdbc.core.JdbcTemplate;

public class AccountDaoImpl implements AccountDao {

    private JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public void insert(Account account) {
        String sql = "INSERT INTO account values(null,?,?)";
        jdbcTemplate.update(sql, account.getName(), account.getMoney());
    }
}
编写账户业务层对象
编写账户service接口
package com.server;

import com.po.Account;

public interface Server {
    void insert(Account account);
}

编写账户service实现类
package com.server.impl;

import com.dao.AccountDao;
import com.po.Account;
import com.server.Server;

public class ServerImpl implements Server {

    private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    @Override
    public void insert(Account account) {
        accountDao.insert(account);
      // int i = 1 / 0;
        accountDao.insert(account);
    }
}

spring事务控制中需要明确的事项

第一件事

j2EE体系中项目按照分层进行设计开发,有表现层、业务层、持久层。事务处理位于业务层。spring提供了分层设计业务层的事务处理解决方案。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5osaapJB-1582267033352)(media/5273fdd6903c2b4f7879435104c34b49.png)]

第二件事

spring框架为我们提供了一组事务控制接口,该组接口位于spring-tx-xxx.RELEASE.jar包中。具体我们在后面的内容中详细介绍。

第三件事

spring的事务都是基于AOP的实现,它既可以使用编程的方式实现,也可以使用配置的方式实现。我们这里重点是使用配置的方式来实现。

spring中事务控制API介绍

PlatformTransactionManager接口

它是一个接口,是spring的事务管理器核心接口,spring并不实现具体的事务,而是负责包装底层事务,提供规范。应用底层支持什么样的事务策略,spring就支持什么样的事务策略。该接口提供了我们操作事务的常用方法。如下:

在这里插入图片描述
我们在开发中都是直接使用它的实现类,有如下常用实现类:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4eRZUIhI-1582267033353)(media/d3498f963c3dbd7aac44054471ebfef3.png)]

org.springframework.jdbc.datasource.DataSourceTransactionManager实现类

–支持使用spring JDBC 或者Mybatis框架的事务管理器

TransactionDefinition接口

它是一个接口,是定义事务的信息对象,提供了如下常用的方法:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EBcCP1Of-1582267033353)(media/5ea0797f4cbeeae8ce5634a02d836ea6.png)]

事务隔离级别 【理解】

说明:事务隔离级别,反应了事务在并发访问时的处理态度。

ISOLATION_DEFAULT:

默认级别,归属于下列某一种隔离级别。在项目中使用默认值即可。

ISOLATION_READ_UNCOMMITTED:

可以读取其他事务未提交的数据(脏读

ISOLATION_READ_COMMITTED:

只读取其他事务已经提交的数据,解决脏读的问题。有可能两次读取不一致的问题,不可重复读(oracle数据库默认级别)

ISOLATION_REPEATABLE_READ

是否读取其他事务提交修改后的数据,解决不可重复读的问题。保证两次读取一致,可重复读mysql数据库默认级别

**ISOLATION_SERIALIZABLE:**是否读取其他事务添加后的数据,解决幻影读的问题

细节:

1.事务级别从低到高:脏读->不可重复读->可重复读->解决幻读

2.事务级别越高,数据越安全,消耗的资源越多,数据库操作性能越低

3.在企业项目中,使用哪一种级别的事务,需要根据业务需求来确定

事务传播行为【理解】

说明:事务传播行为,决定在不同的执行环境中,事务该如何传递。

REQUIRED

如果已经有事务,就加入该事务中执行;如果没有事务,则新建一个事务。对应增/删/改操作(默认值)

SUPPORTS

如果已经有事务,支持当前事务的执行;如果没有事务,就以非事务的方式执行。对应查询操作

MANDATORY

要求在事务环境下执行,如果当前没有事务,则抛出异常

REQUIRES_NEW

新建事务,如果当前已经存在事务,则把当前事务挂起

NOT_SUPPORTED

以非事务方式执行,如果当前有事务,则把当前事务挂起

NEVER

以非事务方式执行,如果当前有事务,则抛出异常

NESTED

如果当前已经有事务,嵌套在当前事务中执行;如果当前没有事务,则类似于REQUIRED操作

事务超时时间

以秒为单位进行设置。如果设置为-1(默认值),表示没有超时限制。在企业项目中使用默认值即可。

是否只读事务

只读事务比读写事务性能要高,实际项目中,查询一般建议设置为只读。

TransactionStatus

它是一个接口,提供了事务具体的执行状态,描述了某一个时间点上事务对象的状态信息。提供了如下常用的方法:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EGN17mn7-1582267033354)(media/61625a88d59e4a41c52d3d6bc42ab133.png)]

基于xml的声明式事务配置【掌握】

创建项目

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8zWvIeAd-1582267033354)(media/16786c7dd532fdc17213f1ba32cc5bf2.png)]

事务配置环境准备

配置pom.xml,加入依赖包

说明:spring的事务配置,是通过我们第三天知识点aop实现的。需要导入aop开发相关的jar包,以及jdbc和tx包。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.itheima</groupId>
    <artifactId>spring-day04-02bankcase-trans-xml</artifactId>
    <version>1.0-SNAPSHOT</version>

    <packaging>jar</packaging>

    <properties>
        <!--spring版本-->
        <spring.version>5.0.2.RELEASE</spring.version>
        <!--druid版本-->
        <druid.version>1.0.29</druid.version>
        <!--mysql驱动版本-->
        <mysql.version>5.1.6</mysql.version>
        <!--aopalliance版本-->
        <aopalliance.version>1.0</aopalliance.version>
    </properties>

    <dependencies>
        <!--spring ioc依赖包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!--spring jdbc包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!-- spring aspects包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!--aopalliance包-->
        <dependency>
            <groupId>aopalliance</groupId>
            <artifactId>aopalliance</artifactId>
            <version>${aopalliance.version}</version>
        </dependency>
        <!--数据库连接池druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>${druid.version}</version>
        </dependency>
        <!--mysql驱动包-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>

    </dependencies>
</project>

配置步骤【重点】

第一步:配置事务管理器
<bean id="txm" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入连接池-->
        <constructor-arg ref="dataSource"/>
</bean>
第二步:配置事务通知规则
<!--事务通知规则(拦截到指定的方法后如何管理事务:读写、只读)-->
<tx:advice id="txAdvice" transaction-manager="txm">
    <tx:attributes>
        <!--tx:method标签:配置业务方法的名称规则,说明:
            name:方法名称规则,可以使用通配符*
            isolation:事务隔离级别,使用默认值即可
            propagation:事务传播行为,增/删/改方法使用REQUIRED。查询方法使用SUPPORTS。
            read-only:是否只读事务。增/删/改方法使用false。查询方法使用true。
            timeout:配置事务超时。使用默认值-1即可。永不超时。
            rollback-for:发生某种异常时,回滚;发生其它异常不回滚。没有默认值,任何异常都回滚
            no-rollback-for:发生某种异常时不回滚;发生其它异常时回滚。没有默认值,任何异常都回			滚。-->
        <tx:method name="*" propagation="REQUIRED" read-only="false"/>
    </tx:attributes>
</tx:advice>
第三步:Aop配置:建立切入点表达式与事务通知规则的关系
<aop:config>
    <!--切入点表达式-->
    <aop:pointcut id="pt" expression="execution(* com.server.impl.ServerImpl.insert(..))"/>
    <!--建立关联-->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>

完整配置
<?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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--设置组件扫描-->
    <context:component-scan base-package="com"/>

    <!--扫描配置文件-->
    <context:property-placeholder location="jdbc.properties"/>

    <!--开始注入AccountDaoImpl依赖-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <bean id="jdbcTamplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="dao" class="com.dao.impl.AccountDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTamplate"/>
    </bean>
    <!--结束注入AccountDaoImpl依赖-->

    <!--开始注入ServerImpl依赖-->
    <bean id="sever" class="com.server.impl.ServerImpl">
        <property name="accountDao" ref="dao"/>
    </bean>
    <!--结束注入ServerImpl依赖-->

    <!--开始Spring声明式事务控制的配置-->

    <!-- 配置事务管理器(事务切面类)-->
    <bean id="txm" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入连接池-->
        <constructor-arg ref="dataSource"/>
    </bean>
    <!--事务通知规则(拦截到指定的方法后如何管理事务:读写、只读)-->
    <tx:advice id="txAdvice" transaction-manager="txm">
        <tx:attributes>
            <tx:method name="*" propagation="REQUIRED" read-only="false"/>
        </tx:attributes>
    </tx:advice>
    <!--Aop配置:建立切入点表达式与事务通知规则的关系-->
    <aop:config>
        <!--切入点表达式-->
        <aop:pointcut id="pt" expression="execution(* com.server.impl.ServerImpl.insert(..))"/>
        <!--建立关联-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
    </aop:config>
    <!--结束Spring声明式事务控制的配置-->

</beans>

测试

import com.po.Account;
import com.server.Server;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(value = SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:beans.xml")
public class App {

    @Autowired
    private Server server;

    @Test
    public void test() {
        System.out.println(server);
        Account account = new Account();
        account.setName("晓红");
        account.setMoney(999.99);
        server.insert(account);
    }

}

当业务不报错时,会添加两条插入信息。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YBfeeEDp-1582267033355)(media/1540464175806.png)]

运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kv6rU4Oy-1582267033355)(media/1540464247455.png)]

但业务报错时,不会添加信息。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kNEs6UYG-1582267033355)(media/1540464322992.png)]

运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ih6LZ8mz-1582267033355)(media/1540464367637.png)]

基于xml和注解的声明式事务配置【掌握】

改造账户dao实现类,使用注解进行配置

package com.dao.impl;

import com.dao.AccountDao;
import com.po.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository("dao")
public class AccountDaoImpl implements AccountDao {
    @Autowired
    @Qualifier("jdbcTamplate")
    private JdbcTemplate jdbcTemplate;

    @Override
    public void insert(Account account) {
        String sql = "INSERT INTO account values(null,?,?)";
        jdbcTemplate.update(sql, account.getName(), account.getMoney());
    }
}

改造账户service实现类,使用注解进行配置

通过**@Transactional注解,配置service实现类事务**【重点】

package com.server.impl;

import com.dao.AccountDao;
import com.po.Account;
import com.server.Server;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service("server")
@Transactional(transactionManager = "txm")
public class ServerImpl implements Server {
    @Autowired
    @Qualifier("dao")
    private AccountDao accountDao;

    @Override
    public void insert(Account account) {
        accountDao.insert(account);
        //  int i = 1 / 0;
        accountDao.insert(account);
    }
}

配置bean.xml

开启spring对注解事务支持【关键步骤】,不需要配置事务通知规则。

<?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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--设置组件扫描-->
    <context:component-scan base-package="com"/>

    <!--扫描配置文件-->
    <context:property-placeholder location="jdbc.properties"/>

    <!--开始注入AccountDaoImpl依赖-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <bean id="jdbcTamplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="dao" class="com.dao.impl.AccountDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTamplate"/>
    </bean>
    <!--结束注入AccountDaoImpl依赖-->

    <!--开始注入ServerImpl依赖-->
    <bean id="sever" class="com.server.impl.ServerImpl">
        <property name="accountDao" ref="dao"/>
    </bean>
    <!--结束注入ServerImpl依赖-->

    <!--开始Spring声明式事务控制的配置-->

    <!-- 配置事务管理器(事务切面类)-->
    <bean id="txm" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入连接池-->
        <constructor-arg ref="dataSource"/>
    </bean>
    <!--结束Spring声明式事务控制的配置-->

    <!--开启spring对注解事务的支持-->
     <tx:annotation-driven transaction-manager="txm"/>

</beans>

测试

不报错时:

在这里插入图片描述

报错时:

在这里插入图片描述

关于@Transactional注解特别说明【重点】

  • @Transactional注解和xml中属性含义一致。该注解可以出现在接口上、类上、方法上。

  • 出现在接口上,表明该接口的所有实现类都有事务支持。

  • 出现在类上,表明类中所有方法都有事务支持。

  • 出现在方法上,表明该方法有事务支持。

  • 三者同时出现,优先级为:方法>类>接口。

纯注解的声明式事务配置【了解】

编写配置类

说明:分模块进行编写配置类。分别有持久层对象配置类、主配置类。

编写持久层对象配置类
package com.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;


@PropertySource("classpath:jdbc.properties")
public class SqlConfig {

    @Value("${jdbc.driverClassName}")
    private String className;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String name;
    @Value("${jdbc.password}")
    private String password;
    
	//编写DruidDataSource
    @Bean
    public DruidDataSource getDateSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(className);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(name);
        druidDataSource.setPassword(password);
        return druidDataSource;
    }
	//编写JdbcTemplate
    @Bean("jdbcTamplate")
    public JdbcTemplate getJdbcTemplate(DruidDataSource dataSource){
        return  new JdbcTemplate(dataSource);
    }

    //编写事务管理配置类
    @Bean("tx")
    public DataSourceTransactionManager getTXmanger(DruidDataSource dataSource){
       return new DataSourceTransactionManager(dataSource);
    }

}

编写主配置类
package com.config;


import org.springframework.context.annotation.*;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@ComponentScan(basePackages = {"com"})
@EnableAspectJAutoProxy
@EnableTransactionManagement
@Import(SqlConfig.class)
public class SpringConfig {
}

测试

报错时:

在这里插入图片描述

不报错时:

在这里插入图片描述

spring编程式事务控制【了解】

编程式事务管理API介绍

PlatformTransactionManager

它是spring事务管理的接口,参考声明式事务中的介绍。

TransactionDefinition

它是事务定义接口,参考声明式事务中的介绍。

TransactionTemplate

它是事务管理模版类,继承DefaultTransactionDefinition类。用于简化事务管理,事务管理由模板类定义,主要是通过TransactionCallback回调接口或TransactionCallbackWithoutResult回调接口指定,通过调用模板类的execute()方法来自动实现事务管理。

TransactionCallback

通过实现该接口的“T doInTransaction(TransactionStatus status)”方法来定义需要事务管理的操作代码。适合于有返回值的目标方法。

TransactionCallbackWithoutResult

实现TransactionCallback接口,提供“void doInTransactionWithoutResult(TransactionStatus status)”。适合于不需要返回值的目标方法

编程式事务管理代码模版(测试类)

import com.config.SpringConfig;
import com.dao.AccountDao;
import com.po.Account;
import com.server.Server;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

@RunWith(value = SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class App {

    @Autowired
    @Qualifier("dao")
    private AccountDao accountDao;

    @Autowired
    private Server server;

    @Autowired
    private TransactionTemplate transactionTemplate;

    //适合于有返回值的目标方法
    @Test
    public void test() {
        Account account = new Account();
        account.setName("晓红");
        account.setMoney(999.99);
        accountDao.insert(account);
        TransactionCallback<Object> callback = new TransactionCallback<Object>() {
            @Override
            public Object doInTransaction(TransactionStatus transactionStatus) {
                server.insert(account);
                return null;
            }
        };
        //接受返回的值,为上面的null
        Object execute = transactionTemplate.execute(callback);

    }

    //适合于无返回值的目标方法
    @Test
    public void test2() {
        Account account = new Account();
        account.setName("晓红");
        account.setMoney(999.99);
        accountDao.insert(account);
        TransactionCallbackWithoutResult withoutResult = new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                server.insert(account);
            }
        };
        transactionTemplate.execute(withoutResult);
    }

}

编程式事务案例

配置SqlConfig.java

说明:增加事务管理模版TransactionTemplate配置。

package com.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;

@PropertySource("classpath:jdbc.properties")
public class SqlConfig {

    @Value("${jdbc.driverClassName}")
    private String className;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String name;
    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DruidDataSource getDateSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(className);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(name);
        druidDataSource.setPassword(password);
        return druidDataSource;
    }

    @Bean("jdbcTamplate")
    public JdbcTemplate getJdbcTemplate(DruidDataSource dataSource){
        return  new JdbcTemplate(dataSource);
    }

    @Bean("tx")
    public DataSourceTransactionManager getTXmanger(DruidDataSource dataSource){
       return new DataSourceTransactionManager(dataSource);
    }
	//增加事务管理模版TransactionTemplate配置
    @Bean
    public TransactionTemplate getTransactionTemplate(DataSourceTransactionManager dataSourceTransactionManager){
        return  new TransactionTemplate(dataSourceTransactionManager);
    }

}

配置SqlConfig.java

package com.config;


import org.springframework.context.annotation.*;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@ComponentScan(basePackages = {"com"})
@EnableAspectJAutoProxy
@EnableTransactionManagement
@Import(SqlConfig.class)
public class SpringConfig {
}

改造账户Dao实现类

package com.dao.impl;

import com.dao.AccountDao;
import com.po.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository("dao")
public class AccountDaoImpl implements AccountDao {
    @Autowired
    @Qualifier("jdbcTamplate")
    private JdbcTemplate jdbcTemplate;

    @Override
    public void insert(Account account) {
        String sql = "INSERT INTO account values(null,?,?)";
        jdbcTemplate.update(sql, account.getName(), account.getMoney());
    }
}

改造账户service实现类

package com.server.impl;

import com.dao.AccountDao;
import com.po.Account;
import com.server.Server;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class ServerImpl implements Server {
    @Autowired
    @Qualifier("dao")
    private AccountDao accountDao;

    @Override
    public void insert(Account account) {
        accountDao.insert(account);
        int i = 1 / 0;
        accountDao.insert(account);


    }
}

测试

报错时:

在这里插入图片描述

不报错时:

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值