Java的Hibernate框架数据库操作中锁的使用和查询类型_java

 Hibernate与数据库锁
一、为什么要使用锁?

要想弄清楚锁机制存在的原因,首先要了解事务的概念。
事务是对数据库一系列相关的操作,它必须具备ACID特征:

  • A(原子性):要么全部成功,要么全部撤销。
  • C(一致性):要保持数据库的一致性。
  • I(隔离性):不同事务操作相同数据时,要有各自的数据空间。
  • D(持久性):一旦事务成功结束,它对数据库所做的更新必须永久保持。

我们常用的关系型数据库RDBMS实现了事务的这些特性。其中,原子性、
一致性和持久性都是采用日志来保证的。而隔离性就是由今天我们关注的
锁机制来实现的,这就是为什么我们需要锁机制。

如果没有锁,对隔离性不加控制,可能会造成哪些后果呢?

  1. 更新丢失:事务1提交的数据被事务2覆盖。
  2. 脏读:事务2查询到了事务1未提交的数据。
  3. 虚读:事务2查询到了事务1提交的新建数据。
  4. 不可重复读:事务2查询到了事务1提交的更新数据。

下面来看Hibernate的例子,两个线程分别开启两个事务操作tb_account表中
的同一行数据col_id=1。

package com.cdai.orm.hibernate.annotation; 

import java.io.Serializable; 

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table; 

@Entity
@Table(name = "tb_account")
public class Account implements Serializable { 

  private static final long serialVersionUID = 5018821760412231859L; 

  @Id
  @Column(name = "col_id")
  private long id; 

  @Column(name = "col_balance")
  private long balance; 

  public Account() {
  } 

  public Account(long id, long balance) {
    this.id = id;
    this.balance = balance;
  } 

  public long getId() {
    return id;
  } 

  public void setId(long id) {
    this.id = id;
  } 

  public long getBalance() {
    return balance;
  } 

  public void setBalance(long balance) {
    this.balance = balance;
  } 

  @Override
  public String toString() {
    return "Account [id=" + id + ", balance=" + balance + "]";
  } 

}
package com.cdai.orm.hibernate.transaction; 

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.AnnotationConfiguration; 

import com.cdai.orm.hibernate.annotation.Account; 

public class DirtyRead { 

  public static void main(String[] args) { 

    final SessionFactory sessionFactory = new AnnotationConfiguration().
        addFile("hibernate/hibernate.cfg.xml").
        configure().
        addPackage("com.cdai.orm.hibernate.annotation").
        addAnnotatedClass(Account.class).
        buildSessionFactory(); 

    Thread t1 = new Thread() { 

      @Override
      public void run() {
        Session session1 = sessionFactory.openSession();
        Transaction tx1 = null;
        try {
          tx1 = session1.beginTransaction();
          System.out.println("T1 - Begin trasaction");
          Thread.sleep(500); 

          Account account = (Account)
              session1.get(Account.class, new Long(1));
          System.out.println("T1 - balance=" + account.getBalance());
          Thread.sleep(500); 

          account.setBalance(account.getBalance() + 100);
          System.out.println("T1 - Change balance:" + account.getBalance()); 

          tx1.commit();
          System.out.println("T1 - Commit transaction");
          Thread.sleep(500);
        }
        catch (Exception e) {
          e.printStackTrace();
          if (tx1 != null)
            tx1.rollback();
        }
        finally {
          session1.close();
        }
      } 

    }; 

    // 3.Run transaction 2
    Thread t2 = new Thread() { 

      @Override
      public void run() {
        Session session2 = sessionFactory.openSession();
        Transaction tx2 = null;
        try {
          tx2 = session2.beginTransaction();
          System.out.println("T2 - Begin trasaction");
          Thread.sleep(500); 

          Account account = (Account)
              session2.get(Account.class, new Long(1));
          System.out.println("T2 - balance=" + account.getBalance());
          Thread.sleep(500); 

          account.setBalance(account.getBalance() - 100);
          System.out.println("T2 - Change balance:" + account.getBalance()); 

          tx2.commit();
          System.out.println("T2 - Commit transaction");
          Thread.sleep(500);
        }
        catch (Exception e) {
          e.printStackTrace();
          if (tx2 != null)
            tx2.rollback();
        }
        finally {
          session2.close();
        }
      } 

    }; 

    t1.start();
    t2.start(); 

    while (t1.isAlive() || t2.isAlive()) {
      try {
        Thread.sleep(2000L);
      } catch (InterruptedException e) {
      }
    } 

    System.out.println("Both T1 and T2 are dead.");
    sessionFactory.close(); 

  } 

}

