通向架构师的道路(第七天)之漫谈使用 ThreadLocal 改进你的层次的划分 ( 下 )

2018 年 2 月 8 日 ImportNew

(点击上方公众号,可快速关注)


来源:袁鸣凯 ,

blog.csdn.net/lifetragedy/article/details/7751059


2.6.8 ClassRoomDAOImpl类


package sky.org.dao.impl;

  

import java.sql.*;

import java.util.*;

import sky.org.dao.ClassRoomDAO;

import sky.org.util.db.ConnectionManager;

 

public class ClassRoomDAOImpl implements ClassRoomDAO {

 

         public void addStudentClassRoom(String roomId, String sNo) throws Exception {

 

                   Connection conn = null;

 

                   PreparedStatement pstmt = null;

 

                   try {

 

                            conn = ConnectionManager.getConnection();

 

                            pstmt = conn

 

                                               .prepareStatement(ClassRoomDAOSql.ADD_STUDENT_CLASSROOM);

 

                            pstmt.setString(1, roomId);

 

                            pstmt.setString(2, sNo);

 

                            pstmt.executeUpdate();

 

                   } catch (Exception e) {

 

                            throw new Exception("addStudentClassRoom:" + e.getMessage(), e);

 

                   } finally {

 

                            try {

 

                                     if (pstmt != null) {

 

                                               pstmt.close();

 

                                               pstmt = null;

 

                                     }

 

                            } catch (Exception e) {

 

                            }

 

                   }

 

         }

 

}


2.6.9 StudentDAO接口


package sky.org.dao;

 

import java.util.*;

 

import sky.org.bean.Student;

 

public interface StudentDAO {

 

         public void addStudent(Student std) throws Exception;

 

}


2.6.10 StudentDAOImpl类


package sky.org.dao.impl;

 

  

 

import java.sql.*;

 

import javax.sql.*;

 

  

 

import org.apache.commons.logging.Log;

 

import org.apache.commons.logging.LogFactory;

 

  

 

import sky.org.bean.Student;

 

import sky.org.dao.StudentDAO;

 

import sky.org.util.db.ConnectionManager;

 

  

 

import java.util.List;

 

import java.util.ArrayList;

 

import java.util.Vector;

 

import java.text.*;

 

import sky.org.util.StringUtil;

 

  

 

public class StudentDAOImpl implements StudentDAO {

 

         private Log logger = LogFactory.getLog(this.getClass());

 

  

 

         public void addStudent(Student std) throws Exception {

 

                   Connection conn = null;

 

                   PreparedStatement pstmt = null;

 

                   try {

 

                            conn = ConnectionManager.getConnection();

 

                            pstmt = conn.prepareStatement(StudentDAOSql.ADD_STUDENT);

 

                            pstmt.setString(1, std.getsNo());

 

                            pstmt.setString(2, std.getsName());

 

                            pstmt.setString(3, std.getsAge());

 

                            pstmt.setString(4, std.getGender());

 

                            pstmt.setDate(5, StringUtil.convertStrToDate(std.getSbirth()));

 

  

 

                            pstmt.executeUpdate();

 

                   } catch (Exception e) {

 

                            throw new Exception("addStudent:" + e.getMessage(), e);

 

                   } finally {

 

                            try {

 

                                     if (pstmt != null) {

 

                                               pstmt.close();

 

                                               pstmt = null;

 

                                     }

 

                            } catch (Exception e) {

 

                            }

                   }

         }

 

  

 

         public void delStudent(String sNo) throws Exception {

 

                   Connection conn = null;

 

                   PreparedStatement pstmt = null;

 

                   try {

 

                            conn = ConnectionManager.getConnection();

 

                            pstmt = conn.prepareStatement(StudentDAOSql.DEL_STUDENT);

 

                            pstmt.setString(1, sNo);

 

                            pstmt.executeUpdate();

 

                   } catch (Exception e) {

 

                            throw new Exception("delStudent:" + e.getMessage(), e);

 

                   } finally {

 

                            try {

 

                                     if (pstmt != null) {

 

                                               pstmt.close();

 

                                               pstmt = null;

 

                                     }

 

                            } catch (Exception e) {

 

                            }

 

                   }

 

         }

 

}


2.6.11 StudentDAOSql类


package sky.org.dao.impl;

 

public class StudentDAOSql {

 

public final static String ADD_STUDENT = "insert into t_student(sno, sname, sage, gender,

 

sbirth)values(?,?,?,?,?)";

 

}


2.6.12 ClassRoomDAOSql类


package sky.org.dao.impl;

 

public class ClassRoomDAOSql {

 

         public static String ADD_STUDENT_CLASSROOM = "insert into

t_student_classroom(room_id,sno)values(?,?)";

 

}


