04_ThreadLocal整合事务操作
文章导读:
本文主要讲解了如何在没有框架情况下如何解决Dao的事务问题, 重点理解Connection存放到WeakReference中为什么垃圾回收的时候Connection不回收
视频与源码下载:http://edu.51cto.com/lecturer/index/user_id-9166337.html (代码在视频的附件中)
先准备下测试的环境:db.sql、model、dao、Service
db.sql 代码如下(只有一张表)
1 /*删除商城数据库,如果存在*/ 2 drop database if exists demo; 3 create database demo default character set utf8; 4 use demo; 5 drop table if exists book; 6 7 create table book 8 ( 9 id int not null auto_increment, 10 name varchar(20), 11 price decimal(8,2), 12 remark longtext, 13 storage int, 14 primary key (id) 15 ); 16 17 /* 图书测试用例 */ 18 insert into book (name,price,remark,storage) values ('Java编程思想',99.99,'Java学习必读经典,殿堂级著作!赢得了全球程序员的广泛赞誉',200); 19 insert into book (name,price,remark,storage) values ('HTML5与CSS3权威指南',49.99,'一本前端应用开发的书籍',300); 20 insert into book (name,price,remark,storage) values ('Android从入门到精通',79.99,'移动互联网书籍',100); 21 22 select * from book;
执行SQL语句结果如下:
根据表生成Java的model(模型)
1 public class Book { 2 3 private Integer id; 4 private String name; 5 private Double price; 6 private String remark; 7 public Integer storage; 8 9 // ..... 省略了 set get方法 10 }
编写一个BookDaoImpl完成数据的插入功能
1 public class BookDaoImpl { 2 3 public int save(Book book) { 4 String sql = "insert into book (name,price,remark,storage) values (?,?,?,?)"; 5 Connection conn = null; 6 PreparedStatement pre = null; 7 try { 8 conn = JdbcUtils.getConnection(); 9 System.out.println("BookDaoImpl conn:" + conn); 10 pre = conn.prepareStatement(sql); 11 pre.setString(1, book.getName()); 12 pre.setDouble(2, book.getPrice()); 13 pre.setString(3, book.getRemark()); 14 pre.setInt(4, book.getStorage()); 15 return pre.executeUpdate(); 16 } catch (SQLException e) { 17 throw new RuntimeException(e); 18 } finally { 19 try { 20 pre.close(); 21 } catch (SQLException e) { 22 throw new RuntimeException(e); 23 } 24 } 25 } 26 }
编写一个AuthorDaoImpl完成作者插入功能(主要是为了测试事务, 如果两个方法有一个方法执行失败,则事务要回滚)
public class AuthorDaoImpl { public int save() { Connection conn = JdbcUtils.getConnection(); System.out.println("AuthorDaoImpl conn:" + conn); System.out.println("----另一张表插入作者信息-----"); // Integer.parseInt("xxxx"); return 0; } }
最后编写一个Service把需要的事务操作整合起来, Conn的创建与销毁都在Service中.
1 public class BookServiceImpl { 2 3 private BookDaoImpl bookDaoImpl = new BookDaoImpl(); 4 private AuthorDaoImpl authorDaoImpl = new AuthorDaoImpl(); 5 6 public static void main(String[] args) { 7 BookServiceImpl serviceImpl=new BookServiceImpl(); 8 serviceImpl.save(); 9 System.out.println("---------------------over------------------------"); 10 } 11 12 // 业务逻辑完成之后调用数据访问层,来实现数据入库 13 public void save(){ 14 Connection conn = null; 15 try{ 16 conn = JdbcUtils.getConnection(); 17 // 手动提交事务 18 conn.setAutoCommit(false); 19 // 事务应该在业务逻辑层创建,包含所有事务的dao 20 bookDaoImpl.save(new Book(null, "测试书籍", 45.67, "我是备注", 100)); 21 System.gc(); // 手动调用垃圾回收并不会导致弱引用Connection关闭,因为当前处于Open状态 22 authorDaoImpl.save(); 23 conn.commit(); 24 }catch (Exception e) { 25 try { 26 System.out.println("conn.rollback()"); 27 conn.rollback(); 28 } catch (SQLException e1) { 29 throw new RuntimeException(e1); 30 } 31 throw new RuntimeException(e); 32 }finally{ 33 JdbcUtils.closeConnection(); // 必须要关闭,否则不会被销毁.因为Connecion默认是open状态 34 } 35 } 36 }
测试结果如下(通过测试我们会发现,两个Dao中使用的是同一个事务,如果出错事务将会回滚,在测试的时候会发现如果调用的gc也不会导致弱引用的Connection回收, 为什么呢,因为Connection是Open状态,只要没有关闭是不会被回收的,理解相信的原理可以参考当前文章的视频教程)