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

news2024/11/24 14:41:59

一、什么是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个方法,我们先来了解一下:

²  void set(Object value)

设置当前线程的线程局部变量的值。

²  public Object get()

该方法返回当前线程所对应的线程局部变量。

²  public void remove()

将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。

²  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()。

https://img-my.csdn.net/uploads/201212/12/1355289274_9973.jpg

二、来看一个实际案例

2.1 同一Service方法中调用多个Dao方法

https://img-my.csdn.net/uploads/201212/12/1355289286_1325.jpg

可以看到,我们有一个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的声明式事务会帮我们负责,这种风格我们称为“优雅”,各层间耦合度极大程度上的降低,封装性好。

因此,我们可以总结出下面这些好处:

²  Service层的方法只管开启事务(如果讲究点的还会设一个Transaction);

²  在该Service层中的所有dao使用该service方法中开启的事务(即connection);

²  Dao中每次只管getCurrentConnection(获取当前的connection),与进行数据处理

²  Dao层中如果发生错误就抛回Service层

²  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改造ServiceDao

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分离ServiceDAO

先来看表结构:

T_Student

https://img-my.csdn.net/uploads/201207/16/1342416110_3227.jpg

 

T_ClassRoom

https://img-my.csdn.net/uploads/201207/16/1342416124_1918.jpg

T_Student_ClassRoom

https://img-my.csdn.net/uploads/201207/16/1342416134_9745.jpg

需求:

很简单,T_ClassRoom表里已经有值了,在插入T_Student表的数据时同时要给这个学生分配一个班级并且插入T_Student_ClassRoom表,这就是一个事务,这两步中有任何一步出错,事务必须回滚。

看来工程的结构吧:

https://img-my.csdn.net/uploads/201207/16/1342416146_4349.jpg

https://img-my.csdn.net/uploads/201207/16/1342416151_1595.jpg

https://img-my.csdn.net/uploads/201207/16/1342416160_1462.jpg

 

 

下面开始放出所有源代码:

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();

                            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 getCurrentSessionopenSession的区别

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 openSessiongetCurrentSession的区别

严重注意下面3点:

²  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操作。请按照下述原则:

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/841588.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MapTR论文笔记

MAPTR: STRUCTURED MODELING AND LEARNING FOR ONLINE VECTORIZED HD MAP CONSTRUCTION 目的 传统高精地图 通过一些离线的基于 SLAM 的方法生成&#xff0c;需要复杂的流程以及高昂的维护费用。基于 bev 分割的建图方法&#xff0c;缺少向量化 实例级的信息&#xff0c;比如…

SPM(Swift Package Manager)开发及常见事项

SPM怎么使用的不再赘述&#xff0c;其优点是Cocoapods这样的远古产物难以望其项背的&#xff0c;而且最重要的是可二进制化、对xcproj项目无侵入&#xff0c;除了网络之外简直就是为团队开发的项目库依赖最好的管理工具&#xff0c;是时候抛弃繁杂低下的cocoapods了。 一&…

如何使用 ChatGPT 规划家居装修

你正在计划家庭装修项目&#xff0c;但不确定从哪里开始&#xff1f;ChatGPT 随时为你提供帮助。从集思广益的设计理念到估算成本&#xff0c;ChatGPT 可以简化你的家居装修规划流程。在本文中&#xff0c;我们将讨论如何使用 ChatGPT 有效地规划家居装修&#xff0c;以便你的项…

vue diff 前后缀+最长递增子序列算法

文章目录 查找相同前后缀通过前后缀位置信息新增节点通过前后缀位置信息删除节点 中间部份 diff判断节点是否需要移动删除节点删除未查找到的节点删除多余节点 移动和新增节点最长递增子序列 求解最长递增子序列位置信息 查找相同前后缀 如上图所示&#xff0c;新旧 children 拥…

SCT82A30DHKR_5.5V-100V Vin同步降压控制器

SCT82A30是一款100V电压模式控制同步降压控制器&#xff0c;具有线路前馈。40ns受控高压侧MOSFET的最小导通时间支持高转换比&#xff0c;实现从48V输入到低压轨的直接降压转换&#xff0c;降低了系统复杂性和解决方案成本。如果需要&#xff0c;在低至6V的输入电压下降期间&am…

Photoshop 2023 25.0beta「Mac」

Photoshop 2023是一款专业图像处理软件&#xff0c;它主要用于图像编辑、合成和设计等方面。 Photoshop beta创新式填充的功能特色包括&#xff1a; 自动识别和删除对象&#xff1a;该功能可以自动识别图像中的对象&#xff0c;并用周围的图像填充空白部分&#xff0c;使图像看…

window系统下 tinymce富文本编辑器在搜狗输入法下placeholder不消失现象

window 搜狗输入法下编辑器占位符和内容重叠问题 这种情况是&#xff0c;tinymce插件库存在一些兼容BUG&#xff0c;需要我们自行手写样式或者js替换掉placeholder&#xff0c;代码如下&#xff1a; // 获取富文本框的内容const handleChange (editorContent) > {// cons…

C++11 新特性 ---- final 和 override

