mvc结构:
准备阶段:jar包 ,dbcpconfig.propertie(数据源配置文件 ) ,DBCPUtil。
jar包:
dbcp配置文件:
driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/zhl username=root password=root initialSize=10 maxActive=50 maxIdle=20 minIdle=5 maxWait=60000 connectionProperties=useUnicode=true;characterEncoding=utf8 defaultAutoCommit=true defaultReadOnly= defaultTransactionIsolation=REPEATABLE_READ
DBCPUti类:
package com.chensi.test; import java.io.InputStream; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSourceFactory; public class DBCPUtil { private static DataSource ds; public static DataSource getDs() { return ds; } public static void setDs(DataSource ds) { DBCPUtil.ds = ds; } static{ try { InputStream in = DBCPUtil.class.getClassLoader().getResourceAsStream("dbcpconfig.properties"); Properties props = new Properties(); props.load(in); ds = BasicDataSourceFactory.createDataSource(props); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection(){ try { return ds.getConnection(); } catch (SQLException e) { throw new RuntimeException(e); } } public static void release(ResultSet rs,Statement stmt,Connection conn){ if(rs!=null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } rs = null; } if(stmt!=null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } stmt = null; } if(conn!=null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } conn = null; } } }
业务代码:
实体类账户类:
package com.chensi.domain; public class Account { private String Id; private String name; private int money; public String getId() { return Id; } public void setId(String id) { Id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getMoney() { return money; } public void setMoney(int money) { this.money = money; } @Override public String toString() { return "Account [Id=" + Id + ", name=" + name + ", money=" + money + "]"; } }
dao层:
package com.chensi.dao.impl; import java.sql.SQLException; import org.apache.commons.dbutils.DbUtils; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanHandler; import com.chensi.dao.TransformDao; import com.chensi.domain.Account; import com.chensi.test.DBCPUtil; import com.chensi.utils.ManagerThreadLocal; public class TransformDaoImpl implements TransformDao { public Account findAccountByUsername(String username) throws SQLException { QueryRunner queryRunner = new QueryRunner(DBCPUtil.getDs()); Account account = queryRunner.query(ManagerThreadLocal.getConnection(),"select * from Account where name=?", new BeanHandler<Account>(Account.class),username); return account; } public void TransfromMoney(Account account) throws SQLException { QueryRunner queryRunner = new QueryRunner(DBCPUtil.getDs()); queryRunner.update(ManagerThreadLocal.getConnection(),"update account set money=? where name=?",account.getMoney(),account.getName()); } }
service层:
package com.chensi.service.impl; import java.sql.SQLException; import com.chensi.dao.TransformDao; import com.chensi.dao.impl.TransformDaoImpl; import com.chensi.domain.Account; import com.chensi.service.TransfromService; import com.chensi.utils.ManagerThreadLocal; public class TransfromServiceInmpl implements TransfromService { TransformDao dao = new TransformDaoImpl(); //转账的方法 public void tranFromMoney(String fromUsername, String toUserName, int money) throws SQLException { try { ManagerThreadLocal.startTransaction(); Account fromAccount = dao.findAccountByUsername(fromUsername); Account toAccount = dao.findAccountByUsername(toUserName); fromAccount.setMoney(fromAccount.getMoney()-money); toAccount.setMoney(toAccount.getMoney()+money); dao.TransfromMoney(toAccount); dao.TransfromMoney(fromAccount); ManagerThreadLocal.commit(); } catch (Exception e) { ManagerThreadLocal.rollback(); }finally{ ManagerThreadLocal.close(); } } }
controller层(测试用的,所以写的比较简单):
package com.chensi.controller; import java.sql.SQLException; import org.junit.Test; import com.chensi.service.TransfromService; import com.chensi.service.impl.TransfromServiceInmpl; public class TransFromController { public static void main(String[] args) throws SQLException { TransfromService service = new TransfromServiceInmpl(); service.tranFromMoney("zhl", "chensi", 200); } }
控制线程安全的类(自己实现的线程内部类)
package com.chensi.utils; import java.sql.Connection; import java.sql.SQLException; import com.chensi.test.DBCPUtil; //用ThreadLocal线程内部类 来控制 取到的connection是一个线程中的connection,这样可以- //安全的控制事务 public class ManagerThreadLocal { private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>(); //获取到一个连接 (static 是为了 类名. 调用) public static Connection getConnection(){ Connection conn = tl.get(); if(conn==null){ conn = DBCPUtil.getConnection(); tl.set(conn); //从DBCP线程池中取出一个connn放入到ThreadLocal的Map之中 } return conn; } //开启事务 public static void startTransaction(){ Connection connection = getConnection(); try { connection.setAutoCommit(false); //开启事务 (这个连接是从当前线程取出来的) } catch (SQLException e) { e.printStackTrace(); } } //提交 public static void commit(){ try { getConnection().commit(); //提交事务 } catch (SQLException e) { e.printStackTrace(); } } //回滚 public static void rollback(){ try { getConnection().rollback(); //回滚事务 } catch (SQLException e) { e.printStackTrace(); } } //释放,也即是将conn从ThreadLocal中的Map中移出出去 public static void close(){ try { getConnection().close(); //连接关闭,然后将threadLocal中的connection 清除 tl.remove(); //threadLocal(当前线程中清除掉connection) } catch (SQLException e) { e.printStackTrace(); } } }
效果:转账前:
转账之后: