spring&JdbcTemplate&事务入门笔记

上一篇-日志框架

第八章 JdbcTemplate

8.1 什么是Jdbc

Java数据库连接(Java Database Connectivity,简称JDBC),jdbc是java访问数据库的一种规范,它规定了访问数据库的标准,必须遵循它的标准才能对数据进行增删改查操作,如果不做统一的规定,那么不同的数据库厂商就要有不同的访问数据库规范,及其不容易管理。所以为了统一数据访问的方式,就有了jdbc的规范。

8.2 什么是JdbcTemplate

JDBC已经能够满足大部分用户最基本的需求,但是在使用JDBC时,必须自己来管理数据库资源如:获取PreparedStatement,设置SQL语句参数,关闭连接等步骤。JdbcTemplate是Spring对JDBC的封装,目的是使JDBC更加易于使用。JdbcTemplate是Spring的一部分,JdbcTemplate处理了资源的建立和释放,他帮助我们避免一些常见的错误,比如忘了总要关闭连接。他运行核心的JDBC工作流,如Statement的建立和执行,而我们只需要提供SQL语句和提取结果。

在JdbcTemplate中执行SQL语句的方法大致分为3类:

execute:可以执行所有SQL语句,但是该方法没有返回值,一般用于执行DDL语句。
update:用于执行INSERT、UPDATE、DELETE等DML语句。
queryXxx:用于DQL数据查询语句。

8.3 快速入门

导入pom

<!--        spring容器-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.10.RELEASE</version>
        </dependency>
        <!--        spring 测试-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!--        单元测试5-->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>RELEASE</version>
            <scope>test</scope>
        </dependency>
        <!--        spring JdbcTemplate-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.10.RELEASE</version>
        </dependency>
<!--        数据库连接工具-->
        <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.2.4</version>
        </dependency>
       <!--        使用log4j2 日志-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.14.0</version>
        </dependency>

创建数据库连接信息

jdbc.dev.driverClass=com.mysql.jdbc.Driver
jdbc.dev.url=jdbc:mysql://localhost:3306/test
jdbc.dev.username=root
jdbc.dev.password=root

数据库对象

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


    @Autowired
    private Environment env;


    
    @Bean
    public DataSource dataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(env.getProperty("jdbc.dev.driverClass"));
        dataSource.setUrl(env.getProperty("jdbc.dev.url"));
        dataSource.setUsername(env.getProperty("jdbc.dev.username"));
        dataSource.setPassword(env.getProperty("jdbc.dev.password"));
        return dataSource;
    }

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource){
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        return jdbcTemplate;
    }
}

容器配置类

@Configuration
@ComponentScan("com.lyf.jdbcTemplate")
public class JdbcAppConfig {
}

单元测试5 测试数据库连接

@SpringJUnitConfig(JdbcAppConfig.class)
public class MyJdbcTemplate {

    private static final Logger log = LoggerFactory.getLogger(MyJdbcTemplate.class);
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Test
    public void jdbc() throws SQLException {
        Connection connection = jdbcTemplate.getDataSource().getConnection();

        log.info("连接状态:{}",connection.getCatalog());

    }
}

在这里插入图片描述

8.4 对数据库进行操作

8.4.1 基本增删改查

package jdbcTemplate.MyJdbcTemplate;

import com.lyf.jdbcTemplate.config.JdbcAppConfig;
import com.lyf.jdbcTemplate.config.User;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;


@SpringJUnitConfig(JdbcAppConfig.class)
public class MyJdbcTemplate {

    private static final Logger log = LoggerFactory.getLogger(MyJdbcTemplate.class);
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Test
    public void jdbc() throws SQLException {
        Connection connection = jdbcTemplate.getDataSource().getConnection();

        log.info("连接状态:{}",connection.getCatalog());

    }

    /**
     * 创建表
     * @throws SQLException
     */
    @Test
    public void test() throws SQLException {
        String sql = "create table sys_user(id int not null primary key auto_increment, " +
                "username varchar(20), password varchar , status int(20))";
        jdbcTemplate.execute(sql);
        log.info("数据表创建成功");
    }

    /**
     * 删除表
     */
    @Test
    public void test1(){
        String sql = "drop table test";
        jdbcTemplate.execute(sql);
        log.info("数据表删除成功");

    }