2.6.13 ClassRoom 类


package sky.org.bean;

 

import java.io.*;

 

 

public class ClassRoom implements Serializable {

 

         private String roomId = "";

         private String roomName = "";

 

  

         public String getRoomId() {

                   return roomId;

 

         }

 

         public void setRoomId(String roomId) {

 

                   this.roomId = roomId;

 

         }

 

         public String getRoomName() {

                   return roomName;

 

         }

 

         public void setRoomName(String roomName) {

                   this.roomName = roomName;

 

         }

 

}


2.6.14 Student类


package sky.org.bean;

import java.io.*;

 

public class Student implements Serializable {

 

         public String getsNo() {

                   return sNo;

         }

 

         public void setsNo(String sNo) {

 

                   this.sNo = sNo;

 

         }

 

 

         public String getsName() {

                   return sName;

         }

 

         public void setsName(String sName) {

 

                   this.sName = sName;

         }

 

         public String getsAge() {

                   return sAge;

         }

 

  

 

         public void setsAge(String sAge) {

 

                   this.sAge = sAge;

 

         }

 

  

 

         public String getGender() {

                   return gender;

         }

 

         public void setGender(String gender) {

                   this.gender = gender;

         }

 

 

         private String sNo = "";

         private String sName = "";

         private String sAge = "";

         private String gender = "";

         private String sbirth = "";

         private String classRoomId = "";

         private String classRoomName = "";

 

 

         public String getClassRoomId() {

 

                   return classRoomId;

 

         }

 

         public void setClassRoomId(String classRoomId) {

 

                   this.classRoomId = classRoomId;

         }

 

         public String getClassRoomName() {

                   return classRoomName;

 

         }

 

  

 

        public void setClassRoomName(String classRoomName) {

                   this.classRoomName = classRoomName;

 

         }

 

 

         public String getSbirth() {

 

                   return sbirth;

 

         }

 

 

         public void setSbirth(String sbirth) {

                   this.sbirth = sbirth;

         }

}


2.6.15 StudentCRUD类(运行主类)


package sky.org.test;

 

import sky.org.bean.Student;

 

import sky.org.service.StudentService;

 

import sky.org.service.impl.StudentServiceImpl;

 

public class StudentCRUD {

 

         public void addStudent() throws Exception {

 

                   StudentService stdService = new StudentServiceImpl();

 

                   Student std = new Student();

 

                   std.setsNo("101");

 

                   std.setsName("abc");

 

                   std.setSbirth("1977/01/01");

 

                   std.setsAge("35");

 

                   std.setGender("m");

 

                   std.setClassRoomId("1");

 

                   std.setClassRoomName("class1");

 

                   stdService.addStudent(std);

 

         }

 

  

 

         public static void main(String[] args) {

 

                   StudentCRUD testStudentCRUD = new StudentCRUD();

 

                   try {

 

                            testStudentCRUD.addStudent();

 

                   } catch (Exception e) {

 

                            e.printStackTrace();

 

                            System.exit(-1);

 

                   }

 

         }

 

}


三、Hibernate里的ThreadLocal


Hibernate在事务操作中也支持ThreadLocal的作法,我们这边指的是不用Spring去做代理,而直接用Hibernate。即:


Service Method{

 

hbDAO1.doSomething();

 

hbDAO2.doSomething();

 

hbDAO3.doSomething();

 

。。。

 

}


Hibernate版本3后增加了新特性,即getCurrentSession()。


3.1 getCurrentSession与openSession的区别


3.1.1 openSession


我们传统的做法是openSession即:


public void testUser() throws Exception {

 

                   Transaction tran = null;

 

                   SessionFactory factory = null;

 

                   UserDAO userDAO = new UserDAOImpl();

 

                   try {

 

                            factory = HibernateUtil.getInstance().getSessionFactory();

 

                            Session session = factory.openSession();

 

                            tran = session.beginTransaction();

 

                            TUser testUser = new TUser();

 

                        testUser.setId(new Integer(i));

 

                            testUser.setName("abc");

 

                            userDAO.addUser(testUser);

 

                            tran.commit();

 

                   } catch (Exception e) {

 

                            tran.rollback();

 

                            throw new Exception(e);

 

                   } finally {

 

                            try{

 

    if(session!=null){

 

    session.close();

    session=null();

 

}

 

}catch(Excepton e){}

 

                   }

         }


这样做,能够保证我们每次在finally块中正确关闭session,但是,如果我们也遇上了这样的case即:


Service Method{

 

hbDAO1.doSomething();

 

hbDAO2.doSomething();

 

hbDAO3.doSomething();

 

。。。

 

}


这时,我们如果用的是openSession,应该怎么办?


