SSM整合之装配事务管理器(三)

 什么是事务?

    事务是数据库的核心概念之一,它代表数据库一系列操作的集合,这些操作必须在一个事务当中,要么全部执行成功,要么全部不执行

ACID特性

原子性(Atomicity)

原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响

一致性(Consistency)

一个事务执行之前和执行之后都必须处于一致状态,例如转账,假设用户A和用户B二者的钱加起来一共是50000,那么不管A和B之间如何转账,事务结束之后二个用户的钱加起来一共是5000,这就是事务的一致性

隔离性(Lsolation)

隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离

持久性(Durability)

持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

<!-- 装配JDBC事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 注入数据源 -->
    <property name="dataSource" ref="dataSource"/>
</bean>
<!-- 启用事务注解驱动, transaction-manager引用事务管理器的id,
     如果事务管理器的id是transactionManager,那么transaction-manager可以不用指定。
     proxy-target-class设置为true,表示强制使用CGLIB创建事务代理-->
<tx:annotation-driven proxy-target-class="true"/>

案例:

entity层:

import lombok.Data;

@Data
public class Account {
    private Integer aid;
    private Integer money;
}

dao层引用

public interface AccountDao {

    /**
     * 根据账号id查询账号信息
     * @param aid
     * @return
     */
    Account getAccountById(int aid);

    /**
     * 更新账户信息
     * @param count
     */
    void updateAccount(Account count);
}

resource文件中的mapper

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="edu.nf.ch03.dao.AccountDao">

    <resultMap id="accountMap" type="account">
        <id property="aid" column="a_id"/>
        <result property="money" column="money"/>
    </resultMap>

    <select id="getAccountById" parameterType="int" resultMap="accountMap">
        select a_id, money from account_info where a_id = #{aid}
    </select>

    <select id="updateAccount" parameterType="account">
        update account_info set money = #{money} where a_id = #{aid}
    </select>
</mapper>

applicationContext配置xml文件

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

    <!-- 启用包扫描 -->
    <context:component-scan base-package="edu.nf.ch03"/>

    <!-- 1. 整合druid连接池,其实也就是将druid的数据源纳入spring的ioc容器中,
         init-method指定数据源的初始化方法,destroy-method指定数据源的close方法-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <!-- 注入数据库连接属性 -->
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/city?useSSL=false&amp;useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=Asia/Shanghai"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
        <!-- 注入连接池配置属性-->
        <!-- 最大连接池数量-->
        <property name="maxActive" value="20"/>
        <!-- 初始化连接池时创建的连接个数-->
        <property name="initialSize" value="5"/>
        <!-- 最小连接池数量,建议和初始化大小一致-->
        <property name="minIdle" value="5"/>
        <!-- 获取连接最大等待时间,超时则抛异常。单位:毫秒 -->
        <property name="maxWait" value="2000"/>
        <!-- 连接保持空闲而不被驱逐出连接池的最小时间,单位毫秒-->
        <property name="minEvictableIdleTimeMillis" value="300000"/>
        <!-- 销毁连接的线程检测的间隔时间,单位:毫秒-->
        <property name="timeBetweenEvictionRunsMillis" value="60000"/>
        <!-- 检测连接是否有效-->
        <property name="testWhileIdle" value="true"/>
        <property name="testOnBorrow" value="true"/>
        <!-- 归还连接时检测连接是否有效-->
        <property name="testOnReturn" value="true"/>
        <!-- 是否缓存PreparedStatement,mysql建议关闭 -->
        <property name="poolPreparedStatements" value="false"/>
        <!-- 定义一条伪sql,用于检查连接的可用性 -->
        <property name="validationQuery" value="select 1"/>
    </bean>

    <!-- 2. 整合mybatis, 其核心就是将SqlSessionFactory纳入spring的IOC容器 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 注入数据源, 引用上面配置数据源的bean的id -->
        <property name="dataSource" ref="dataSource"/>
        <!-- 指定实体的别名-->
        <property name="typeAliasesPackage" value="edu.nf.ch03.entity"/>
        <!-- 指定mapper映射文件的目录-->
        <property name="mapperLocations" value="classpath:mapper/*.xml"/>
        <!-- 配置文件插件-->
        <property name="plugins">
            <!-- 装配分页的拦截器 -->
            <bean class="com.github.pagehelper.PageInterceptor">
                <!-- 注入分页属性 -->
                <property name="properties">
                    <props>
                        <!-- 数据库方言 -->
                        <prop key="helperDialect">mysql</prop>
                        <!-- 启用分页注解支持-->
                        <prop key="supportMethodsArguments">true</prop>
                        <!-- 分页合理化-->
                        <prop key="reasonable">true</prop>
                    </props>
                </property>
            </bean>
        </property>
    </bean>

    <!-- 3. 扫描dao的接口包。这样会利用动态代理的机制在运行时创建所有dao接口的代理实现-->
    <mybatis:scan base-package="edu.nf.ch03.dao"/>

    <!-- 装配JDBC事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 注入数据源 -->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!-- 启用事务注解驱动, transaction-manager引用事务管理器的id,
         如果事务管理器的id是transactionManager,那么transaction-manager可以不用指定。
         proxy-target-class设置为true,表示强制使用CGLIB创建事务代理-->
    <tx:annotation-driven proxy-target-class="true"/>
