您的位置:首页 > 编程语言 > Java开发

遇到问题---java--hibernate多线程中使用getCurrentSession报错innerSetException

2016-09-25 23:02 519 查看

现象

一般单线程中使用hibernate可以直接使用getCurrentSession如下:

public BigInteger countToday(String shareType, String shareCode) {
Session  session=sessionFactory.getCurrentSession();
String hqlString = "select count(*) from share_basic as p where p.shareCode ='"+shareCode+"'  and p.shareType='"+shareType+"'  and year(dateValue)=year(now()) and month(dateValue)=month(now()) and day(dateValue)=day(now())";
BigInteger result= (BigInteger) session.createSQLQuery(hqlString).uniqueResult();
return result;
}


但是把countToday方法在多线程中调用时会直接跳转到FutureTask类中的innerSetException异常。

其实是报No session异常。

原因

getCurrentSession创建的session会和绑定到当前线程。

如果在多线程中运行,当前线程是不明确的,所以就会报No session了。

解决方法

方法一 使用openSession

1 getCurrentSession创建的session会和绑定到当前线程,而openSession不会。

2 getCurrentSession创建的线程会在事务回滚或事物提交后自动关闭,而openSession必须手动关闭

getCurrentSession () 使用当前的session
openSession() 重新建立一个新的session

在一个应用程序中,如果DAO 层使用Spring 的hibernate 模板,通过Spring 来控制session 的生命周期,则首选getCurrentSession ()。
多线程中一般需要用openSession()

我们这里可以增加2个方法封装一下。

主要是增加这2个方法

public  boolean bindHibernateSessionToThread(SessionFactory sessionFactory) {
if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
return true;
} else {
Session session = sessionFactory.openSession();
session.setFlushMode(FlushMode.MANUAL);
SessionHolder sessionHolder = new SessionHolder(session);
TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);
}
return false;
}

public  void closeHibernateSessionFromThread(boolean participate, Object sessionFactory) {

if (!participate) {
SessionHolder sessionHolder = (SessionHolder)TransactionSynchronizationManager.unbindResource(sessionFactory);
SessionFactoryUtils.closeSession(sessionHolder.getSession());
}
}


和在调用的地方加上这2行:

boolean participate = bindHibernateSessionToThread(sessionFactory);
closeHibernateSessionFromThread(participate, sessionFactory);

则现象中的代码改为:

public BigInteger countToday(String shareType, String shareCode) {
boolean participate = bindHibernateSessionToThread(sessionFactory);
Session  session=sessionFactory.getCurrentSession();
String hqlString = "select count(*) from share_basic as p where p.shareCode ='"+shareCode+"'  and p.shareType='"+shareType+"'  and year(dateValue)=year(now()) and month(dateValue)=month(now()) and day(dateValue)=day(now())";
BigInteger result= (BigInteger) session.createSQLQuery(hqlString).uniqueResult();
closeHibernateSessionFromThread(participate, sessionFactory);
return result;
}


方法二 利用ThreadLocal存储线程本地session

Hibernate并发机制:
a、Hibernate的Session对象是非线程安全的,对于单个请求,单个会话,单个的工作单元(即单个事务,单个线程),它通常只使用一次,然后就丢弃。
如果一个Session 实例允许共享的话,那些支持并发运行的,例如Http request,session beans将会导致出现资源争用。
如果在Http Session中有hibernate的Session的话,就可能会出现同步访问Http Session。只要用户足够快的点击浏览器的“刷新”,就会导致两个并发运行的线程使用同一个Session。
b、多个事务并发访问同一块资源,可能会引发第一类丢失更新,脏读,幻读,不可重复读,第二类丢失更新一系列的问题。
在利用Hibernate开发DAO模块时,我们和Session打的交道最多,所以如何合理的管理Session,避免Session的频繁创建和销毁,对于提高系统的性能来说是非常重要的,以往是通过eclipse的插件来自动完成这些代码的,当然效果是不错的,但是总是觉得不爽(没有读懂那些冗长的代码),所以现在打算自己实现Session管理的代码。我们知道Session是由SessionFactory负责创建的,而SessionFactory的实现是线程安全的,多个并发的线程可以同时访问一个SessionFactory并从中获取Session实例,那么Session是否是线程安全的呢?很遗憾,答案是否定的。Session中包含了数据库操作相关的状态信息,那么说如果多个线程同时使用一个Session实例进行CRUD,就很有可能导致数据存取的混乱,你能够想像那些你根本不能预测执行顺序的线程对你的一条记录进行操作的情形吗?
在Session的众多管理方案中,我们今天来认识一种名为ThreadLocal模式的解决方案。
早在Java1.2推出之时,Java平台中就引入了一个新的支持:java.lang.ThreadLocal,给我们在编写多线程程序时提供了一种新的选择。ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是thread local variable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有一个该变量。
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。比如下面的示例实现(为了简单,没有考虑集合的泛型):
1. public class ThreadLocal {
2.   private Map values = Collections.synchronizedMap(new HashMap());
3.   public Object get() {
4.    Thread currentThread = Thread.currentThread();
5.     Object result = values.get(currentThread);
6.    if(result == null&&!values.containsKey(currentThread)) {
7.     result = initialValue();
8.     values.put(currentThread, result);
9.    }
10.    return result;
11.   }
12.   public void set(Object newValue) {
13.    values.put(Thread.currentThread(), newValue);
14.   }
15.   public Object initialValue() {
16.    return null;
17.   }
18. }
那麽具体如何利用ThreadLocal来管理Session呢?Hibernate官方文档手册的示例之中,提供了一个通过ThreadLocal维护Session的好榜样:
1. public class HibernateUtil {
2. public static final SessionFactory sessionFactory;
3. static {
4. try {
5. sessionFactory = new Configuration().configure()
6. .buildSessionFactory();
7. } catch (Throwable ex) {
8. throw new ExceptionInInitializerError(ex);
9. }
10. }
11. public static final ThreadLocal session =
12. new ThreadLocal();
13. public static Session currentSession() throws HibernateException {
14. Session s = session.get();
15. if(s == null) {
16. s = sessionFactory.openSession();
17. session.set(s);
18. }
19. return s;
20. }
21. public static void closeSession() throws HibernateException {
22. Session s = session.get();
23. if(s != null) {
24. s.close();
25. }
26. session.set(null);
27. }
28. }