    /**
     * 对sys_user进行插入
     */
    @Test
    public void test2() {
        String sql ="INSERT INTO `test`.`sys_user`(`id`, `username`, `password`, `status`) VALUES (null , 'xiaoqi', '1e191d851b3b49a248f4ea62f6b06410', 4)";
        int update = jdbcTemplate.update(sql);
        if (update > 1) {
            log.info("数据表成功");

        }
    }

    /**
     * 修改数据
     */
    @Test
    public void test3(){
        String sql = "UPDATE `test`.`sys_user` SET `username` = 'xiaoqi', `password` = '1e191d851b3b49a248f4ea62f6b06410', `status` = 4 WHERE `id` = 5;";
        int update = jdbcTemplate.update(sql);
        if (update>1){
            log.info("修改成功");

        }
    }

    /**
     * 查询所有数据
     */
    @Test
    public  void test4(){
        String sql = "select * from sys_user";
        List<User> query = jdbcTemplate.query(sql, new BeanPropertyRowMapper<User>(User.class));

        for (User user :query) {
            log.info("{}",user);
        }
    }

    /**
     * 查询一个
     */
    @Test
    public void test5(){
        Integer id = 1;
        String sql = "select * from sys_user where id ="+id;
        User query = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class));
        System.out.println(query);

    }

    /**
     * 统计个数
     */
    @Test
    public void test6(){
        String sql = "select count(*) from sys_user";
        Integer query = jdbcTemplate.queryForObject(sql,Integer.class);
        System.out.println(query);

    }
}

第九章 spring事务

9.1 什么是事务

Transactions代表事务,简写为tx,它是用户定义的一个操作序列,这些操作要么全做要么全不做,是一个不可分割的工作单位。
应用场景:银行转账。操作必须按顺序执行,如果中途遇到异常失败,则之前执行的操作必须回滚。

9.2 事务特性

原子性(Atomicity):强调事务的不可分割
一致性(Consistency):事务的执行的前后数据的完整性保持一致
隔离性(Isolation):一个事务执行的过程中,不应该受到其他事务的干扰
持久性(Durability):一个事务一旦结束,数据就会持久到数据库

9.3 事务隔离级别

如果不考虑事务的隔离性,可能会引发读安全性问题:

脏读:一个事务读到了另一个事务未提交的数据
不可重复读:一个事务读到了另一个事务已经提交的 update 的数据,导致多次查询结果不一致
幻读 / 虚读:一个事务读到了另一个事务已经提交的 insert 的数据,导致多次查询结果不一致

事务隔离级别

要想解决读安全性问题,我们就需要给事务设置隔离级别,Spring定义了4种隔离级别:

隔离级别中文说明说明
READ UNCOMMITTED读未提交不能解决以上所有读问题,效率最高,安全性最低,一般不用
READ COMMITTED都已提交避免脏读,不可重复读和幻读有可能发生,Oracle默认的隔离级别
REPEATABLE READ可重复读避免脏读、不可重复读,幻读有可能发生,MySQL默认的隔离级别
SERIALIZABLE串行化可以解决以上所有读问题,效率最差,安全性最高,一般不用

事务传播行为