事务1将col_balance减小100,而事务2将其减少100,最终结果可能是0,也
可能是200,事务1或2的更新可能会丢失。log输出也印证了这一点,事务1和2
的log交叉打印。

T1 - Begin trasaction
T2 - Begin trasaction
Hibernate: select account0_.col_id as col1_0_0_, account0_.col_balance as col2_0_0_ from tb_account account0_ where account0_.col_id=?
Hibernate: select account0_.col_id as col1_0_0_, account0_.col_balance as col2_0_0_ from tb_account account0_ where account0_.col_id=?
T1 - balance=100
T2 - balance=100
T2 - Change balance:0
T1 - Change balance:200
Hibernate: update tb_account set col_balance=? where col_id=?
Hibernate: update tb_account set col_balance=? where col_id=?
T1 - Commit transaction
T2 - Commit transaction
Both T1 and T2 are dead.

由此可见,隔离性是一个需要慎重考虑的问题,理解锁很有必要。

二、有多少种锁?

常见的有共享锁、更新锁和独占锁。

1.共享锁:用于读数据操作,允许其他事务同时读取。当事务执行select语句时,
数据库自动为事务分配一把共享锁来锁定读取的数据。
2.独占锁:用于修改数据,其他事务不能读取也不能修改。当事务执行insert、
update和delete时,数据库会自动分配。
3.更新锁:用于避免更新操作时共享锁造成的死锁,比如事务1和2同时持有
共享锁并等待获得独占锁。当执行update时,事务先获得更新锁,然后将
更新锁升级成独占锁,这样就避免了死锁。

此外,这些锁都可以施加到数据库中不同的对象上,即这些锁可以有不同的粒度。
如数据库级锁、表级锁、页面级锁、键级锁和行级锁。

所以锁是有很多种的,这么多锁要想完全掌握灵活使用太难了,我们又不是DBA。
怎么办?还好,锁机制对于我们一般用户来说是透明的,数据库会自动添加合适的
锁,并在适当的时机自动升级、降级各种锁,真是太周到了!我们只需要做的就是
学会根据不同的业务需求,设置好隔离级别就可以了。

三、怎样设置隔离级别?

一般来说,数据库系统会提供四种事务隔离级别供用户选择:

1.Serializable(串行化):当两个事务同时操纵相同数据时,事务2只能停下来等。

2.Repeatable Read(可重复读):事务1能看到事务2新插入的数据,不能看到对
已有数据的更新。

3.Read Commited(读已提交数据):事务1能看到事务2新插入和更新的数据。

4.Read Uncommited(读未提交数据):事务1能看到事务2没有提交的插入和更新
数据。

四、应用程序中的锁

当数据库采用Read Commited隔离级别时,可以在应用程序中采用悲观锁或乐观锁。

1.悲观锁:假定当前事务操作的数据肯定还会有其他事务访问,因此悲观地在应用
程序中显式指定采用独占锁来锁定数据资源。在MySQL、Oracle中支持以下形式:

   select ... for update

显式地让select采用独占锁锁定查询的记录,其他事务要查询、更新或删除这些被
锁定的数据,都要等到该事务结束后才行。

在Hibernate中,可以在load时传入LockMode.UPGRADE来采用悲观锁。修改前面的例子,
在事务1和2的get方法调用处,多传入一个LockMode参数。从log中可以看出,事务1和2
不再是交叉运行,事务2等待事务1结束后才可以读取数据,所以最终col_balance值是正确
的100。

package com.cdai.orm.hibernate.transaction; 

import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction; 

import com.cdai.orm.hibernate.annotation.Account;
import com.cdai.orm.hibernate.annotation.AnnotationHibernate; 

public class UpgradeLock { 

  @SuppressWarnings("deprecation")
  public static void main(String[] args) { 

    final SessionFactory sessionFactory = AnnotationHibernate.createSessionFactory();  

    // Run transaction 1
    Thread t1 = new Thread() { 

      @Override
      public void run() {
        Session session1 = sessionFactory.openSession();
        Transaction tx1 = null;
        try {
          tx1 = session1.beginTransaction();
          System.out.println("T1 - Begin trasaction");
          Thread.sleep(500); 

          Account account = (Account)
              session1.get(Account.class, new Long(1), LockMode.UPGRADE);
          System.out.println("T1 - balance=" + account.getBalance());
          Thread.sleep(500); 

          account.setBalance(account.getBalance() + 100);
          System.out.println("T1 - Change balance:" + account.getBalance()); 

          tx1.commit();
          System.out.println("T1 - Commit transaction");
          Thread.sleep(500);
        }
        catch (Exception e) {
          e.printStackTrace();
          if (tx1 != null)
            tx1.rollback();
        }
        finally {
          session1.close();
        }
      } 

    }; 

    // Run transaction 2
    Thread t2 = new Thread() { 

      @Override
      public void run() {
        Session session2 = sessionFactory.openSession();
        Transaction tx2 = null;
        try {
          tx2 = session2.beginTransaction();
          System.out.println("T2 - Begin trasaction");
          Thread.sleep(500); 

          Account account = (Account)
              session2.get(Account.class, new Long(1), LockMode.UPGRADE);
          System.out.println("T2 - balance=" + account.getBalance());
          Thread.sleep(500); 

          account.setBalance(account.getBalance() - 100);
          System.out.println("T2 - Change balance:" + account.getBalance()); 

          tx2.commit();
          System.out.println("T2 - Commit transaction");
          Thread.sleep(500);
        }
        catch (Exception e) {
          e.printStackTrace();
          if (tx2 != null)
            tx2.rollback();
        }
        finally {
          session2.close();
        }
      } 

    }; 

    t1.start();
    t2.start(); 

    while (t1.isAlive() || t2.isAlive()) {
      try {
        Thread.sleep(2000L);
      } catch (InterruptedException e) {
      }
    } 

    System.out.println("Both T1 and T2 are dead.");
    sessionFactory.close(); 

  } 

}
T1 - Begin trasaction
T2 - Begin trasaction
Hibernate: select account0_.col_id as col1_0_0_, account0_.col_balance as col2_0_0_ from tb_account account0_ with (updlock, rowlock) where account0_.col_id=?
Hibernate: select account0_.col_id as col1_0_0_, account0_.col_balance as col2_0_0_ from tb_account account0_ with (updlock, rowlock) where account0_.col_id=?
T2 - balance=100
T2 - Change balance:0
Hibernate: update tb_account set col_balance=? where col_id=?
T2 - Commit transaction
T1 - balance=0
T1 - Change balance:100
Hibernate: update tb_account set col_balance=? where col_id=?
T1 - Commit transaction
Both T1 and T2 are dead.

Hibernate对于SQLServer 2005会执行SQL:

复制代码 代码如下:

select account0_.col_id as col1_0_0_, account0_.col_balance as col2_0_0_ from tb_account account0_ with (updlock, rowlock) where account0_.col_id=?

为选定的col_id为1的数据行加上行锁和更新锁。

2.乐观锁:假定当前事务操作的数据不会有其他事务同时访问,因此完全依靠数据库
的隔离级别来自动管理锁的工作。在应用程序中采用版本控制来避免可能低概率出现
的并发问题。

在Hibernate中,使用Version注解来定义版本号字段。