或者

public class HibernateUtil {
public static SessionFactory sessionFactory;
public static ConnectionManager cm;

public static final ThreadLocal<Connection> connectionHolder=new ThreadLocal<Connection>();
public static final ThreadLocal<Session> session = new ThreadLocal<Session>();
public static final ThreadLocal<Transaction> transaction = new ThreadLocal<Transaction>();
public static final String HIBERNATE_PATH=System.getProperty("user.dir") + "/conf/hibernate.properties";
public static void dataSourceConf(String url,String userName,String password) throws MappingException, IOException{
InputStream in =null;
Properties p= new Properties();
try{
in = new BufferedInputStream(new FileInputStream(HIBERNATE_PATH));
p.load(in);
}catch(Exception e){
e.printStackTrace();
SPCUI.log.append(e.getMessage());
}finally{
in.close();
}
Configuration configuration=new Configuration().addProperties(p);
configuration.setProperty( "hibernate.connection.url" , url);
configuration.setProperty( "hibernate.connection.username" ,userName );
configuration.setProperty( "hibernate.connection.password" , password);
ServiceRegistry  sr = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(sr);
cm= ConnectionManager.getInstance(url,userName,password);
if(cm.getDs() instanceof ComboPooledDataSource){
SPCUI.log.append("数据库连接成功!\n");
p.setProperty("hibernate.connection.url", url);
p.setProperty("hibernate.connection.username",userName);
p.setProperty("hibernate.connection.password", password);
FileOutputStream fos = new FileOutputStream(HIBERNATE_PATH);
try{
p.store(fos, "Copyright (c) Boxcode Studio");
}catch(Exception e){
e.printStackTrace();
SPCUI.log.append(e.getMessage());
}finally{
fos.close();
}
}
}

public static Session getSession() throws HibernateException {
Session s = (Session)session.get();
if (s == null || !s.isOpen()) {
s = null;
try{
s = sessionFactory.openSession();
s.setFlushMode(FlushMode.AUTO);
session.set(s);
}catch(Exception e){
SPCUI.log.append("数据库未连接,请先连接数据库\n");
}

}
return s;
}

public static void closeSession() throws HibernateException {
Session s = (Session)session.get();
session.set(null);
if (s!=null && s.isOpen()) {
s.close();
}
}

public static void beginTransaction() {
Transaction tx = (Transaction)transaction.get();
if (tx == null) {
tx = getSession().beginTransaction();
transaction.set(tx);
}
}

public static void commitTransaction() {
Transaction tx = (Transaction)transaction.get();
try {
if (tx != null && !tx.wasCommitted() && !tx.wasRolledBack()) {
getSession().flush();
tx.commit();
}
transaction.set(null);
} catch(HibernateException ex) {
rollbackTransaction();
throw ex;
}
}

public static void rollbackTransaction() {
Transaction tx = (Transaction)transaction.get();
try {
if (tx != null && !tx.wasCommitted() && !tx.wasRolledBack()) {
tx.rollback();
}
} catch (HibernateException ex){
throw ex;
} finally {
closeSession();
}
}

public static void closeThreadLocalConnection(){
Connection conn = connectionHolder.get();
if (conn != null){
try{
conn.close();
connectionHolder.remove();
}catch(SQLException e){
e.printStackTrace();
}
}
}

public static Connection getConnection() throws HibernateException {
Connection conn=null;
try {
conn = cm.getConnection();
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}

public static Connection getThreadLocalConnection() throws HibernateException, ServiceLocatorException, SQLException {
Connection conn = connectionHolder.get();
if (conn == null){
conn = cm.getConnection();
connectionHolder.set(conn);
}
return conn;
}

public static void close() throws Throwable{
if(cm!=null)
cm.finalize();
connectionHolder.remove();
session.remove();
transaction.remove();
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: