9-分析事物问题并编写 Utils 文件
Posted zuiren
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了9-分析事物问题并编写 Utils 文件相关的知识,希望对你有一定的参考价值。
目录
接下来安排
- 完善 account 案例
- 分析案例中问题
- 回顾之前讲过的一个技术:动态代理
- 动态代理另一种实现方式
- 解决案例中的问题
- AOP 的概念
- spring 中的 AOP 相关术语
- 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
@Test
public 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>();
@Autowired
private 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
@Autowired
private 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
@Autowired
private IAccountDao accountDao;
@Autowired
private 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
@Autowired
private QueryRunner runner;
@Autowired
private 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
@Test
public void testTransfer()
as.transfer("aaa","bbb",10f);
4.结果
你会发现报错,事务回滚,aaa 的 money 没有变化,转账正常执行。
问题是现在依赖特别混乱,以后会解决
以上是关于9-分析事物问题并编写 Utils 文件的主要内容,如果未能解决你的问题,请参考以下文章
“Android Utils“ 实现TextView 区域自定义点击