(因为存在了转账操作,我们需要在一个线程上运行,并且将数据库连接绑定到线程上)
其中包括了:
1.创建一个ThreadLocal获取当前线程
ThreadLocal作用:
1.1、在进行对象跨层传递的时候,使用ThreadLocal可以避免多次传递,打破层次间的约束。
1.2、线程间数据隔离
1.3、进行事务操作,用于存储线程事务信息。
1.4、数据库连接,Session会话管理。
2. 获取数据连接源
使用dataSource
3.创建一个获取连接的方法,并且将当前的连接绑定当前线程
4.创建一个与线程解绑的方法
package cn.kitey.utils;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* 连接的工具类,用于获取数据源中的一个连接,并且实现线程的绑定
*/
public class ConnectionUtils {
private ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
DataSource dataSource;
/**
* 用于从IOC中注入
* @param dataSource
*/
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public Connection getThreadConnection(){
try {
//1.先从ThreadLocal上获取
Connection conn = threadLocal.get();
//2.判断当前线程上是否存在连接
if(conn == null){
conn = dataSource.getConnection();
threadLocal.set(conn);
}
//返回当前线程的连接
return conn;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
/**
* 把连接与线程解绑了
*/
public void removeConnection(){
//移除连接
threadLocal.remove();
}
}
package cn.kitey.utils;
import java.sql.SQLException;
/**
* 和事务管理相关的工具类,包含了,开启事务,提交事务,回滚事务,释放连接
*/
public class TransactionManger {
private ConnectionUtils connectionUtils;
/**
* 定义set方法,可以通过IOC容器注入
* @param connectionUtils
*/
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
/**
* 开启事务
*/
public void beginTransaction(){
try {
connectionUtils.getThreadConnection().setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 提交事务
*/
public void commitTransaction(){
try {
connectionUtils.getThreadConnection().commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 回滚事务
*/
public void rollbackTransaction(){
try {
connectionUtils.getThreadConnection().rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 释放事务
*/
public void relaseTransaction(){
try {
//将连接归还到数据连接池
connectionUtils.getThreadConnection().close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
`其中可以不用获取数据连接
<!--配置QueryRunner-->
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
</bean>
首相需要创建一个 private ConnectionUtils connectionUtils;
并设置set方法用于数据的注入
例如:
在query()方法中加上了connectionUtils.getThreadConnection()
public List<Account> findAll() {
try {
return queryRunner.query(connectionUtils.getThreadConnection(),"select * from account",
new BeanListHandler<Account>(Account.class));
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
全部代码
package cn.kitey.dao.impl;
import cn.kitey.dao.AccountDao;
import cn.kitey.domain.Account;
import cn.kitey.utils.ConnectionUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import java.sql.SQLException;
import java.util.List;
/**
* 持久层实现类
*/
public class AccountDaoImpl implements AccountDao {
private QueryRunner queryRunner;
private ConnectionUtils connectionUtils;
/**
* 从spring中注入相关数据
* @param connectionUtils
*/
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
/**
* 从spring中配置
* @param queryRunner
*/
public void setQueryRunner(QueryRunner queryRunner) {
this.queryRunner = queryRunner;
}
public List<Account> findAll() {
try {
return queryRunner.query(connectionUtils.getThreadConnection(),"select * from account",
new BeanListHandler<Account>(Account.class));
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public Account findById(Integer accountId) {
try {
return queryRunner.query(connectionUtils.getThreadConnection(),"select * from account where id = ?", new BeanHandler<Account>(Account.class),accountId);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void saveAccount(Account account) {
try {
queryRunner.update(connectionUtils.getThreadConnection(),"insert into account(id,uid, money) values(?,?,?)", account.getId(),account.getUid(),account.getMoney());
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void updateAccount(Account account) {
try {
queryRunner.update(connectionUtils.getThreadConnection(),"update account set uid = ?, money = ? where id = ?", account.getUid(),account.getMoney(),account.getId());
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void deleteAccount(Integer accountId) {
try {
queryRunner.update(connectionUtils.getThreadConnection(),"delete from account where id = ?",accountId);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public Account findAccountByName(String accountName) {
try {
List<Account> accounts = queryRunner.query(connectionUtils.getThreadConnection(),"select * from account where name = ?",
new BeanListHandler<Account>(Account.class),accountName);
if(accounts == null|| accounts.size() == 0){
return null;
}else if(accounts.size() > 1){
throw new RuntimeException("结果不唯一,数据错误");
}else{
return accounts.get(0);
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
还需现在接口中先添加对应的方法
/**
* 这里需要在同一个线程中只有一个能控制事务对象
* @param sourceId
* @param targetId
* @param money
*/
public void transfer(Integer sourceId, Integer targetId, double money) {
try {
//1.开启事务
tManger.beginTransaction();
//2.执行操作
//2.1.根据名称查询传出账户
Account source = accountDao.findById(sourceId);
//2.2.根据id
Account target = accountDao.findById(targetId);
//2.3.转出账户减钱
source.setMoney(source.getMoney() - money);
int i = 10/0;
//2.4.转入账户加钱
target.setMoney(target.getMoney() + money);
//2.5.更新转出账户
accountDao.updateAccount(source);
//2.6.更新转入账户
accountDao.updateAccount(target);
//3.提交事务
tManger.commitTransaction();
} catch (Exception e) {
//5.回滚操作
tManger.rollbackTransaction();
//产生错误了,程序就不在继续运行
throw new RuntimeException(e);
} finally {
//6.释放连接
tManger.relaseTransaction();
}
}
全部代码(添加了事务的CRUD操作)
首相要获取事务方法,并且提供set方法
package cn.kitey.service.impl;
import cn.kitey.dao.AccountDao;
import cn.kitey.dao.impl.AccountDaoImpl;
import cn.kitey.domain.Account;
import cn.kitey.service.AccountService;
import cn.kitey.utils.TransactionManger;
import java.util.List;
/**
* 账户的业务实现层
*/
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
private TransactionManger tManger;
public void settManger(TransactionManger tManger) {
this.tManger = tManger;
}
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
/**
* 通过IOC容器进行配置
* @param accountDao
*/
public void setAccountDao(AccountDaoImpl accountDao) {
this.accountDao=accountDao;
}
public List<Account> findAll() {
try {
//1.开启事务
tManger.beginTransaction();
//2.执行操作
List<Account> list = accountDao.findAll();
//3.提交事务
tManger.commitTransaction();
//4.返回结果
return list;
} catch (Exception e) {
//5.回滚操作
tManger.rollbackTransaction();
//产生错误了,程序就不在继续运行
throw new RuntimeException(e);
} finally {
//6.释放连接
tManger.relaseTransaction();
}
}
public Account findById(Integer accountId) {
try {
//1.开启事务
tManger.beginTransaction();
//2.执行操作
Account byId = accountDao.findById(accountId);
//3.提交事务
tManger.commitTransaction();
//4.返回结果
return byId;
} catch (Exception e) {
//5.回滚操作
tManger.rollbackTransaction();
//产生错误了,程序就不在继续运行
throw new RuntimeException(e);
} finally {
//6.释放连接
tManger.relaseTransaction();
}
}
public void saveAccount(Account account) {
try {
//1.开启事务
tManger.beginTransaction();
//2.执行操作
accountDao.saveAccount(account );
//3.提交事务
tManger.commitTransaction();
} catch (Exception e) {
//5.回滚操作
tManger.rollbackTransaction();
//产生错误了,程序就不在继续运行
throw new RuntimeException(e);
} finally {
//6.释放连接
tManger.relaseTransaction();
}
}
public void updateAccount(Account account) {
try {
//1.开启事务
tManger.beginTransaction();
//2.执行操作
accountDao.updateAccount(account);
//3.提交事务
tManger.commitTransaction();
} catch (Exception e) {
//5.回滚操作
tManger.rollbackTransaction();
//产生错误了,程序就不在继续运行
throw new RuntimeException(e);
} finally {
//6.释放连接
tManger.relaseTransaction();
}
}
public void deleteAccount(Integer accountId) {
try {
//1.开启事务
tManger.beginTransaction();
//2.执行操作
accountDao.deleteAccount(accountId);
//3.提交事务
tManger.commitTransaction();
} catch (Exception e) {
//5.回滚操作
tManger.rollbackTransaction();
//产生错误了,程序就不在继续运行
throw new RuntimeException(e);
} finally {
//6.释放连接
tManger.relaseTransaction();
}
}
/**
* 这里需要在同一个线程中只有一个能控制事务对象
* @param sourceId
* @param targetId
* @param money
*/
public void transfer(Integer sourceId, Integer targetId, double money) {
try {
//1.开启事务
tManger.beginTransaction();
//2.执行操作
//2.1.根据名称查询传出账户
Account source = accountDao.findById(sourceId);
//2.2.根据id
Account target = accountDao.findById(targetId);
//2.3.转出账户减钱
source.setMoney(source.getMoney() - money);
int i = 10/0;
//2.4.转入账户加钱
target.setMoney(target.getMoney() + money);
//2.5.更新转出账户
accountDao.updateAccount(source);
//2.6.更新转入账户
accountDao.updateAccount(target);
//3.提交事务
tManger.commitTransaction();
} catch (Exception e) {
//5.回滚操作
tManger.rollbackTransaction();
//产生错误了,程序就不在继续运行
throw new RuntimeException(e);
} finally {
//6.释放连接
tManger.relaseTransaction();
}
}
}
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置Service-->
<bean id="accountService" class="cn.kitey.service.impl.AccountServiceImpl">
<!--注入dao-->
<property name="accountDao" ref="accountDao"></property>
<!--注入事务管理器-->
<property name="tManger" ref="tManger"></property>
</bean>
<!--配置Dao对象-->
<bean id="accountDao" class="cn.kitey.dao.impl.AccountDaoImpl">
<!-- 注入QueryRunner -->
<property name="queryRunner" ref="runner"></property>
<!--注入connectionUtils数据-->
<property name="connectionUtils" ref="connectionUtils"></property>
</bean>
<!--配置QueryRunner-->
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
</bean>
<!--配置数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/eesy?serverTimezone=GMT%2B8"></property>
<property name="user" value="root"></property>
<property name="password" value="25002500"></property>
</bean>
<!--配置Connection的工具类,ConnectionUtils-->
<bean id = "connectionUtils" class="cn.kitey.utils.ConnectionUtils">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置Connection工具类中的:TransactionManger-->
<bean id="tManger" class="cn.kitey.utils.TransactionManger">
<property name="connectionUtils" ref="connectionUtils"></property>
</bean>
</beans>
package cn.kitey.test;
import cn.kitey.domain.Account;
import cn.kitey.service.AccountService;
import cn.kitey.service.impl.AccountServiceImpl;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
import static org.junit.Assert.*;
public class AccountServiceTest {
private AccountService xmlBean;
@Before
public void init(){
ApplicationContext xml = new ClassPathXmlApplicationContext("bean.xml");
xmlBean = xml.getBean("accountService", AccountServiceImpl.class);
}
@Test
public void transfer(){
xmlBean.transfer(1,2,100);
}
@Test
public void findAll() {
List<Account> all = xmlBean.findAll();
System.out.println("findAll()数据结果为:");
for (Account account : all) {
System.out.println(account);
}
}
@Test
public void findById() {
Account byId = xmlBean.findById(1);
System.out.println(byId);
}
@Test
public void saveAccount() {
Account acc = new Account();
acc.setId(6);
acc.setUid(1);
acc.setMoney(123456);
xmlBean.saveAccount(acc);
List<Account> all = xmlBean.findAll();
for (Account account : all) {
System.out.println("进行数据插入后的结果:");
System.out.println(account);
}
}
@Test
public void updateAccount() {
Account acc = new Account(6, 1, 8888);
xmlBean.updateAccount(acc);
List<Account> all = xmlBean.findAll();
for (Account account : all) {
System.out.println("进行数据更新后的结果后的结果:");
System.out.println(account);
}
}
@Test
public void deleteAccount() {
xmlBean.deleteAccount(6);
List<Account> all = xmlBean.findAll();
for (Account account : all) {
System.out.println("进行数据删除后的结果:");
System.out.println(account);
}
}
}