四种控制事务的方法
第一种
在DAO层控制事务
新建项目tx1
1.导入所需jar包
commons-dbcp-1.4.jar
commons-dbutils-1.4.jar
commons-pool-1.5.6.jar
mysql-connector-java-5.0.8-bin.jar
2.新建com.view包,模拟客户端调用action
package com.view;
import com.service.BusinessService;
import com.service.impl.BusinessServiceImpl;
public class Client {
//模拟action中调用service
public static void main(String[] args) {
BusinessService s = new BusinessServiceImpl();
s.transfer("aaa","bbb",100);
}
}
3.新建com.service包,写业务接口
package com.service;
public interface BusinessService {
/**
* 模拟转账
* @param from 转出的账户
* @param to 目标账户
* @param money 交易金额
*/
void transfer(String from, String to, float money);
}
4.新建com.service.impl包,实现业务接口
package com.service.impl;
import java.sql.Connection;
import com.dao.AccountDao;
import com.dao.impl.AccountDaoImpl;
import com.service.BusinessService;
import com.utils.DBCPUtil;
public class BusinessServiceImpl implements BusinessService {
private AccountDao dao = new AccountDaoImpl();
@Override
public void transfer(String from, String to, float money) {
dao.transfer(from, to, money);
}
}
5.新建com.dao包,写数据接口DAO
package com.dao;
public interface AccountDao {
/**
* 转账,这里有点牵扯到业务的意思
* @param from
* @param to
* @param money
*/
void transfer(String from, String to, float money);
}
6.新建com.dao.impl,写数据接口DAO的实现
package com.dao.impl;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.commons.dbutils.QueryRunner;
import com.dao.AccountDao;
import com.utils.DBCPUtil;
public class AccountDaoImpl implements AccountDao {
private QueryRunner qr = new QueryRunner();
@Override
public void transfer(String from, String to, float money) {
Connection conn = null;
try {
conn =DBCPUtil.getConnection();
conn.setAutoCommit(false);
qr.update(conn, "update account set money=money-? where name=?",money,from);
qr.update(conn, "update account set money=money+? where name=?",money,to);
} catch (Exception e) {
if(conn!=null){
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
}finally{
if(conn!=null){
try {
conn.commit();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
7.工具类
package com.utils;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
public class DBCPUtil {
private static DataSource dataSource;
static{
try {
InputStream in = DBCPUtil.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
Properties props = new Properties();
props.load(in);
dataSource = BasicDataSourceFactory.createDataSource(props);
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
public static DataSource getDataSource(){
return dataSource;
}
//获得连接
public static Connection getConnection(){
try {
return dataSource.getConnection();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
8.dbconfig.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc\:mysql\://localhost\:3306/jdbc
username=root
password=19941119liu
initialSize=10
maxActive=50
maxIdle=20
minIdle=5
maxWait=60000
connectionProperties=useUnicode=true;characterEncoding=utf8
defaultAutoCommit=true
defaultReadOnly=
defaultTransactionIsolation=REPEATABLE_READ
第二种
业务层控制事务
拷贝tx1项目到tx2
1.新建com.domain包,描述实体Account
package com.domain;
public class Account {
private int id;
private String name;
private float money;
public int getId() {
return id;
}
public void setId(int 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;
}
}
2.修改AccountDao.java
AccountDao不在涉及到业务,只做crud操作
package com.dao;
import com.domain.Account;
public interface AccountDao {
/**
* 根据户名查询账户
* @param accountName
* @return
*/
Account findByName(String accountName);
/**
* 更新账户
* @param account
*/
void updateAccount(Account account);
}
3.修改dao的实现
package com.dao.impl;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import com.dao.AccountDao;
import com.domain.Account;
import com.utils.DBCPUtil;
public class AccountDaoImpl implements AccountDao {
private QueryRunner qr = new QueryRunner();
private Connection conn;
public AccountDaoImpl(Connection conn){
this.conn = conn;
}
//查账户
@Override
public Account findByName(String accountName) {
try {
return qr.query(conn, "select * from account where name=?", new BeanHandler<Account>(Account.class),accountName);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//改账户
@Override
public void updateAccount(Account account) {
try {
qr.update(conn, "update account set money=? where id=?",account.getMoney(),account.getId());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
4.业务接口不变,只需修改业务实现
package com.service.impl;
import java.sql.Connection;
import java.sql.SQLException;
import com.dao.AccountDao;
import com.dao.impl.AccountDaoImpl;
import com.domain.Account;
import com.service.BusinessService;
import com.utils.DBCPUtil;
//业务层控制事务
public class BusinessServiceImpl implements BusinessService {
@Override
public void transfer(String from, String to, float money) {
Connection conn = null;
try {
//拿连接
conn = DBCPUtil.getConnection();
conn.setAutoCommit(false);
AccountDao dao = new AccountDaoImpl(conn);
//调取DAO查找两个账户
Account fromAccount = dao.findByName(from);
Account toAccount = dao.findByName(to);
fromAccount.setMoney(fromAccount.getMoney() - money);
toAccount.setMoney(toAccount.getMoney() + money);
//对账户进行更改
dao.updateAccount(fromAccount);
dao.updateAccount(toAccount);
} catch (Exception e) {
if (conn != null) {
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
e.printStackTrace();
}finally {
if (conn != null) {
try {
conn.commit();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
第三种
抽取工具类,封装与事务有关的方法
拷贝tx2项目到tx3项目
1.在com.utils包中新建TransactionManager.java
package com.utils;
import java.sql.Connection;
import java.sql.SQLException;
//封装所有与事务有关的方法
public class TransactionManager {
private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
//获取连接
public static Connection getConnection(){
//先从当前线程中获取连接
Connection conn = tl.get();
if(conn == null){
//如果没有则从数据源中拿
conn = DBCPUtil.getConnection();
tl.set(conn);
}
return conn;
}
//开启事务
public static void startTransaction(){
try {
Connection conn = getConnection();
conn.setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
//回滚
public static void rollback(){
Connection conn = getConnection();
try {
conn.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
//释放资源
public static void release(){
try {
Connection conn = getConnection();
conn.close();
tl.remove();
} catch (SQLException e) {
e.printStackTrace();
}
}
//提交事务
public static void commit(){
try {
Connection conn = getConnection();
conn.commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
3.重写业务的实现
package com.service.impl;
import java.sql.Connection;
import java.sql.SQLException;
import com.dao.AccountDao;
import com.dao.impl.AccountDaoImpl;
import com.domain.Account;
import com.service.BusinessService;
import com.utils.DBCPUtil;
import com.utils.TransactionManager;
//业务层控制事务
public class BusinessServiceImpl implements BusinessService {
private AccountDao dao = new AccountDaoImpl();
@Override
public void transfer(String from, String to, float money) {
try {
TransactionManager.startTransaction();
Account sAccount = dao.findByName(from);
Account tAccount = dao.findByName(to);
sAccount.setMoney(sAccount.getMoney() - money);
tAccount.setMoney(tAccount.getMoney() + money);
dao.updateAccount(sAccount);
dao.updateAccount(tAccount);
} catch (Exception e) {
TransactionManager.rollback();
}finally{
TransactionManager.commit();
TransactionManager.release();
}
}
}
第四种
AOP控制事务
通过AOP拦截业务方法,给业务方法加上事务的方法
1.首先修改业务方法的实现,修改transfer()方法
package com.service.impl;
import java.sql.Connection;
import java.sql.SQLException;
import com.dao.AccountDao;
import com.dao.impl.AccountDaoImpl;
import com.domain.Account;
import com.service.BusinessService;
import com.utils.DBCPUtil;
import com.utils.TransactionManager;
//我不再管事务了,我只管我的业务逻辑
public class BusinessServiceImpl implements BusinessService {
private AccountDao dao = new AccountDaoImpl();
@Override
public void transfer(String from, String to, float money) {
Account sAccount = dao.findByName(from);
Account tAccount = dao.findByName(to);
sAccount.setMoney(sAccount.getMoney() - money);
tAccount.setMoney(tAccount.getMoney() + money);
dao.updateAccount(sAccount);
dao.updateAccount(tAccount);
}
}
2.客户端再调用的时候,调用的是工厂产生的代理对象,代理对象的方法结合了业务方法和事务方法
package com.utils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.service.BusinessService;
import com.service.impl.BusinessServiceImpl;
//AOP控制事务
public class BeanFactory {
public static BusinessService getBusinessService(){
final BusinessService s = new BusinessServiceImpl();
BusinessService proxyS = (BusinessService) Proxy.newProxyInstance(s.getClass().getClassLoader(), s.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
try {
TransactionManager.startTransaction();
Object rtValue = method.invoke(s, args);
return rtValue;
} catch (Exception e) {
TransactionManager.rollback();
throw new RuntimeException(e);
}finally{
TransactionManager.commit();
TransactionManager.release();
}
}
});
return proxyS;
}
}
3.客户端通过工厂拿到业务实例
package com.view;
import com.service.BusinessService;
import com.service.impl.BusinessServiceImpl;
import com.utils.BeanFactory;
public class Client {
//模拟action中调用service
public static void main(String[] args) {
BusinessService s = BeanFactory.getBusinessService();
s.transfer("aaa","bbb",100);
}
}