#简介
- 的三个案例是为了更好地理解Spring AOP的机制,以及复习数据库事务管理。具体的功能是实现数据库的转账操作,核心知识主要分为三部分:数据库的连接,事务管理,依赖注入和- ** <ü>案例一:直接在业务层添加代码,来进行事务管理</ù> ** - 数据库的管理问题:通过连接工具类来实现 - ** 事务管理</ u>:通过事务工具类,在服务业务层中实现** - 依赖注入:通过xml配置文件 - 测试:通过Junit测试 -** 案例二:通过代理模式增强代码,来进行事务管理</ u
** - 数据库的管理问题:通过连接工具类来实现 - ** 事务管理</ u>:通过事务工具类,在BeanFactory代理类中实现。(获取增强过的业务层接口)** - 依赖注入:通过xml配置文件 - 测试:通过Junit测试 - ** 案例三:通过SpringAOP,来进行事务管理</ u> ** - 数据库的管理问题:通过连接工具类来实现 - ** 事务管理</ u
:完全通过交易工具类实现(AOP,高内聚)** - 依赖注入:通过xml配置文件+注解 - 测试:通过Spring Junit测试
项目环境
数据库代码
- 创建数据库spring,在数据库中创建 Account表。
# 创建数据库
CREATE DATABASE spring;
# 使用数据库
USE spring;
# 创建表
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(40),
money FLOAT
)CHARACTER SET utf8 COLLATE utf8_general_ci;
# 插入数据
INSERT INTO account(NAME,money) VALUES('Cat',1000);
INSERT INTO account(NAME,money) VALUES('Dog',1000);
INSERT INTO account(NAME,money) VALUES('Rat',1000);
Maven依赖
<dependencies>
<!-- Spring 框架 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- Spring AOP -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
<!-- MySQL数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!-- 操作数据库的工具包 -->
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.4</version>
</dependency>
<!-- Spring 测试 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
业务层实现事务
目录结构
- src
-
main
-
java
-
cn.water
-
momain:用于存放实体类。
- Account.java(实体类)
-
dao:用于存放持久层的接口和实现类。(具体的CRUD操作代码)
- AccountDao.java(持久层接口)
- AccountDaoImp.java(持久层实现类)
-
service:用于存放业务层的接口和实现类。
- AccountService.java(业务层接口)
- AccountServiceImp.java(业务层接口)
-
utils:用于存放工具类
- ConnectionUtils.java(数据库连接工具类)
- TransactionManager.java(事务管理工具类)
-
-
-
resources:用于存放配置文件
-
Beans.xml(Spring配置文件)
-
-
test:用于测试。
- cn.water.test
- SpringTest.java(测试类)
- cn.water.test
-
实体类
- 封装数据库查询操作的结果集
Account.java
package cn.water.domain;
public class Account {
/* 成员变量 */
private Integer id;
private String name;
private Float money;
/* 构造函数 */
public Account() {
}
public Account(Integer id, String name, Float money) {
this.id = id;
this.name = name;
this.money = 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 Float getMoney() {
return money;
}
public void setMoney(Float money) {
this.money = money;
}
/* toString */
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
持久层
- 实现具体的数据库操作代码
AccountDao.java
package cn.water.dao;
import cn.water.domain.Account;
import java.util.List;
public interface AccountDao {
/** 查询单个 */
Account findByID(int id);
/** 修改 */
void update(Account account);
}
AccountDaoImp.java
package cn.water.dao;
import cn.water.domain.Account;
import cn.water.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.Connection;
import java.sql.SQLException;
import java.util.List;
public class AccountDaoImp implements AccountDao {
/* 成员方法 */
private QueryRunner queryRunner;
private ConnectionUtils connectionUtils;
/* 设值方法 */
public void setQueryRunner(QueryRunner queryRunner) {
this.queryRunner = queryRunner;
}
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
/** 查询单个 */
public Account findByID(int id) {
try {
/* QueryRunner.query */
return queryRunner.query(
/* Connection对象 */
connectionUtils.getThreadConnection(),
/* SQL语句 */
"SELECT * FROM account WHERE id = ? ",
/* 结果集 */
new BeanHandler<Account>(Account.class),
id);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
/** 修改 */
public void update(Account account) {
try {
/* QueryRunner.update */
queryRunner.update(
/* Connection对象 */
connectionUtils.getThreadConnection(),
/* SQL语句 */
" UPDATE account SET name=?,money=? WHERE id=? ",
/* 参数 */
account.getName(),
account.getMoney(),
account.getId());
} catch (SQLException e) {
e.printStackTrace();
}
}
}
数据库连接工具类
- 获取当前线程上的Connection对象
ConnectionUtils.java
package cn.water.utils;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class ConnectionUtils {
/* 成员变量 */
private ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
private DataSource dataSource;
/* 设值函数 */
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
/** 获取Connection对象 */
public Connection getThreadConnection(){
try {
/* 获取从当前线程上的Connection对象 */
Connection connection = threadLocal.get();
/* 如果当前线程没有绑定Connection */
if (connection==null) {
/* 从数据库连接池中,获取一个Connection对象 */
connection = dataSource.getConnection();
/* 并绑定至当前线程 */
threadLocal.set(connection);
}
/* 返回此Connection对象 */
return connection;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/** 将Connection和当前线程 解绑 */
public void removeConnection(){
threadLocal.remove();
}
}
事务管理工具类
- 对当前线程上的 Connection对象 进行事务管理的具体方法
TransactionManager.java
package cn.water.utils;
import java.sql.Connection;
import java.sql.SQLException;
public class TransactionManager {
/* 成员变量 */
private ConnectionUtils connectionUtils;
private Connection connection;
/* 设值函数 */
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.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();
/* 将Connection对象与线程解绑 */
connectionUtils.removeConnection();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
业务层(TM)
- 直接调用事务管理工具类的方法,实现事务管理
AccountService.java
package cn.water.service;
import cn.water.domain.Account;
public interface AccountService {
/** 转账 */
void transfer(int sourceId, int targetId, float money);
}
AccountServiceImp.java
package cn.water.service;
import cn.water.dao.AccountDao;
import cn.water.domain.Account;
import cn.water.utils.TransactionManager;
import java.util.List;
public class AccountServiceImp implements AccountService {
/* 成员变量 */
private AccountDao dao;
private TransactionManager txManager;
/* 设值方法 */
public void setDao(AccountDao dao) {
this.dao = dao;
}
public void setTxManager(TransactionManager txManager) {
this.txManager = txManager;
}
/** 转账 */
public void transfer(int sourceId, int targetId, float money) {
try {
/* 开启事务 */
txManager.beginTransaction();
/* 执行操作 */
Account source = dao.findByID(sourceId);
Account target = dao.findByID(targetId);
Float sourceMoney = source.getMoney();
Float targetMoney = target.getMoney();
source.setMoney(sourceMoney - money);
// int i = 10/0;
target.setMoney(targetMoney + money);
dao.update(source);
dao.update(target);
/* 提交事务 */
txManager.commit();
} catch (Exception e) {
/* 回滚 */
txManager.rollback();
/* 抛出异常 */
throw new RuntimeException();
} finally {
/* 释放连接 */
txManager.release();
}
}
}
配置文件
- ServiceImp(业务层)
- DaoImp
- DaoImp(持久层)
- QueryRunner
- QueryRunner(数据库操作类)
- DataSource
- TransactionManager(事务管理工具类)
- ConnectionUtils
- ConnectionUtils(数据库连接工具类)
- DataSource
- DataSource(数据库连接池)
Beans.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- DataSource -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
</bean>
<!-- ConnectionUtils -->
<bean id="connectionUtils" class="cn.water.utils.ConnectionUtils">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- TransactionManager -->
<bean id="txManager" class="cn.water.utils.TransactionManager">
<property name="connectionUtils" ref="connectionUtils"/>
</bean>
<!-- QueryRunner -->
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner">
<constructor-arg name="ds" ref="dataSource"/>
</bean>
<!-- Dao -->
<bean id="daoImp" class="cn.water.dao.AccountDaoImp">
<property name="connectionUtils" ref="connectionUtils"/>
<property name="queryRunner" ref="runner"/>
</bean>
<!-- Service -->
<bean id="serviceImp" class="cn.water.service.AccountServiceImp">
<property name="dao" ref="daoImp"/>
<property name="txManager" ref="txManager"/>
</bean>
</beans>
测试类
SpringTest.java
package cn.water.test;
import cn.water.service.AccountService;
import cn.water.service.AccountServiceImp;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTest {
@Test
public void testTransfer(){
ApplicationContext app = new ClassPathXmlApplicationContext("Beans.xml");
AccountService service = app.getBean("serviceImp", AccountServiceImp.class);
service.transfer(1,2,10f);
}
}
代理类实现事务
目录结构
-
src
-
main
-
java
-
cn.water
-
momain:用于存放实体类。
- Account.java(实体类)
-
dao:用于存放持久层的接口和实现类。(具体的CRUD操作代码)
- AccountDao.java(持久层接口)
- AccountDaoImp.java(持久层实现类)
-
service:用于存放业务层的接口和实现类。
- AccountService.java(业务层接口)
- AccountServiceImp.java(业务层接口)
-
utils:用于存放工具类
- ConnectionUtils.java(数据库连接工具类)
- TransactionManager.java(事务管理工具类)
-
factory:用于存放工厂类
- BeanFactory.java(代理工厂类)
-
-
-
resources:用于存放配置文件
-
-
-
Beans.xml(Spring配置文件)
-
test:用于测试。
- cn.water.test
- SpringTest.java(测试类)
- cn.water.test
实体类(不变)
- 封装数据库查询操作的结果集
Account.java
package cn.water.domain;
public class Account {
/* 成员变量 */
private Integer id;
private String name;
private Float money;
/* 构造函数 */
public Account() {
}
public Account(Integer id, String name, Float money) {
this.id = id;
this.name = name;
this.money = 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 Float getMoney() {
return money;
}
public void setMoney(Float money) {
this.money = money;
}
/* toString */
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
持久层
- 实现具体的数据库操作代码
- 引入 ConnectionUtils ,获取 Connection对象,在调用QueryRunner类的方法时传入。
- 我们不难看出,QueryRunner类和Connection对象的关系:
- 一、向QueryRunner类传入DataSource对象;
- 二、调用QueryRunner类的方法时传入Connection对象。
- 我们不难看出,QueryRunner类和Connection对象的关系:
AccountDao.java
package cn.water.dao;
import cn.water.domain.Account;
import java.util.List;
public interface AccountDao {
/** 查询单个 */
Account findByID(int id);
/** 修改 */
void update(Account account);
}
AccountDaoImp.java
package cn.water.dao;
import cn.water.domain.Account;
import cn.water.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.Connection;
import java.sql.SQLException;
import java.util.List;
public class AccountDaoImp implements AccountDao {
/* 成员方法 */
private QueryRunner queryRunner;
private ConnectionUtils connectionUtils;
/* 设值方法 */
public void setQueryRunner(QueryRunner queryRunner) {
this.queryRunner = queryRunner;
}
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
/** 查询单个 */
public Account findByID(int id) {
try {
/* QueryRunner.query */
return queryRunner.query(
/* Connection对象 */
connectionUtils.getThreadConnection(),
/* SQL语句 */
"SELECT * FROM account WHERE id = ? ",
/* 结果集 */
new BeanHandler<Account>(Account.class),
id);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
/** 修改 */
public void update(Account account) {
try {
/* QueryRunner.update */
queryRunner.update(
/* Connection对象 */
connectionUtils.getThreadConnection(),
/* SQL语句 */
" UPDATE account SET name=?,money=? WHERE id=? ",
/* 参数 */
account.getName(),
account.getMoney(),
account.getId());
} catch (SQLException e) {
e.printStackTrace();
}
}
}
数据库连接工具类(不变)
- 获取当前线程上的Connection对象(不变)
ConnectionUtils.java
package cn.water.utils;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class ConnectionUtils {
/* 成员变量 */
private ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
private DataSource dataSource;
/* 设值函数 */
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
/** 获取Connection对象 */
public Connection getThreadConnection(){
try {
/* 获取从当前线程上的Connection对象 */
Connection connection = threadLocal.get();
/* 如果当前线程没有绑定Connection */
if (connection==null) {
/* 从数据库连接池中,获取一个Connection对象 */
connection = dataSource.getConnection();
/* 并绑定至当前线程 */
threadLocal.set(connection);
}
/* 返回此Connection对象 */
return connection;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/** 将Connection和当前线程 解绑 */
public void removeConnection(){
threadLocal.remove();
}
}
事务管理工具类(不变)
- 对当前线程上的 Connection对象 进行事务管理的具体方法
TransactionManager.java
package cn.water.utils;
import java.sql.Connection;
import java.sql.SQLException;
public class TransactionManager {
/* 成员变量 */
private ConnectionUtils connectionUtils;
/* 设值函数 */
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.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();
/* 将Connection对象与线程解绑 */
connectionUtils.removeConnection();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
业务层
- 直接调用事务管理工具类的方法,实现事务管理
AccountService.java
package cn.water.service;
import cn.water.domain.Account;
public interface AccountService {
/** 转账 */
void transfer(int sourceId, int targetId, float money);
}
AccountServiceImp.java
package cn.water.service;
import cn.water.dao.AccountDao;
import cn.water.domain.Account;
import java.util.List;
public class AccountServiceImp implements AccountService {
/* 成员变量 */
private AccountDao dao;
/* 设值方法 */
public void setDao(AccountDao dao) {
this.dao = dao;
}
/** 转账 */
public void transfer(int sourceId, int targetId, float money) {
Account source = dao.findByID(sourceId);
Account target = dao.findByID(targetId);
Float sourceMoney = source.getMoney();
Float targetMoney = target.getMoney();
source.setMoney(sourceMoney - money);
dao.update(source);
// int i = 10/0;
target.setMoney(targetMoney + money);
dao.update(target);
}
}
代理工厂类(TM)
- 代理模式:创建代理对象,并增强代码
- 工厂模式:返回一个代理对象。
ProxyFactory.java
package cn.water.factory;
import cn.water.service.AccountService;
import cn.water.utils.TransactionManager;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class BeanFactory {
/* 成员变量 */
private AccountService service;
private TransactionManager txManager;
/* 设值函数 */
public void setService(AccountService service) {
this.service = service;
}
public void setTxManager(TransactionManager txManager) {
this.txManager = txManager;
}
/** 获取代理对象 */
public AccountService getProxyService(){
return (AccountService) Proxy.newProxyInstance(
/* 类加载器 */
service.getClass().getClassLoader(),
/* 接口 */
service.getClass().getInterfaces(),
/** InvocationHandler接口:事务管理 */
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/* 查询方法不需要进行事务管理 */
if ("find".equals(method.getName())){
return method.invoke(service,args);
/* 增删改方法进行进行事务管理 */
}else {
try {
/* 开启事务 */
txManager.beginTransaction();
/* 执行操作 */
Object result = method.invoke(service, args);
/* 提交事务 */
txManager.commit();
/* 返回结果集 */
return result;
} catch (Exception e) {
/* 回滚 */
txManager.rollback();
/* 抛出异常 */
throw new RuntimeException();
} finally {
/* 释放连接 */
txManager.release();
}
}
}
}
);
}
}
配置文件
- ProxyService(代理类)
- BeanFactory(代理工厂类)
- ServiceImp
- TransactionManager
- ServiceImp(业务层)
- DaoImp
- DaoImp(持久层)
- QueryRunner
- ConnectionUtils
- QueryRunner(数据库操作类)
- ConnectionUtils 能够为 DaoImp 提供 Connection对象,因此不必再为 QueryRunner 提供 DataSource
- 但要转而为 DaoImp 提供 ConnectionUtils
- TransactionManager(事务管理工具类)
- ConnectionUtils
- ConnectionUtils(数据库连接工具类)
- DataSource
- DataSource(数据库连接池)
Beans.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- DataSource -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
</bean>
<!-- ConnectionUtils -->
<bean id="connectionUtils" class="cn.water.utils.ConnectionUtils">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- TransactionManager -->
<bean id="txManager" class="cn.water.utils.TransactionManager">
<property name="connectionUtils" ref="connectionUtils"/>
</bean>
<!-- QueryRunner -->
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner"></bean>
<!-- Dao -->
<bean id="daoImp" class="cn.water.dao.AccountDaoImp">
<property name="connectionUtils" ref="connectionUtils"/>
<property name="queryRunner" ref="runner"/>
</bean>
<!-- Service -->
<bean id="serviceImp" class="cn.water.service.AccountServiceImp">
<property name="dao" ref="daoImp"/>
</bean>
<!-- BeanFactory -->
<bean id="factory" class="cn.water.factory.BeanFactory">
<property name="service" ref="serviceImp"/>
<property name="txManager" ref="txManager"/>
</bean>
<!-- 代理service -->
<bean id="proxyService" factory-bean="factory" factory-method="getProxyService"></bean>
</beans>
测试类
- Spring框架整合junit
SpringTest.java
package cn.water.test;
import cn.water.domain.Account;
import cn.water.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:Beans.xml")
public class SpringTest {
/* 获取代理对象 */
@Resource(name = "proxyService")
private AccountService service;
@Test
public void testTransfer(){
service.transfer(1,2,10f);
}
}
通知类实现事务(AOP)
目录结构
- src
-
main
- java
- cn.water
- dao:用于存放持久层的接口和实现类。(具体的CRUD操作代码)
- AccountDao.java(持久层接口)
- AccountDaoImp.java(持久层实现类)
- momain:用于存放实体类。
- Account.java(实体类)
- service:用于存放业务层的接口和实现类。
- AccountService.java(业务层接口)
- AccountServiceImp.java(业务层接口)
- utils:用于存放工具类
- ConnectionUtils.java(数据库连接工具类)
- TransactionManager.java(事务管理工具类)
- dao:用于存放持久层的接口和实现类。(具体的CRUD操作代码)
- cn.water
- resources:用于存放配置文件
- Beans.xml(Spring配置文件)
- java
-
test:用于测试。
- cn.water.test
- SpringTest.java(测试类)
- cn.water.test
-
实体类(不变)
- 封装数据库查询操作的结果集
Account.java
package cn.water.domain;
public class Account {
/* 成员变量 */
private Integer id;
private String name;
private Float money;
/* 构造函数 */
public Account() {
}
public Account(Integer id, String name, Float money) {
this.id = id;
this.name = name;
this.money = 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 Float getMoney() {
return money;
}
public void setMoney(Float money) {
this.money = money;
}
/* toString */
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
持久层
- 实现具体的数据库操作代码
AccountDao.java
package cn.water.dao;
import cn.water.domain.Account;
import java.util.List;
public interface AccountDao {
/** 查询单个 */
Account findByID(int id);
/** 修改 */
void update(Account account);
}
AccountDaoImp.java
package cn.water.dao;
import cn.water.domain.Account;
import cn.water.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.stereotype.Component;
import javax.annotation.Resource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
@Component("daoImp")
public class AccountDaoImp implements AccountDao {
/* 成员方法 */
@Resource(name = "runner")
private QueryRunner queryRunner;
@Resource(name = "connectionUtils")
private ConnectionUtils connectionUtils;
/** 查询单个 */
public Account findByID(int id) {
try {
/* QueryRunner.query */
return queryRunner.query(
/* Connection对象 */
connectionUtils.getThreadConnection(),
/* SQL语句 */
"SELECT * FROM account WHERE id = ? ",
/* 结果集 */
new BeanHandler<Account>(Account.class),
id);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
/** 修改 */
public void update(Account account) {
try {
/* QueryRunner.update */
queryRunner.update(
/* Connection对象 */
connectionUtils.getThreadConnection(),
/* SQL语句 */
" UPDATE account SET name=?,money=? WHERE id=? ",
/* 参数 */
account.getName(),
account.getMoney(),
account.getId());
} catch (SQLException e) {
e.printStackTrace();
}
}
}
数据库连接工具类(不变)
- 获取当前线程上的Connection对象
ConnectionUtils.java
package cn.water.utils;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class ConnectionUtils {
/* 成员变量 */
private ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
private DataSource dataSource;
/* 设值函数 */
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
/** 获取Connection对象 */
public Connection getThreadConnection(){
try {
/* 获取从当前线程上的Connection对象 */
Connection connection = threadLocal.get();
/* 如果当前线程没有绑定Connection */
if (connection==null) {
/* 从数据库连接池中,获取一个Connection对象 */
connection = dataSource.getConnection();
/* 并绑定至当前线程 */
threadLocal.set(connection);
}
/* 返回此Connection对象 */
return connection;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/** 将Connection和当前线程 解绑 */
public void removeConnection(){
threadLocal.remove();
}
}
事务管理工具类(TM)
- 使用Spring框架的AOP机制,进行事务管理
TransactionManager.java
package cn.water.utils;
import org.aspectj.lang.annotation.*;
import javax.annotation.Resource;
import java.sql.Connection;
import java.sql.SQLException;
@Aspect
public class TransactionManager {
/* 成员变量 */
@Resource(name = "connectionUtils")
private ConnectionUtils connectionUtils;
/** 切入点 */
@Pointcut(value = "execution(* cn.water.utils.*.*(..))")
public void all(){}
/** 开启事务(前置通知) */
@Before("all()")
public void beginTransaction(){
try {
connectionUtils.getThreadConnection().setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
/** 提交事务(后置通知) */
@AfterReturning("all()")
public void commit(){
try {
connectionUtils.getThreadConnection().commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
/** 回滚事务(异常通知) */
@AfterThrowing("all()")
public void rollback(){
try {
connectionUtils.getThreadConnection().rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
/** 释放连接(最终通知) */
@After("all()")
public void release(){
try {
/* 返回至连接池 */
connectionUtils.getThreadConnection().close();
/* 将Connection对象与线程解绑 */
connectionUtils.removeConnection();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
业务层
- 直接调用事务管理工具类的方法,实现事务管理
AccountService.java
package cn.water.service;
import cn.water.domain.Account;
public interface AccountService {
/** 转账 */
void transfer(int sourceId, int targetId, float money);
}
AccountServiceImp.java
package cn.water.service;
import cn.water.dao.AccountDao;
import cn.water.domain.Account;
import cn.water.utils.TransactionManager;
import java.util.List;
public class AccountServiceImp implements AccountService {
/* 成员变量 */
private AccountDao dao;
private TransactionManager txManager;
/* 设值方法 */
public void setDao(AccountDao dao) {
this.dao = dao;
}
public void setTxManager(TransactionManager txManager) {
this.txManager = txManager;
}
/** 转账 */
public void transfer(int sourceId, int targetId, float money) {
try {
/* 开启事务 */
txManager.beginTransaction();
/* 执行操作 */
Account source = dao.findByID(sourceId);
Account target = dao.findByID(targetId);
Float sourceMoney = source.getMoney();
Float targetMoney = target.getMoney();
source.setMoney(sourceMoney - money);
// int i = 10/0;
target.setMoney(targetMoney + money);
dao.update(source);
dao.update(target);
/* 提交事务 */
txManager.commit();
} catch (Exception e) {
/* 回滚 */
txManager.rollback();
/* 抛出异常 */
throw new RuntimeException();
} finally {
/* 释放连接 */
txManager.release();
}
}
}
配置文件
- QueryRunner(数据库操作类)
- DataSource(数据库连接池)
- 开启Spring注解扫描
- 开启SpringAOP注解支持
Beans.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:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- DataSource -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
</bean>
<!-- QueryRunner -->
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner"></bean>
<!-- 开启Spring注解扫描 -->
<context:component-scan base-package="cn.water"/>
<!-- 开启SpringAOP注解支持 -->
<aop:aspectj-autoproxy/>
</beans>
测试类
SpringTest.java
package cn.water.test;
import cn.water.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:Beans.xml")
public class SpringTest {
@Resource(name = "serviceImp")
private AccountService service;
@Test
public void testTransfer(){
服务。传递(1 ,2 ,10F ); } } ``