事务传播行为(Propagation Behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何执行。

事务传播行为既然是传播,那么至少有两个东西,才可以发生传播,单体不存在传播这个行为。

例如:methodA事务方法调用methodB事务方法时,methodB是继续在调用者methodA的事务中运行呢,还是为自己开启一个新事务运行,这就是由methodB的事务传播行为决定的。

通常事务都是加在 Service 层,事务的传播行为是为了解决特别复杂的业务,业务层方法互相调用的问题,Spring定义了7种传播行为:

在这里插入图片描述
在这里插入图片描述
注意:使用REQUIRES_NEW时,内层事务与外层事务就像两个独立的事务一样,一旦内层事务进行了提交后,外层事务不能对其进行回滚,两个事务互不影响,两个事务不是一个真正的嵌套事务。使用NESTED时,外层事务的回滚可以引起内层事务的回滚,而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务。

只读事务概念

如果你一次执行单条查询语句,则没有必要启用事务支持,数据库默认支持SQL执行期间的读一致性。

如果你一次执行多条查询语句,例如统计查询,报表查询,在这种场景下,多条查询SQL必须保证整体的读一致性,否则,在前条SQL查询之后,后条SQL查询之前,数据被其他用户改变,则该次整体的统计查询将会出现读数据不一致的状态,此时,应该启用事务支持。

在将事务设置成只读后,相当于将数据库设置成只读数据库,此时若要进行写的操作,会出现错误。由于只读事务不存在数据的修改,因此数据库将会为只读事务提供一些优化手段,例如Oracle对于只读事务,不启动回滚段,不记录回滚log。

9.4 事务配置(xml)

DROP DATABASE IF EXISTS `test`;

CREATE DATABASE `test`;

USE `test`;

DROP TABLE IF EXISTS `t_account`;

CREATE TABLE `t_account` (
  `aid` INT(11) NOT NULL AUTO_INCREMENT COMMENT '用户主键',
  `aname` VARCHAR(20) DEFAULT NULL COMMENT '用户名称',
  `amoney` INT(11) DEFAULT NULL COMMENT '用户余额',
  PRIMARY KEY (`aid`)
) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

INSERT  INTO `t_account`(`aid`,`aname`,`amoney`) VALUES (1,'张三',1000);
INSERT  INTO `t_account`(`aid`,`aname`,`amoney`) VALUES (2,'李四',1000);

导入pom

<?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">
    <parent>
        <artifactId>springStudy</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lyf</groupId>
    <artifactId>transactions</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
<!--        spring 容器-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.10.RELEASE</version>
        </dependency>
<!--        jdbcTemplate-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.10.RELEASE</version>
        </dependency>
<!--        spring 测试-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.10.RELEASE</version>
        </dependency>
<!--        mysql 驱动包-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>
<!--        数据库连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.4</version>
        </dependency>
<!--        单元测试5-->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.7.0</version>
            <scope>test</scope>
        </dependency>
        <!--        log4j2 日志-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.14.0</version>
        </dependency>
    </dependencies>
</project>

配置dao层

package com.lyf.tx.dao;

/**
 * @Author: lyf
 * @CreateTime: 2022-11-28
 * @description:
 */
public interface AccountDao {


    /**
     * 减钱
     * @param id
     * @param money
     */
    public void reduceMoney(Integer id, Integer money);

    /**
     * 加钱
     * @param id
     * @param money
     */
    public void addMoney(Integer id ,Integer money);
}

package com.lyf.tx.dao;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;


//@Repository
public class AccountDaoImpl implements AccountDao{

    private static final Logger log = LoggerFactory.getLogger(AccountDaoImpl.class);

   // @Autowired
    private JdbcTemplate jdbcTemplate;

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

    /**
     * 减钱
     *
     * @param id
     * @param money
     */
    public void reduceMoney(Integer id, Integer money) {
        String sql ="update t_account set amoney = amoney-? where aid =? ";
        jdbcTemplate.update(sql,money,id);
        log.info("{}少钱",id);
    }

    /**
     * 加钱
     *
     * @param id
     * @param money
     */
    public void addMoney(Integer id, Integer money) {
        String sql ="update t_account set amoney = amoney+? where aid =? ";
        jdbcTemplate.update(sql,money,id);
        log.info("{}多钱",id);
    }
}

配置service层

package com.lyf.tx.service;


public interface AccountService {

    /**
     * 转转操作
     * @param formId
     * @param toId
     * @param money
     */
    void transfer(Integer formId,Integer toId ,Integer money);
}

package com.lyf.tx.service;

import com.lyf.tx.dao.AccountDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;


@Service
public class AccountServiceImpl implements AccountService{


    @Autowired
    private AccountDao accountDao;

    private TransactionTemplate transactionTemplate;

    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }

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

    
    @Override
    public void transfer(Integer formId, Integer toId, Integer money) {
         transactionTemplate.execute(new TransactionCallbackWithoutResult() {
             @Override
             protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                 //减钱
                 accountDao.reduceMoney(formId,money);
                 //模拟异常
                 int i = 1/0;
                 //加钱
                 accountDao.addMoney(toId,money);
             }
         });
    }
}

数据库连接信息

jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=root

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:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       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

">

<!--    引入外部属性文件-->
    <context:property-placeholder location="jdbc.properties"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
     p:driverClassName="${jdbc.driverClass}"
          p:url="${jdbc.url}"
          p:username="${jdbc.username}"
          p:password="${jdbc.password}"
    />
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
    p:dataSource-ref="dataSource"/>

    <bean id="accountDao" class="com.lyf.tx.dao.AccountDaoImpl"
        p:jdbcTemplate-ref="jdbcTemplate"
    />

    <bean class="com.lyf.tx.service.AccountServiceImpl"
        p:accountDao-ref="accountDao"
          p:transactionTemplate-ref="transactionTemplate"
    />

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
        p:dataSource-ref="dataSource"
    />

    <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"
        p:transactionManager-ref="transactionManager"
        p:isolationLevelName="ISOLATION_REPEATABLE_READ"
          p:propagationBehaviorName="PROPAGATION_REQUIRED"
    />
