Hibernate 5 的基本用法1

目录

ORM

Hibernate

Hibernate的数据库操作

Hibernate体系结构的各个对象

hibernate.cfg.xml等常用的配置属性

JDBC 连接属性

数据库方言

Hibernate 事务属性

二级缓存相关属性和 JNDI 数据源的连接属性

外连接抓取属性

其他常用的配置属性

深入理解持久化对象 

持久化类的要求

持久化对象的状态 

改变持久化对象状态的方法

持久化实体

根据主键加载持久化实体

更新持久化实体

删除持久化实体

HQL和批处理策略

批量增加用户

批量删除用户资料

批量修改用户资料

查询用户资料

ORM

Your relational data. Objectively. - Hibernate ORMIdiomatic persistence for Java and relational databases.http://hibernate.org/orm/

ORM 的全称是 Objec/Relation Mapping ,即对象/关系数据库映射。 ORM 可理解成一种规范,它概述了这类框架的基本特征:完成面向对象的编程语言到关系数据库的映射。

 ORM 工具的唯一作用就是:把对持久化对象的保存、删除、修改等操作,转换 成对数据库的操作。从此,程序员可以 以面向对象的方式操作持久化对象,而 ORM 框架则负责转换成对应的(SQL 结构化查询语句)操作。

ORM 工具提供了持久化类(一个普通Java类)和数据表之间的映射关系,ORM 基本映射有如下几条映射关系:

数据表映射类:持久化类被映射到一个数据表。程序使用这个持久化类来创建实例、 修改属性、删除实例时,系统自动会转换为对这个表进行 CRUD 操作。

数据表的行映射对象(即实例):持久化类会生成很多实例,每个实例就对应数据表中的一行记录。当程序在应用中修改持久化类的某个实例时, ORM 工具将会转换成对 对应数据表中特定行的操作。 

数据表的列(字段)映射对象的属性:当程序修改某个持久化对象的指定属性时(持久化实例映射到数据行), ORM 将会转换成对 对应数据表中指定数据行、指定列 的操作 。

Hibernate

Hibernate 是一个面向 Java 环境的对象/关系数据库映射工具,用于把面向对象模型表示的对象映射到基于 SQL 的关系模型的数据结构中。

Hibernate 是低侵入式的设计,完全采用普通的 Java 对象作为持久化对象使用。Hibernate 不要求持久化类继承任何父类,或者实现任何接口,这样保证代码不被污染。

Hibernate的数据库操作

先建立一个普通的、传统的 Java 对象(POJO)作为持久化类->User类。

以我所知有几种方法可以使User类具备持久化操作的能力,这里列两种:

一:为这个 POJO 添加一些注解:

@Entity
@Table(name="user") 
public class User{ 
//User类的标识属性
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private String name;
private String sex;
//省略所以属性的get和set方法
...
...
}

@Entity:注解声明该类是一个 Hibernate 的持久化类。

@Table:指定该类映射的表。 此处指定该类映射到user表。

@Id:用指定该类的标识属性。所谓标识属性,就是可以唯一标识该对象的属性, 标识属性通常映射到数据表的主键列。

@GeneratedValue 用于指定主键生成策略 ,其 strategy 属性指定了主键生成策略IDENTITY策略,也就是采用自动增长的主键生成策略。

二:配置名为User.hbm.xmlXML映射文件:

User.hbm.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.bnuz.domain">
    <class name="User" table="user">
        <id name="id" type="java.lang.Integer">
            <generator class="identity"/>
        </id>
        <property name="name"/>
        <property name="sex"/>
    </class>
</hibernate-mapping>

该xml文件要User同一目录,<class name="User" table="user_table">:这一句是配置一个对象对应数据库的哪一个表。

通过上面的两种方式选其中一种即可,官方一般推荐使用第一种方式,而目前少部分企业仍继续用第二种方式,两种方式本质上的一样的。

Hibernate 可以理解持久化类和数据表之间的对应关系,也可以理解持久化类的属性与数据表的各列之间的对应关系。

为了知道连接哪个数据库,以及连接数据库时所用的连接池、用户名和密码等详细信息。这些信息对于所有的持久化类都是通用的,Hibernate将这些通用信息称为配置信息,配置信息使用hibernate.cfg.xml 配置文件指定。