将DirtyLock中的Account对象替换成AccountVersion,其他代码不变,执行出现异常。

package com.cdai.orm.hibernate.transaction; 

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Version; 

@Entity
@Table(name = "tb_account_version")
public class AccountVersion { 

  @Id
  @Column(name = "col_id")
  private long id; 

  @Column(name = "col_balance")
  private long balance; 

  @Version
  @Column(name = "col_version")
  private int version; 

  public AccountVersion() {
  } 

  public AccountVersion(long id, long balance) {
    this.id = id;
    this.balance = balance;
  } 

  public long getId() {
    return id;
  } 

  public void setId(long id) {
    this.id = id;
  } 

  public long getBalance() {
    return balance;
  } 

  public void setBalance(long balance) {
    this.balance = balance;
  } 

  public int getVersion() {
    return version;
  } 

  public void setVersion(int version) {
    this.version = version;
  } 

}

log如下:

T1 - Begin trasaction
T2 - Begin trasaction
Hibernate: select accountver0_.col_id as col1_0_0_, accountver0_.col_balance as col2_0_0_, accountver0_.col_version as col3_0_0_ from tb_account_version accountver0_ where accountver0_.col_id=?
Hibernate: select accountver0_.col_id as col1_0_0_, accountver0_.col_balance as col2_0_0_, accountver0_.col_version as col3_0_0_ from tb_account_version accountver0_ where accountver0_.col_id=?
T1 - balance=1000
T2 - balance=1000
T1 - Change balance:900
T2 - Change balance:1100
Hibernate: update tb_account_version set col_balance=?, col_version=? where col_id=? and col_version=?
Hibernate: update tb_account_version set col_balance=?, col_version=? where col_id=? and col_version=?
T1 - Commit transaction
2264 [Thread-2] ERROR org.hibernate.event.def.AbstractFlushingEventListener - Could not synchronize database state with session
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.cdai.orm.hibernate.transaction.AccountVersion#1]
   at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:1934)
   at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2578)
   at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2478)
   at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2805)
   at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:114)
   at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:268)
   at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:260)
   at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:180)
   at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
   at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
   at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1206)
   at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:375)
   at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:137)
   at com.cdai.orm.hibernate.transaction.VersionLock$2.run(VersionLock.java:93)
Both T1 and T2 are dead.

由于乐观锁完全将事务隔离交给数据库来控制,所以事务1和2交叉运行了,事务1提交
成功并将col_version改为1,然而事务2提交时已经找不到col_version为0的数据了,所以
抛出了异常。

Hibernate查询方法比较
Hibernate主要有三种查询方法:

1.HQL (Hibernate Query Language)

和SQL很类似,支持分页、连接、分组、聚集函数和子查询等特性,
但HQL是面向对象的,而不是面向关系数据库中的表。正因查询语句
是面向Domain对象的,所以使用HQL可以获得跨平台的好处,Hibernate
会自动帮我们根据不同的数据库翻译成不同的SQL语句。这在需要支持
多种数据库或者数据库迁移的应用中是十分方便的。

但得到方便的同时,由于SQL语句是由Hibernate自动生成的,所以这不
利于SQL语句的效率优化和调试,当数据量很大时可能会有效率问题,
出了问题也不便于排查解决。

2.QBC/QBE (Query by Criteria/Example)

QBC/QBE是通过组装查询条件或者模板对象来执行查询的。这在需要
灵活地支持许多查询条件自由组合的应用中是比较方便的。同样的问题
是由于查询语句是自由组装的,创建一条语句的代码可能很长,并且
包含许多分支条件,很不便于优化和调试。

3.SQL

Hibernate也支持直接执行SQL的查询方式。这种方式牺牲了Hibernate跨
数据库的优点,手工地编写底层SQL语句,从而获得最好的执行效率,
相对前两种方法,优化和调试方便了一些。

下面来看一组简单的例子。

package com.cdai.orm.hibernate.query; 

import java.util.Arrays;
import java.util.List; 

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Expression; 

import com.cdai.orm.hibernate.annotation.Account; 

