一、目录结构
二、数据库结构及测试数据
三、代码详解
1、xml配置文件: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>com.mollen</groupId>
<artifactId>exam</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.6</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<target>1.7</target>
<source>1.7</source>
</configuration>
</plugin>
</plugins>
</build>
</project>
2、数据源配置文件:db.properties
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/my_db2
jdbc.user=root
jdbc.password=root
3、实体类:Account.java
package com.mollen.bean;
/**
* @ClassName: Account
* @Auther: Mollen
* @CreateTime: 2018-11-06 11:53:29
* @Description:
*/
public class Account {
private int id;
private String name;
private double money;
public Account() {
}
public Account(int id, String name, double money) {
this.id = id;
this.name = name;
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
//...getter/setter
}
4、dao层:
1、Dao接口:AccountDao.java
package com.mollen.dao;
import com.mollen.bean.Account;
/**
* @ClassName: AccountDao
* @Auther: Mollen
* @CreateTime: 2018-11-06 11:55:11
* @Description:
*/
public interface AccountDao {
/**
* 1.根据用户名查询账户信息
* @return
*/
public Account findByName(String name) throws Exception;
/**
* 2.更新账户信息
*/
public void upDate(String name,double money) throws Exception;
}
2、Dao实现类:AccountDaoImpl.java
package com.mollen.dao.impl;
import com.mollen.bean.Account;
import com.mollen.dao.AccountDao;
import com.mollen.utils.JdbcUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import java.sql.SQLException;
/**
* @ClassName: AccountDaoImpl
* @Auther: Mollen
* @CreateTime: 2018-11-06 11:58:35
* @Description:
*/
public class AccountDaoImpl implements AccountDao {
QueryRunner queryRunner = new QueryRunner();
public void setQueryRunner(QueryRunner queryRunner) {
this.queryRunner = queryRunner;
}
/**
* 1.根据用户名查询账户信息
* @return
*/
public Account findByName(String name) throws SQLException{
return queryRunner.query(JdbcUtils.getConnection(),"SELECT * FROM account WHERE name = ?",new BeanHandler<Account>(Account.class),name);
}
/**
* 2.更新账户信息
*/
public void upDate(String name,double money) throws SQLException{
queryRunner.update(JdbcUtils.getConnection(),"UPDATE account SET money = ? WHERE name = ?",money,name);
}
}
5、Service层:
1、Service层接口:AccountService.java
package com.mollen.service;
/**
* @ClassName: AccountService
* @Auther: Mollen
* @CreateTime: 2018-11-06 14:13:50
* @Description:
*/
public interface AccountService {
/**
* 转账业务
* @param source
* @param target
* @param money
* @throws Exception
*/
public void transfer(String source,String target,double money) throws Exception;
}
2、Service接口实现类:AccountServiceImpl.java
package com.mollen.service.impl;
import com.mollen.bean.Account;
import com.mollen.dao.AccountDao;
import com.mollen.dao.impl.AccountDaoImpl;
import com.mollen.service.AccountService;
/**
* @ClassName: AccountServiceImpl
* @Auther: Mollen
* @CreateTime: 2018-11-06 14:17:16
* @Description:
*/
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao = new AccountDaoImpl();
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void transfer(String source, String target, double money) throws Exception {
//1.查询转出账户的账户余额
Account source_account = accountDao.findByName(source);
//2.转出账户的余额减少
source_account.setMoney(source_account.getMoney()-money);
//3.修改转出账户余额
accountDao.upDate(source,source_account.getMoney());
//手动抛出异常
//int i = 10 /0 ;
//1.查询转入账户的余额
Account target_account = accountDao.findByName(target);
//2.转入账户余额增加
target_account.setMoney(target_account.getMoney()+money);
//3.修改转入账户的余额
accountDao.upDate(target,target_account.getMoney());
}
}
6、创建jdbc工具类:JdbcUtils.java
package com.mollen.utils;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
/**
* @ClassName: JdbcUtils
* @Auther: Mollen
* @CreateTime: 2018-11-06 14:49:43
* @Description:
*
*/
public class JdbcUtils {
//1.创建线程标志对象存储对象(保证事务过程中用的是同一个连接)
private static ThreadLocal<Connection> threadLocal = new InheritableThreadLocal<>();
//3.获取线程池连接对象
public static Connection getConnection(){
//获取标志
Connection connection = threadLocal.get();
try {
if (connection == null) {
connection = JdbcUtils.getDataSource().getConnection();
//存入标志
threadLocal.set(connection);
}
return connection;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//4.获取数据源
public static DataSource getDataSource() throws IOException, PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
Properties properties = new Properties();
InputStream is = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
properties.load(is);
dataSource.setDriverClass(properties.getProperty("jdbc.driverClass"));
dataSource.setJdbcUrl(properties.getProperty("jdbc.jdbcUrl"));
dataSource.setUser(properties.getProperty("jdbc.user"));
dataSource.setPassword(properties.getProperty("jdbc.password"));
return dataSource;
}
//5.解除线程与连接绑定
public static void remove(){
threadLocal.remove();
}
}
7、创建事务类:TransactionManager.java
package com.mollen.utils;
import java.sql.SQLException;
/**
* @ClassName: TransactionManager
* @Auther: Mollen
* @CreateTime: 2018-11-06 14:49:58
* @Description:
*/
public class TransactionManager {
/**
* 1.开启事务
*/
public static void startTransaction(){
try {
JdbcUtils.getConnection().setAutoCommit(false);
System.out.println("事务开启!");
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 2.提交事务
*/
public static void commitTransaction(){
try {
JdbcUtils.getConnection().commit();
System.out.println("事务提交!");
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 3.回滚事务
*/
public static void rollbackTransaction(){
try {
JdbcUtils.getConnection().rollback();
System.out.println("事务回滚!");
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 4.关闭连接
*/
public static void close(){
try {
JdbcUtils.getConnection().close();
System.out.println("连接关闭!");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
8、创建动态代理测试类
package com.mollen.test;
import com.mollen.service.AccountService;
import com.mollen.service.impl.AccountServiceImpl;
import com.mollen.utils.TransactionManager;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.junit.Test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @ClassName: MyTest
* @Auther: Mollen
* @CreateTime: 2018-11-06 18:41:31
* @Description:
*/
public class MyTest {
AccountService accountService = new AccountServiceImpl();
/**
* 1、第一种方案:Jdk动态代理方式进行事务控制 --> 真实对象必须实现接口
*/
@Test
public void transfer1() {
/**
* ClassLoader loader, 真实对象的类加载器
* Class<?>[] interfaces, 真实对象实现的所有接口
* InvocationHandler h 代理对象的处理类
*/
AccountService accountService_proxy = (AccountService) Proxy.newProxyInstance(
accountService.getClass().getClassLoader(),
accountService.getClass().getInterfaces(),
new InvocationHandler() {
/**
* 调用代理对象任意方法,InvocationHandler中的invoke()都会执行
* @param proxy 代理对象
* @param method 代理对象调用的方法
* @param args 代理对象调用方法时掺入的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//声明一个事务执行结果
Object obj = null;
try {
//1.开启事务--业务执行之前
TransactionManager.startTransaction();
//2.执行业务--执行业务操作
obj = method.invoke(accountService, args);
//3.提交事务--业务正常执行
TransactionManager.commitTransaction();
} catch (Exception e) {
e.printStackTrace();
//4.回滚事务--业务执行异常
TransactionManager.rollbackTransaction();
} finally {
//5.关闭连接
TransactionManager.close();
}
return obj;
}
});
try {
accountService_proxy.transfer("aaa", "bbb", 100);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 2、第二种方案:使用cglib动态代理技术
*/
@Test
public void transfer2() {
//1. 创建cglib的核心对象
Enhancer enhancer = new Enhancer();
//2. 设置产生的代理对象的父类
enhancer.setSuperclass(accountService.getClass());
//3. 设置调用代理对象的回调函数
enhancer.setCallback(new MethodInterceptor() {
/**
*
* @param proxy 代理对象
* @param method 代理对象执行的方法
* @param args 代理对象调用方法时传入的参数
* @param methodProxy 调用方法的代理对象 一般没什么用
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object obj = null;
try {
//1. 在转账业务执行之前 开启事物
TransactionManager.startTransaction();
//2. 执行原有的转账业务
obj = method.invoke(accountService, args);
//3. 转账业务执行成功-- 提交事物
TransactionManager.commitTransaction();
} catch (Exception e) {
e.printStackTrace();
//4. 转账业务执行失败---回滚事物
TransactionManager.rollbackTransaction();
} finally {
TransactionManager.close();
}
return obj;
}
});
AccountService accountService_proxy = (AccountService) enhancer.create();
try {
accountService_proxy.transfer("aaa", "bbb", 100);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 3、第三种方案:使用cglib动态代理技术第二种方式
*/
@Test
public void transfer3() {
AccountService accountService_proxy = (AccountService) Enhancer.create(
accountService.getClass(),
new MethodInterceptor() {
/**
*
* @param proxy 代理对象
* @param method 代理对象执行的方法
* @param args 代理对象调用方法时传入的参数
* @param methodProxy 调用方法的代理对象 一般没什么用
* @return
* @throws Throwable
*/
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object obj = null;
try {
//1. 在转账业务执行之前 开启事物
TransactionManager.startTransaction();
//2. 执行原有的转账业务
obj = method.invoke(accountService, args);
//3. 转账业务执行成功-- 提交事物
TransactionManager.commitTransaction();
} catch (Exception e) {
e.printStackTrace();
//4. 转账业务执行失败---回滚事物
TransactionManager.rollbackTransaction();
} finally {
TransactionManager.close();
}
return obj;
}
});
try {
accountService_proxy.transfer("aaa", "bbb", 100);
} catch (Exception e) {
e.printStackTrace();
}
}
}