hibernate.cfg.xml

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <!-- 指定连接数据库所用的驱动 -->
        <property name="connection.driver_class">com.mysql.cj.jdbc.Driver</property>

        <!-- 指定连接数据库的URL,sys为数据库名 -->
        <property name="connection.url">
               jdbc:mysql://localhost:3306/sys?useSSL=true</property>

        <!-- 指定连接数据库的用户名 -->
        <property name="connection.username">root</property>

        <!-- 指定连接数据库的密码 -->
        <property name="connection.password">******</property>

        <!-- 指定连接池里最大连接数 -->
        <property name="hibernate.c3p0.max_size">20</property>

        <!-- 指定连接池里最小连接数 -->
        <property name="hibernate.c3p0.min_size">1</property>

        <!-- 指定数据库方言,根据所使用的数据库指定对应的方言 -->
        <property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>

        <!-- 根据需要自动创建数据表,如果没有创建数据表则需要指定对应方言 -->
        <property name="hbm2ddl.auto">update</property>

        <!-- 将SQL脚本进行格式化后再输出 -->
        <property name="hibernate.format_sql">true</property>

        <!-- 显示Hibernate持久化操作所生成的SQL输出到控制台 -->
        <property name="show_sql">true</property>

        <!-- 一:指定持续化类名来进行映射 -->
        <mapping class="com.bnuz.domain.User"/>

        <!-- 二:指定XML形式来进行映射 -->
        <mapping resource="com/bnuz/domain/User.hbm.xml"/>
    </session-factory>
</hibernate-configuration>

除此之外, Hibernate 并不推荐采用 DriverManager 来连接数据库,而是推荐使用数据源来管理数 库连接,这样能保证最好的性能。还可指定多个mapping元素,每个mapping元素指定一个持久化类。

下面是完成消息插入的代码:

UserManager.java
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

public class UserManager {
    public static void main(String[] args) throws Exception{

        //实例化 Configuration
        //不带参数的 configure()方法默认加载 hibernate.cfg xml 文件
        //如果传入 abc.xml 作为参数,则不再加载 hibernate.cfg xml ,改为加载 abc.xml
        Configuration conf = new Configuration().configure();

        SessionFactory sessionFactory = conf.buildSessionFactory();
        Session sess = sessionFactory.openSession();
        Transaction startTask = sess.beginTransaction();

        User user = new User();
        user.setName("lyq");
        user.setSex("男");

        sess.save(user);
        startTask.commit();
        sess.close();
        sessionFactory.close();

    }

}

执行前sys数据库里没有user表:

 执行后,自动生成了user表并插入数据:

Hibernate体系结构的各个对象

回顾一下刚刚的UserManger.java的部分代码:

Configuration conf = new Configuration().configure();
SessionFactory sessionFactory = conf.buildSessionFactory();
Session sess = sessionFactory.openSession();
Transaction startTask = sess.beginTransaction();
...
...
...
sess.save(user);
startTask.commit();
sess.close();
sessionFactory.close();

SessionFactory:这是Hibernate 的关键对象,它是单个数据库映射关系经过编译后的内存镜像,也是线程安全的。它是生成Session 的工厂,本身需要依赖于ConnectionProvider。该对象可以
在进程或集群的级别上,为那些事务之间可以重用的数据提供可边的二级缓存。

Session :它是应用程序与持久存储层之间交互操作的一个单线程对象。它也是Hibernate 持久化
操作的关键对象,所有的持久化对象必须在Session 管理下才可以进行持久化操作。此对象生存
期很短。它底层封装了JDBC 连接,它也是Transaction 的工厂。Session 对象持有必边的一级缓
存,在显式执行flush 之前,所有持久化操作的数据都在缓存中的Session 对象处。

持久化对象( Persistent Object ):系统创建的POJO 实例, 一旦与特定的Session 关联, 并对应数据表的指定记录, 该对象就处于持久化状态, 这一系列对象都被称为持久化对象。在程序中对持久化对象执行的修改,都将自动被转换为对持久层的修改。持久化对象完全可以是普通的
JavaBeans/POJO ,唯一的区别是它们正与一个Session 关联。

