日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2024/4/14 编程问答 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 通向架构师的道路(第七天)之漫谈使用ThreadLocal改进你的层次的划分 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、什么是ThreadLocal

早在JDK 1.2的版本中就提供Java.lang.ThreadLocal,ThreadLocal為解決多線程程序的并發問題提供了一種新的思路。使用這個工具類可以很簡潔地編寫出優美的多線程程序。

ThreadLocal很容易讓人望文生義,想當然地認為是一個“本地線程”。其實,ThreadLocal并不是一個Thread,而是Thread的局部變量,也許把它命名為ThreadLocalVariable更容易讓人理解一些。

當使用ThreadLocal維護變量時,ThreadLocal為每個使用該變量的線程提供獨立的變量副本,所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應的副本。

從線程的角度看,目標變量就象是線程的本地變量,這也是類名中“Local”所要表達的意思。

線程局部變量并不是Java的新發明,很多語言(如IBM IBM XL FORTRAN)在語法層面就提供線程局部變量。在Java中沒有提供在語言級支持,而是變相地通過ThreadLocal的類提供支持。

所以,在Java中編寫線程局部變量的代碼相對來說要笨拙一些,因此造成線程局部變量沒有在Java開發者中得到很好的普及。

ThreadLocal的接口方法

ThreadLocal類接口很簡單,只有4個方法,我們先來了解一下:

2??void set(Object value)

設置當前線程的線程局部變量的值。

2??public Object get()

該方法返回當前線程所對應的線程局部變量。

2??public void remove()

將當前線程局部變量的值刪除,目的是為了減少內存的占用,該方法是JDK 5.0新增的方法。需要指出的是,當線程結束后,對應該線程的局部變量將自動被垃圾回收,所以顯式調用該方法清除線程的局部變量并不是必須的操作,但它可以加快內存回收的速度。

2??protected ObjectinitialValue()

返回該線程局部變量的初始值,該方法是一個protected的方法,顯然是為了讓子類覆蓋而設計的。這個方法是一個延遲調用方法,在線程第1次調用get()或set(Object)時才執行,并且僅執行1次。ThreadLocal中的缺省實現直接返回一個null。

值得一提的是,在JDK5.0中,ThreadLocal已經支持泛型,該類的類名已經變為ThreadLocal<T>。API方法也相應進行了調整,新版本的API方法分別是void set(T value)、T get()以及T initialValue()。

一、來看一個實際案例

2.1?同一Service方法中調用多個Dao方法

可以看到,我們有一個Service方法,在該Service方法中調用多個Dao方法,所有在該Service方法中的的Dao都處于同一事務中。

該Service方法結束后,提交事務;

該Service方法中有任何錯,回滾事務;

2.2?傳統的做法

來看下面這段偽代碼

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){}

}

}

每個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){}

}

}

}

如果我一個Service方法有調用一堆dao方法,先不說這樣寫首先破壞了OOP的封裝性原則,如果有一個dao多關了一個conn,那就會導致其它的dao得到的conn為null,這種事在這樣的寫法下由其當你還有業務邏輯混合在一起時很容易發生。

筆者曾經遇見過2個項目,出現out of memory或者是connection pool has been leakage,經查代碼就是在每個dao中多關或者在service層中漏關,或者是每個dao有自己的conntionconn=getConnection(),然后還跑到Service層里去關這個connection(那關什么,關個P關!)。

當然,如果你說你在寫法上絕對promise絕對注意這樣的問題不會發生,但是我們來看看下面的這種做法,是否會比上面這個寫法更好呢?

2.3 Spring中的做法

先來看spring中的寫法。

大家應該都很熟悉Spring中的寫法了,來看一下它是怎么解決的。

Service層

public void serviceMethod(){

try{

??? //aop 自動加入connection,并且將conn.setAutoCommit(false);

dao1.doSomething();

dao2.doSomething();

dao3.doSomething();

}catch(Exception e){

??? //aop 自動加入rollback

}finally{

??? //aop自動加入conn.setAutoCommit(true)

??? //aop 自動加入conn.close();

}

這邊我們不講AOP,因為用類反射結合xml很容易將aop 自動。。。這些東西加入我們的代碼中去是不是?我們只管寫dao方法,service方法,不需要關心在哪邊commit哪邊rollback何時connection,spring的聲明式事務會幫我們負責,這種風格我們稱為“優雅”,各層間耦合度極大程度上的降低,封裝性好。

因此,我們可以總結出下面這些好處:

2? Service層的方法只管開啟事務(如果講究點的還會設一個Transaction);

2? 在該Service層中的所有dao使用該service方法中開啟的事務(即connection);

2? Dao中每次只管getCurrentConnection(獲取當前的connection),與進行數據處理

2? Dao層中如果發生錯誤就拋回Service層

2? Service層中接到exception,在catch{}中rollback,在try{}未尾commit,在finally塊中關閉整個connection。

這。。。就是我們所說的ThreadLocal。

舉個更實際的例子再次來說明ThreadLocal:

我們有3個用戶訪問同一個service方法,該service方法內有3個dao方法為一個完整事務,那么整個web容器內只因該有3個connection,并且每個connection之間的狀態,彼此“隔離”。

我們下面一起來看我們如何用代碼實現類似于Spring的這種做法。

首先,根據我們的ThreadLocal的概念,我們先聲明一個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層(注意紅色標粗-好粗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,如果我把上述標粗(沒有加紅色)的地方,全部用AOP的方式從這塊代碼的外部“切”進去。。。是不是一個Spring里的Service方法就誕生了?

下面來看一個完整的例子

2.6?使用ThreadLocal分離Service、DAO層

先來看表結構:

T_Student表

T_ClassRoom表

T_Student_ClassRoom表

需求:

很簡單,T_ClassRoom表里已經有值了,在插入T_Student表的數據時同時要給這個學生分配一個班級并且插入T_Student_ClassRoom表,這就是一個事務,這兩步中有任何一步出錯,事務必須回滾。

看來工程的結構吧:

下面開始放出所有源代碼:

2.6.1 ConnectionManager類

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) {

?????????????????? }

???????? }

}

2.6.2 DBConnection類

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;

???????? }

}

2.6.3 JdbcProperties類

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;

???????? }

}

2.6.4 Resource目錄下的jdbc.properties

jdbc.driverClassName=com.MySQL.jdbc.Driver

jdbc.databaseURL=jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8

jdbc.username=mysql

jdbc.password=password_1

2.6.5 StudentService接口

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;

}

2.6.6 StudentServiceImpl類

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) {

??????????????????????????? }

?????????????????? }

???????? }

}

2.6.7 ClassRoomDAO接口

package sky.org.dao;

import java.util.HashMap;

import java.util.List;

public interface ClassRoomDAO {

???????? public void addStudentClassRoom(String roomId, String sNo) throws Exception;

}

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的區別

嚴重注意下面3點:

2? openSession一旦被調用,必須且一定要在finally塊中close,要不然你就等著out of memory吧;

2? 如果你使用的是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中

2? 如果你使用的是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操作。請按照下述原則:

1)????? 將屬于select的操作,單獨做成一個dao方法,該dao使用openSession并且在finally塊中及時關閉session,該dao只需要返回一個java的object如:List<Student>即可,如果出錯將exception拋回給調用它的service方法。

2)????? 對于其它的delete, insert, update的dao操作,請使用getCurrentSession。

4.?????? 忌諱,把select類的操作放在“事務”中;

總結

以上是生活随笔為你收集整理的通向架构师的道路(第七天)之漫谈使用ThreadLocal改进你的层次的划分的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。