解决方案一:


自己用ThreadLocal模式写一个SessionManagement类,来维护这个session。


解决方案二:


把在Service方法中打开的session,传到每个dao方法中,使每个dao方法使用同一个session,最后在Service方法中去关闭它(很烂的做法)。


下面我们来看看Hibernate自身提供的getCurrentSession()的做法吧


3.1.2 getCurrentSession


要使用这个getCurrentSession,你的hibernate的设置必须如下(红色加粗部分显示-就喜欢粗J):


<?xml version='1.0' encoding='UTF-8'?>

 

<!DOCTYPE hibernate-configuration PUBLIC

 

          "-//Hibernate/Hibernate Configuration DTD 3.0//EN"

 

          "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

 

<hibernate-configuration>

 

         <session-factory>

 

                   <property name="connection.url">

 

                            jdbc:oracle:thin:@localhost:1521:myorcl

 

                   </property>

 

                   <property name="dialect">

 

                            org.hibernate.dialect.Oracle9Dialect

 

                   </property>

 

                   <property name="connection.username">abc</property>

 

                   <property name="connection.password">abc</property>

 

                   <property name="connection.driver_class">

 

                            oracle.jdbc.OracleDriver

 

                   </property>

 

                   <property name="show_sql">true</property>

 

                   <property name="hibernate.hbm2ddl.auto">update</property>

 

                   <property name="hibernate.current_session_context_class">thread</property>

 

                   <mapping resource="com/cts/testhb/model/TUser.hbm.xml" />

 

         </session-factory>

 

</hibernate-configuration>


然后上述代码将变成如下的样子:


public void testUser() throws Exception {

 

                   Transaction tran = null;

 

                   SessionFactory factory = null;

 

                   UserDAO userDAO = new UserDAOImpl();

 

                   try {

 

                            factory = HibernateUtil.getInstance().getSessionFactory();

 

                            Session session = factory.getCurrentSession();

 

                            tran = session.beginTransaction();

 

                            for (int i = 0; i < 100; i++) {

 

                                     TUser testUser = new TUser();

 

                                     testUser.setId(new Integer(i));

 

                                     testUser.setName("abc");

 

                                     userDAO.addUser(testUser);

 

                            }

 

                            tran.commit();

 

                   } catch (Exception e) {

 

                            tran.rollback();

 

                            throw new Exception(e);

 

                   } finally {

 

                            ThreadLocalSessionContext.unbind(factory);

 

                   }

 

         }


而你的每个DAO方法中的代码是这样实现的:


public void addUser(TUser user) throws Exception {

 

         SessionFactory factory = HibernateUtil.getInstance()

 

                            .getSessionFactory();

 

         Session session = factory.getCurrentSession();

 

         session.save(user);

 

}


是不是很方便的哈。


3.1.3 openSession与getCurrentSession的区别


严重注意下面几点:


  • openSession一旦被调用,必须且一定要在finally块中close,要不然你就等着out of memory吧;

  • 如果你使用的是getCurrentSession,那么你不能在finally块中调用”session.close()”,不行你可以在finally块中用try-catch把session.close();包起来,然后在catch{}块中抛出这个exception,这个exception将会是:sessionhas been already closed。


因为:


l   如果你用的是getCurrentSession,那么它在session.commit()或者是session.rollback()时就已经调用了一次session.close()了,因此你只要正确放置session.commit()与rollback()即可。


l   你必须在finally块中调用”ThreadLocalSessionContext.unbind(factory);”,以使得当前的事务结束时把session(即dbconnection)还回db connection pool中


如果你使用的是getCurrentSession,那么就算你是一个简单的select语句,也必须包含在:


tran = session.beginTransaction();

 

//your select hibernate query

 

tran.commit();


这样的事务块中,要不然它将会抛出这样的一个错误:


NoHibernate Session bound to thread, and configuration does not allow creation ofnon-transactional


看下面的例子:


try {

 

                    factory = HibernateUtil.getInstance().getSessionFactory();

 

                    Session session = factory.getCurrentSession();

 

                    tran = session.beginTransaction();

 

                    TUser testUser = userDAO.getUserByID("1");

 

                    log.info("user id===="+testUser.getId()+"  user name===="+testUser.getName());

 

                    tran.commit();

 

           } catch (Exception e) {

 

                    tran.rollback();

 

                    throw new Exception(e);

 

           } finally {

 

                    ThreadLocalSessionContext.unbind(factory);

 

                   }


可以看到我们的查询是被tran=session.beginTransaction一直到tran.commit()或者是tran.rollback()结束的,如果,你把你的hibernate查询移到了tran=session.beginTransaction的上面。。。就会抛上述这个错误。


