目录

  • 接下来安排
  • 一、创建新工程
    • 1. pom.xml
    • 2.结构
    • 3.接口与实现类添加的操作
      • ⅠIAccountDao
      • Ⅱ AccountDaoImpl
      • Ⅲ IAccountService
      • 四 IAccountService
      • Ⅴ Test
      • Ⅵ 问题
  • 二、编写 ConnectionUtils
    • 1. ConnectionUtils 类
    • 2. TransactionManager类
  • 三、编写业务层和持久层事务控制代码并配置 spring 的 ioc
    • 1.AccountServiceImpl
    • 2.AccountDaoImpl
    • 3.Test
    • 4.结果

接下来安排

  1. 完善 account 案例
  2. 分析案例中问题
  3. 回顾之前讲过的一个技术:动态代理
  4. 动态代理另一种实现方式
  5. 解决案例中的问题
  6. AOP 的概念
  7. spring 中的 AOP 相关术语
  8. spring 中基于 XML 和注解的 AOP

一、创建新工程

创建新的 maven 工程,命名为 spring07

做一个转账的小操作

1. pom.xml

<?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"><modelVersion>4.0.0</modelVersion><groupId>spring07</groupId><artifactId>spring07</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging><dependencies><!-- https://mvnrepository.com/artifact/org.springframework/spring-context --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.9.RELEASE</version></dependency><!-- https://mvnrepository.com/artifact/commons-dbutils/commons-dbutils --><dependency><groupId>commons-dbutils</groupId><artifactId>commons-dbutils</artifactId><version>1.7</version></dependency><!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>6.0.6</version></dependency><!-- https://mvnrepository.com/artifact/com.mchange/c3p0 --><dependency><groupId>com.mchange</groupId><artifactId>c3p0</artifactId><version>0.9.5.4</version></dependency><!-- https://mvnrepository.com/artifact/junit/junit --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><!-- https://mvnrepository.com/artifact/org.springframework/spring-test --><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.1.9.RELEASE</version><scope>test</scope></dependency></dependencies>
</project>

2.结构

3.接口与实现类添加的操作

ⅠIAccountDao

    /*** 根据名称查询账户* @param accountName 账户名称* @return 如果有唯一结果就返回,没有就返回 Null, 如果结果有多个,这返回错误*/Account findAccountByName(String accountName);

