1 工具類
@Component("connectionUtils")
public class ConnectionUtils {
private ThreadLocal<Connection> tl = new ThreadLocal<>();
@Autowired
private DataSource dataSource;
public Connection getThreadConnection() {
Connection conn = tl.get();
try {
if (null == conn) {
conn = dataSource.getConnection();
tl.set(conn);
}
return conn;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void removeConnection(){
tl.remove();
}
}
- 切面類
如果使用註解,後置通知和最終通知順序會錯亂。所以需要使用環繞通知
@Component("txManager")
@Aspect
public class TransactionManager {
@Autowired
private ConnectionUtils connectionUtils;
@Pointcut("execution(* com.tzb.service.impl.*.*(..))")
private void pt1() {
}
public void beginTransaction() {
try {
connectionUtils.getThreadConnection().setAutoCommit(false);
} catch (Exception e) {
e.printStackTrace();
}
}
public void commit() {
try {
connectionUtils.getThreadConnection().commit();
} catch (Exception e) {
e.printStackTrace();
}
}
public void rollback() {
try {
connectionUtils.getThreadConnection().rollback();
} catch (Exception e) {
e.printStackTrace();
}
}
public void release() {
try {
connectionUtils.getThreadConnection().close();
connectionUtils.removeConnection();
} catch (Exception e) {
e.printStackTrace();
}
}
@Around("pt1()")
public Object aroundAdvice(ProceedingJoinPoint pjp) {
Object rtValue = null;
try {
Object[] args = pjp.getArgs();
this.beginTransaction();
rtValue = pjp.proceed(args);
this.commit();
return rtValue;
} catch (Throwable e) {
this.rollback();
throw new RuntimeException(e);
} finally {
this.release();
}
}
}
2 DAO
@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao {
@Autowired
private QueryRunner runner;
@Autowired
private ConnectionUtils connectionUtils;
@Override
public List<Account> findAllAccount() {
try{
return runner.query(connectionUtils.getThreadConnection(),"select * from account",new BeanListHandler<Account>(Account.class));
}catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public Account findAccountById(Integer accountId) {
try{
return runner.query(connectionUtils.getThreadConnection(),"select * from account where id = ? ",new BeanHandler<Account>(Account.class),accountId);
}catch (Exception 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 (Exception 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 (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void deleteAccount(Integer accountId) {
try{
runner.update(connectionUtils.getThreadConnection(),"delete from account where id=?",accountId);
}catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public Account findAccountByName(String accountName) {
try{
List<Account> accounts = runner.query(connectionUtils.getThreadConnection(),"select * from account where name = ? ",new BeanListHandler<Account>(Account.class),accountName);
if(accounts == null || accounts.size() == 0){
return null;
}
if(accounts.size() > 1){
throw new RuntimeException("結果集不唯一,數據有問題");
}
return accounts.get(0);
}catch (Exception e) {
throw new RuntimeException(e);
}
}
}
3 Service
@Service("accountService")
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao accountDao;
@Override
public List<Account> findAllAccount() {
return accountDao.findAllAccount();
}
@Override
public Account findAccountById(Integer accountId) {
return accountDao.findAccountById(accountId);
}
@Override
public void saveAccount(Account account) {
accountDao.saveAccount(account);
}
@Override
public void updateAccount(Account account) {
accountDao.updateAccount(account);
}
@Override
public void deleteAccount(Integer acccountId) {
accountDao.deleteAccount(acccountId);
}
@Override
public void transfer(String sourceName, String targetName, Float money) {
System.out.println("transfer....");
Account source = accountDao.findAccountByName(sourceName);
Account target = accountDao.findAccountByName(targetName);
source.setMoney(source.getMoney()-money);
target.setMoney(target.getMoney()+money);
accountDao.updateAccount(source);
int i=1/0;
accountDao.updateAccount(target);
}
}
4 bean.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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.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">
<context:component-scan base-package="com.tzb"></context:component-scan>
<bean id="runner" 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>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring5?userSSL=false"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
5 單元測試
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class AccountServiceTest {
@Autowired
private IAccountService as;
@Test
public void testTransfer(){
as.transfer("Mike","Jenny",100f);
}
}