目录
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.xml的XML映射文件:
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();
}
}
由于篇幅过长,关于映射方面会在第二篇中...