public class BasicQuery { 

  public static void main(String[] args) { 

    SessionFactory sessionFactory = new AnnotationConfiguration().
                      addFile("hibernate/hibernate.cfg.xml").
                      configure().
                      addPackage("com.cdai.orm.hibernate.annotation").
                      addAnnotatedClass(Account.class).
                      buildSessionFactory(); 

    Session session = sessionFactory.openSession(); 

    // 1.HQL
    Query query = session.createQuery("from Account as a where a.id=:id");
    query.setLong("id", 1);
    List result = query.list();
    for (Object row : result) {
      System.out.println(row);
    } 

    // 2.QBC
    Criteria criteria = session.createCriteria(Account.class);
    criteria.add(Expression.eq("id", new Long(2)));
    result = criteria.list();
    for (Object row : result) {
      System.out.println(row);
    } 

    // 3.QBE
    Account example= new Account();
    example.setBalance(100);
    result = session.createCriteria(Account.class).
            add(Example.create(example)).
            list();
    for (Object row : result) {
      System.out.println(row);
    } 

    // 4.SQL
    query = session.createSQLQuery(
        " select top 10 * from tb_account order by col_id desc ");
    result = query.list();
    for (Object row : result) {
      System.out.println(Arrays.toString((Object[]) row));
  } 

    session.close();
  } 

}
Hibernate: select account0_.col_id as col1_0_, account0_.col_balance as col2_0_ from tb_account account0_ where account0_.col_id=?
Account [id=1, balance=100]
Hibernate: select this_.col_id as col1_0_0_, this_.col_balance as col2_0_0_ from tb_account this_ where this_.col_id=?
Account [id=2, balance=100]
Hibernate: select this_.col_id as col1_0_0_, this_.col_balance as col2_0_0_ from tb_account this_ where (this_.col_balance=?)
Account [id=1, balance=100]
Account [id=2, balance=100]
Hibernate: select top 10 * from tb_account order by col_id desc
[2, 100]
[1, 100]

从log中可以清楚的看到Hibernate对于生成的SQL语句的控制,具体选择
哪种查询方式就要看具体应用了。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索java
, 数据库
, hibernate
, 查询

hibernate连接数据库、hibernate操作数据库、hibernate 数据库配置、hibernate查询数据库、hibernate访问数据库,以便于您获取更多的相关知识。

时间: 2024-10-27 06:28:33

Java的Hibernate框架数据库操作中锁的使用和查询类型_java的相关文章

详解Java的Hibernate框架中的set映射集与SortedSet映射_java

Set集合Set是一个java集合不包含任何重复的元素.更正式地说,Set不包含任何元素对e1和e2,使得e1.equals(e2),和至多一个空元素.所以被添加到一组对象必须实现equals()和hashCode()方法,使Java可以判断任何两个元素/对象是否是相同的. 集被映射到与映射表中<set>元素,并在java.util.HashSet中初始化.可以使用Set集合在类时,有一个集合中不需要重复的元素. 定义RDBMS表: 考虑一个情况下,我们需要我们的员工记录存储在EMPLOYEE

详解Java的Hibernate框架中的List映射表与Bag映射_java

List映射表List列表是一个java集合存储在序列中的元素,并允许重复的元素.此接口的用户可以精确地控制,其中列表中的每个元素插入.用户可以通过他们的整数索引访问元素,并搜索列表中的元素.更正式地说,列表通常允许对元素e1和e2,使得e1.equals(e2),它们通常允许多个null元素,如果他们允许的null元素. List列表被映射在该映射表中的<list>元素,并将java.util.ArrayList中初始化. 定义RDBMS表: 考虑一个情况,需要员工记录存储在EMPLOYEE

在Java的Hibernate框架中对数据库数据进行查询操作_java

