早在JDK 1.2的版本中就提供java.lang.ThreadLocal,,ThreadLocal為解決多線程程序的并發(fā)問(wèn)題提供了一種新的思路。使用這個(gè)工具類可以很簡(jiǎn)潔地編寫出優(yōu)美的多線程程序,。
ThreadLocal很容易讓人望文生義,,想當(dāng)然地認(rèn)為是一個(gè)“本地線程”。其實(shí),,ThreadLocal并不是一個(gè)Thread,,而是Thread的局部變量,也許把它命名為ThreadLocalVariable更容易讓人理解一些,。
當(dāng)使用ThreadLocal維護(hù)變量時(shí),,ThreadLocal為每個(gè)使用該變量的線程提供獨(dú)立的變量副本,,所以每一個(gè)線程都可以獨(dú)立地改變自己的副本,而不會(huì)影響其它線程所對(duì)應(yīng)的副本,。
從線程的角度看,,目標(biāo)變量就象是線程的本地變量,這也是類名中“Local”所要表達(dá)的意思,。
線程局部變量并不是Java的新發(fā)明,,很多語(yǔ)言(如IBM IBM XL FORTRAN)在語(yǔ)法層面就提供線程局部變量。在Java中沒(méi)有提供在語(yǔ)言級(jí)支持,,而是變相地通過(guò)ThreadLocal的類提供支持,。
所以,在Java中編寫線程局部變量的代碼相對(duì)來(lái)說(shuō)要笨拙一些,,因此造成線程局部變量沒(méi)有在Java開發(fā)者中得到很好的普及,。
ThreadLocal的接口方法
ThreadLocal類接口很簡(jiǎn)單,只有4個(gè)方法,,我們先來(lái)了解一下:
2 void set(Object value)
設(shè)置當(dāng)前線程的線程局部變量的值,。
2 public Object get()
該方法返回當(dāng)前線程所對(duì)應(yīng)的線程局部變量。
2 public void remove()
將當(dāng)前線程局部變量的值刪除,,目的是為了減少內(nèi)存的占用,,該方法是JDK 5.0新增的方法。需要指出的是,,當(dāng)線程結(jié)束后,,對(duì)應(yīng)該線程的局部變量將自動(dòng)被垃圾回收,,所以顯式調(diào)用該方法清除線程的局部變量并不是必須的操作,,但它可以加快內(nèi)存回收的速度。
2 protected ObjectinitialValue()
返回該線程局部變量的初始值,,該方法是一個(gè)protected的方法,,顯然是為了讓子類覆蓋而設(shè)計(jì)的。這個(gè)方法是一個(gè)延遲調(diào)用方法,,在線程第1次調(diào)用get()或set(Object)時(shí)才執(zhí)行,,并且僅執(zhí)行1次。ThreadLocal中的缺省實(shí)現(xiàn)直接返回一個(gè)null,。
值得一提的是,,在JDK5.0中,ThreadLocal已經(jīng)支持泛型,,該類的類名已經(jīng)變?yōu)門hreadLocal<T>,。API方法也相應(yīng)進(jìn)行了調(diào)整,新版本的API方法分別是void set(T value),、T get()以及T initialValue(),。
2.1 同一Service方法中調(diào)用多個(gè)Dao方法
可以看到,我們有一個(gè)Service方法,,在該Service方法中調(diào)用多個(gè)Dao方法,,所有在該Service方法中的的Dao都處于同一事務(wù)中。
該Service方法結(jié)束后,,提交事務(wù),;
該Service方法中有任何錯(cuò),回滾事務(wù),;
2.2 傳統(tǒng)的做法
來(lái)看下面這段偽代碼
Service層代碼:
public void serviceMethod(){
Connection conn=null;
try{
Connection conn=getConnection();
conn.setAutoCommit(false);
Dao1 dao1=new Dao1(conn);
dao1.doSomething();
Dao2 dao2=new Dao2(conn);
dao2.doSomething();
Dao3 dao3=new Dao3(conn);
dao3.doSomething();
conn.commit();
}catch(Exception e){
try{
conn.rollback();
}catch(Exception ex){}
}finally{
try{
conn.setAutoCommit(true);
}catch(Exception e){}
try{
if(conn!=null){
conn.close();
conn=null;
}
}catch(Exception e){}
}
}
|
每個(gè)Dao層的代碼:
Class Dao1{
private Connection conn=null;
public Dao1(Connection conn){
this.conn=conn;
}
public void doSomething(){
PreparedStatement pstmt=null;
try{
pstmt=conn.preparedStatement(sql);
pstmt.execute…
…
}catch(Exception e){
log.error(e,”Exeception occurred in Dao1.doSomething():”+e.getMessage,e);
}finally{
try{
if(pstmt!=null){
pstmt.close();
pstmt=null;
}
}catch(Exception e){}
}
}
}
|
如果我一個(gè)Service方法有調(diào)用一堆dao方法,,先不說(shuō)這樣寫首先破壞了OOP的封裝性原則,如果有一個(gè)dao多關(guān)了一個(gè)conn,,那就會(huì)導(dǎo)致其它的dao得到的conn為null,,這種事在這樣的寫法下由其當(dāng)你還有業(yè)務(wù)邏輯混合在一起時(shí)很容易發(fā)生。
筆者曾經(jīng)遇見過(guò)2個(gè)項(xiàng)目,,出現(xiàn)out of memory或者是connection pool has been leakage,,經(jīng)查代碼就是在每個(gè)dao中多關(guān)或者在service層中漏關(guān),或者是每個(gè)dao有自己的conntionconn=getConnection(),,然后還跑到Service層里去關(guān)這個(gè)connection(那關(guān)什么,,關(guān)個(gè)P關(guān)!),。
當(dāng)然,,如果你說(shuō)你在寫法上絕對(duì)promise絕對(duì)注意這樣的問(wèn)題不會(huì)發(fā)生,但是我們來(lái)看看下面的這種做法,,是否會(huì)比上面這個(gè)寫法更好呢,?
先來(lái)看Spring中的寫法。
大家應(yīng)該都很熟悉Spring中的寫法了,,來(lái)看一下它是怎么解決的,。
Service層
public void serviceMethod(){
try{
//aop 自動(dòng)加入connection,并且將conn.setAutoCommit(false);
dao1.doSomething();
dao2.doSomething();
dao3.doSomething();
}catch(Exception e){
//aop 自動(dòng)加入rollback
}finally{
//aop自動(dòng)加入conn.setAutoCommit(true)
//aop 自動(dòng)加入conn.close();
}
|
這邊我們不講AOP,,因?yàn)橛妙惙瓷浣Y(jié)合xml很容易將aop 自動(dòng),。。,。這些東西加入我們的代碼中去是不是,?我們只管寫dao方法,service方法,,不需要關(guān)心在哪邊commit哪邊rollback何時(shí)connection,,spring的聲明式事務(wù)會(huì)幫我們負(fù)責(zé),這種風(fēng)格我們稱為“優(yōu)雅”,,各層間耦合度極大程度上的降低,,封裝性好,。
因此,我們可以總結(jié)出下面這些好處:
2 Service層的方法只管開啟事務(wù)(如果講究點(diǎn)的還會(huì)設(shè)一個(gè)Transaction),;
2 在該Service層中的所有dao使用該service方法中開啟的事務(wù)(即connection),;
2 Dao中每次只管getCurrentConnection(獲取當(dāng)前的connection),與進(jìn)行數(shù)據(jù)處理
2 Dao層中如果發(fā)生錯(cuò)誤就拋回Service層
2 Service層中接到exception,,在catch{}中rollback,,在try{}未尾commit,在finally塊中關(guān)閉整個(gè)connection,。
這,。。,。就是我們所說(shuō)的ThreadLocal,。
舉個(gè)更實(shí)際的例子再次來(lái)說(shuō)明ThreadLocal:
我們有3個(gè)用戶訪問(wèn)同一個(gè)service方法,該service方法內(nèi)有3個(gè)dao方法為一個(gè)完整事務(wù),,那么整個(gè)web容器內(nèi)只因該有3個(gè)connection,,并且每個(gè)connection之間的狀態(tài),彼此“隔離”,。
我們下面一起來(lái)看我們?nèi)绾斡么a實(shí)現(xiàn)類似于Spring的這種做法,。
首先,根據(jù)我們的ThreadLocal的概念,,我們先聲明一個(gè)ConnectionManager的類,。
2.4 利用ThreadLocal制作ConnectionManager
public class ConnectionManager {
private static ThreadLocal tl = new ThreadLocal();
private static Connection conn = null;
public static void BeginTrans(boolean beginTrans) throws Exception {
if (tl.get() == null || ((Connection) tl.get()).isClosed()) {
conn = SingletonDBConnection.getInstance().getConnection();
conn = new ConnectionSpy(conn);
if (beginTrans) {
conn.setAutoCommit(false);
}
tl.set(conn);
}
}
public static Connection getConnection() throws Exception {
return (Connection) tl.get();
}
public static void close() throws SQLException {
try {
((Connection) tl.get()).setAutoCommit(true);
} catch (Exception e) {
}
((Connection) tl.get()).close();
tl.set(null);
}
public static void commit() throws SQLException {
try {
((Connection) tl.get()).commit();
} catch (Exception e) {
}
try {
((Connection) tl.get()).setAutoCommit(true);
} catch (Exception e) {
}
}
public static void rollback() throws SQLException {
try {
((Connection) tl.get()).rollback();
} catch (Exception e) {
}
try {
((Connection) tl.get()).setAutoCommit(true);
} catch (Exception e) {
}
}
}
|
2.5 利用ThreadLocal改造Service與Dao層
Service層(注意紅色標(biāo)粗-好粗yeah,的地方)
package sky.org.service.impl;
public class StudentServiceImpl implements StudentService {
public void addStudent(Student std) throws Exception {
StudentDAO studentDAO = new StudentDAOImpl();
ClassRoomDAO classRoomDAO = new ClassRoomDAOImpl();
try {
ConnectionManager.BeginTrans(true);
studentDAO.addStudent(std);
classRoomDAO
.addStudentClassRoom(std.getClassRoomId(), std.getsNo());
ConnectionManager.commit();
} catch (Exception e) {
try {
ConnectionManager.rollback();
} catch (Exception de) {
}
throw new Exception(e);
}finally {
try {
ConnectionManager.close();
} catch (Exception e) {
}
}
}
}
|
Look,,如果我把上述標(biāo)粗(沒(méi)有加紅色)的地方,,全部用AOP的方式從這塊代碼的外部“切”進(jìn)去。,。,。是不是一個(gè)Spring里的Service方法就誕生了?
下面來(lái)看一個(gè)完整的例子
2.6 使用ThreadLocal分離Service,、DAO層
先來(lái)看表結(jié)構(gòu):
T_Student表
T_ClassRoom表
T_Student_ClassRoom表
需求:
很簡(jiǎn)單,T_ClassRoom表里已經(jīng)有值了,,在插入T_Student表的數(shù)據(jù)時(shí)同時(shí)要給這個(gè)學(xué)生分配一個(gè)班級(jí)并且插入T_Student_ClassRoom表,,這就是一個(gè)事務(wù),這兩步中有任何一步出錯(cuò),,事務(wù)必須回滾,。
看來(lái)工程的結(jié)構(gòu)吧:
下面開始放出所有源代碼:
package sky.org.util.db;
import java.sql.*;
public class ConnectionManager {
private static ThreadLocal tl = new ThreadLocal();
private static Connection conn = null;
public static void BeginTrans(boolean beginTrans) throws Exception {
if (tl.get() == null || ((Connection) tl.get()).isClosed()) {
conn = DBConnection.getInstance().getConnection();
conn = new ConnectionSpy(conn);
if (beginTrans) {
conn.setAutoCommit(false);
}
tl.set(conn);
}
}
public static Connection getConnection() throws Exception {
return (Connection) tl.get();
}
public static void close() throws SQLException {
try {
((Connection) tl.get()).setAutoCommit(true);
} catch (Exception e) {
}
((Connection) tl.get()).close();
tl.set(null);
}
public static void commit() throws SQLException {
try {
((Connection) tl.get()).commit();
} catch (Exception e) {
}
try {
((Connection) tl.get()).setAutoCommit(true);
} catch (Exception e) {
}
}
public static void rollback() throws SQLException {
try {
((Connection) tl.get()).rollback();
} catch (Exception e) {
}
try {
((Connection) tl.get()).setAutoCommit(true);
} catch (Exception e) {
}
}
}
|
package sky.org.util.db;
public class DBConnection {
private static DBConnection instance = null;
private static String driverClassName = null;
private static String connectionUrl = null;
private static String userName = null;
private static String password = null;
private static Connection conn = null;
private static Properties jdbcProp = null;
private DBConnection() {
}
private static Properties getConfigFromPropertiesFile() throws Exception {
Properties prop = null;
prop = JdbcProperties.getPropObjFromFile();
return prop;
}
private static void initJdbcParameters(Properties prop) {
driverClassName = prop.getProperty(Constants.DRIVER_CLASS_NAME);
connectionUrl = prop.getProperty(Constants.CONNECTION_URL);
userName = prop.getProperty(Constants.DB_USER_NAME);
password = prop.getProperty(Constants.DB_USER_PASSWORD);
}
private static void createConnection() throws Exception {
Class.forName(driverClassName);
conn = DriverManager.getConnection(connectionUrl, userName, password);
}
public static Connection getConnection() throws Exception {
return conn;
}
public synchronized static DBConnection getInstance()throws Exception{
if (instance == null) {
jdbcProp = getConfigFromPropertiesFile();
instance = new DBConnection();
}
initJdbcParameters(jdbcProp);
createConnection();
return instance;
}
}
|
package sky.org.util.db;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.*;
public class JdbcProperties {
private static Log logger = LogFactory.getLog(JdbcProperties.class);
public static Properties getPropObjFromFile() {
Properties objProp = new Properties();
ClassLoader classLoader = Thread.currentThread()
.getContextClassLoader();
URL url = classLoader.getResource(Constants.JDBC_PROPERTIES_FILE);
if (url == null) {
classLoader = ClassLoader.getSystemClassLoader();
url = classLoader.getResource(Constants.JDBC_PROPERTIES_FILE);
}
File file = new File(url.getFile());
InputStream inStream = null;
try {
inStream = new FileInputStream(file);
objProp.load(inStream);
} catch (FileNotFoundException e) {
objProp = null;
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (inStream != null) {
inStream.close();
inStream = null;
}
} catch (Exception e) {
}
}
return objProp;
}
}
|
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.databaseURL=jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8
jdbc.username=mysql
jdbc.password=password_1
|
package sky.org.service;
import java.util.List;
import java.util.Vector;
import sky.org.bean.*;
public interface StudentService {
public void addStudent(Student std) throws Exception;
}
|
package sky.org.service.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import sky.org.util.db.ConnectionManager;
import sky.org.util.*;
import sky.org.bean.*;
import sky.org.dao.*;
import sky.org.dao.impl.*;
import sky.org.service.*;
public class StudentServiceImpl implements StudentService {
public void addStudent(Student std) throws Exception {
StudentDAO studentDAO = new StudentDAOImpl();
ClassRoomDAO classRoomDAO = new ClassRoomDAOImpl();
try {
ConnectionManager.BeginTrans(true);
studentDAO.addStudent(std);
classRoomDAO
.addStudentClassRoom(std.getClassRoomId(), std.getsNo());
ConnectionManager.commit();
} catch (Exception e) {
try {
ConnectionManager.rollback();
} catch (Exception de) {
}
throw new Exception(e);
} finally {
try {
ConnectionManager.close();
} catch (Exception e) {
}
}
}
}
|
package sky.org.dao;
import java.util.HashMap;
import java.util.List;
public interface ClassRoomDAO {
public void addStudentClassRoom(String roomId, String sNo) throws Exception;
}
|
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) {
}
}
}
}
|
package sky.org.dao;
import java.util.*;
import sky.org.bean.Student;
public interface StudentDAO {
public void addStudent(Student std) throws Exception;
}
|
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) {
}
}
}
}
|
package sky.org.dao.impl;
public class StudentDAOSql {
public final static String ADD_STUDENT = "insert into t_student(sno, sname, sage, gender,
sbirth)values(?,?,?,?,?)";
}
|
package sky.org.dao.impl;
public class ClassRoomDAOSql {
public static String ADD_STUDENT_CLASSROOM = "insert into
t_student_classroom(room_id,sno)values(?,?)";
}
|
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;
}
}
|
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;
}
}
|
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在事務(wù)操作中也支持ThreadLocal的作法,,我們這邊指的是不用Spring去做代理,,而直接用Hibernate,。即:
Service Method{
hbDAO1.doSomething();
hbDAO2.doSomething();
hbDAO3.doSomething();
。,。,。
}
|
Hibernate版本3后增加了新特性,即getCurrentSession(),。
我們傳統(tǒng)的做法是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塊中正確關(guān)閉session,但是,,如果我們也遇上了這樣的case即:
Service Method{
hbDAO1.doSomething();
hbDAO2.doSomething();
hbDAO3.doSomething();
,。。,。
}
|
這時(shí),,我們?nèi)绻玫氖莖penSession,應(yīng)該怎么辦,?
解決方案一:
自己用ThreadLocal模式寫一個(gè)SessionManagement類,,來(lái)維護(hù)這個(gè)session。
解決方案二:
把在Service方法中打開的session,,傳到每個(gè)dao方法中,,使每個(gè)dao方法使用同一個(gè)session,最后在Service方法中去關(guān)閉它(很爛的做法),。
下面我們來(lái)看看Hibernate自身提供的getCurrentSession()的做法吧
要使用這個(gè)getCurrentSession,,你的hibernate的設(shè)置必須如下(紅色加粗部分顯示-就喜歡粗J):
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate./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);
}
}
|
而你的每個(gè)DAO方法中的代碼是這樣實(shí)現(xiàn)的:
public void addUser(TUser user) throws Exception {
SessionFactory factory = HibernateUtil.getInstance()
.getSessionFactory();
Session session = factory.getCurrentSession();
session.save(user);
}
|
是不是很方便的哈。
嚴(yán)重注意下面3點(diǎn):
2 openSession一旦被調(diào)用,,必須且一定要在finally塊中close,,要不然你就等著out of memory吧;
2 如果你使用的是getCurrentSession,,那么你不能在finally塊中調(diào)用”session.close()”,,不行你可以在finally塊中用try-catch把session.close();包起來(lái),然后在catch{}塊中拋出這個(gè)exception,,這個(gè)exception將會(huì)是:sessionhas been already closed,。
因?yàn)椋?/p>
l 如果你用的是getCurrentSession,那么它在session.commit()或者是session.rollback()時(shí)就已經(jīng)調(diào)用了一次session.close()了,,因此你只要正確放置session.commit()與rollback()即可,。
l 你必須在finally塊中調(diào)用”ThreadLocalSessionContext.unbind(factory);”,以使得當(dāng)前的事務(wù)結(jié)束時(shí)把session(即dbconnection)還回db connection pool中
2 如果你使用的是getCurrentSession,,那么就算你是一個(gè)簡(jiǎn)單的select語(yǔ)句,,也必須包含在:
tran = session.beginTransaction();
//your select hibernate query
tran.commit();
|
這樣的事務(wù)塊中,要不然它將會(huì)拋出這樣的一個(gè)錯(cuò)誤:
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()結(jié)束的,,如果,,你把你的hibernate查詢移到了tran=session.beginTransaction的上面,。。,。就會(huì)拋上述這個(gè)錯(cuò)誤,。
getCurrentSession非常好,不需要我們自己寫ThreadLocal只需要在hibernate.cfg的配置文件中聲音一下就可以獲得ThreadLocal的好處,,便于我們劃分我們的程序的層次與封裝,,帶也帶來(lái)了一定的性能問(wèn)題。
特別是“如果你使用的是getCurrentSession,,那么就算你是一個(gè)簡(jiǎn)單的select語(yǔ)句,,也必須包含在事務(wù)塊中”。這給我們帶來(lái)了很大的問(wèn)題,。
因此,,本人建議,在碰到如果:
1. 一個(gè)service方法中只有單個(gè)dao操作且此操作是一個(gè)select類的操作,,請(qǐng)使用openSession,,并且即時(shí)在finally塊中關(guān)閉它;
2. 如果一個(gè)service方法中涉及到多個(gè)dao操作,,請(qǐng)一定使用getCurrentSession,;
3. 如果一個(gè)service方法中混合著select操作,delete, update, insert操作,。請(qǐng)按照下述原則:
1) 將屬于select的操作,,單獨(dú)做成一個(gè)dao方法,該dao使用openSession并且在finally塊中及時(shí)關(guān)閉session,該dao只需要返回一個(gè)java的object如:List<Student>即可,,如果出錯(cuò)將exception拋回給調(diào)用它的service方法,。
2) 對(duì)于其它的delete, insert, update的dao操作,請(qǐng)使用getCurrentSession,。
4. 忌諱,,把select類的操作放在“事務(wù)”中;