Ⅱ AccountDaoImpl

    public void updateAccount(Account account) {try {runner.update("update account02 set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());}catch (SQLException e){e.printStackTrace();}}public Account findAccountByName(String accountName) {List<Account> accounts=null;try {accounts= runner.query("select *from account02 where name=?",new BeanListHandler<Account>(Account.class),accountName);if (accounts==null || accounts.size()==0){return null;}if(accounts.size()>1){throw new RuntimeException("结果集不唯一,数据有问题");}} catch (SQLException e) {e.printStackTrace();}return accounts.get(0);}

Ⅲ IAccountService

    /*** 转账* @param sourceName 转出账户名称* @param targetName 转入账户名称* @param money      转账金额*/void transfer(String sourceName,String targetName,Float money);/*** 更新* @param account*/void updateAccount(Account account);

四 IAccountService

    public void transfer(String sourceName, String targetName, Float money) {//1.根据名称查询转出账户Account source=accountDao.findAccountByName(sourceName);//2.根据名称查询转入账户Account target=accountDao.findAccountByName(targetName);//3.转出账户减钱source.setMoney(source.getMoney()-money);//4.转入账户加钱target.setMoney(target.getMoney()+money);//5.更新转出账户accountDao.updateAccount(source);//6.更新转入账户accountDao.updateAccount(target);}

Ⅴ Test

    @Testpublic void testTransfer(){as.transfer("aaa","bbb",10f);}

测试结果没有问题

Ⅵ 问题

假如 我们在AccountServiceImpl 这样操作一波,多加了个 int i=1/0;

    public void transfer(String sourceName, String targetName, Float money) {//1.根据名称查询转出账户Account source=accountDao.findAccountByName(sourceName);//2.根据名称查询转入账户Account target=accountDao.findAccountByName(targetName);//3.转出账户减钱source.setMoney(source.getMoney()-money);//4.转入账户加钱target.setMoney(target.getMoney()+money);//5.更新转出账户accountDao.updateAccount(source);int i=1/0;//6.更新转入账户accountDao.updateAccount(target);}

运行,你会看到 source 的账户减钱了,而 target 的账户却没有价钱,这怎么办?

原因:

这是一个多例对象

解决:

要让四个操作全为同一个 connection 来实现

需要使用 ThreadLocal 对象把 Connection 和当前线程绑定,从而使一个线程中只有一能控制事务的对象

二、编写 ConnectionUtils

创建 utiles 文件夹

创建 ConnectionUtils 类

创建 TransactionManager类

1. ConnectionUtils 类

package com.utils;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import javax.sql.DataSource;
import java.sql.Connection;/*** 描述:* 〈连接的工具类,它用于从数据源中获取一个连接,并且实现和线程的绑定〉** @author zuiren* @create 2019/8/30* @since 1.0.0*/
@Component("connectionUtils")
public class ConnectionUtils {private ThreadLocal<Connection> tl=new ThreadLocal<Connection>();@Autowiredprivate DataSource dataSource;public Connection getThreadConnection(){//1.先从 ThreadLocal 上获取Connection conn=tl.get();try {//2.判断当前线程是否有连接if (conn==null){//3.从数据源中获取一个链接,并且存入 ThreadLocal 中conn=dataSource.getConnection();tl.set(conn);}//4.返回当前线程上的连接return conn;}catch (Exception e){throw new RuntimeException(e);}}/*** 把连接和线程解绑*/public void removeConnection(){tl.remove();}
}

2. TransactionManager类

package com.utils;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.sql.SQLException;/*** 描述:* 〈和事务相关的工具类,它包含了,开启事务,提交事务,回滚事务和释放连接〉** @author zuiren* @create 2019/8/30* @since 1.0.0*/
@Component(value = "txManager")
public class TransactionManager {@Autowiredprivate ConnectionUtils connectionUtils;/*** 开启事务*/public void beginTransaction() {try {connectionUtils.getThreadConnection().setAutoCommit(false);} catch (SQLException e) {e.printStackTrace();}}/*** 开启事务*/public void commit(){try {connectionUtils.getThreadConnection().commit();} catch (SQLException e) {e.printStackTrace();}}/*** 回滚事务*/public void rollback(){try {connectionUtils.getThreadConnection().rollback();} catch (SQLException e) {e.printStackTrace();}}/*** 释放连接*/public void release(){try {//还回池中connectionUtils.getThreadConnection().close();connectionUtils.removeConnection();} catch (SQLException e) {e.printStackTrace();}}
}

细节

连接使用了连接池,

连接池的好处:把消耗时间连接获取连接的这部分放到应用加载一开始。

在 web 工程中,当我们启动tomcat加载应用时,我们创建一些连接,从而在后续项目运行阶段不在跟数据库获取链接保证了我们使用 connection 时的使用效率。

我们使用服务器,服务器也会有一个池的技术叫做线程池,它的特点是当 connection 启动时,会启动一大堆的线程放到一个容器中,接下来我们每次访问,它都是从线程池中拿出一个线程给我们使用。

这样的话线程池中的线程也跟我们连接池中的一样,所以我们最后调用

connectionUtils.getThreadConnection().close();

的方法并不是将其关闭,而是将他放回线程池中。

造理推断,线程用完了,也不是真正的关了,而是把线程还回线程池中。

所以这个线程中是绑着一个连接的,当我们把连接关闭,线程还回池中时,线程上是有连接的,只不过这个连接已经被关闭了,当我们下次再获取这个线程判断上面有没有连接时,你得到的结果一定是有,但是这个连接已经不能用了,因为它已经被 close 过了,被加载过池子里去了。

所以从这点上来说:我们应该在整个这个线程用完了之后,把这个线程和这个连接进行解绑。

在 ConnectionUtils 类中添加一方法

    /*** 把连接和线程解绑*/public void removeConnection(){tl.remove();}

在 TransactionManager 类

    /*** 释放连接*/public void release(){try {//还回池中connectionUtils.getThreadConnection().close();connectionUtils.removeConnection();} catch (SQLException e) {e.printStackTrace();}}

三、编写业务层和持久层事务控制代码并配置 spring 的 ioc

1.AccountServiceImpl

package com.service.Impl;import com.dao.IAccountDao;
import com.domain.Account;
import com.service.IAccountService;
import com.utils.TransactionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;
import java.util.TreeMap;/*** 描述:* 〈〉** @author zuiren* @create 2019/8/29* @since 1.0.0*/
@Service("accountService")
public class AccountServiceImpl implements IAccountService {@Autowiredprivate IAccountDao accountDao;@Autowiredprivate TransactionManager txManager;public List<Account> findAllAccount() {List<Account> accounts=null;try {//1.开启事务txManager.beginTransaction();//2.执行操作accounts=accountDao.findAllAccount();//3.提交事务txManager.commit();//4.返回结果return accounts;}catch (Exception e){//5.回滚操作txManager.rollback();}finally {//6.释放资源txManager.release();}return accounts;}public Account findAccountById(Integer accountId) {Account account=null;try {//1.开启事务txManager.beginTransaction();//2.执行操作account=accountDao.findAccountById(accountId);//3.提交事务txManager.commit();//4.返回结果return account;}catch (Exception e){//5.回滚操作txManager.rollback();}finally {//6.释放资源txManager.release();}return account;}public void saveAccount(Account account) {try {//1.开启事务txManager.beginTransaction();//2.执行操作accountDao.saveAccount(account);//3.提交事务txManager.commit();//4.返回结果}catch (Exception e){//5.回滚操作txManager.rollback();}finally {//6.释放资源txManager.release();}}public void updateAccount(Account account) {try {//1.开启事务txManager.beginTransaction();//2.执行操作accountDao.updateAccount(account);//3.提交事务txManager.commit();//4.返回结果}catch (Exception e){//5.回滚操作txManager.rollback();}finally {//6.释放资源txManager.release();}}public void deleteAccount(Integer accountId) {try {//1.开启事务txManager.beginTransaction();//2.执行操作accountDao.deleteAccount(accountId);//3.提交事务txManager.commit();//4.返回结果}catch (Exception e){//5.回滚操作txManager.rollback();}finally {//6.释放资源txManager.release();}}public void transfer(String sourceName, String targetName, Float money) {try {txManager.beginTransaction();//1.根据名称查询转出账户Account source=accountDao.findAccountByName(sourceName);//2.根据名称查询转入账户Account target=accountDao.findAccountByName(targetName);//3.转出账户减钱source.setMoney(source.getMoney()-money);//4.转入账户加钱target.setMoney(target.getMoney()+money);//5.更新转出账户accountDao.updateAccount(source);int i=1/0;//6.更新转入账户accountDao.updateAccount(target);txManager.commit();}catch (Exception e){txManager.rollback();}finally {txManager.release();}}
}

2.AccountDaoImpl

这里只是在 runner 执行操作时,给它配置连接

package com.dao.Impl;import com.dao.IAccountDao;
import com.domain.Account;
import com.utils.ConnectionUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;import java.sql.SQLException;
import java.util.List;/*** 描述:* 〈〉** @author zuiren* @create 2019/8/29* @since 1.0.0*/
@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao {@Autowiredprivate QueryRunner runner;@Autowiredprivate ConnectionUtils connectionUtils;public List<Account> findAllAccount() {try {return runner.query(connectionUtils.getThreadConnection(),"select *from account02",new BeanListHandler<Account>(Account.class));}catch (Exception e){throw new RuntimeException(e);}}public Account findAccountById(Integer accountId) {try {return runner.query(connectionUtils.getThreadConnection(),"select *from account02 where id = ?",new BeanHandler<Account>(Account.class),accountId);}catch (Exception e){throw new RuntimeException(e);}}public void saveAccount(Account account) {try {runner.update(connectionUtils.getThreadConnection(),"insert into account02(name,money) values(?,?)",account.getName(),account.getMoney());}catch (SQLException e){e.printStackTrace();}}public void updateAccount(Account account) {try {runner.update(connectionUtils.getThreadConnection(),"update account02 set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());}catch (SQLException e){e.printStackTrace();}}public void deleteAccount(Integer accountId) {try {runner.update(connectionUtils.getThreadConnection(),"delete from account02 where id=?",accountId);}catch (SQLException e){e.printStackTrace();}}public Account findAccountByName(String accountName) {List<Account> accounts=null;try {accounts= runner.query(connectionUtils.getThreadConnection(),"select *from account02 where name=?",new BeanListHandler<Account>(Account.class),accountName);if (accounts==null || accounts.size()==0){return null;}if(accounts.size()>1){throw new RuntimeException("结果集不唯一,数据有问题");}} catch (SQLException e) {e.printStackTrace();}return accounts.get(0);}
}

3.Test

    @Testpublic void testTransfer(){as.transfer("aaa","bbb",10f);}

4.结果

你会发现报错,事务回滚,aaa 的 money 没有变化,转账正常执行。

问题是现在依赖特别混乱,以后会解决

转载于:https://www.cnblogs.com/zuiren/p/11437528.html

9-分析事物问题并编写 Utils 文件相关推荐

  1. Antlr4入门(三)如何编写语法文件

    本章我们将会学习词法及语法规则,以及四种抽象的计算机语言模式.因为ANTLR的语法规则跟正则表达式是很类似的,所以还是推荐先阅读下正则表达式的相关内容,这样在编写语法文件时可以事半功倍. 一.四种语言 ...

  2. 实验五——手工编写PE文件

    [实验名称] 手工编写PE文件 [实验目的] 1.了解PE文件的概念.结构 2.熟悉PE编辑查看工具,详细了解PE文件格式 3.重点分析PE文件文件头.引入表.引出表,以及资源表 [实验原理] 1.P ...

  3. MATLAB编写ode文件,MATLABODE45问题M文件为br/functiondq 爱问知识人

    编写M文件cdq.m function dy=cdq(x,y) dy=zeros(2,1); dy(1)=y(2); dy(2)=-2*y(2)-y(1) cos(x); 编写M文件cdq1. m f ...

  4. aidl生成java文件_Android Studio编写AIDL文件后如何实现自动编译生成

    Android Studio编写AIDL文件后如何实现自动编译生成 发布时间:2020-09-17 23:34:54 来源:脚本之家 阅读:111 作者:EdwardChu123 在目录src/mai ...

  5. 设计所需的各种输出格式(包括整数、实数、字符串等),用一个文件名format.h把这些信息都包括到此文件内,另编写一个文件,用文件包含命令验证可以使用这些格式

    <程序设计基础-c语言>杨莉 刘鸿翔 ISBN-978-7-03-032903-5 p241 习题7 16.设计所需的各种输出格式(包括整数.实数.字符串等),用一个文件名"fo ...

  6. [转]如何编写 INF 文件

    INF文件全称Information File文件,是Winodws操作系统下用来描述设备或文件等数据信息的文件.INF文件是由标准的ASCII码组成,您可以用任何一款文字编辑器查看修改其中的内容.一 ...

  7. 制造内核崩溃并使用crash分析内核崩溃产生的vmcore文件

    制造内核崩溃并使用crash分析内核崩溃产生的vmcore文件 1,安装kernel-debuginfo$(uname -r).rpm和kernel-debuginfo-common-$(uname ...

  8. VS.net下编写makefile文件--NMAKE用法

    1.程序源文件: 如有三个文件:主文件:hello.cpp,类NUM的说明和实现文件:Num.h和Num.cpp,内容如下: main.cpp: #include "iostream&quo ...

  9. c语言是以文件为单位编译,c语言从头开始(三:编译器工作原理) (我们还可以自己编写头文件后缀是xx.h并把它当前代码文件所在目录我们要用就可以直接通过下面代码使用)...

    c语言从头开始(三:编译器工作原理) [[枫歌枫歌]c语言从头开始(三:编译器工作原理)]https://toutiao.com/group/6568056688174170628/?iid=1590 ...

最新文章

  1. pandas为dataframe所有的列名称名添加前缀(add_prefix)
  2. bootstrap学习笔记五(表单一)
  3. 【ABAP】报表进度提示
  4. 在大规模系统中使用Scala
  5. 在Activity的Title中加入进度条
  6. 使用 kind 快速搭建一个 Kubernetes 测试环境
  7. (37)FPGA花样流水灯设计(第8天)
  8. 计蒜课挑战难题:罗马数字转换成整数
  9. 【转】 sqlserver 异地备份
  10. 2022年电工杯A题高比例风电电力系统储能运行及配置分析参考代码
  11. 持续交付和DevOps是一对好基友
  12. 线性代数术语中英文对照
  13. windows vista本该有却没有的功能
  14. PyTorch基础:Tensor的组合与分块
  15. 追根溯源之最好的Spring AOP解析
  16. 操作系统(Linux)
  17. 快速寻找勾股数算法的实现和优化
  18. 网站服务器发生故障,全国DNS服务器发生故障
  19. 用友企业空间 - http://upesn.com
  20. windows 复制文件夹命令 xcopy .

热门文章

  1. 第三篇——第二部分——第三文 配置SQL Server镜像——域环境
  2. 一个c++程序员的学习历程自述
  3. scapy-yield的含义和使用
  4. HTML DOCTYPE 的重要性
  5. mysql带有OR关键字的多条件查询
  6. C++深复制(深拷贝)、浅复制(浅拷贝)和复制构造函数(拷贝构造函数)详解+实例
  7. CPU 以字节为单位编址,而 C 语言指针以指向的数据类型长度作自增和自减。
  8. python二十三:装饰器 ?
  9. mariadb(mysql)的安装
  10. C-指针,二级指针,二维数组作为函数参数使用,C语言链表(详解)