原生JDBC事务:

package dao;

import cn.dzz.util.DruidUtil;
import org.apache.commons.dbutils.QueryRunner;
import org.junit.Test; import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException; /**
* @author DaiZhiZhou
* @file OA-Project
* @create 2020-08-03 15:13
*/
public class TransactionTest { QueryRunner queryRunner = new QueryRunner();
DataSource dataSource = DruidUtil.getDataSource(); @Test
public void tx01() throws SQLException { Connection connection = null;
try {
// 事务的前提是我们使用的连接需要保持一致
connection = dataSource.getConnection(); // 开启事务,关闭MYSQL的自动提交
connection.setAutoCommit(false); int update01 = queryRunner.update(connection, "这是执行影响数据库的SQL1"); // 中间的执行过程将会产生异常 int update02 = queryRunner.update(connection, "这是执行影响数据库的SQL2"); // 如果一切正常,则进行提交
connection.commit(); } catch (Exception exception) {
exception.printStackTrace();
// 发生了异常,触发Catch,执行回滚
connection.rollback();
} finally {
// 无论回滚还是提交,最终释放资源
connection.close();
}
} }

事务封装类:

即数据源提供给这个封装类,当我们Dao层执行SQL的连接对象就要从这里获取,

不能独立获取,否则事务将无法作用了,因为获取的不是同一个连接对象就没有办法处理了

package cn.dzz.util;

/**
* @author DaiZhiZhou
* @file OA-Project
* @create 2020-08-03 15:27
*/ import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException; public class TransactionManager { // 私有化
private TransactionManager(){} // 当前线程对象,用以存放和绑定连接对象
private static final ThreadLocal<Connection> threadLocal = new ThreadLocal<>(); // 我们需要一个数据源来获取连接,且事务正常执行的条件是来自于同一个连接对象才会有效
private static DataSource dataSource ; /**
* 我不希望只能从一个数据源获取,这个对象可以是任意类型的
* @param dataSource
*/
public static void setDataSource(DataSource dataSource) {
TransactionManager.dataSource = dataSource;
} /**
* 首先是获取连接对象获取,如果当前线程实例中没有连接对象,则从这个数据源中获取
* @return
*/
public static Connection getCurrentThreadConnection(){
Connection connection = null;
try {
connection = threadLocal.get();
if (connection == null) {
connection = dataSource.getConnection();
threadLocal.set(connection);
}
} catch (SQLException sqlException) {
sqlException.printStackTrace();
}
return connection;
} /**
* 开启事务
*/
public static void begin(){
Connection connection = getCurrentThreadConnection();
try {
connection.setAutoCommit(false);
} catch (SQLException sqlException) {
sqlException.printStackTrace();
}
} /**
* 如果业务逻辑的每一步代码都正常执行,无任何异常,则开始提交
*/
public static void commit(){
Connection connection = getCurrentThreadConnection();
try {
connection.commit();
} catch (SQLException sqlException) {
sqlException.printStackTrace();
}
} /**
* 如果业务逻辑发生了错误,立刻捕获,进入catch,执行回滚
*/
public static void rollback(){
Connection connection = getCurrentThreadConnection();
try {
connection.rollback();
} catch (SQLException sqlException) {
sqlException.printStackTrace();
}
} /**
* 最后,不管是失败还是成功,都需要释放资源
*/
public static void close(){
Connection connection = getCurrentThreadConnection();
try {
connection.close();
threadLocal.remove();
} catch (SQLException sqlException) {
sqlException.printStackTrace();
}
} }

业务层的应用:

如果要在业务添加就必须要为每一个业务的方法这样添加

    public JsonResult altUserById2(HttpServletRequest request) {

        try {
// 开启事务
TransactionManager.begin(); int i = userDao.updateUserById(new User(
Integer.valueOf(request.getParameter("user_id")),
request.getParameter("user_name"),
request.getParameter("user_password"),
Integer.valueOf(request.getParameter("user_status")),
Integer.valueOf(request.getParameter("user_is_del"))
)); // 提交
TransactionManager.commit();
return new JsonResult(100, "修改成功");
} catch (Exception exception) {
// 回滚
TransactionManager.rollback();
exception.printStackTrace();
} finally {
// 释放
TransactionManager.close();
}
return new JsonResult(200, "修改失败");
}

但是业务方法那么多,怎么可能一个个添加,

何况事务并不是业务的范畴之内,但是这出现了异常就得撤销SQL执行恢复原状啊

演化到动态代理完成:

