【Spring】Spring中事务管理

生活中,最使人疲惫的往往不是道路的遥远,而是心中的郁闷;最使人痛苦的往往不是生活的不幸,而是希望的破灭;最使人颓废的往往不是前途的坎坷,而是自信的丧失;最使人绝望的往往不是挫折的打击,而是心灵的死亡。所以我们要有自己的梦想,让梦想的星光指引着我们走出落漠,走出惆怅,带着我们走进自己的理想。

导读:本篇文章讲解 【Spring】Spring中事务管理,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

一、闲话

教练,我想打游戏啊!!!!

二、基本要点

1、事务相关回顾

事务的四大特性(ACID)

  • 原子性(atomicity):一个事务是一个不可分割的单位,事务中包括的操作要么都做,要么都不做。
  • 一致性(consistency):事务必须是使数据库从一个一致性状态变到另一个一致性状态,与原子性是密切相关。
  • 隔离性(isolation):一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
  • 持久性(durability):持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响

2、spring中事务管理

主要分为声明式事务:AOP(代码是横切进去的,不影响原有代码逻辑)、编程式事务

1)声明式事务

将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。
将事务管理作为横切关注点,通过aop方法模块化。通过Spring AOP框架支持声明式事务管理

假设我们原来的表中有3个人,现在我们要删除掉标号为3的那个,然后假如编号为4的
在这里插入图片描述
我们新建一个人员类

import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 人员信息
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserInfo {
    private String userId;
    private String name;
    private int age;
    private String sex;
}

编写接口及实现类,新增、删除和查询接口

package com.decade.mapper;

import com.decade.entity.UserInfo;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface UserInfoDao {

    List<UserInfo> queryUserInfo();

    void addUserInfo(UserInfo userInfo);

    void deleteUserInfo(@Param("id") String id);
}


package com.decade.mapper;

import com.decade.entity.UserInfo;
import org.mybatis.spring.support.SqlSessionDaoSupport;

import java.util.List;

public class UserInfoDaoImpl extends SqlSessionDaoSupport implements UserInfoDao{
    @Override
    public List<UserInfo> queryUserInfo() {
        UserInfo userInfo = new UserInfo("004", "刘亦菲", 18, "女");
        addUserInfo(userInfo);
        deleteUserInfo("003");
        return getSqlSession().getMapper(UserInfoDao.class).queryUserInfo();
    }

    @Override
    public void addUserInfo(UserInfo userInfo) {
        getSqlSession().getMapper(UserInfoDao.class).addUserInfo(userInfo);
    }

    @Override
    public void deleteUserInfo(String id) {
        getSqlSession().getMapper(UserInfoDao.class).deleteUserInfo(id);
    }
}

编写接口的xml映射文件,这里把删除的sql写错,测试能否成功加入4号删除3号

<?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="com.decade.mapper.UserInfoDao">

    <select id="queryUserInfo" resultType="com.decade.entity.UserInfo">
        select * from t_decade_user;
    </select>

    <insert id="addUserInfo" parameterType="com.decade.entity.UserInfo">
        insert into t_decade_user(id,name,age,sex) values (#{userId},#{name},#{age},#{sex});
    </insert>

    <delete id="deleteUserInfo" parameterType="java.lang.String">
    	<!-- 这里把删除的sql写错,测试能否成功加入4号删除3号 -->
        deletes from t_decade_user where id = #{id}
    </delete>
</mapper>

我们需要导入tx约束,管理数据库连接的配置文件spring-dao2.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:aop="http://www.springframework.org/schema/aop"
       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/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx
       https://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 配置数据源,使用spring的数据源替换mybatis, c3p0 druid dbcp -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <!--驱动配置,com.mysql.jdbc.driver -->
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/decade_test?useUnicode=true&amp;characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>

    <!-- 创建sqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!-- 绑定mybatis的配置文件 -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/decade/mapper/*.xml"/>
    </bean>

    <!--创建sqlSessionTemplate,代替原来的sqlSession -->
    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
        <!--因为sqlSessionTemplate没有set方法,所以我们使用构造器注入 -->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>

    <!-- 配置声明式事务 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 结合AOP实现事务的织入 -->
    <!-- 配置事务通知 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!-- 需要给哪些字段开头的方法需要配置事务,事务的传播特性,默认为propagation="REQUIRED" -->
            <tx:method name="add*" propagation="REQUIRED" read-only="false"/>
            <tx:method name="delete*" propagation="REQUIRED" read-only="false"/>
            <!-- read-only属性为true时,意思是只有只读权限,无法修改数据库
             特殊情况,add方法调用update方法,数据是可以被更新的,因为add的read-only是false
             -->
            <tx:method name="update*" propagation="REQUIRED" read-only="true"/>
            <tx:method name="query*" read-only="false"/>
            <!-- 这里的*代表所有方法 -->
            <!-- <tx:method name="*"/>-->
        </tx:attributes>
    </tx:advice>

    <!-- 配置事务的织入 -->
    <aop:config>
        <aop:pointcut id="txPointCut" expression="execution(* com.decade.mapper.UserInfoDaoImpl.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
    </aop:config>
</beans>

下面是mybatis配置文件mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    
    <typeAliases>
        <package name="com.decade.entity"/>
    </typeAliases>
</configuration>

和总的spring配置文件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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <import resource="spring-dao2.xml"/>

    <bean id="userDao" class="com.decade.mapper.UserInfoDaoImpl">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
</beans>

最后编写一个测试类

import com.decade.mapper.UserInfoDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    @org.junit.Test
    public void test() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserInfoDao userDao = context.getBean("userDao", UserInfoDao.class);
        userDao.queryUserInfo().forEach(System.out::println);
    }
}

运行后,我们发现该方法报错,4号未加入,3号未删除,符合预期
在这里插入图片描述

2)编程式事务

将事务管理代码嵌到业务方法中来控制事务的提交和回滚
缺点是必须在每个事务操作业务逻辑中包含额外的事务管理代码

在方法出现异常时,调用回滚,我们简单通过一个例子了解一下

public class UserService {
  private final PlatformTransactionManager transactionManager;
  
  public UserService(PlatformTransactionManager transactionManager) {
    this.transactionManager = transactionManager;
  }
  
  public void createUser() {
    TransactionStatus txStatus =
        transactionManager.getTransaction(new DefaultTransactionDefinition());
    try {
      userMapper.insertUser(user);
    } catch (Exception e) {
      transactionManager.rollback(txStatus);
      throw e;
    }
    transactionManager.commit(txStatus);
  }
}

// 在使用 TransactionTemplate 的时候,可以省略对 commit 和 rollback 方法的调用。
public class UserService {
  private final PlatformTransactionManager transactionManager;
  
  public UserService(PlatformTransactionManager transactionManager) {
    this.transactionManager = transactionManager;
  }
  
  public void createUser() {
    TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
    transactionTemplate.execute(txStatus -> {
      userMapper.insertUser(user);
      return null;
    });
  }
}

如有错误,欢迎指正

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/136781.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
半码博客——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!