一、final C中增加了final关键字&#xff0c;作用如下&#xff1a; ① 限制某个类不能被继承② 或者某个虚函数不能被重写 ① 限制某个类不能被继承 // ① 限制某个类不能被继承,也就是说这个类不能有派生类 class Base{ public:virtual void print() {cout<<"Ba…

电商数据获取:网络爬虫还是付费数据接口?

随着电商行业的迅速发展&#xff0c;对电商数据的需求也越来越大。在获取电商数据时&#xff0c;常常面临一个选择&#xff1a;是自己编写网络爬虫进行数据爬取&#xff0c;还是使用现有的付费数据接口呢&#xff1f;本文将从成本、可靠性、数据质量等多个角度进行分析&#xf…

【css】组合器

组合器是解释选择器之间关系的某种机制。在简单选择器器之间&#xff0c;可以包含一个组合器&#xff0c;从而实现简单选择器难以达到的效果。 CSS 中有四种组合器&#xff1a; 后代选择器 (空格)&#xff1a;匹配属于指定元素后代的所有元素&#xff0c;示例&#xff1a;div …

element-ui表格数据为空,图片占位提示

当表格的绑定数据为空时常需要显示暂无数据等字样&#xff0c;这时候就用到了empty-text <el-table:data"tableData"stripeborderempty-text"暂无数据"> 但&#xff0c;当数据为空&#xff0c;想用图片展示呢&#xff0c;如下图 方法一&#xff1a…

java.lang.UnsupportedClassVersionError TestCase

JavaFramework-JDK6.jar 放到JDK17运行没有问题 JavaFramework源码放到JDK17环境下编译出来的JavaFramework-JDK17.jar JavaFramework-JDK17.jar 放到JDK17运行没有问题 JavaFramework-JDK17.jar 放到JDK8运行没有问题&#xff0c;这个好像不对啊&#xff0c;可能之前编译设置…

day39反转字符串总结

反转字符串原理其实就是交换位置&#xff0c;以中间为分隔点&#xff1b; 基本套路&#xff1a;遍历前一般字符&#xff0c;互换位置&#xff1b; for循环模板 void reverseString(char* s, int sSize){char temp;for (int i 0, j sSize - 1; i < sSize/2; i, j--) {temp…

【无公网IP】本地电脑搭建个人博客网站(并发布公网访问 )和web服务器

【无公网IP】本地电脑搭建个人博客网站&#xff08;并发布公网访问 &#xff09;和web服务器 文章目录 【无公网IP】本地电脑搭建个人博客网站&#xff08;并发布公网访问 &#xff09;和web服务器前言1. 安装套件软件2. 创建网页运行环境 指定网页输出的端口号3. 让WordPress在…

【Rust】Rust学习第三章常见编程概念

包含第一、二章 文档&#xff1a;Rust 程序设计语言 - Rust 程序设计语言 简体中文版 (bootcss.com) 墙裂推荐这个文档 第一章入门 入门指南 - Rust 程序设计语言 简体中文版 第二章猜猜看游戏 猜猜看游戏教程 - Rust 程序设计语言 简体中文版 (bootcss.com) // 导入库 us…

Stable Diffusion 硬核生存指南:WebUI 中的 GFPGAN

本篇文章聊聊 Stable Diffusion WebUI 中的核心组件&#xff0c;强壮的人脸图像面部画面修复模型 GFPGAN 相关的事情。 写在前面 本篇文章的主角是开源项目 TencentARC/GFPGAN&#xff0c;和上一篇文章《Stable Diffusion 硬核生存指南&#xff1a;WebUI 中的 CodeFormer》提…

H263压缩码流如何分解为一个一个单元并查询到其宽高?

H263码流尺寸规格有限&#xff0c;只有以下几种&#xff1a; H263码流有四个分层&#xff1a; 1、图像层 2、块组 3、宏块 4、块 下面分别介绍&#xff1a; 具体介绍如下&#xff0c;5.1.3中红色框选部分就是压缩码流的宽高指示&#xff1a; 图像层 上面就是H263的图像层&am…

QT开发学习相关笔记

QT中配置文件读取 QT中使用的config文件为&#xff1a;xxx.ini文件,基本格式如下&#xff1a; 使用 QSetting&#xff08;QT自带类&#xff09;中的相关接口实现设置配置文件数据&#xff0c;或者读取数据。 读取配置文件路径设置如下&#xff0c;其中 iniPath为设置路径 ne…

word2003脚注问题

问题分析&#xff1a; 在题目上插入脚注的时候&#xff0c;脚注放在文件结尾&#xff0c;然后正文拆开了&#xff0c;不能续前节 解决办法&#xff1a; word2003中&#xff0c;工具->选项->兼容性

进程间通讯(IPC机制) 管道 信号量 共性内存 消息队列 详细图解

进程间通讯-IPC机制 常用命令管道有名管道读写编程有名管道示意图 无名管道 信号量信号量的概念信号量接口函数进程 a 和进程 b 模拟访问打印机 用信号量互斥画图分析代码实现测试结果显示和操作 共享内存 信号量 消息队列 的命令 共享内存共享内存定义共享内存函数接口实例编程…