spring事务

一、jdbcTemplate

1.1 添加依赖

    <!-- Spring 在执行持久化层操作与持久化层技术进行整合过程中需要使用orm、jdbc、tx三个jar包 -->
    <!-- 导入 orm 包就可以通过 Maven 的依赖传递性把其他两个也导入 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>6.1.14</version>
    </dependency>
    <!--数据库-->
    <dependency>
      <groupId>com.mysql</groupId>
      <artifactId>mysql-connector-j</artifactId>
      <version>8.4.0</version>
    </dependency>
    <!--数据源,选一个就行-->
    <!--阿里巴巴数据源-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.2.23</version>
    </dependency>
    <!--c3p0数据源-->
    <dependency>
      <groupId>com.mchange</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.10.1</version>
    </dependency>

 1.2 创建jdbc.properties

db.xxx是可以自己取的,用的时候根据这个键调用

db.user=root
db.password=密码
db.url=jdbc:mysql://localhost:3306/库名?serverTimezone=UTC
db.driver=com.mysql.cj.jdbc.Driver

 1.3 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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <!--引入外部属性文件-->
    <context:property-placeholder location="classpath:db.properties"/>
    <!--配置数据源,pom导入的哪个就写哪个-->
    <!--c3p0-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${db.user}"/>
        <property name="password" value="${db.password}"/>
        <property name="jdbcUrl" value="${db.url}"/>
        <property name="driverClass" value="${db.driver}"/>
    </bean>
    <!--阿里巴巴-->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="username" value="${db.user}"/>
        <property name="password" value="${db.password}"/>
        <property name="url" value="${db.url}"/>
        <property name="driverClassName" value="${db.driver}"/>
    </bean>
    <!--配置jdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--装配数据源-->
        <property name="dataSource" ref="druidDataSource"/>
    </bean>
</beans>

1.4 数据表

根据表创建实体类并提供get、set方法

1.5 实现CURD

增删改的代码都是一致的

@SpringJUnitConfig(locations = "classpath:jdbc.xml")
public class JdbcTemplateTest {
    @Resource
    private JdbcTemplate jdbcTemplate;

    /*增删改操作*/
    @Test
    public void insert(){
        //1.编写sql语句
        String sql = "insert into user(username,money) values(?,?)";
        //2.调用jdbcTemplate方法,传入相关参数
        int i = jdbcTemplate.update(sql, "王五", 20);
        System.out.println(i);
    }
    @Test
    public void update(){
        //1.编写sql语句
        String sql = "update user set money=? where username=?";
        //2.调用jdbcTemplate方法,传入相关参数
        int i = jdbcTemplate.update(sql,  400,"王五");
        System.out.println(i);
    }
    @Test
    public void del(){
        //1.编写sql语句
        String sql = "delete from user where id=?";
        //2.调用jdbcTemplate方法,传入相关参数
        int i = jdbcTemplate.update(sql, 3);
        System.out.println(i);
    }
}

 单条数据查询

@Test
    public void selOne(){
        //1.编写sql语句
        String sql = "select id,username,money from user where id=?";
        //2.调用jdbcTemplate方法,传入相关参数
        //方法一 手动封装
        User u = jdbcTemplate.queryForObject(sql, (rs, rowNum) -> {
            User user = new User();
            user.setId(rs.getInt("id"));
            user.setUsername(rs.getString("username"));
            user.setMoney(rs.getInt("money"));
            return user;
        }, 2);
        System.out.println(u);
    }
    @Test
    public void selOne(){
        //1.编写sql语句
        String sql = "select id,username,money from user where id=?";
        //2.调用jdbcTemplate方法,传入相关参数
        //方法二
        User u = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), 2);
        System.out.println(u);
    }

全查

    @Test
    public void selAll(){
        //1.编写sql语句
        String sql = "select id,username,money from user";
        //2.调用jdbcTemplate方法,传入相关参数
        //2.1手动封装
        /*List<User> list = jdbcTemplate.query(sql, (rs, rowNum) -> {
            User user = new User();
            user.setId(rs.getInt("id"));
            user.setUsername(rs.getString("username"));
            user.setMoney(rs.getInt("money"));
            return user;
        });*/
        //2.2使用BeanPropertyRowMapper
        List<User> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));
        System.out.println(list);
    }

聚合函数COUNT()

    //查询返回单个值
    @Test
    public void selValue(){
        //1.编写sql语句
        String sql = "select count(*) from user";
        //2.调用jdbcTemplate方法,传入相关参数
        int i = jdbcTemplate.queryForObject(sql, Integer.class);
        System.out.println(i);
    }

二、声明式事务的概念

要么都成功,有一个失败全部都失败。

事务的特性:ACID,原子性,一致性,隔离性,持久性