瞬态对象和脱管对象:系统通过new 关键宇创建的Java 实例,没有与Session 相关联, 此时处
瞬态。瞬态实例可能是在被应用程序实例化后,尚未进行持久化的对象。如果一个曾经持久
化过的实例,如果Session 被关闭则转换为脱管状态。

事务( Transaction ):代表一次原子操作,它具有数据库事务的概念。Hibernate 事务是对底层
具体的JDBC 、JTA 以及COREA 事务的抽象。在某些情况下, 一个Session 之内可能包含多个
Transaction 对象。虽然事务操作是可选的,但所有持久化操作都应该在事务管理下进行,即使
是只读操作。

连接提供者( ConnectionProvider ):它是生成JDBC 连接的工厂,它通过抽象将应用程序与底
层的DataSource 或DriverManager 隔离开。这个对象无须应用程序直接访问,仅在应用程序需要扩展时使用。

事务工厂( TransactionFactory ):它是生成Transaction 对象实例的工厂。该对象也无须应用程序直接访问。它负责对底层具体的事务实现进行封装,将底层具体的事务抽象成Hibernate 事务。

通过上面的介绍,可以知道Hibernate 的持久化操作离不开 SessionFactory 对象,这个对象是整个数据库映射关系经过编译后的内存镜像,该对象的 openSession() 方法可打开 Session 对象。该对象通常由Configuration 对象产生。

hibernate.cfg.xml等常用的配置属性

JDBC 连接属性

Hibernate 需要进行数据库访问,因此必须设置连接数据库的相关属性。所有Hibernate 属性的名字和语义都在 org.hibemate.cfg.Environment 中定义。

下面是关于JDBC 连接配置中最重要的设置:
connection.driver class :设置连接数据库的驱动。
connection.url : 设置所需连接数据库服务的URL 。
connection.usemame :设置连接数据库的用户名。
connection. password : 设置连接数据库的密码。
connection.pool_size :设置Hibernate 数据库连接池的最大并发连接数。
dialect:设置连接数据库所使用的方言。

上面的hibernate . connection.pool_size 属性用于配置Hibernate 连接池的最大并发连接数,但Hibernate 自带的连接池仅有测试价值,并不推荐在实际项目中使用。在实际项目中可以使用C3PO 或Proxool 连接池。

<!-- 指定连接池里最大连接数 -->
<property name="hibernate.c3p0.max_size">20</property>
<!-- 指定连接池里最小连接数 -->
<property name="hibernate.c3p0.min_size">1</property>

数据库方言

Hibernate 底层依然使用SQL 语句来执行数据库操作, 虽然所有关系数据库都支持使用标准SQL 语句,但所有数据库都对标准SQL 进行了一些扩展,所以在语法细节上存在一些差异。因此,H ibernate需要根据数据库来识别这些差异。

告诉 Hibernate 应用程序的底层即将使用哪种数据库一一这就是数据库方言。

不同数据库所使用的方言如下:

 

Hibernate 事务属性

事务也是Hibernate 持久层访问的重要方面, Hibernate 不仅提供了局部事务支持,也允许使用容器管理的全局事务。Hibernate 关于事务管理的属性有如下几个:

hibernate.transaction.factory_class : 指定Hibernate 所用的事务工厂的类型, 该属性值必须是TransactionFactory 的直接或间接子类。
jta.UserTransaction : 该属性值是一个JNDI 名, Hibernate 将使用JTA TransactionFactory 从应用服务器获取JTAUserTransaction。
hibernate.transaction.manager_lookup_class:该属性值应为一个TransactionManagerLookup 类名,当使用JVM 级别的缓存时,或者在JTA 环境中使用hilo 生成器策略时,需要该类。
hibernate. transaction.flush_before_completion :指定Session 是否在事务完成后自动将数据刷新( flush)到底层数据库。该属性值只能为true或false。现在更好的方法是使用Context 相关的
Session 管理。
hibernate.transaction.auto_close_session:指定是否在事务结束后自动关闭Session 。该属性值只能是true或false。现在更好的方法是使用 Context 相关的 Session 管理。

二级缓存相关属性和 JNDI 数据源的连接属性

