spring不使用aop实现事务处理
此处为目录结构
创建数据库的连接,因为需要统一事务,防止出现事务使得数据库数据对应不上,所以使用ThreadLocal 来统一获取数据库连接
package com.zho.utils;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class ConnectionUtils {
private ThreadLocal<Connection> t1 = new ThreadLocal<>();
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public Connection getThreadConnection() {
Connection conn = t1.get();//先从ThreadLocal里面获取
try{
if (conn == null) { //若没有连接
conn = dataSource.getConnection();//从数据库获取连接
t1.set(conn) ;//存入本地
}
return conn;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
//把连接和线程解绑
Public void removeConnection(){
t1.remove();
}
}
创建一个事务统一处理的类,进行事务管理
package com.zho.utils;
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();
//事务回滚时,把连接在本地中杀掉,下次还需使用时,重新创建连接
connectionUtils.removeConnection();
} catch (SQLException e) {
e.printStackTrace();
}
}
//关闭连接
//不需要把连接杀掉,没有回滚,所以下次仍可继续使用
public void close() {
try {
connectionUtils.getThreadConnection().close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
使用代理模式,简化代码,在每次执行事务操作时,都能在方法的前后实现事务的控制
因为这里使用另外的类来代理AccountService 所以在写配置文件时 标签需要选择普通工厂的注入方式
<bean id="proxyAccountService" factory-bean="beanFactory" factory-method="getAccountService">
</bean>
代理
package com.zho.factory;
import com.zho.service.IAccountService;
import com.zho.utils.TransactionManager;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class BeanFactory {
private IAccountService accountService;
private TransactionManager transactionManager;
public void setTransactionManager(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public final void setAccountService(IAccountService accountService) {
this.accountService = accountService;
}
//代理
public IAccountService getAccountService() {
return (IAccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader,
accountService.getClass().getInterfaces(), new InvocationHandler() {
//在此处进行事务操作
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
Object rtValue = null;
try {
transactionManager.beginTransaction();
rtValue = method.invoke(accountService, objects);
transactionManager.commit();
return rtValue;
} catch (Exception e) {
transactionManager.rollback();
throw new RuntimeException(e);
} finally {
transactionManager.close();
}
}
});
}
}
dao层的代码实现
package com.zho.dao.impl;
import com.zho.dao.IAccountDao;
import com.zho.domain.Account;
import com.zho.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 IAccountDaoImpl implements IAccountDao {
private QueryRunner runner;
private ConnectionUtils connectionUtils;
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
public void setRunner(QueryRunner runner) {
this.runner = runner;
}
@Override
public List<Account> findAllAccount() {
try {
return runner.query(connectionUtils.getThreadConnection(),"select * from account", new BeanListHandler<Account>(Account.class));
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public Account findAccountById(Integer id) {
try {
return runner.query(connectionUtils.getThreadConnection(),"select * from account where id =?", new BeanHandler<>(Account.class), id);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void saveAccount(Account account) {
try {
runner.update(connectionUtils.getThreadConnection(),"insert into account(name,money) values(?,?)", account.getName(), account.getMoney());
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void updateAccount(Account account) {
try {
runner.update(connectionUtils.getThreadConnection(),"update account set name=?,money=? where id=?", account.getName(), account.getMoney(),account.getId());
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void deleteAccount(Integer id) {
try {
runner.update(connectionUtils.getThreadConnection(),"delete from account where id=?",id);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public Account findAccountByName(String name) {
try {
List<Account> query = runner.query(connectionUtils.getThreadConnection(),"select * from account where name = ?", new BeanListHandler<Account>(Account.class), name);
if (query == null || query.size() == 0)
return null;
if (query.size() > 1) {
throw new RuntimeException("结果集不唯一");
}
return query.get(0);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
service层代码实现
package com.zho.service.impl;
import com.zho.dao.IAccountDao;
import com.zho.domain.Account;
import com.zho.service.IAccountService;
import java.util.List;
public class IAccountServiceImpl implements IAccountService {
private IAccountDao accountDao;
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public List<Account> findAllAccount() {
return accountDao.findAllAccount();
}
@Override
public Account findAccountById(Integer id) {
return accountDao.findAccountById(id);
}
@Override
public void saveAccount(Account account) {
accountDao.saveAccount(account);
}
@Override
public void updateAccount(Account account) {
accountDao.updateAccount(account);
}
@Override
public void deleteAccount(Integer id) {
accountDao.deleteAccount(id);
}
@Override
public void transFor(String sourceName, String targetName, Float money) {
Account source = accountDao.findAccountByName(sourceName);
Account target = accountDao.findAccountByName(targetName);
source.setMoney(source.getMoney() + money);
// int k = 1 / 0;
target.setMoney(target.getMoney() - money);
accountDao.updateAccount(source);
accountDao.updateAccount(target);
System.out.println("转账成功");
}
}
测试代码
package com.zho.test;
import com.zho.service.IAccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class AccountServiceTest {
@Autowired
@Qualifier("proxyAccountService")
private IAccountService as;
@Test
public void testTransFor(){
as.transFor("aaa","bbb",100f);
}
}
配置文件
<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">
<bean id="beanFactory" class="com.zho.factory.BeanFactory">
<property name="accountService" ref="accountService" />
<property name="transactionManager" ref="txManager" />
</bean>
<!--service层-->
<bean id="proxyAccountService" factory-bean="beanFactory" factory-method="getAccountService">
</bean>
<!--dao层-->
<bean id="accountDao" class="com.zho.dao.impl.IAccountDaoImpl">
<property name="runner" ref="queryRunner"/>
<property name="connectionUtils" ref="connectionUtils"/>
</bean>
<!--数据库连接-->
<bean id="queryRunner" 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 name="jdbcUrl" value="jdbc:mysql://localhost:3306/test_spring?characterEncoding=utf8"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
</bean>
<bean id="connectionUtils" class="com.zho.utils.ConnectionUtils">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="txManager" class="com.zho.utils.TransactionManager">
<property name="connectionUtils" ref="connectionUtils"/>
</bean>
</beans>