package cn.dzz.util;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; /**
* @author DaiZhiZhou
* @file OA-Project
* @create 2020-08-03 15:49
*/
public class ServiceTxProxy <GenericService>{ /**
* 目标对象,也就是业务对象
* 我们希望不仅仅是一个业务逻辑接口,应该整个业务逻辑层的接口都可以被代理
*/
private GenericService genericService; /**
* 我创建这个代理对象可以不先设置目标对象
* 对实例重载
*/
public ServiceTxProxy() {} /**
* 创建实例时就注入目标对象
* @param genericService
*/
public ServiceTxProxy(GenericService genericService) {
this.genericService = genericService;
} /**
* 调用注入方法来注入目标对象
* @param genericService
*/
public void setGenericService(GenericService genericService) {
this.genericService = genericService;
} /**
* 得到代理对象,我们将由代理对象来操作业务
* 在得到代理对象之前注入数据源,我们不希望只能使用单一的数据源
* 业务层的调用,一定是在代理之后执行,所以在这里注入数据源不会造成空指针的问题
* @param
* @return
*/
public GenericService getGenericService() {
// 调用代理类的创建代理实例方法
return (GenericService) Proxy.newProxyInstance(
// 第一参数:类加载器实例,可以反射任意实例获取
genericService.getClass().getClassLoader(),
// 第二参数:目标对象的接口数组,因为接口是允许多实现的
genericService.getClass().getInterfaces(),
// 第三参数: 调用处理器的实现实例,需要重新调用方法,对目标对象增强
new InvocationHandler() { /**
*
* @param proxy ? 代理对象?
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 目标对象就是我们的Service对象,在对象调用之前,开启事务
TransactionManager.begin(); Object theGenericServiceData = null; try {
// 目标对象的方法执行!!!
theGenericServiceData = method.invoke(genericService, args); // 对执行完毕的Service对象 进行提交或者回滚
TransactionManager.commit();
} catch (Exception exception) {
exception.printStackTrace();
TransactionManager.rollback();
}
return theGenericServiceData;
}
}
);
}
}

注意事务管理器的数据源设置问题。。。

Spring事务管理:

依赖的组件坐标:

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>

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: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/tx http://www.springframework.org/schema/tx/spring-tx.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"> </beans>

配置Spring提供的事务管理器:

需要数据源注入

<!--事务-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>

配置事务的通知,引用事务管理器:

<tx:advice id="txAdvice" transaction-manager="txManager">
<!--在 tx:advice 标签内部 配置事务的属性 -->
<tx:attributes>
  <!-- 指定方法名称:是业务核心方法
read-only:是否是只读事务。默认 false,不只读。
isolation:指定事务的隔离级别。默认值是使用数据库的默认隔离级别。
propagation:指定事务的传播行为。
timeout:指定超时时间。默认值为:-1。永不超时。
rollback-for:用于指定一个异常,当执行产生该异常时,事务回滚。产生其他异常,事务不回滚。
没有默认值,任何异常都回滚。
no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时,事务回
滚。没有默认值,任何异常都回滚。
  -->
  <tx:method name="*" read-only="false" propagation="REQUIRED"/>
  <tx:method name="find*" read-only="true" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>

结合动态代理:

<!-- 配置 aop -->
<aop:config>
  <!-- 配置切入点表达式 -->
  <aop:pointcut expression="execution(* com.tc51.service.impl.*.*(..))" id="pt1"/>
</aop:config>

配置切入点表达式和事务通知的对应关系:

<!-- 配置 aop -->
<aop:config>
<!-- 配置切入点表达式 -->
<aop:pointcut expression="execution(* com.tc51.service.impl.*.*(..))" id="pt1"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1" />
</aop:config>

Dao层的设计:

查询功能不需要这个事务,注入好的数据源给的连接对象是独立的

但是如果是更新操作,就需要从事务管理器中获取连接保证连接一致

@Component
public class AccountDao implements IAccountDao { @Autowired
private QueryRunner queryRunner; public Account getAccount(String name) throws SQLException {
String sql = "SELECT * FROM `account` WHERE `name`=?"; return queryRunner.query(sql, new BeanHandler<Account>(Account.class),name);
} public int updateAccount(Account account) throws SQLException {
String sql = "update account set name=?,money=? where id=?";
Connection conn= DataSourceUtils.getConnection(queryRunner.getDataSource());
System.out.println(conn);
Object[] object = {account.getName(), account.getMoney(), account.getId()};
return queryRunner.update(conn,sql, object);
}
}

【Spring】09 后续的学习补充 vol3的更多相关文章

  1. 写给大忙人的spring cloud 1.x学习指南

    这几天抽空搞了下spring cloud 1.x(2.0目前应该来说还不成熟),因为之前项目中使用dubbo以及自研的rpc框架,所以总体下来还是比较顺利,加上spring boot,不算笔记整理,三 ...

  2. Spring Cloud微服务学习笔记

    Spring Cloud微服务学习笔记 SOA->Dubbo 微服务架构->Spring Cloud提供了一个一站式的微服务解决方案 第一部分 微服务架构 1 互联网应用架构发展 那些迫使 ...

  3. Spring+SpringMVC+MyBatis深入学习及搭建(二)——MyBatis原始Dao开发和mapper代理开发

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6869133.html 前面有写到Spring+SpringMVC+MyBatis深入学习及搭建(一)——My ...

  4. Spring+SpringMVC+MyBatis深入学习及搭建(三)——MyBatis全局配置文件解析

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6874672.html 前面有写到Spring+SpringMVC+MyBatis深入学习及搭建(二)——My ...

  5. Spring+SpringMVC+MyBatis深入学习及搭建(四)——MyBatis输入映射与输出映射

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6878529.html 前面有讲到Spring+SpringMVC+MyBatis深入学习及搭建(三)——My ...

  6. Spring+SpringMVC+MyBatis深入学习及搭建(五)——动态sql

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6908763.html 前面有讲到Spring+SpringMVC+MyBatis深入学习及搭建(四)——My ...

  7. Spring+SpringMVC+MyBatis深入学习及搭建(六)——MyBatis关联查询

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6923464.html 前面有将到:Spring+SpringMVC+MyBatis深入学习及搭建(五)--动 ...

  8. Spring+SpringMVC+MyBatis深入学习及搭建(七)——MyBatis延迟加载

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6953005.html 前面讲到:Spring+SpringMVC+MyBatis深入学习及搭建(六)——My ...

  9. Spring+SpringMVC+MyBatis深入学习及搭建(八)——MyBatis查询缓存

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6956206.html 前面讲到:Spring+SpringMVC+MyBatis深入学习及搭建(七)——My ...

  10. Spring+SpringMVC+MyBatis深入学习及搭建(九)——MyBatis和Spring整合

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6964162.html 前面讲到:Spring+SpringMVC+MyBatis深入学习及搭建(八)--My ...

随机推荐

  1. 面试必会 --> MyBatis篇

    什么是MyBatis Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动.创建连接.创建statement等繁杂的过程 ...

  2. macbookrpro使用体验

    前言 之前用的电脑是拯救者y7000 2020,用了四五年,年前就有换电脑的打算.计划就是买一个苹果电脑,在查看了挺多电脑,多方面对比后,最终还是买了Macbook pro. 我买的笔记本的配置如下: ...

  3. collections.sort()使用时注意的问题

    问题描述: 自定义排序一个List<Pair<Integer, Integer>>,根据pair的key由小到大排序,如果相同,则根据pair的value由大到小排序. 最开始 ...

  4. C#.NET 简单使用log4net

    1.nuget 中安装log4net 2.添加一个配置文件"log4net2.config",内容如下: <?xml version="1.0" enco ...

  5. python-API开发zk客户端

    前面于超老师讲完了,zk运维的基本命令行玩法,更多的还是开发需要通过代码和zk结合处理. 大多数场景是java后端去操作. 这里我们以运维更友好的python来学习. 1.kazoo模块 zookee ...

  6. python 日志写入文件,参数说明及动态判断文件是创建还是追加

    import logging import os ''' 格式符 含义 %(levername)s 日志级别名称 %(pathname)s 当前执行程序的路径(即脚本所在的位置) %(filename ...

  7. kylin-3.1.1-bin-hadoop3搭建,构建cube报的错误,Cannot modify dfs.replication at runtime. It is not in list of params that are allowed to be modified at runtime

    主要是每次构建cube时会去读取kylin安装目录下的conf/kylin_hive_conf.xml文件, 副本是无法在hive查询时修改的,注释掉这两项 这个其实还有一些参数的控制: 添加这俩个参 ...

  8. QT6设置应用程序图标

    准备好一个ico格式的图标, 放到源码文件中, 比如放在 resources/logo.ico 在源码目录中新建一个icon.rc的文件, 内容如下: IDI_ICON1 ICON DISCARDAB ...

  9. STM32的内存管理(转)

    背景 这里针对STM32F407芯片+1M外部内存的内存管理!(全篇是个人愚见,如果错误,请不吝指出!) 定义 首先,定义3个内存池,分别是内部SRAM,外表SRAM和CCM:通过指定内存中的绝对地址 ...

  10. STM32 CubeMX 学习:05-串口

    --- title: mcu-stm32-cube-05-using-serial.md date: 2020-03-09 10:37:34 categories: tags: - stm32 - c ...