如果无须Hibernate 自己管理数据源,而是直接访问容器管理数据源, Hibernate 可使用JNDI数据源的相关配置。

Hibernate的SessionFactory可持有一个可选的二级缓存,通过使用这种二级缓存可以提高Hibernate的持久化访问的性能。

关于这两个属性请自行查阅。

外连接抓取属性

外连接抓取能限制执行SQL i吾句的次数来提高效率, 这种外连接抓取通过在单个select i吾句中使
用outer join 来一次抓取多个数据表的数据。
外连接抓取允许在单个select 语句中,通过@Man y ToOne 、@ OneToMany 、@ ManyToMany 和@ OneToOne 等关联获取连接对象的整个对象图。

将hibernate.max fetchdepth 设为0 ,将在全局范围内禁止外连接抓取, 设为l 或更高值能启用N
一l 或1 一l 的外连接抓取。除此之外,还应该在持久化注解中通过fetch=FetchType.EAGER 来指定这种外连接抓取。

其他常用的配置属性

除了上面介绍的必要配置属性之外, Hibernate 常用的配置属性还有如下几个。
hibemate.show_sql:是否在控制台输出Hibernate 持久化操作底层所使用的SQL 语句。只能为true和false 两个值。
hibernate.format_sql:是否将SQL 语句转成格式良好的SQL 。只接受true 和false 两个值。
hibernate.use_sqI_comments:是否在Hibernate 生成的SQL 语句中添加有助于调试的注释。只接受true 和false 两个值。
hibemate.jdbc.fetch_size :指定JDBC 抓取数量的大小。它可接受一个整数值,其实质是调用
Statement.setFetchSize()方法。
hibemate.jdbc.batch_size:指定Hibernate 使用JDBC2 的批量更新的大小。它可接受一个整数值,建议取5 ~ 30 之间的值。
hibemate.connection.autocommit: 设置是否自动提交。通常不建议打开自动提交。
hibemate.hbm2ddl.auto :设置当创建SessionFactory 时,是否根据持久化类的映射关系自动建立数据库表。该属性可以为validate 、update 、create 和create-drop 这4 个值。

如果设置为create,每次创建SessionFactory 时都会重新建表,因此前面插入的数据会丢失;

如果设置为create-drop ,每次显式关闭SessionFactory 时,程序会自动Drop刚刚创建的数据表;

如果设置为update ,每次创建SessionFactory 时, 如果数据库中没有与持久化类对应的表, Hibernate 会自动建表;如果数据库中己有与持久化类对应的表,则保留己有的数据表和数据,只是更新或插入数据,因此通常将该属性设置为update 。
当然,Hibernate 配置文件中的配置属性还有很多,因为篇幅关系,此处不再一一列举。如果读者
需要关于这些配置属性的详细介绍,请参考Hibernate 官方参考文档。

深入理解持久化对象 

Hibernate 是一个纯粹的O/RMapping 框架,通过Hibernate 的支持,程序开发者只需要管理对象的
状态,无须理会底层数据库系统的细节。相对于常见的JDBC 持久层方案中需要手工管理SQL 语句,Hibernate 采用完全面向对象的方式来操作数据库。对于程序开发者而言,眼里只有对象、属性,无须理会底层数据表、数据列等概念。 

持久化类的要求

虽然Hibernate 对持久化类没有太多的要求,但还是应该遵守如下规则:

提供一个无参数的构造器:所有的持久化类都应该提供一个无参数的构造器,这个构造器可以
不采用public访问控制符。只要提供了无参数的构造器,Hiberτiate就可以使用
Constructor.newlnstance()来创建持久化类的实例了。通常,为了方便Hibernate 在运行时生成代
理,构造器的访问控制修饰符至少是包可见的,即大于或等于默认的访问控制符。

提供一个标识属性:标识属性通常映射数据库表的主键宇段。这个属性可以叫任何名字,其类
型可以是任何的基本类型、基本类型的包装类型、java.lang.String 或者java.util.Date。如果使用了数据库表的联合主键,甚至可以用一个用户自定义的类,该类拥有这些类型的属性。当然,
也可以不指定任何标识属性,而是在持久化注解中直接将多个普通属性映射成一个联合主键,
但通常不推荐这么做。 