</beans>

日志配置

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <!--默认使用rootLogger-->
        <Root level="info">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>

测试代码

import com.lyf.tx.config.TxAppConfig;
import com.lyf.tx.dao.AccountDao;
import com.lyf.tx.service.AccountService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import java.sql.Connection;
import java.sql.SQLException;


//@SpringJUnitConfig(TxAppConfig.class)
@SpringJUnitConfig(locations = "classpath:context.xml")
public class TransactionsTest {


    @Autowired
    AccountService accountService;
    @Autowired
    JdbcTemplate jdbcTemplate;
	/**
     * 测试数据库连接
     * @throws SQLException
     */
    @Test
    public void test() throws SQLException {

        Connection connection = jdbcTemplate.getDataSource().getConnection();
        System.out.println(connection.getCatalog());
    }

    @Test
    public void test1(){
        accountService.transfer(1,2,100);
    }
}

运行结果
在这里插入图片描述

9.5 事务配置(aop)

pom依赖

<?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">
    <parent>
        <artifactId>springStudy</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lyf</groupId>
    <artifactId>transactions</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
<!--        spring 容器-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.10.RELEASE</version>
        </dependency>
<!--        jdbcTemplate-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.10.RELEASE</version>
        </dependency>
<!--        spring 测试-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.10.RELEASE</version>
        </dependency>
<!--        mysql 驱动包-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>
<!--        数据库连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.4</version>
        </dependency>
<!--        单元测试5-->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.7.0</version>
            <scope>test</scope>
        </dependency>
        <!--        log4j2 日志-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.14.0</version>
        </dependency>
<!--AOP-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.1.10.RELEASE</version>
        </dependency>
    </dependencies>
</project>

保持 AccountDao 、AccountDaoImpl 、AccountService 、测试代码类不变 log4j2.xml 不变。
修改AccountServiceImpl类

package com.lyf.tx.service;

import com.lyf.tx.dao.AccountDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import org.springframework.transaction.support.TransactionCallbackWithoutResult;


@Service
public class AccountServiceImpl implements AccountService{


    @Autowired
    private AccountDao accountDao;



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

    
    @Override
    public void transfer(Integer formId, Integer toId, Integer money) {
                 //减钱
                 accountDao.reduceMoney(formId,money);
                 //模拟异常
                 int i = 1/0;
                 //加钱
                 accountDao.addMoney(toId,money);
             }
    
}

配置文件

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


">

<!--    引入外部属性文件-->
    <context:property-placeholder location="jdbc.properties"/>
<!--    配置数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
     p:driverClassName="${jdbc.driverClass}"
          p:url="${jdbc.url}"
          p:username="${jdbc.username}"
          p:password="${jdbc.password}"
    />
<!--    配置jdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
    p:dataSource-ref="dataSource"/>


    <bean id="accountDao" class="com.lyf.tx.dao.AccountDaoImpl"
        p:jdbcTemplate-ref="jdbcTemplate"
    />

    <bean class="com.lyf.tx.service.AccountServiceImpl"
        p:accountDao-ref="accountDao"
    />

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
        p:dataSource-ref="dataSource"
    />
<!--事务-->
    <tx:advice id="transactionInterceptor" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

<!--    配置切面-->
    <aop:config>
<!--        配置通知点-->
        <aop:pointcut id="transfer" expression="execution(* com.lyf.tx.service.AccountServiceImpl.transfer(..))"/>
<!--        通知-->
        <aop:advisor advice-ref="transactionInterceptor" pointcut-ref="transfer"/>
    </aop:config>
</beans>

9.5 事务配置 (注解)

pom依赖

<?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">
    <parent>
        <artifactId>springStudy</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lyf</groupId>
    <artifactId>transactions</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
<!--        spring 容器-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.10.RELEASE</version>
        </dependency>
<!--        jdbcTemplate-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.10.RELEASE</version>
        </dependency>
<!--        spring 测试-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.10.RELEASE</version>
        </dependency>