</beans>

service层

/**
 * @author wangl
 * @date 2022/10/18
 * 转账接口
 */
public interface TransferService {

    /**
     * 转账服务
     * @param money 转账金额
     * @param fromUser 转账人信息
     * @param toUser 接收人信息
     */
    void transfer(int money, Account fromUser, Account toUser);
}

impl

@Service
@Slf4j
@RequiredArgsConstructor
/**
 * @Transactional事务注解,可以标注在类上或者方法上,如果同时存在,则优先方法的事务
 *
 * rollbackFor指定遇到什么类型的异常就回滚;
 * noRollbackFor指定遇到什么类型的异常不进行回滚;
 * readOnly指定是否是只读的事务
 * propagation属性用来指定事务传播级别,传播级别请参照文档说明
 * 如果不指定propagation,默认传播级别就是REQUIRED
 */
@Transactional(rollbackFor = RuntimeException.class)
public class TransferServiceImpl implements TransferService {

    private final AccountDao dao;

    @Override
    public void transfer(int money, Account fromUser, Account toUser) {
        //先查询转账人的余额
        fromUser = dao.getAccountById(fromUser.getAid());
        //检查余额是否充足
        if(fromUser.getMoney() < money) {
            throw new TransferException("对不起,余额不足");
        }
        //查询接收人的账号信息
        toUser = dao.getAccountById(toUser.getAid());
        //先扣除转账人的余额
        fromUser.setMoney(fromUser.getMoney() - money);
        //添加接受人的余额
        toUser.setMoney(toUser.getMoney() + money);
        //更新转账人和接收人的信息
        dao.updateAccount(fromUser);
        System.out.println(10/0);
        dao.updateAccount(toUser);
    }
}

exception自定义异常

public class TransferException extends RuntimeException{

    public TransferException(String message) {
        super(message);
    }

    public TransferException(String message, Throwable cause) {
        super(message, cause);
    }

    public TransferException(Throwable cause) {
        super(cause);
    }
}

测试层

public class TransferServiceTest {

    @Test
    public void testTransfer() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        TransferService service = context.getBean(TransferService.class);
        //创建转账人
        Account fromUser = new Account();
        fromUser.setAid(1);
        //创建接收人
        Account toUser = new Account();
        toUser.setAid(2);
        //转账
        service.transfer(500, fromUser, toUser);
    }


    @Test
    public void testPropagation() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        TestService service = (TestService) context.getBean("testService");
        service.methodA();
    }

    @Test
    public void testReadOnly() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Account account = new Account();
        account.setAid(1);
        account.setMoney(1500);
        TestService service = (TestService) context.getBean("testService");
        service.find(account);
    }
}

所要用到的依赖(所有spring中的版本号都要一致,要不会报错)

这里没有版本号是因为我们已经在父模块中已经进行按需依赖

<dependencies>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
    </dependency>
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值