为持久化类的每个成员变量提供set和get方法: Hibernate 默认采用属性方式来访问持久化
类的成员变量。如果持久化类有foo 成员变量,则应该提供setFoo()和getFoo()方法。Hibernate
持久化JavaBeans 风格的属性,认可如下形式的方法名: getFoo()、isFoo()和setFoo()。如果需
要,也可以切换属性的访问策略。

使用非final 的类:在运行时生成代理是Hibernate 的一个重要功能。如果持久化类没有实现任何接口, Hibernate 使用Javassist 生成代理,该代理对象是持久化类的子类的实例。如果使用了
final 类, 则无法生成Javassist 代理,将无法进行性能优化。还有一个可边的策略,就是让Hibernate持久化类实现一个所有方法都声明为public 的接口,此时将使用JDK 的动态代理。同时应应避免在非final 类中声明public final 的方法。如果非要使用一个有public final 方法的类, 则必须通过设置lazy=”false"来明确地禁用代理。

重写equals()和hashCode()方法:如果需要把持久化类的实例放入Set 中(当需要进行关联映射
时,推荐这么做),则应该为该持久化类重写equals()和hashCode()方法。

实现equals()/hashCode()最显而易见的方法是比较两个对象标识属性的值。如果值相同,则两个对象对应于数据库的同一行,因此它们是相等的(如果都被添加到Set 中,则Set 中只有一个元素)。遗憾的是,对采用自动生成标识值的对象不能使用这种方法。Hibernate 仅为那些持久化对象指定标识值,一个新创建的实例将不会有任何标识值。因此,如果一个实例没有被保存过,但它又确实在一个Set中,那么保存它将会给这个对象赋一个标识值。如果equals()和hashCode()是基于标识值实现的,则其hashCode 返回值会发生改变,这将违反Set 的规则。

持久化对象的状态 

Hibernate 持久化对象支持如下几种对象状态:

瞬态:对象由new操作符创建,且尚未与Hibernate Session 关联的对象被认为处于瞬态。瞬态
对象不会被持久化到数据库中,也不会被赋予持久化标识。如果程序中失去了瞬态对象的引用,
瞬态对象将被垃圾回收机制销毁。可以使用Hibernate Session 可以将其变为持久化状态。 

持久化:持久化实例在数据库中有对应的记录,并拥有一个持久化标识( identifier )。持久化的实例可以是刚刚保存的,也可以是刚刚被加载的。无论哪一种,持久化对象都必须与指定的
Hibernate Session 关联。Hibernate 会检测到处于持久化状态对象的改动,在当前操作执行完成时
将对象数据写回数据库。开发者不需要手动执行update 。

脱管:某个实例曾经处于持久化状态,但随着与之关联的Session被关闭, 该对象就变成脱管状
态。脱管对象的引用依然有效,对象可继续被修改。如果重新让脱管对象与某个Session 关联,
这个脱管对象会重新转换为持久化状态,而脱管期间的改动不会丢失,也可被写入数据库。正
是因为这个功能,逻辑上的长事务成为可能,它被称为应用程序事务,即事务可以跨越用户的
思考,因为当对象处于脱管状态时,对该对象的操作无须锁定数据库,不会造成性能的下降。 

下面为Hibernate 持久化对象的状态演化图:

改变持久化对象状态的方法

持久化实体

为了让瞬态对象转换为持久化状态, Hibernate Session 提供了如下几个方法:
Serializable save(Object obj):将obj 对象变为持久化状态,该对象的属性将被保存到数据库。
void persist(Object obj ):将obj 对象转化为持久化状态, 该对象的属性将被保存到数据库。

//创建消息实例
User user =new User();
//设置消息标题和消息内容
user.setName("lyq");
user.setSex("男");
//保存消息
sess.save(user);

把一个瞬态实体变成持久化状态时, Hibernate 会在底层对应地生成一条insert 语句,这条语句负责把该实体对应的数据记录插入数据表。

