1, 整体的项目结构
2,项目所需要的依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
</dependencies>
3,实体类 Account
实体类Account与我们的数据库字段对应即可,提供setter和getter,后期如果想查看对象,我们可以重写toString(方法)
package com.offcn.bean;
import java.io.Serializable;
public class Account implements Serializable {
private Integer id;
private String name;
private double money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
4,创建工具类
ConnectionUtils,这个工具类我们是主要获取我们的Connection,管理我们的Connection,
在这里面我们遇到了一个新的对象ThreadLocal,这个对象我需要说一下,这个是线程的对象的局部变量,在其内部,维护了一个Map集合,Map<Thread,Object> map = new HashMap<>(); 这个ThreadLocal对象,赋值的通过set(传的是Object),取值通过get(),移除通过remove(),操作的时候, key不需要指定,当前线程对象做key。具体方式,如下代码演示:
// set
public void set(Object obj){
map.put(CurrentThread,obj);//key 不需要指定,默认是当前线程对象。
}
//get
public Object get(){
return map.get(CurrentThread);
}
// remove 移除
public Object remove(){
return map.remove(currentThread);
}
package com.offcn.utils;
import javax.sql.DataSource;
import java.sql.Connection;
public class ConnectionUtils {
//创建线程局部变量
static ThreadLocal<Connection> threadLocal = new ThreadLocal();
// Connection 是从我们数据源中获取
private static DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public DataSource getDataSource() {
return dataSource;
}
// 创建方式 获取我们的Connection,包装获取的是同一个conn对象
public Connection getConnection() throws Exception {
Connection connection = threadLocal.get();
if (connection == null) {
// 从C3P0文件里面进行获取
connection = dataSource.getConnection();
// 绑定在当前线程的局部变量
threadLocal.set(connection);
}
return connection;
}
// 从线程中进行解绑
public void remove() {
threadLocal.remove();
}
}
TxManager:是管理事务的工具类,主要定义了和事务相关的方法,例如:事务的开启,事务的提交,事务的回滚操作等。该工具类目前需要手动创建和管理,使用Spring进行事务管理后,该工具类由Spring提供,不需要手动编写和管理。所以该工具类重在理解,为Spring进行事务管理做好铺垫
package com.offcn.utils;
import java.sql.Connection;
import java.sql.SQLException;
public class TxManager {
// 创建ConnectionUtils
private ConnectionUtils connectionUtils;
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
public ConnectionUtils getConnectionUtils() {
return connectionUtils;
}
// 创建开启事物的方法
public void beginTransaction() {
try {
// 获取我们的连接对象 //开启事务
connectionUtils.getConnection().setAutoCommit(false);
} catch (Exception e) {
e.printStackTrace();
}
}
// 创建提交事物的方法
public void commit() {
try {
// 提交事务
connectionUtils.getConnection().commit();
} catch (Exception e) {
e.printStackTrace();
}
}
// 创建事务回滚
public void rollback() {
try {
connectionUtils.getConnection().rollback();
} catch (Exception e) {
e.printStackTrace();
}
}
// 释放资源
public void close() throws Exception {
connectionUtils.getConnection().close();
}
}
5, 所需资源
dbConfig.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/xxxxx
jdbc.username=root
jdbc.password=root
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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<context:property-placeholder location="classpath:dbConfig.properties"/>
<!-- 创建dataSource对象-->
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 创建ConnectionUtils对象,里面有dataSource属性,需要通过ioc创建对象-->
<bean class="com.offcn.utils.ConnectionUtils" id="connectionUtils">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 创建txManager 对象,里面需要ConnectionUtils对象-->
<bean id="txManager" class="com.offcn.utils.TxManager">
<property name="connectionUtils" ref="connectionUtils"></property>
</bean>
<!-- 创建QueryRunner 对象-->
<bean id="qr" class="org.apache.commons.dbutils.QueryRunner"></bean>
<!--这里是AccountDao对象 其内部有两个属性,分别为 QueryRunner 和 ConnectionUtils 对象-->
<bean id="accountDao" class="com.offcn.dao.impl.AccountDaoImpl">
<property name="qr" ref="qr"></property>
<property name="connectionUtils" ref="connectionUtils"></property>
</bean>
<!-- 这里我们是需要对我们的业务做事务处理,所以我们在这里面创建AccountService对象-->
<bean id="accountService" class="com.offcn.service.impl.AccountServiceImpl">
<!-- 其内部,我们需要对其中的属性进行赋值,我们发现accountDao 和 txManager 属性为对象,
所以我们继续通过ioc创建对象-->
<property name="accountDao" ref="accountDao"></property>
<property name="txManager" ref="txManager"></property>
</bean>
</beans>
6, 数据访问层
AccountDao 数据库访问层,我们定义根据名字查询用户,通过用户修改数据即可
public interface AccountDao {
public Account findByName(String name);
public void update(Account account);
}
AccountDaoImpl
package com.offcn.dao.impl;
import com.offcn.bean.Account;
import com.offcn.dao.AccountDao;
import com.offcn.utils.ConnectionUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
public class AccountDaoImpl implements AccountDao {
//我们通过ioc来获取QueryRunner对象 和 ConnectionUtils对象
private ConnectionUtils connectionUtils;
QueryRunner qr;
public ConnectionUtils getConnectionUtils() {
return connectionUtils;
}
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
public QueryRunner getQr() {
return qr;
}
public void setQr(QueryRunner qr) {
this.qr = qr;
}
@Override
public Account findByName(String name) {
String sql = "select * from account where name = ?";
try {
return qr.query(connectionUtils.getConnection(), sql, new BeanHandler<Account>(Account.class), name);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 通过查询到的用户名,进行数据修改
*/
@Override
public void update(Account account) {
String sql = "update account set money = ? where name = ?";
try {
qr.update(connectionUtils.getConnection(), sql, account.getMoney(), account.getName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
7, 业务处理层
AccountService: 我们的事务处理需要在业务处理层处理
public interface AccountService {
/**
* 根据账户名查询账户信息
*
* @param outName 账户名
* @param inName 账户名
* @param money 转账金额
*/
public void transfer(String outName, String inName, double money);
}
AccountServiceImpl
package com.offcn.service.impl;
import com.offcn.bean.Account;
import com.offcn.dao.AccountDao;
import com.offcn.service.AccountService;
import com.offcn.utils.TxManager;
public class AccountServiceImpl implements AccountService {
// 引入事务相关的代码
private TxManager txManager;
private AccountDao accountDao;
public TxManager getTxManager() {
return txManager;
}
public void setTxManager(TxManager txManager) {
this.txManager = txManager;
}
public AccountDao getAccountDao() {
return accountDao;
}
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String outName, String inName, double money) {
try {
// 开启事务
txManager.beginTransaction();
// 查询用户
Account byName = accountDao.findByName(outName); // 来源
Account byName2 = accountDao.findByName(inName); // 目标
// 更改用户余额
byName.setMoney(byName.getMoney() - money); // 来源
byName2.setMoney(byName2.getMoney() + money);//目标
//更新数据库
accountDao.update(byName);
//int i = 1 / 0;
accountDao.update(byName2);
// 事务提交
txManager.commit();
} catch (Exception e) {
e.printStackTrace();
//回滚
txManager.rollback();
} finally {
try {
// 关闭连接
txManager.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
8, 测试
package com.offcn.mytest;
import com.offcn.service.AccountService;
import com.offcn.service.impl.AccountServiceImpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class MyTest01 {
@Autowired
private AccountService accountService;
@Test
public void test01() {
accountService.transfer("aaa", "bbb", 100D);
}
}