3.1.4 getCurrentSession带来的问题


getCurrentSession非常好,不需要我们自己写ThreadLocal只需要在hibernate.cfg的配置文件中声音一下就可以获得ThreadLocal的好处,便于我们划分我们的程序的层次与封装,带也带来了一定的性能问题。


特别是“如果你使用的是getCurrentSession,那么就算你是一个简单的select语句,也必须包含在事务块中”。这给我们带来了很大的问题。


因此,本人建议,在碰到如果:


  1. 一个service方法中只有单个dao操作且此操作是一个select类的操作,请使用openSession,并且即时在finally块中关闭它;

  2. 如果一个service方法中涉及到多个dao操作,请一定使用getCurrentSession;

  3. 如果一个service方法中混合着select操作,delete, update, insert操作。请按照下述原则:

  4. 将属于select的操作,单独做成一个dao方法,该dao使用openSession并且在finally块中及时关闭session,该dao只需要返回一个java的object如:List<Student>即可,如果出错将exception抛回给调用它的service方法。

  5. 对于其它的delete, insert, update的dao操作,请使用getCurrentSession。

  6. 忌讳,把select类的操作放在“事务”中。


系列


看完本文有收获?请转发分享给更多人

关注「ImportNew」,提升Java技能

登录查看更多
0

相关内容

一份简明有趣的Python学习教程,42页pdf
专知会员服务
76+阅读 · 2020年6月22日
【经典书】Python数据数据分析第二版,541页pdf
专知会员服务
193+阅读 · 2020年3月12日
算法与数据结构Python,369页pdf
专知会员服务
162+阅读 · 2020年3月4日
【阿里技术论文】AliMe KBQA:阿里小蜜中的结构化知识问答
专知会员服务
82+阅读 · 2019年12月14日
【电子书】C++ Primer Plus 第6版,附PDF
专知会员服务
87+阅读 · 2019年11月25日
手把手教你用Python实现“坦克大战”,附详细代码!
机器学习算法与Python学习
11+阅读 · 2019年6月8日
Pupy – 全平台远程控制工具
黑白之道
43+阅读 · 2019年4月26日
LeetCode的C++ 11/Python3 题解及解释
专知
16+阅读 · 2019年4月13日
实战 | 用Python做图像处理(二)
七月在线实验室
17+阅读 · 2018年5月25日
Xgboost算法——Kaggle案例
R语言中文社区
13+阅读 · 2018年3月13日
Keras】基于SegNet和U-Net的遥感图像语义分割
全球人工智能
11+阅读 · 2018年1月22日
【干货】--基于Python的文本情感分类
R语言中文社区
5+阅读 · 2018年1月5日
Python3爬虫之入门和正则表达式
全球人工智能
7+阅读 · 2017年10月9日
强化学习 cartpole_a3c
CreateAMind
9+阅读 · 2017年7月21日
Arxiv
4+阅读 · 2018年5月24日
Arxiv
4+阅读 · 2018年5月10日
Arxiv
6+阅读 · 2018年3月28日
Arxiv
4+阅读 · 2018年3月19日
VIP会员
相关VIP内容
一份简明有趣的Python学习教程,42页pdf
专知会员服务
76+阅读 · 2020年6月22日
【经典书】Python数据数据分析第二版,541页pdf
专知会员服务
193+阅读 · 2020年3月12日
算法与数据结构Python,369页pdf
专知会员服务
162+阅读 · 2020年3月4日
【阿里技术论文】AliMe KBQA:阿里小蜜中的结构化知识问答
专知会员服务
82+阅读 · 2019年12月14日
【电子书】C++ Primer Plus 第6版,附PDF
专知会员服务
87+阅读 · 2019年11月25日
相关资讯
手把手教你用Python实现“坦克大战”,附详细代码!
机器学习算法与Python学习
11+阅读 · 2019年6月8日
Pupy – 全平台远程控制工具
黑白之道
43+阅读 · 2019年4月26日
LeetCode的C++ 11/Python3 题解及解释
专知
16+阅读 · 2019年4月13日
实战 | 用Python做图像处理(二)
七月在线实验室
17+阅读 · 2018年5月25日
Xgboost算法——Kaggle案例
R语言中文社区
13+阅读 · 2018年3月13日
Keras】基于SegNet和U-Net的遥感图像语义分割
全球人工智能
11+阅读 · 2018年1月22日
【干货】--基于Python的文本情感分类
R语言中文社区
5+阅读 · 2018年1月5日
Python3爬虫之入门和正则表达式
全球人工智能
7+阅读 · 2017年10月9日
强化学习 cartpole_a3c
CreateAMind
9+阅读 · 2017年7月21日
Top
微信扫码咨询专知VIP会员