使用save()方法保存持: 久化对象时,该方法返回该持久化对象的标识属性值(即对应记录的主键值);但使用persist()方法来保存持久化对象时,该方法没有任何返回值。因为save()方法需要立即返回持久化对象的标识属性值,所以程序执行save()方法会立即将持久化对象对应的数据插入
数据库; 而persist()则保证当它在一个事务外部被调用时,并不立即转换成insert 语句。这个功能是很有用的,尤其是需要封装一个长会话流程的时候, persist()方法就显得尤为重要了。

根据主键加载持久化实体

程序可以通过load()来加载一个持久化实例,这种加载就是根据持久化类的标识属性值加载持久化实例。其实质就是根据主键从数据表中加载一条新记录。

User user = sess.load(User.class, 1);
//上面代码中的 1 就是需要加载的持久化实例的标识属性。即返回主键为1的持久化类

如果没有匹配的数据库记录, load()方法可能抛出HibemateException 异常;如果在持久化注解中指定了延迟加载,则load()方法会返回一个未初始化的代理对象(可以理解为持久化对象的替身),这个代理对象并没有加载数据记录,直到程序调用该代理对象的某方法时, Hibernate 才会去访问数据库。

与load()方法类似的是get()方法, get()方法也用于根据主键加载持久化实例,但get()方法会立刻访
问数据库,如果没有对应的记录, get()方法返回null ,而不是返回一个代理对象。
当程序通过load()或get()方法加载实体时, Hibernate 会在底层对应地生成一条select 语句,这条select语句带有"where <主键列>=<标识属性值>"子句,表明将会根据主键加载。

更新持久化实体

对于一个曾经持久化过、但现在己脱离了Session 管理的持久化对象,它被认为处于脱管状态。当
程序修改脱管对象的状态后,程序应该显式地使用新的Session 来保存这些修改。Hibernate 提供了update()、merge()和updateOrSave()等方法来保存这些修改。

User n = firstSess.load(User.class , 1);
//第一个Session 已经关闭了
firstSess.close();
//修改脱管状态下的持久化对象
n.setName("新标题");
//打开第二个Session
Session secondSess = ...
//保存脱管对象所做的修改
secondSess.update(n);

 使用另一个Session 保存这种修改后,该脱管对象再次回到Session 的管理之下,也就再次回到持
久化状态。

当需要使用update()来保存程序对持久化对象所做的修改时,如果不清楚该对象是否曾经持久化过,那么程序可以选择使用updateOrSave()方法,该方法自动判断该对象是否曾经持久化过,如果曾经持久化过,就执行update()操作;否则将执行save()操作。

merge()方法也可将程序对脱管对象所做的修改保存到数据库,但merge ()与update()方法的最大区
别是: merge()方法不会持久化给定的对象。

举例来说,当程序执行sess.update(a)代码后, a 对象将会变成持久化状态:而执行sess . merge(a)代码后, a 对象依然不是持久化状态, a 对象依然不会被关联到Session上,merge()方法会返回a 对象的副本该副本处于持久化状态。
当程序使用merge()方法来保存程序对脱管对象所做的修改时,如果Session 中存在相同持久化标识的持久化对象, merge()方法里提供的对象状态将覆盖原有持久化实例的状态。如果Session 中没有相应的持久化实例,则尝试从数据库中加载,或者创建新的持久化实例,最后返回该持久化实例。merge()方法代替了Hibernate 早期版本的saveOrUpdateCopy()方法。

使用load()或get()方法加载持久化对象时,还可指定一个“锁模式”参数。Hibernate 的LockOptions
对象代表"锁模式",LockOptions 提供了READ 和 UPRREAD两个静态属性来代表共享、修改锁。

User n = Session.get(User.class , pk, LockOptions.UPGRADE);

删除持久化实体

还可以通过Sess ion 的de l ete ()方法来删除该持久化实例, 一旦删除了该恃久化实例,该持久化实例对应的数据记录也将被删除。

User n = sess.load(User.class, 1);
sess.delete(n);

HQL和批处理策略

面对批量处理内容的场景, Hibernate 提供了批量处理的解决方案。下面分别从批量插入、批量更新和批量删除三个方面介绍如何面对这种批量处理的情况。

根据上面的User例子继续进行示范。

创建一个HibernateUtil类,使用单例模式控制管理sessionFactory:

package hibernate;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {

    public static final SessionFactory sessionFactory;

    //创建线程变量
    public static final ThreadLocal<Session> sessions = new ThreadLocal<Session>();

    //静态代码块
    static {
        try {
            Configuration configuration = new Configuration().configure();
            sessionFactory = configuration.buildSessionFactory();
        }
        catch (Throwable ex){
            System.out.println("失败" + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static Session currentSession() throws HibernateException{
        Session s = sessions.get();
        if(s == null){
            s = sessionFactory.openSession();
            sessions.set(s);
        }
        return s;
    }

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

批量增加用户

private void addUsers() throws Exception {
        //打开线程安全的Session对象
        Session session = HibernateUtil.currentSession();;
        Transaction tx = session.beginTransaction();
        for(int i = 0; i < 100; i++){
            User u = new User();
            u.setName("lyq" + i);
            u.setSex("男");
            session.save(u);
            if(i % 20 == 0){
                //flush()方法用于打断session操作,目的让它执行完一部分内容
                session.flush();
                //clear()用于消除一级缓存
                session.clear();
            }
        }
        tx.commit();
        HibernateUtil.closeSession();
    }

批量删除用户资料

private void deleteAllUser() throws Exception{
        Session sess = HibernateUtil.currentSession();;
        Transaction tx = sess.beginTransaction();

        //删除符合条件的用户
        String deleteHql = "delete User";
        int deletedEntities = sess.createQuery(deleteHql).executeUpdate();
        if(deletedEntities>0){
            System.out.println("删除成功");
        }else {
            System.out.println("删除失败");
        }
        tx.commit();
        HibernateUtil.closeSession();
    }

批量修改用户资料

private void updateAllUser() throws Exception {
        Session sess = HibernateUtil.currentSession();;
        Transaction tx = sess.beginTransaction();

        //获得表中所有User
        ScrollableResults users = sess.createQuery("from User")
                .setCacheMode(CacheMode.IGNORE)
                .scroll(ScrollMode.FORWARD_ONLY);
        int count = 0;
        while(users.next()){
            User u = (User)users.get(0);
            u.setName("lyqs"+count);
            if(++count % 20 == 0){
                sess.flush();
                sess.clear();
            }
        }

        tx.commit();
        HibernateUtil.closeSession();
    }

 这种做法效执行效率不高(先查询又每行进行更新),性能非常低下。为了避免这种情况,Hibernate提供了一种类似于DML语句的批量更新、批量删除的HQL语句。如同刚刚上面删除用户资料的例子。关于HQL相关后面会讲到。

批量更新、批量删除的语法格式如下:

update | delete from? <ClassName> [where where_conditions]

关于上面的语法格式有如下四点值得注意:

1.在from子句中,from关键字是可选的,即完全可以不用from关键字。

2.在from子句中只能有一个类名,可以在该类名后指定别名。

3.不能在批量 HQL 语句中使用连接,显式或者隐式的都不行。但可以在 WHERE 子句中使用子查询。

4.整个 where 子句是可选的,where 子句的语法和 HQL 语句中 where 子句的语法完全相同。

private void updateAllUser() throws Exception {
        Session sess = HibernateUtil.currentSession();;
        Transaction tx = sess.beginTransaction();

        //定义批量更新 HQL 语句
        String hqlUpdate ="update User u SET name = :newName WHERE id = :i";
        for (int i = 1; i <= 100; i++){
            //执行更新
            int updatedEntities = sess.createQuery(hqlUpdate).
                    setParameter("newName", "lyq" + i).
                    setParameter("i", i).
                    executeUpdate();
        }
        
        tx.commit();
        HibernateUtil.closeSession();
    }

 

查询用户资料

HQL 语句被设计为完全面向对象的查询, 它可以理解如继承、 多态和关联之类的概念。而且 HQL 可以使用绝大部分 SQL 函数、 EJB 3.0 操作和函数,并提供了一些 HQL 函数,用以提高 HQL 查询的功能。

HQL 查询依赖于Query 类,每个Query 实例对应一个查询对象。使用HQL 查询按如下步骤进行:

1.获取Hibernate Session 对象。
2.编写HQL 语句。
3.以HQL 语句作为参数,调用Session 的createQuery()方法创建查询对象。
4.如果HQL 语句包含参数,则调用Querγ 的setXxx()方法为参数赋值。
5.调用Query对象的getResultList()或gets ingleResult()方法返回查询结果列表或单条结果。

private void findAllUser(){
        Session sess = HibernateUtil.currentSession();;
        Transaction tx = sess.beginTransaction();

        //将表中所以行存进List集合里面
        List<User> list = sess.createQuery("from User").getResultList();
        for (User u : list){
            System.out.println(u.getName());
        }

        tx.commit();
        HibernateUtil.closeSession();
    }

 从控制台可看出成功查询所以行。

自定义删除方法

删除name为lyq1的行:

private void delete(String deleteHql) throws Exception {
        Session sess = HibernateUtil.currentSession();;
        Transaction tx = sess.beginTransaction();

        String hqlDelete ="DELETE FROM User WHERE name = '" + deleteHql + "'";
        int deletedEntities = sess.createQuery(hqlDelete).executeUpdate();
        if(deletedEntities > 0){
            System.out.println("删除成功");
        }else {
            System.out.println("删除失败");
        }

        tx.commit();
        HibernateUtil.closeSession();
    }

全部代码整合如下:

import domain.User;
import hibernate.HibernateUtil;
import org.hibernate.*;

import java.util.List;


public class UserManager {

    public static void main(String[] args) throws Exception{
        UserManager um = new UserManager();
//        um.addUsers();
//        um.deleteAllUser();
//        um.updateAllUser();
//        um.findAllUser();
        um.delete("lyq1");
    }

    private void addUsers() throws Exception {
        //打开线程安全的Session对象
        Session session = HibernateUtil.currentSession();;
        Transaction tx = session.beginTransaction();
        for(int i = 0; i < 100; i++){
            User u = new User();
            u.setName("lyq" + i);
            u.setSex("男");
            session.save(u);
            if(i % 20 == 0){
                //flush()方法用于打断session操作,目的让它执行完一部分内容,clear()用于消除缓存
                session.flush();
                session.clear();
            }
        }
        tx.commit();
        HibernateUtil.closeSession();
    }

    private void deleteAllUser() throws Exception{
        Session sess = HibernateUtil.currentSession();;
        Transaction tx = sess.beginTransaction();

        //删除符合条件的用户
        String deleteHql = "delete User";
        int deletedEntities = sess.createQuery(deleteHql).executeUpdate();
        if(deletedEntities > 0){
            System.out.println("删除成功");
        }else {
            System.out.println("删除失败");
        }
        tx.commit();
        HibernateUtil.closeSession();
    }

    private void updateAllUser() throws Exception {
        Session sess = HibernateUtil.currentSession();;
        Transaction tx = sess.beginTransaction();

        //定义批量更新 HQL 语句
        String hqlUpdate ="update User u SET name = :newName WHERE id = :i";
        for (int i = 1; i <= 100; i++){
            //执行更新
            int updatedEntities = sess.createQuery(hqlUpdate).
                    setParameter("newName", "lyq" + i).
                    setParameter("i", i).
                    executeUpdate();
        }

        tx.commit();
        HibernateUtil.closeSession();
    }

    private void findAllUser(){
        Session sess = HibernateUtil.currentSession();;
        Transaction tx = sess.beginTransaction();

        //将表中所以行存进List集合里面
        List<User> list = sess.createQuery("from User").getResultList();
        for (User u : list){
            System.out.println(u.getName());
        }

        tx.commit();
        HibernateUtil.closeSession();
    }

    private void delete(String deleteHql) throws Exception {
        Session sess = HibernateUtil.currentSession();;
        Transaction tx = sess.beginTransaction();

        String hqlDelete ="DELETE FROM User WHERE name = '" + deleteHql + "'";
        int deletedEntities = sess.createQuery(hqlDelete).executeUpdate();
        if(deletedEntities > 0){
            System.out.println("删除成功");
        }else {
            System.out.println("删除失败");
        }

        tx.commit();
        HibernateUtil.closeSession();
    }

}

由于篇幅过长,关于映射方面会在第二篇中...

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值