Hibernate查询语言(HQL)是一种面向对象的查询语言,类似于SQL,但不是对表和列操作,HQL适用于持久对象和它们的属性. HQL查询由Hibernate转换成传统的SQL查询,这在圈上的数据库执行操作. 虽然可以直接使用SQL语句和Hibernate使用原生SQL,但建议使用HQL尽可能避免数据库可移植性的麻烦,并采取Hibernate的SQL生成和缓存策略的优势. 都像SELECT,FROM和WHERE等关键字不区分大小写,但如表名和列名的属性是区分在HQL敏感. FROM 语句使用

Java的Hibernate框架中用于操作数据库的HQL语句讲解_java

 上次我们一起学习了用Criteria进行相关的操作,但由于Criteria并不是Hibernate官方推荐的查询方式,我们也并不多用.现在我们来看一下官方推荐的HQL,一起学习一下它的强大.  说是HQL,也就是Hibernate查询语句,和SQL有什么区别呢?一个字母的区别,哈哈.  当然不是这样,HQL和SQL的区别在于思想的不同,HQL是用面向对象的方向进行查询,而SQL则是对数据库二维表进行查询,这里包含的是思想的不同.HQL实际上也是SQL,它由Hibernate帮我们在内部进行转换

详解Java的Hibernate框架中的Interceptor和Collection_java

Interceptor讲到Interceptor,相信熟悉struts2的童鞋肯定不会陌生了,struts2可以自定义拦截器进行自己想要的一系列相关的工作.而这里我们说的Interceptor也是差不多相似的功能.  废话不说,直接来代码:  下面这个是MyInterceptor类,它实现了Interceptor接口: public String onPrepareStatement(String arg0) { return arg0; } public boolean onSave(Obje

在Java的Hibernate框架中使用SQL语句的简单介绍_java

Hibernate中有HQL查询语法.但我们用得比较熟的还是数SQL语句,那么应该怎么来让Hibernate支持SQL呢?这个不用我们去考虑了,Hibernate团队已经早就做好了.        废话不说,直接来例子啦. select * from t_user usr     上面是一条SQL语句,又是废话,是个人都知道.我们想让Hibernate执行这条语句,怎么办呢?看代码: Query query = session.createSQLQuery("select * from t_us

解析Java的Hibernate框架中的持久化类和映射文件_java

持久化类Hibernate的整个概念是采取从Java类属性的值,并将持久到数据库表.一个映射文件Hibernate的帮助确定如何从拉动类的值,并将它们映射与表和相关的域. 其对象或实例将存储在数据库表中的Java类在Hibernate中称为持久化类. Hibernate的效果最好,如果这些类遵循一些简单的规则,也称为普通Java对象(POJO)编程模型.有下列持久化类的主要规则,但是,这些规则并不是必需的. 将所有的持久化Java类需要一个默认的构造函数. 所有类应该包含为了让容易识别对象内Hi

详解Java的Hibernate框架中的注解与缓存_java

注解Hibernate注解是一个没有使用XML文件来定义映射的最新方法.可以在除或替换的XML映射元数据使用注解. Hibernate的注解是强大的方式来提供元数据对象和关系表的映射.所有的元数据被杵到一起的代码POJO java文件这可以帮助用户在开发过程中同时要了解表的结构和POJO. 如果打算让应用程序移植到其他EJB3规范的ORM应用程序,必须使用注解来表示映射信息,但仍然如果想要更大的灵活性,那么应该使用基于XML的映射去. 环境设置Hibernate注释 首先,必须确保使用的是JDK

详解Java的Hibernate框架中的缓存与原生SQL语句的使用_java

Hibernate缓存缓存是所有关于应用程序的性能优化和它位于应用程序和数据库之间,以避免数据库访问多次,让性能关键型应用程序有更好的表现. 缓存对Hibernate很重要,它采用了多级缓存方案下文所述: 第一级缓存: 第一级缓存是Session的缓存,是一个强制性的缓存,通过它所有的请求都必须通过. Session对象不断自身的动力的对象,提交到数据库之前. 如果发出多个更新一个对象,Hibernate试图拖延尽可能长的时间做了更新,以减少发出的更新SQL语句的数量.如果您关闭会话,所有被缓存