前面一篇转账我们发现处理逻辑不对,应该在service层来处理转账操作,DAO层不能写金额的加减,我们可以写通用的修改操作,但是不能涉及具体业务。本篇就来优化前面一篇代码,来解决这个问题
1.AccountDao接口代码
package com.anthony.dao;
import com.anthony.entity.Account;
public interface AccountDao {
@Deprecated //过时了
public void updateAccount(String fromName, String toName, double money) throws Exception;
/**
* 根据账户信息修改金额
* @param account
* @throws Exception
*/
public void updateAccount(Account account) throws Exception;
/**
* 根据用户名查找账户信息
* @param name
* @return
* @throws Exception
*/
public Account findAccountByName(String name) throws Exception;
}
增加uodateAccount就是一个普通修改操作,这里并看不出和转账有什么关系,由于是根据Account对象来修改数据,所以我们这里写了一个findAccountByName的方法。
2.AccountDaoImpl.java代码
package com.anthony.dao.impl;
import java.sql.Connection;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import com.anthony.dao.AccountDao;
import com.anthony.datasource.C3P0Utils;
import com.anthony.entity.Account;
public class AccountDaoImpl implements AccountDao {
private Connection conn;
public AccountDaoImpl(Connection conn) {
this.conn = conn;
}
public void updateAccount(String fromName, String toName, double money) throws Exception {
//创建一个QueryRunner对象
QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
//转出
qr.update("update account set money=money-? where name=?", money, fromName);
qr.update("update account set money=money+? where name=?", money, toName);
}
@Override
public void updateAccount(Account account) throws Exception {
QueryRunner qr = new QueryRunner();
qr.update(conn, "update account set money=? where name=?", account.getMoney(), account.getName());
}
public Account findAccountByName(String name) throws Exception {
QueryRunner qr = new QueryRunner();
Account account = qr.query(conn, "select * from account where name=?", new BeanHandler<Account>(Account.class), name);
return account;
}
}
这里conn这个连接对象这样设计很重要,就是为了保证后面service层处理sql代码中的conn是一致,这样才能使用事务处理操作,例如开启事务,回滚事务和提交事务。
3.AccountService接口
方法不变,去除抛异常。
package com.anthony.service;
public interface AccountService {
/**
* 转账
* @param fromName
* @param toName
* @param money
* @throws Exception
*/
public void transfer(String fromName, String toName, double money);
}
4.AccountServiceImpl.java类
package com.anthony.service.impl;
import java.sql.Connection;
import java.sql.SQLException;
import com.anthony.dao.AccountDao;
import com.anthony.dao.impl.AccountDaoImpl;
import com.anthony.datasource.C3P0Utils;
import com.anthony.entity.Account;
import com.anthony.service.AccountService;
public class AccountServiceImpl implements AccountService {
Connection conn = C3P0Utils.getConnection();
AccountDao ad = new AccountDaoImpl(conn);
public void transfer(String fromName, String toName, double money) {
//ad.updateAccount(fromName, toName, money);
try {
conn.setAutoCommit(false); //开启事务
Account fromAccount = ad.findAccountByName(fromName);
Account toAccount = ad.findAccountByName(toName);
//设置各自账户金额
fromAccount.setMoney(fromAccount.getMoney()-money);
toAccount.setMoney(toAccount.getMoney()+money);
//完成转账操作
ad.updateAccount(fromAccount);
ad.updateAccount(toAccount);
conn.commit();
} catch (Exception e) {
e.printStackTrace();
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
这里防止转账过程发生异常,所以使用事务提交,如果异常发生,catch住并执行回滚操作。
5.问题和缺陷
虽然这个版本使用了事务,考虑到了转账的异常,但是还是有一个问题,问题就是我们在service层中使用了C3P0来得到Connection对象,这个是不好的做法。Connection对象应该是DAO层处理,在service层处理不合适。