Hibernate核心知识(二)

我们之前已经在 Hibernate核心知识(一) 中了解了Hibernate是个啥东西,有什么作用,也在 MyEclipse2014使用Hibernate逆向工程生成实体类 中学会了怎么使用Hibernate。那么还有什么可以继续扩展的呢?光是知道怎么用Hibernate还不够,这跟普通的用JDBC来连接数据库有什么区别吗?还要多写那么多配置文件。所以我们应该学会如何 正确使用 Hibernate,以及如何用Hibernate来 提高开发效率。这两点才是我们应该去深挖的东西。正确使用是指使用Hibernate事务来保证在并发时数据的正确性,提高开发效率则是利用Hibernate的关系映射从而减少我们的代码量。今天,我们先来了解一下Hibernate事务。

Hibernate中的session是什么,与Web中的session有何区别?

  • 前者是app与数据库之间的一个会话,可以通过这个会话来操作数据库,并且可以在该会话缓存中存储已经操作过的数据对象。

  • 是浏览器与服务器之前的一个会话,可以在这个会话缓存里面存储同一个浏览器不同请求共用的数据。

理解web app中“请求”、“线程”、“hibernate session”之间的关系
Tomcat等web服务器,就是一个并发服务器,基于多线程机制下设计的:一个新的用户请求,就会从线程池中取出一条线程来响应请求,请求结束后线程便回到线程池。这里就会有线程安全的问题。对于静态方法(static method),如果操作了静态字段(static field)就会引起安全性问题,二如果没有操作静态字段(static field),只操作了实例字段(instance field)就不会引起安全性问题。

这里在介绍一下 线程绑定。线程绑定最大的作用就是跨层面传递数据。就比如一个新的用户请求到来,请求了一个线程,我们在servlet层获取到了参数,这时我们可以将这个参数绑定(存储)到线程中,然后再在dao层取出来用。org.hibernate.SessionFactory.getCurrentSession()方式获取的session便是线程绑定的。

一个活动线程,绑定一个session。session存活于一个活动线程中。若是一个线程,绑定一个session将会出现数据不同步的情况。程池中若干个线程,分别对应不同的session,我用sessionA更新了一条数据,sessionB中缓存的同一个数据对象,并不会更新。因为,只有通过当前session的修改,缓存才能与数据库同步。
要理解活动线程这个概念,一个请求到达服务器,服务器从线程池中拿一个空闲线程出来处理这个请求,那么,这个线程就是活动线程,请求处理结束、响应完毕后,线程会被释放回到线程池中。也就是说session是存活在一个“请求->响应”的过程中。

public class ThreadLocalUtil {
private static final ThreadLocal<String> keywords = new ThreadLocal<String>();
    public static String getKeywords ()  {  
           String var = (String) keywords.get();        
        return var;
    }
    public static void setKeywords (String _keywords)  {    
          keywords.set(_page);
    }

如何理解事务?

  • 事务是由一组数据库原子操作组成的,本质上,可以理解为一组SQL指令。“事务控制、事务管理”是指对一组sql指令的执行情况进行控制管理:1、允许事务中的某些操作不成功。2、不允许事务中的操作不成功,保证事务的完整性、一致性。遇到一个不成功的操作就撤回所有的操作(rollback)。

  • Java事务例子

import java.sql.*;
public class Main{
    public static void main (String [] args){
        Connection conn = null;
        Statement stmt = null;
        try{
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost/test","root","root");
            // 更改JDBC事务的默认提交方式
            conn.setAutoCommit(false);
            stmt=conn.createStatement();

            //原子操作1
            //原子操作2
            //...

            //提交JDBC事务 
            conn.commit();
            // 恢复JDBC事务的默认提交方式
            conn.setAutoCommit(true); 

        }catch(Exception e){
            try{
                //遇到一个操作不成功,回滚JDBC事务 
                if (conn != null) conn.rollback();
            }catch(Exception ex){
                ex.printStackTrace();
            }
            e.printStackTrace();
        }finally{
            try{
                if (stmt != null) stmt.close();
            }catch(Exception e){
                e.printStackTrace();
            }           

            try{
                if (conn != null) conn.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }
}
  • Hibernate事务例子
Transaction tx = null;      
try {           
    tx = session.beginTransaction(); //开启事务

    //原子操作1
    //原子操作2

    //提交事务
    tx.commit();
} catch (HibernateException e) {
    //如果Transaction依旧存在,即系出现异常,回滚事务 
    if (tx!=null&tx.isActive()) {  
        tx.rollback();
}

那我们应该在哪一层进行事务管理呢
Jdbc事务是在一个jdbc connection中存在的【当然,也有跨connection的事务,称为JTA事务,暂不涉及】。
Hibernate事务,也就是jdbc事务,一个session对应着一个连接。因此,Hibernate事务也是存活于一个session中。
如果connection或session关闭后进行事务commit、rollback等操作会抛出异常。

  • DAO层

事务管理主要是体现在保证多个数据操作的完整、一致,而DAO层的方法往往只有一个数据库操作,已经是一个“原子操作”了,仅仅对一个操作进行事务管理,没啥意义,还不如直接在hibernate配置文件中设为自动提交。

  • View层

在过滤器中定义事务,懒人的做法,即针对一个线程定义事务,在一个线程中,统一使用一个事务。但是会有一个问题存在,某些业务本来不需要事务,在这种方式下,都被强迫加上了事务,结果就是效率降低。因为加了事务,数据操作效率会下降,毕竟多了一个控制。

  • Service层

在service中定义事务,是最正确的一种方式,因为一个service层方法,对应着一个业务,一个业务中可能有多个数据操作,业务与业务之间,很少会需要处于同一个事务中(当然不是绝对的:两个业务方法之间可能需要调用)。
特别注意,事务不能重叠,一个线程可以有多个事务,但是,不能同时有两个事务以上,即事务里面不能包含事务。

猜你喜欢

转载自blog.csdn.net/hhhhhsw/article/details/81146689