就是功能实现
例:Account类要求编写业务逻辑层AccountServiceImpl类实现一个转账功能
先定义DBUtils类封装数据库连接代码、定义Account类、定义PersonDao类提供增删改查方法
public class AccountServiceImpl{public static void transfer(int fromId,String pwd,int toId,double money){//判断转账的用户id和密码是否正确//定义DBUtils类封装数据库连接代码、定义Account类、定义PersonDao类提供增删改查方法PersonDao personDao = new PersonDao();Account account = null;account = personDao.select(fromid);if(account==null){throw new RuntimeException("卡号错误");}if(account.getPassword()!=pwd){throw new RuntimeException("密码错误");}Account account1 = null;account1 = personDao.select(toId);if(account2==null){throw new RuntimeException("对方卡号不存在");)if(account.getBalance()<money){throw new RuntimeException("你的余额不足");}account.setBalance(account.getBalance() - money);personDao.Update(account);account1.setBanlance(account.getBalance() + money);personDao.Update(account1); }
}
以上代码在出错后不可逆,所以需要事务控制,当报错后,对数据库的内容修改回到初始态
1. Service层实现控制事务:(案例中余额减少出现异常情况)
获得连接对象:
设置当前事务的自动提交为手动提交,开启事务
connection.setAutoCommit(false);//false参数即关闭自动提交
connection.commit();//提交事务
connection.rollback();//回滚
关闭connection问题:
解决方法1:传递connection
缺点:不能复用,容易造成接口污染
解决方法2:ThreadLocal:
单线程中,存储一个共享值
线程拥有一个类似Map的属性,键值对结构<ThreadLocal对象,值>
一个共享同一个ThreadLocal,在整个流程中任一环节可以存值或取值。
2. 利用ThreadLocal重写DBUtils并封装事务
1. ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
利用ThreadLocal类的get()和set()方法,如果connection==null,则threadLocal.set(DriverManager.getConnection)
使我们拿到的connection为同一个
2. 因为connection是连接数据库的,所以不能把connection的事务控制继续写在Service层,由于connection是在DBUtils里面拿到的,所以应该封装到DBUtils里面
3. 把Dao层的增删改查方法中的closeAll()方法中的形参Connection connection全部设置为空
4. ThreadLocal里面的ThreadLocalMap集合里面的key值是弱引用(key值是当前的threadLocal对象,value就是connection),会造成内存泄漏,所以每次关闭连接以后,通过threadLocal.remove把通过set方法放到ThreadLocalMap集合的connection移除
public class DBUtils{public static final Properties PROPERTIES = new Proerties();private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();static{InputStream is = DBUtils.class.getResourceAsStream("database.properties");PROPERTIES.load(is);class.forName(PROPERTIES.getProperties("driver"));}public static Connection getConnection(){Connection connection = threadLocal.get();if(conncetion==null){connection = DriverManager.getConnection(PROPERTIES.getProperties("url"),PROPRETIES.getProperties("user"),PROPERTIES.getProperties("password"));threadLocal.set(connection);}return connection;}public static void begin() {try {Connection connection = getConnection();connection.setAutoCommit(false);} catch (SQLException e) {e.printStackTrace();}}public static void commit(){Connection connection = null;try {connection = getConnection();connection.commit();} catch (SQLException e) {e.printStackTrace();} finally {CloseAll(connection,null,null);}}public static void rollback() throws SQLException {Connection connection = null;try {connection = getConnection();connection.rollback();} catch (SQLException e) {e.printStackTrace();} finally {CloseAll(connection,null,null);}}//把Dao层的增删改查方法中的closeAll()方法中的形参Connection connection全部设置为空public static void closeAll(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet){if(connection!=null){try {connection.close();threadLocal.remove();//关闭连接后,移除connection对象} catch (SQLException e) {e.printStackTrace();}}if(preparedStatement!=null){try {preparedStatement.close();} catch (SQLException e) {e.printStackTrace();}}if(resultSet!=null){try {resultSet.close();} catch (SQLException e) {e.printStackTrace();}}}
}
3. 把重写过的DBUtils控制事务方法添加到Service业务逻辑层的方法中
DBUtils.begin();//写在transfar()方法的开头
DBUtils.commit();//写在transfar()方法结尾
DBUtils.rollback;//写在trycatch中的catch代码里面