<!--        mysql 驱动包-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>
<!--        数据库连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.4</version>
        </dependency>
<!--        单元测试5-->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.7.0</version>
            <scope>test</scope>
        </dependency>
        <!--        log4j2 日志-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.14.0</version>
        </dependency>

    </dependencies>
</project>

AccountDao

package com.lyf.tx.dao;

/**
 * @Author: lyf
 * @CreateTime: 2022-11-28
 * @description:
 */
public interface AccountDao {


    /**
     * 减钱
     * @param id
     * @param money
     */
    public void reduceMoney(Integer id, Integer money);

    /**
     * 加钱
     * @param id
     * @param money
     */
    public void addMoney(Integer id ,Integer money);
}

package com.lyf.tx.dao;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

/**
 * @Author: lyf
 * @CreateTime: 2022-11-28
 * @description:
 */
@Repository
public class AccountDaoImpl implements AccountDao{

    private static final Logger log = LoggerFactory.getLogger(AccountDaoImpl.class);

    @Autowired
    private JdbcTemplate jdbcTemplate;

       /**
     * 减钱
     *
     * @param id
     * @param money
     */
    public void reduceMoney(Integer id, Integer money) {
        String sql ="update t_account set amoney = amoney-? where aid =? ";
        jdbcTemplate.update(sql,money,id);
        log.info("{}少钱",id);
    }

    /**
     * 加钱
     *
     * @param id
     * @param money
     */
    public void addMoney(Integer id, Integer money) {
        String sql ="update t_account set amoney = amoney+? where aid =? ";
        jdbcTemplate.update(sql,money,id);
        log.info("{}多钱",id);
    }
}

package com.lyf.tx.service;

/**
 * @Author: lyf
 * @CreateTime: 2022-11-28
 * @description:
 */
public interface AccountService {

    /**
     * 转转操作
     * @param formId
     * @param toId
     * @param money
     */
    void transfer(Integer formId,Integer toId ,Integer money);
}
package com.lyf.tx.service;

import com.lyf.tx.dao.AccountDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

/**
 * @Author: lyf
 * @CreateTime: 2022-11-28
 * @description:
 */
@Service
public class AccountServiceImpl implements AccountService{


    @Autowired
    private AccountDao accountDao;


    @Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED)
    @Override
    public void transfer(Integer formId, Integer toId, Integer money) {

                 //减钱
                 accountDao.reduceMoney(formId,money);
                 //模拟异常
                 int i = 1/0;
                 //加钱
                 accountDao.addMoney(toId,money);
             }

}

数据库配置类

package com.lyf.tx.config;

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

import javax.sql.DataSource;

/**
 * @Author: lyf
 * @CreateTime: 2022-11-28
 * @description:
 */
@Configuration
@PropertySource("classpath:jdbc.properties")
public class MyDataSource {

    @Autowired
    private Environment env;

    @Bean
    public DataSource dataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(env.getProperty("jdbc.driverClass"));
        dataSource.setUrl(env.getProperty("jdbc.url"));
        dataSource.setUsername(env.getProperty("jdbc.username"));
        dataSource.setPassword(env.getProperty("jdbc.password"));
        return dataSource;
    }

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource){
        return new JdbcTemplate(dataSource);
    }

    @Bean
    public DataSourceTransactionManager transactionManager(DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }

    
}

spriing配置

@Configuration
@ComponentScan("com.lyf.tx")
@EnableTransactionManagement
public class TxAppConfig {
}

测试

import com.lyf.tx.config.TxAppConfig;
import com.lyf.tx.dao.AccountDao;
import com.lyf.tx.service.AccountService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * @Author: lyf
 * @CreateTime: 2022-11-28
 * @description:
 */
@SpringJUnitConfig(TxAppConfig.class)
//@SpringJUnitConfig(locations = "classpath:context.xml")
public class TransactionsTest {


    @Autowired
    AccountService accountService;
    @Autowired
    JdbcTemplate jdbcTemplate;

    /**
     * 测试数据库连接
     * @throws SQLException
     */
    @Test
    public void test() throws SQLException {

        Connection connection = jdbcTemplate.getDataSource().getConnection();
        System.out.println(connection.getCatalog());
    }

    @Test
    public void test1(){
        accountService.transfer(1,2,100);
    }
}

数据库数据
在这里插入图片描述
运行结果 异常回滚事务在这里插入图片描述
数据库的值不变。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值