三、基于注解的声明式事务

加一个数据表,编写实体类,提供get、色图、等方法

在1.3中的核心配置文件中开启组件扫描

<context:component-scan base-package="com.hait.tx"/>

按以下目录准备项目 

流程:controller调用service,service调用dao,所以在controller曾注入service,在service曾注入dao 

实现用户买书功能

@Controller
public class LibController {
    @Autowired
    private LibService libService;

    //买书  传入用户id和书id
    public void buyBook(int uid,int lid){
        libService.buyBook(uid,lid);
    }
}
public interface LibService {
    void buyBook(int uid, int lid);
}
@Service
public class LibServiceImpl implements LibService {

    @Autowired
    private LibDao libDao;

    @Override
    public void buyBook(int uid, int lid) {
        //1.查询图书价格
        double price = libDao.selectPrice(lid);
        //2.图书库存-1
        libDao.updateCount(lid);
        //3.用户余额-图书价格
        libDao.updateMoney(uid,price);
    }
}
public interface LibDao {
    //查询价格
    double selectPrice(int lid);
    //更新库存
    void updateCount(int lid);
    //更新用户余额
    void updateMoney(int uid,double price);
}
@Repository
public class LibDaoImpl implements LibDao {
    @Autowired
    private JdbcTemplate  jdbcTemplate;
    @Override
    public double selectPrice(int lid) {
        return jdbcTemplate.queryForObject("select price from lib where id=?", Double.class, lid);
    }

    @Override
    public void updateCount(int lid) {
        jdbcTemplate.update("update lib set count=count-1 where id=?",lid);
    }

    @Override
    public void updateMoney(int uid,double price) {
        jdbcTemplate.update("update user set money=money-? where id=?",price,uid);
    }
}

测试:张三买一本数学书(还没加事务,那么如果买书张三余额不够的话,库存已经-1,张三的余额不变,这肯定是不符合逻辑的)

@SpringJUnitConfig(locations = "classpath:jdbc.xml")
public class test {
    @Autowired
    private LibController  libController;

    @Test
    public void buyBookTest(){
        libController.buyBook(1,1);
    }
}

添加事务:在配置文件中开启事务

xmlns:tx="http://www.springframework.org/schema/tx"

http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd

<!--开启事务-->
    <!--1.事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="druidDataSource"/>
    </bean>
    <!--2.开启注解驱动-->
    <tx:annotation-driven transaction-manager="transactionManager"/>

@Transactional注解加在类上则会影响类中所有方法,加在方法上只会影响该方法,在LibServiceImpl类上加上该注解

测试:用户余额比要买的书的价格少

 控制台报错的意思是,价格无符号,如果执行完方法就变负号了

余额和书本数量均未发生变化,说明事务回滚成功 

事务的属性

只读

@Transactional(readOnly = true)

 超时

@Transactional(timeout = 3)//单位是秒

//TODO 模拟超时
        try {
            TimeUnit.SECONDS.sleep(5);//延时执行5秒
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

回滚策略

设置哪些类进行回滚

rollbackFor:需要设置一个Class类型的对象

rollbackForClassName:需要设置一个字符串类型的全类名

设置哪些类不进行回滚

noRollbackFor:需要设置一个Class类型的对象

noRollbackForClassName:需要设置一个字符串类型的全类名

假设买书的方法中出现了除数为0的异常,让它不进行回滚:

@Transactional(noRollbackFor = ArithmeticException.class)

 

 数据库操作成功

隔离级别

mysql默认的隔离级别是: @Transactional(isolation = Isolation.REPEATABLE_READ)//可重复读

@Transactional(isolation = Isolation.DEFAULT)//使用数据库默认的隔离级别 @Transactional(isolation = Isolation.READ_UNCOMMITTED)//读未提交 @Transactional(isolation = Isolation.READ_COMMITTED)//读已提交 @Transactional(isolation = Isolation.REPEATABLE_READ)//可重复读 @Transactional(isolation = Isolation.SERIALIZABLE)//串行化

传播行为

REQUIRED:如果调用方法有事务,就加入,如果没有就新建自己独立的事务
REQUIRES_NEW:不管调用方法是否有事务,被调用方法都新建事务,都是独立的事务
Propagation.NESTED:如果当前存在事务,则在该事务中嵌套一个新事务如果没有事务,则与Propagation.REQUIRED一样
Propagation.SUPPORTS:如果当前存在事务,则加入该事务,否则以非事务方式执行
Propagation.NOT SUPPORTED:以非事务方式执行,如果当前存在事务挂起该事务
Propagation.MANDATORY:必须在一个已有的事务中执行,否则抛出异常
Propagation.NEVER:必须在没有事务的情况下执行,否则抛出异常

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

尢词

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值