hibernate
一、 IntroductionHibernate是一个开源的对象关系映射(ORM)框架,对JDBC进行了轻量级的封装,将pojo与数据库表建立映射关系,是全自动的orm框架。它可以自动生成sql语句,自动执行。Hibernate可以在java的客户端程序使用,也可以在web端使用,完成数据持久化的任务。二、 核心API1. SessionSess...
一、 Introduction
Hibernate是一个开源的对象关系映射(ORM)框架,对JDBC进行了轻量级的封装,将pojo与数据库表建立映射关系,是全自动的orm框架。它可以自动生成sql语句,自动执行。Hibernate可以在java的客户端程序使用,也可以在web端使用,完成数据持久化的任务。
二、 核心API
1. Session
Session接口负责执行被持久化对象的crud操作。Session对象非线程安全。
2. SessionFactory
SessionFactory接口负责初始化hibernate。它代理数据存储源,并负责创建session对象。
3. Transaction
Transaction接口是可选的,可以不使用它,由hibernate自己的底层事务处理代码来取代。
4. Query
Query接口方便的实现对数据库及持久对象进行查询。有两种表达方式:HQL语言和本地数据库的SQL语句。
5. Criteria
Criteria接口与Query接口非常类似,不能在session之外使用。
6. Configuration
Configuration类的作用是对hibernate进行配置,加载核心配置文件,以及启动hibernate。它是启动hibernate遇到的第一个对象。
三、 使用步骤
1. 创建表结构
2. 创建web工程
3. 导入必须的jar包
4. 编写pojo类
5. 创建pojo类与表结构的映射
6. 创建核心配置文件
7. 编写测试代码
8. 例子
Pojo略,注意不要使用基本数据类型,一律使用包装类,因为包装类的默认值为null,而基本类型为0,可能会不符合实际。
Pojo类与表结构的映射,文件名为:类名.hbm.xml
<?xml version="1.0"encoding="UTF-8"?>
<!DOCTYPE hibernate-mappingPUBLIC
"-//Hibernate/HibernateMapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.hibernate.pojo.Customer"table="cst_customer">
<id name="cust_id"column="cust_id">
<generator class="native"/>
</id>
<property name="cust_name"column="cust_name"></property>
<property name="cust_user_id"column="cust_user_id"></property>
<property name="cust_create_id"column="cust_create_id"></property>
<property name="cust_source"column="cust_source"></property>
<property name="cust_industry"column="cust_industry"></property>
<property name="cust_level"column="cust_level"></property>
<property name="cust_linkman"column="cust_linkman"></property>
<property name="cust_phone"column="cust_phone"></property>
<property name="cust_mobile"column="cust_mobile"></property>
</class>
</hibernate-mapping>
核心配置文件
<?xml version="1.0"encoding="UTF-8"?>
<!DOCTYPE hibernate-configurationPUBLIC
"-//Hibernate/HibernateConfiguration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 配置数据库连接信息 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///hibernate_day01</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">1234</property>
<!-- 数据库的方言: -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 可选配置 -->
<!-- 显示SQL语句,在控制台显示. -->
<!-- 配置映射 -->
<mapping resource="com/hibernate/pojo/Customer.hbm.xml"/>
</session-factory>
</hibernate-configuration>
测试代码
public class Test1 {
/**
* 1,加载配置文件
* 2,创建sessionfactory对象,生成session对象
* 3,session对象
* 4,开启事务
* 5,编写保存代码
* 6,提交事务
* 7,释放资源
*/
@Test
public voidtest1(){
//1,利用configuration对象加载配置文件
Configuration config=new Configuration();
//加载src下面的名字叫hibernate.cfg.xml的配置文件,这个文件名固定为这个,不可以改
config.configure();
//2,创建sessionfactory对象
SessionFactory sessionFactory=config.buildSessionFactory();
//3,开启session会话
Session session=sessionFactory.openSession();
//4,开启事务
Transaction trans=session.beginTransaction();
//5,编写保存代码
Customer c=new Customer();
c.setCust_name("hhh");
c.setCust_linkman("还好");
c.setCust_industry("哈哈哈");
//保存对象,操作对象相应于操作数据库的表结构,因为对象和表做了映射关系,这个是和mybatis不一样的地方
session.save(c);
//6,提交事务
trans.commit();
//7,释放资源
session.close();
}
四、 关于配置文件的其他配置
1. 单表映射关系文件的配置
<class name=”类全路径” table=”表名” catalog=”数据库名”(一般无需配置catalog)>
<id name=”类中的id字段对应的属性” column=”主键的字段”>
<!- -主键的生成策略 native:如果是mysql,则主键递增;如果是oracle,则序列方式递增 - - >
<generator class=”native” />
</id>
<!- - 配置其他属性的映射
length:如果没有设置length,则是使用默认长度;如果设置,则使用设置的长度。利用hibernate,可以通过pojo,生成表结构,这个length就能体现出作用。如果是先有表结构,后配置的映射文件,则length使用表结构中的设置。
type:type有至少3中写法,java中的数据类型,hibernate的类型,数据库中的字段类型。为了避免麻烦,一般不使用。
- - >
<property name=”属性名” column=”字段名” length=”设置的长度”
</class>
2. 核心配置文件的配置
关于<session-factory>的可选配置
1) 是否在控制台显示sql语句
<property name="hibernate.show_sql">true</property>
2) 格式化sql语句
<property name="hibernate.format_sql">true</property>
3) 生成数据库表结构
<property name="hibernate.hbm2ddl.auto">create</property>
create,create-drop属性每次都新建表,请不要在开发用啊。
开发请用update或validate
update:有表用原来的表,没有表创建。添加表的字段,在pojo和映射文件中添加好,就可以修改表的字段,不支持删除字段。
validate:如果有表,用原来表。校验在映射文件中,pojo的属性和表的字段是否一致,不一致报错。
4) 配置核心配置文件可以使用properties文件,但是太麻烦,所有开发不用。
五、 详解主要API
1. Configuration
作用:配置hibernate,加载核心配置文件,提供手动加载映射文件。
//1,利用configuration对象加载配置文件
//Configuration config=new Configuration();
//加载src下面的名字叫hibernate.cfg.xml的配置文件,这个文件名固定为这个,不可以改
//config.configure();
//使用方法链简写代码
new Configuration().configure();
//如果使用属性文件,需要手动加载映射文件
//config.addResource("com/hibernate/pojo/Customer.hbm.xml");
2. SessionFactory
SessionFactory不是一个轻量级的,一个项目一般只需要一个SessionFactory。整合ssh后,利用spring可以是整个项目贯穿一个sessionfactory。
负责初始化hibernate,简单的sql语句缓存在sessionFactory里面,提高效率。在结构组成上,sessionfactory一般分为内部结构和外部结构两部分。内部结构,一般是进行缓存,由框架缓存sql语句,加载的配置。外部结构,一般是二级缓存,由sessionfactory创建的session是一级缓存,因为session的生命周期是从创建到完成sql语句,生命周期短暂,是一级缓存,为了提高效率,可以使用sessionfactory的外部结构作为二级缓存。但是开发中,一般使用更好的技术做缓存,如redis。
封装sessionFactory工具类的例子:
封装sessionfactory工具类
public class HibernateUtil {
private staticConfiguration CONFIG;
private staticSessionFactory FACTORY;
static{
CONFIG=new Configuration().configure();
FACTORY=CONFIG.buildSessionFactory();
}
public staticSession getSession(){
return FACTORY.openSession();
}
}
测试:
public voidtest2(){
//加载配置文件,获取sesseionFactory,获取session
Session session=HibernateUtil.getSession();
Transaction tr=session.beginTransaction();
Customer c=new Customer();
c.setCust_industry("2");
c.setCust_name("hah");
c.setCust_mobile("1324");
session.save(c);
tr.commit();
session.close();
}
3. Session
非线程安全,轻量级,生命周期大概是一个请求过程,session具有一个缓存,一般称之为hibernate的一级缓存。
常用的方法:
1) save(obj) 插入数据
2) delete(obj) 删除数据
关于修改或删除的标准操作。需要先查要修改的内容,在修改或删除。
例子:
public voidtestDelete(){
//加载配置文件,获取sesseionFactory,获取session
Session session=HibernateUtil.getSession();
Transaction tr=session.beginTransaction();
Customer cid=session.get(Customer.class,3L);
System.out.println(cid);
session.delete(cid);
tr.commit();
session.close();
}
3) get(Class,id) 根据主键查询映射表的信息
Class,javabean的class类对象。id,对应主键的值。
例子:
public voidtestGet(){
//加载配置文件,获取sesseionFactory,获取session
Session session=HibernateUtil.getSession();
Transaction tr=session.beginTransaction();
Customer cid=session.get(Customer.class,3L);
System.out.println(cid);
tr.commit();
session.close();
}
4) update(obj) 修改数据
例子:
public voidtestUpdate(){
//加载配置文件,获取sesseionFactory,获取session
Session session=HibernateUtil.getSession();
Transaction tr=session.beginTransaction();
Customer cid=session.get(Customer.class,2L);
System.out.println(cid);
cid.setCust_name("看看");
cid.setCust_level("2");
session.update(cid);
tr.commit();
session.close();
}
5) saveOrUpdate(obj) 保存或者修改
注意:不要设置id的值。如果要修改,要先查询再修改。
例子:
public voidtestSaveOrUpdate(){
//加载配置文件,获取sesseionFactory,获取session
Session session=HibernateUtil.getSession();
Transaction tr=session.beginTransaction();
Customer c=new Customer();
c.setCust_name("hill");
//这是错误的做法
//c.setCust_id(2L);
session.saveOrUpdate(c);
Customer cid=session.get(Customer.class,2L);
System.out.println(cid);
cid.setCust_name("看33看");
cid.setCust_level("2");
session.saveOrUpdate(cid);
tr.commit();
session.close();
}
6) createQuery() HQL语句的查询
例子:
public voidtestquery(){
//加载配置文件,获取sesseionFactory,获取session
Session session=HibernateUtil.getSession();
Transaction tr=session.beginTransaction();
Query query=session.createQuery("from Customer");
List<Customer> clist=query.list();
for(Customer c:clist){
System.out.println(c);
}
tr.commit();
session.close();
}
4. Transaction
事务的接口。
常用的方法:commit() 提交事务 rollback() 回滚事务
例子:
public voidtestTrans(){
Session session=null;
Transaction tr=null;
try{
session=HibernateUtil.getSession();
tr=session.beginTransaction();
Customer c=new Customer();
c.setCust_name("事务");
session.save(c);
int i=10/0;
tr.commit();
}catch(Exception e){
tr.rollback();
e.printStackTrace();
}finally{
if(session!=null){
session.close();
}
}
}
六、 持久化类
持久化类是一个javabean,当某个javabean与表建立映射关系就成了持久化类。在Mybatis中,没有这个概念。这是hibernate在入门上比mybatis门槛高的一个体现。
持久化类的编写规范:
1. 提供一个无参数public构造器;
2. 提供一个标识属性,映射数据表主键字段;
3. 提供public的set或者get方法
4. 标识属性尽量采用基本数据类型的包装类。
在创建持久化类和数据表的映射关系时,需要设置持久化类的唯一标识oid和数据表主键的映射。Java程序通过引用地址确定对象,持久化类通过唯一标识oid确定数据表的哪条记录。
在创建表的时候,这里指使用hibernate的持久化类生成表的时候。比如,有一个持久化类,是关于人的类,具有属性:姓名,性别,地址,身份证。在这些属性中,身份证是唯一的,可以认为是这个持久化类的自然主键,如果使用自然主键作为生成表的主键,可能会造成耦合,不推荐使用。为了降低耦合,使用代理主键作为对应生成表的主键。
例子:
搭建环境略
创建pojo类
public class User {
private Integer id;
private String name;
private Integer age;
public Integer getId() {
return id;
}
public voidsetId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public voidsetName(String name){
this.name = name;
}
public Integer getAge() {
return age;
}
public voidsetAge(Integer age){
this.age = age;
}
}
配置pojo类与表的映射关系,使pojo类成为持久化类
<?xml version="1.0"encoding="UTF-8"?>
<!DOCTYPE hibernate-mappingPUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.hibernate.pojo.User"table="user">
<id name="id"column="id">
<generator class="native"/>
</id>
<property name="name"column="name" length="30"/>
<property name="age"column="age" />
</class>
</hibernate-mapping>
执行测试文件,自动生成表
public class HibernateUtil {
private staticConfiguration CONFIG;
private staticSessionFactory FACTORY;
static{
CONFIG=new Configuration().configure();
FACTORY=CONFIG.buildSessionFactory();
}
public staticSession getSession(){
return FACTORY.openSession();
}
public staticvoidmain(String[] args){
getSession();
}
}
(一) 映射关系表的主键的生成策略
1. increment
适用于用short,int,long作为主键,不是使用的数据库的自动增长机制。获取主键的最大值,然后+1,作为执行的持久化类对象的主键值。
这个属性不常用。因为如果在并发访问的情况下,可能产生不安全的数据。
例子:
<!-- 获取主键最大值,然后+1作为新行的主键值 -->
<generator class="increment"/>
2. identity
适用于short,int,long作为主键。这个必须使用在有自动增长功能的数据库中,采用的是数据库底层的自动增长机制。Mysql,db2,有自增功能,但是oracle是没有的。Oracle用不了这个属性。不利于扩展和多平台。
3. sequence
适用于short,int,long作为主键。底层使用数据库的序列增长方式。Oracle底层是序列的增长可以用。但是mysql用不了。Db2也能用这个sequence属性。不利于扩展和多平台。
4. native
本地的增长策略,根据底层的数据库,自动选择适用于这个数据库的主键生成策略。适用于short,int,long。
如果底层是mysql数据库,相当于identity;如果是oracle数据库,相当于sequence。
5. uuid
适用于char,varchar类型作为主键。使用随机的字符串作为主键。
例子:
<hibernate-mapping>
<class name="com.hibernate.pojo.Person"table="person">
<id name="id"column="id">
<generator class="uuid" />
</id>
<property name="name" column="name"length="30" />
</class>
</hibernate-mapping>
6. assigned
主键的生成不适用hibernate管理,必须手动设置主键。
(二) 持久化类的状态
Hibernate为了管理持久化类,根据持久化类的标识和Session对象对持久化类的管理,将持久化类分为三个状态。
1. 瞬时态:transient object
没有持久化标识,没有被纳入到session对象的管理。Session是一个缓存,如果把对象放入session的缓存,就相当于被session管理。
2. 持久态:persistent object
有持久化标识,已经被纳入到session对象的管理。
3. 脱管态:detached object
有持久化标识, 被纳入到session对象的管理。相当于已经跟session发生过关系了,从session的缓存中退出,或者session的缓存被收回。
例子:
public voidtest3(){
Session session=HibernateUtil.getSession();
Transaction tr=session.beginTransaction();
//持久化User对象
//在此打断点,用debug检查状态。
User user=new User();
user.setName("hh");
user.setAge(22);
//瞬时态,现在的user没有oid,没有纳入session对象的管理,此时,user对象是瞬时态的。
Serializable id=session.save(user);
System.out.println(id);
//调用session的方法对持久化对象进行操作,将产生持久化对象的唯一标识oid,同时,也会将这个对象纳入session的缓存里。此时的user对象是持久态了。
//使用session保存user,
tr.commit();
session.close();
//session被回收后,缓存也随之消失,则缓存中的对象也消失。此时,user对象在缓存的记录消失。user对象仍然存在,则user的oid仍有效。此时,user对象是脱管态。
System.out.println(user.getId());
System.out.println(user.getName());
}
(三) 持久化类三个状态的转换
持久态对象,有更新数据库的能力。利用了session的一级缓存。
例子:
public voidtest1(){
Session session=HibernateUtil.getSession();
Transaction tr=session.beginTransaction();
//获取到一个持久化对象
User u=session.get(User.class, 23);
System.out.println(u.getName());
u.setName("hhii");
//按照正常的逻辑,是调用session的update方法实现对数据库的更新
//session.update(u);
//实际上不用
tr.commit();
session.close();
}
七、 Hibernate的一级缓存
概念:
1. 缓存:在内存中开辟的一块空间,将数据源(数据库或者文件)中的数据存放到这个内存空间,再次获取的时候,直接从缓存中获取,提高程序的性能。内存比硬盘介质速度要快。
2. Hibernate框架提供了两种缓存
一级缓存:自带自开。一级缓存的生命周期与session一致,周期较短,随收回session而消失。一级缓存又称为session级别的缓存。
二级缓存:为了弥补一级缓存的缺陷而使用二级缓存。默认没有开启,需要手动配置才可以用。二级缓存可以在多个session中共享数据,二级缓存又称为sessionFactory级别的缓存。
3. Session对象的缓存
Session接口种,有一些列的java的集合,这些集合构成了session级别的缓存。将对象存入一级缓存中,session没有结束生命周期,对象仍存在于session中。
4. 编写查询的代码证明一级缓存的存在
在同一个session 对象中两次查询,可以证明使用了缓存。
例子:
用save方法证明一级缓存的存在
public voidtest2(){
Session session=HibernateUtil.getSession();
Transaction tr=session.beginTransaction();
User u=new User();
u.setName("hiljjll");
u.setAge(1234);
Serializable id=session.save(u);
//此时,session没有销毁,而且事务没有提交,也就是数据库里面没有存入这个id的user对象,所以,这时是看不到有sql语句执行的,但是现在从session里却可以得到这个id的user对象,说明了什么。
//说明了在session对象里面保存了这个user对象的数据,这就是一级缓存。
System.out.println(id);
User u2=session.get(User.class,id);
System.out.println(u2.getName());
tr.commit();
session.close();
}
利用查询两次,来证明一级缓存的存在
/**
* 查询两次,证明一级缓存的存在
*/
@Test
public voidtest3(){
Session session=HibernateUtil.getSession();
Transaction tr=session.beginTransaction();
User u1=session.get(User.class,23);
System.out.println(u1.getName());
//此时,再次查询,却没有sql语句输出,说明没有从数据库中取,而是从缓存中取了数据
User u2=session.get(User.class,23);
System.out.println(u2.getName());
tr.commit();
session.close();
}
5. 持久态对象能够利用缓存更新数据库依据的原理和机制
比如:使用get方法查询到user对象后,然后设置user对象的一个属性,user.setXxxx(xx)。此时,没有进行对象的update操作,而数据库的记录改变了。
这里利用快照机制来完成的。Snapshot,快照机制。
如下面代码:
/**
* 持久态对象具有更新数据库能力的机制---快照机制
*/
@Test
public voidtest4(){
Session session=HibernateUtil.getSession();
Transaction tr=session.beginTransaction();
User u1=session.get(User.class,23);
u1.setName("sss");
tr.commit();
session.close();
}
当session对象创建时,就完成了对session对象的初始化。这时在session对象中形成缓存区域和快照区域,两块分区。当执行sesson.get(User.class,23)时,先会查询数据库,查询后,会将查询结果放入缓存区,同时,在快照区域也存放一份一样的数据。当执行user.setName(“xxx”);时,会将缓存区中的数据做相应的修改。当执行tr.commit时,执行之前,会比对缓存区和快照区的数据,如果一致,继续执行。如果不一致,会将缓存中的数据更新到数据库,同时,把快照区的数据更新成和缓存区一致。这样,就将数据持久化到数据库。当执行session.close()时,session收回,缓存和快照中的数据清除。
6. 操作一级缓存的方法
session.clear() 清空缓存
例子:
public voidtest5(){
Session session=HibernateUtil.getSession();
Transaction tr=session.beginTransaction();
User u1=session.get(User.class,23);
System.out.println(u1.getName());
//清空缓存
session.clear();
//第二次从数据库查,此时会产生一条sql语句
User u2=session.get(User.class,23);
System.out.println(u2.getName());
tr.commit();
session.close();
}
session.evict(objectentity) 从一级缓存中清除指定的实体对象
例子:
public voidtest6(){
Session session=HibernateUtil.getSession();
Transaction tr=session.beginTransaction();
User u1=session.get(User.class, 23);
System.out.println(u1.getName());
//清除指定的实体对象
session.evict(u1);;
//第二次从数据库查,此时会产生一条sql语句
User u2=session.get(User.class, 23);
System.out.println(u2.getName());
tr.commit();
session.close();
}
session.flush() 刷出缓存,就是刷新缓存
例子:
public voidtest7(){
Session session=HibernateUtil.getSession();
Transaction tr=session.beginTransaction();
User u1=session.get(User.class,23);
u1.setName("aaaa");
//自动刷新缓存
session.flush();
tr.commit();
session.close();
}
八、 Hibernate的事务与并发
(一) 事务的介绍见:http://blog.csdn.net/xingzhishen/article/details/76559566
在hibernate中设置隔离级别
级别的值:
1—Readuncommitted isolation
2—Readcommitted isolation
4—Repeatableread isolation
8—Serializableisolation
在hibernate.cfg.xml中设置:
<!-- 设置事务的隔离级别,默认为可重复读 -->
<property name="hibernate.connection.isolation">4</property>
(二) 丢失更新的问题
1. 不考虑隔离性时,也会产生写入数据的问题,这一类的问题叫丢失更新的问题。更新数据时,数据丢失,强调并发访问时的问题。
2. 产生原因:在高并发访问中,同时对同一id的行的不同字段做了修改,导致后写入数据库的数据覆盖了先写入数据库的数据,从而使得先更新的数据丢失的问题。
例子:
利用断点演示丢失更新
public voidrun1(){
Session session=HibernateUtil.getSession();
Transaction tr=session.beginTransaction();
//在u=这行打断点
User u=session.get(User.class, 23);
u.setName("lll");
tr.commit();
session.close();
}
@Test
public voidrun2(){
Session session=HibernateUtil.getSession();
Transaction tr=session.beginTransaction();
//在u=这行打断点
User u=session.get(User.class,23);
u.setAge(55);
tr.commit();
session.close();
}
3. 解决方案
1) 悲观锁:采用的数据库提供的锁机制。采用这种机制,会在sql语句的后面添加 for update 字句。比如:当a事务在操作这个记录时,会把这条记录锁起来,其他事务不能操作这条记录。只有当a事务提交后,锁事务才能操作这条记录。这样效率会比较慢。
悲观锁
使用:session.get(User.class,1,LockMode.UPGRADE);方法
2) 乐观锁:采用版本号的机制来解决。给javabean添加一个字段叫version=0,这样就给表结构添加一个字段version=0。比如:当a事务操作这条记录时,提交时,先检查版本号,如果版本号的值相同时,才可以提交,同时更新version=1。如果版本号的值不是version=0,而是version=1,说明已经存在并发访问者修改了这条记录,这时一般就会抛异常,提示重新操作这条记录表,那么就是按照新的version=1来操作,如果仍然异常,说明还有并发访问,如此继续抛异常,重新操作,直到version一致,操作成功。
乐观锁的例子:
在持久化类中添加private Integer version属性,创建set,get方法。
private Integer version;
public Integer getVersion() {
return version;
}
public voidsetVersion(Integer version){
this.version = version;
}
配置持久化类映射文件的version标签。
<hibernate-mapping>
<class name="com.hibernate.pojo.User"table="user">
<id name="id"column="id">
<!-- <generator class="native" /> -->
<!-- 获取主键最大值,然后+1作为新行的主键值 -->
<generator class="increment"/>
</id>
<!-- 设置version字段映射在普通字段的前面。在数据库中,首次设置version,默认值为0 -->
<version name="version"/>
<property name="name"column="name" length="30"/>
<property name="age"column="age" />
</class>
</hibernate-mapping>
测试方式同利用断点演示丢失更新。
九、 绑定本地的session
(一) 关于在javaWEB中开启事务,在业务层使用connection来开启事务,为了保证唯一使用connection,需要将connection传递到持久层。有两种方式:
1. 通过参数传递下去。
2. 将connection绑定到ThreadLocal对象中。
ThreadLocal
java.lang
类 ThreadLocal<T>
java.lang.Object
java.lang.ThreadLocal<T>
直接已知子类:
public class ThreadLocal<T>
extends Object
该类提供了线程局部(thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。
每个线程都保持对其线程局部变量副本的隐式引用,只要线程是活动的并且ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。
Method
| |
|
|
|
|
|
ThreadLocal底层采用的是Map集合。
(二) Hibernate绑定session
Hibernate提供了threadlocal的方式。
步骤:
1. 在hibernate.cfg.xml中配置开启绑定本地的session
<!-- 开启绑定本地的session -->
<property name="hibernate.current_session_context_class">thread</property>
2. 重新编写HibernateUtil工具类,使用SessionFactory的getCurrentSession()方法,获取当前的session对象。这个sesssion对象不需要手动关闭,线程结束,它自动关闭。
public static SessiongetCurrentSession(){
return FACTORY.getCurrentSession();
}
例子:
配置hibernate.cfg.xml文件
<!-- 开启绑定本地的session -->
<property name="hibernate.current_session_context_class">thread</property>
在HibernateUtil工具类中,从threadlocal中获取currentSession。
public staticSession getCurrentSession(){
return FACTORY.getCurrentSession();
}
Pojo略
编写servlet
protected voiddoGet(HttpServletRequest request, HttpServletResponse response) throwsServletException, IOException {
User u1=new User();
u1.setName("哈哈");
User u2=new User();
u2.setName("哈哈2");
UserService us=new UserService();
us.save(u1, u2);
}
编写service层,在service层获取session,开启事务,并制造异常
public voidsave(User u1,User u2){
UserDao ud=new UserDao();
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
try{
ud.save1(u1);
int a=110/0;
ud.save2(u2);
tr.commit();
}catch(Exception e){
e.printStackTrace();
tr.rollback();
}
}
修改dao层方法,从currentSession中取得session,取消开启事务和提交,不要关闭session。
public voidsave1(User u1){
Session session=HibernateUtil.getCurrentSession();
session.save(u1);
}
public voidsave2(User u2){
Session session=HibernateUtil.getCurrentSession();
session.save(u2);
}
十、 Hibernate中的查询
(一) Query接口的HQL语句查询
常用方法:
1. 查询所有记录。list()
例子:
public voidquery1(){
Session session=HibernateUtil.getSession();
Transaction tr=session.beginTransaction();
Query query=session.createQuery("from User");
List<User> ulist=query.list();
for(User u:ulist){
System.out.println(u);
}
tr.commit();
session.close();
}
2. 按照条件查询,使用?
在创建query接口的实例时,将查询条件的hql语句传入。HQL:hibernate sql language。Hibernate框架专用的sql语句。
Queryquery=new createQuery(“from User where id = ? and age = ?”);
注意:?的索引从0开始。
?的类型是什么类型,就赋值为什么类型。比如id的类型为Long,就使用query.setLong赋值。有几个参数就设置几个set赋值。
例子1:
public voidquery2(){
Session session=HibernateUtil.getSession();
Transaction tr=session.beginTransaction();
Query query=session.createQuery("from User where id = ?");
query.setLong(0, 23);
List<User> ulist=query.list();
for(User u:ulist){
System.out.println(u);
}
tr.commit();
session.close();
}
例子2:
public voidquery3(){
Session session=HibernateUtil.getSession();
Transaction tr=session.beginTransaction();
Query query=session.createQuery("from User where age > ?and name like ?");
query.setLong(0, 15);
query.setString(1, "%h%");
List<User> ulist=query.list();
for(User u:ulist){
System.out.println(u);
}
tr.commit();
session.close();
}
3. 按照条件查询,使用:xxx
?可以使用:xxx的方式替代。
例子:
public voidquery4(){
Session session=HibernateUtil.getSession();
Transaction tr=session.beginTransaction();
Query query=session.createQuery("from User where age >:age and name like :name");
query.setLong("age",15);
query.setString("name", "%h%");
List<User> ulist=query.list();
for(User u:ulist){
System.out.println(u);
}
tr.commit();
session.close();
}
(二) Criteria接口
Criteria接口完全的面向对象。非常合适做条件查询。一般称为QBC查询。
通过session获得criteria对象,同时将要操作的对象的类作为参数传入。
Criteriacriteria=session.createCriteria(User.class);
1. 不设置添加criteria的参数,默认查询所有。
例子:
public voidcriteria1(){
Session session=HibernateUtil.getSession();
Transaction tr=session.beginTransaction();
Criteria criteria=session.createCriteria(User.class);
List<User> ulist=criteria.list();
for(User u:ulist){
System.out.println(u);
}
tr.commit();
session.close();
}
2. 设置相关参数,按条件查询
使用Restrictions的静态方法,拼接查询的条件。
例子:
public voidcriteria2(){
Session session=HibernateUtil.getSession();
Transaction tr=session.beginTransaction();
Criteria criteria=session.createCriteria(User.class);
//Criterion是Hibernate提供的条件查询的对象,使用Restrictions可以获得这个对象。
criteria.add(Restrictions.gt("age",15));
criteria.add(Restrictions.like("name", "%h%"));
List<User> ulist=criteria.list();
for(User u:ulist){
System.out.println(u);
}
tr.commit();
session.close();
}
十一、 Hibernate关联关系映射
(一) 针对数据库表中的一对多关系,如一个用户有多个订单。需要建立一对多映射。
1. 使用步骤:
1) 搭建开发环境,创建项目,导入jar包,日志文件。
2) 创建数据库表
3) 编写一对多的javabean。如编写客户和联系人的javabean。联系人以客户的主键为外键,形成一对多的关系。
创建一方的javabean时,注意将多方作为属性放入一方,而且Hibernate默认的集合时set集合,所以要使用set集合。而且要初始化这个set集合。
创建多方的javabean时,为了易于扩展,将一方的对象作为属性放入多方。而且,这个属性一定不能初始化,hibernate框架会自动初始化的,否则报错。
4) 编写一对多的映射配置文件
5) 测试
例子:
搭建开发环境和创建数据库表略
一对多的javabean
一方Customer
private Long cust_id;
private String cust_name;
private Long cust_user_id;
private Long cust_create_id;
private String cust_source;
private String cust_industry;
private String cust_level;
private String cust_linkman;
private String cust_phone;
private String cust_mobile;
private String info;
private Set<Linkman> linkmans=newHashSet<Linkman>();
多方Linkman
private Long lkm_id;
private String lkm_name;
private String lkm_gender;
private String lkm_phone;
private String lkm_mobile;
private String lkm_email;
private String lkm_qq;
private String lkm_position;
private String lkm_memo;
private Customer customer;
测试,以双向关联为例
public voidtestMapper(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
//保存客户和联系人
Customer c=new Customer();
c.setCust_name("哈哈哈");
Linkman l1=new Linkman();
l1.setLkm_name("看看");
Linkman l2=new Linkman();
l2.setLkm_name("上述");
//双向关联
//首先用客户关联系人
c.getLinkmans().add(l1);
c.getLinkmans().add(l2);
//然后用联系人管理客户
l1.setCustomer(c);
l2.setCustomer(c);
session.save(c);
session.save(l1);
session.save(l2);
tr.commit();
}
2. 一对多的主要操作
1) 双向关联保存
见上例。
2) 级联保存
保存在一方的时候,同时将一方关联的多方也保存进数据库,也就是保存一方可以级联多方;反之,如果保存多方的时候,同时将一方也保存进数据库,即保存多方可以级联一方。级联保存具有方向性。单象的级联保存,需要进行设置,才可以实现。
步骤:
在执行发放的一方的javabean的映射文件中设置级联的对象的属性cascade。
如上述例子中,采用级联保存,保存客户,级联联系人:
在客户的映射文件中设置
<set name="linkmans"cascade="save-update">
<key column="lkm_cust_id" />
<one-to-many class="com.hibernate.pojo.Linkman" />
</set>
测试
public voidtestMapper2(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
//保存客户和联系人
Customer c=new Customer();
c.setCust_name("哈哈哈");
Linkman l1=new Linkman();
l1.setLkm_name("看看");
Linkman l2=new Linkman();
l2.setLkm_name("上述");
//单向关联
//用客户关联联系人
c.getLinkmans().add(l1);
c.getLinkmans().add(l2);
session.save(c);
tr.commit();
}
反之,如果保存联系人,级联客户:
在联系人的映射文件中设置
<many-to-one name="customer"class="com.hibernate.pojo.Customer" column="lkm_cust_id" cascade="save-update"/>
测试
public voidtestMapper3(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
//保存客户和联系人
Customer c=new Customer();
c.setCust_name("哈哈哈");
Linkman l1=new Linkman();
l1.setLkm_name("看看");
Linkman l2=new Linkman();
l2.setLkm_name("上述");
//单向关联
//用联系人关联客户
l1.setCustomer(c);
l2.setCustomer(c);
session.save(l1);
session.save(l2);
tr.commit();
}
单向链式级联保存
在涉及到下行有级联的对象的javabean的映射文件的被级联的标签中,设置cascade属性。
<!– 下面是客户级联了联系人,所以在客户的映射中设置联系人标签的cascade属性 -- >
<set name="linkmans"cascade="save-update">
<key column="lkm_cust_id" />
<one-to-many class="com.hibernate.pojo.Linkman"/>
</set>
<!– 下面是联系人级联了客户,所以在联系人的映射中设置客户标签的cascade属性 -- >
<many-to-one name="customer"class="com.hibernate.pojo.Customer" column="lkm_cust_id" cascade="save-update"/>
测试
public voidtestMapper4(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
//保存客户和联系人
Customer c=new Customer();
c.setCust_name("哈哈哈");
Linkman l1=new Linkman();
l1.setLkm_name("看看");
Linkman l2=new Linkman();
l2.setLkm_name("上述");
//单向链式关联
//用联系人1关联客户,再用客户关联联系人
l1.setCustomer(c);
c.getLinkmans().add(l2);
session.save(l1);
tr.commit();
}
3) 级联删除
删除某一方的时候,通过级联可以删除这一方有映射关系的一方的数据,是级联删除。
直接使用sql语句,在含有外键的表中删除,会报错。
如果使用hibernate框架可以直接删除含有外键的数据。
例子:
public voidtestMapper6(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
//先查询到id为2的客户
Customer c=session.get(Customer.class, 2L);
session.delete(c);
tr.commit();
}
如果某个表中如,订单中数据是隶属于用户的,那么,当删除用户时,保留订单就没有意义,可以使用级联删除,删掉级联的对象。
例子:
删除客户级联联系人
在客户的映射文件中配置,联系人标签的cascade属性。
<set name="linkmans"cascade="save-update,delete">
<key column="lkm_cust_id" />
<one-to-many class="com.hibernate.pojo.Linkman"/>
</set>
测试
@Test
public voidtestMapper6(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
//先查询到id为2的客户
Customer c=session.get(Customer.class, 2L);
session.delete(c);
tr.commit();
}
删除联系人级联客户
在联系人的映射文件中配置,客户标签的cascade属性。
<many-to-one name="customer"class="com.hibernate.pojo.Customer" column="lkm_cust_id" cascade="save-update,delete"/>
测试
public voidtestMapper8(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
//先查询到id为2的客户
Linkman l1=session.get(Linkman.class, 6L);
session.delete(l1);
tr.commit();
}
注意级联删除的链式单向特性。比如上述级联删除中,如果配置了联系人的映射文件中客户标签的cascade属性,同时也配置了客户的映射文件中联系人标签的cascade属性,则执行删除联系人时,会把客户删除,同时把该客户所有的联系人也删除。注意慎用。
4) 级联中cascade的值
值 | 含义 |
none | 不使用级联 |
save-update | 级联保存或更新 |
delete | 级联删除 |
delete-orphan | 孤儿删除(注意:只能应用在一对多关系) |
all | 除了delete-orphan的所有情况,(包含save-update delete) |
all-delete-orphan | 包含了delete-orphan的所有情况,(包含save-update delete delete-orphan) |
重点:delete-orphan 孤儿删除
又叫孤子删除,只有在一对多的情况下才有。在一对多的关系中,可以将一方认为是父方,将多方认为是子方。孤儿删除指,在解除父子关系的时候,将子方记录就直接删除。
例子:
解除关系的操作
public voidtestMapper9(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
//解除一方与多方的关系
//获得一方
Customer c=session.get(Customer.class, 5L);
//获得多方
Linkman l1=session.get(Linkman.class, 8L);
//解除关系
c.getLinkmans().remove(l1);
tr.commit();
}
单纯的解除关系,而没有设置孤儿删除,不会删除解除掉关系的多方,只是将多方的外键设置为null。
在一方的orm文件中设置cascade=”delete-orphan”。
<set name="linkmans"cascade="delete-orphan">
<key column="lkm_cust_id" />
<one-to-many class="com.hibernate.pojo.Linkman"/>
</set>
5) 级联操作中的外键维护
在一对多的关系中,如果做双向关联的操作,双方都会维护外键,产生多余的sql语句。原因是:session的一级缓存中的快照机制,会让双方都更新数据库,产生多余的sql语句。如果不想产生多余的sql语句,需要一方放弃外键的维护。语法:比如在一方的orm文件的set标签中设置inverse=”true”。true:放弃维护;false:不放弃。默认值是false。放弃维护的好处是速度快。
注意:在一对多中,只有一方具有选择是否放弃外键维护的权利,它可以选择不放弃外键维护。
注意:在多对多的orm中,必须放弃外键维护,否则报错。
例子:
在没有设置放弃维护的情况下,会产生多余的sql语句
public voidtestMapper10(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
//解除一方与多方的关系
//获得一方
Customer c=session.get(Customer.class, 6L);
//获得多方
Linkman l1=session.get(Linkman.class, 9L);
//做双向级联
c.getLinkmans().add(l1);
l1.setCustomer(c);
tr.commit();
}
设置一方的orm的set标签中,放弃外键维护。因为一方需要对多方维护,维护量大,操作时可能要查看多方的情况。而多方只有一个一方,维护量小,有利于效率提高。
<set name="linkmans"inverse="true">
<key column="lkm_cust_id" />
<one-to-many class="com.hibernate.pojo.Linkman"/>
</set>
6) 区分cascade和inverse的区别
功能不同:
cascade用于做级联操作。
inverse用于做外键维护。
例子:如果在一方的orm的set标签中设置了cascade的save或update属性,有设置了inverse的true属性。当通过保存或修改一方向数据库写入数据时,由于放弃了对多方的外键的维护,则多方的外键为null。反之,如果通过保存或修改多方向数据库写入数据时,外键不会为null。
(二) 针对数据库表中的多对多关系,如一个用户有多个角色,一个角色可以被多个用户拥有。需要建立多对多映射。
多对多的关系一般由三个表构成,其中两个表存在逻辑上多对多关系,第三张中间表,维护这两张表的多对多关系。如用户和角色,用户有多个角色,角色也可以同时属于多个用户。利用中间表指向这两张表的外键建立多对多关系。
在没有使用框架之前,中间表需要自己创建。如果使用hibernate,需要配置好javabean和orm文件,hibernate框架会自动创建中间表。
1. 配置步骤:
创建两个javabean,相互设置对方为自己的多方对象。
省略set和get方法。
public class User {
private Long user_id;
private String user_code;
private String user_name;
private String user_password;
private String user_state;
private Set<Role> roles = new HashSet<Role>();
}
public class Role {
private Long role_id;
private String role_name;
private String role_memo;
private Set<User> users = new HashSet<User>();
}
配置各自的orm文件,配置完请放入核心文件的映射。
重点是配置多对多的集合
table属性,如果有表请对应表名,没有可以自定义。
<set name=”属性名” table=”中间表名”>
<key column=”当前对象在中间表的外键的名称” />
<many-to-many class=”集合中对象的去路径” column=”集合中对象的外键的名称” />
</set>
<hibernate-mapping>
<class name="com.hibernate.pojo.Role"table="sys_role" >
<id name="rid"column="rid">
<generator class="native"/>
</id>
<property name="role_name"column="role_name"/>
<property name="role_memo"column="role_memo"/>
<set name="users"table="sys_user_role">
<!--
key:当前对象在中间表的外键的名称
class:集合中的对象的全路径
comlumn:集合中对象在中间表的外键的名称
-->
<key column="role_id"/>
<many-to-many class="com.hibernate.pojo.User"column="user_id"/>
</set>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="com.hibernate.pojo.User"table="sys_user" >
<id name="uid"column="uid">
<generator class="native"/>
</id>
<property name="user_code"column="user_code"/>
<property name="user_name"column="user_name"/>
<property name="user_password"column="user_password"/>
<property name="user_state"column="user_state"/>
<!-- 配置多对多映射 -->
<set name="roles"table="sys_user_role">
<!--
key:当前对象在中间表的外键的名称
class:集合中的对象的全路径
comlumn:集合中对象在中间表的外键的名称
-->
<key column="user_id"/>
<many-to-many class="com.hibernate.pojo.Role"column="role_id"/>
</set>
</class>
</hibernate-mapping>
使用HibernateUtil工具类的main方法测试略
一般在企业开发中,不使用外键,而是设置一个字段作为逻辑外键,只是逻辑上知道它是一个外键的存在,实际上没有设置它为外键。
2. 多对多映射关系的主要操作
多对多的操作中一定要放弃一种一方的外键维护,可以根据效率,让维护上付出更多的一方放弃,以提高效率。
1) 双向级联
例子:
比如让角色放弃外键维护
<set name="users"table="sys_user_role" inverse="true">
<!--
key:当前对象在中间表的外键的名称
class:集合中的对象的全路径
comlumn:集合中对象在中间表的外键的名称
-->
<key column="role_id"/>
<many-to-many class="com.hibernate.pojo.User"column="user_id"/>
</set>
测试保存
public voidtest1(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
//多对多的双向级联
User u1=new User();
u1.setUser_name("看看");
User u2=new User();
u2.setUser_name("看看2");
Role r1=new Role();
r1.setRole_name("信息");
Role r2=new Role();
r2.setRole_name("信息2");
//将u1关联r1和r2
u1.getRoles().add(r1);
u1.getRoles().add(r2);
//将r1关联u1
r1.getUsers().add(u1);
r2.getUsers().add(u2);
//设置u2的关联
u2.getRoles().add(r1);
r1.getUsers().add(u1);
session.save(u1);
session.save(u2);
session.save(r1);
session.save(r2);
tr.commit();
}
2) 级联保存
主要配置级联者的cascade属性
例子:
设置用户的级联保存cascade属性
<set name="roles"table="sys_user_role" cascade="save-update">
<!--
key:当前对象在中间表的外键的名称
class:集合中的对象的全路径
comlumn:集合中对象在中间表的外键的名称
-->
<key column="user_id"/>
<many-to-many class="com.hibernate.pojo.Role"column="role_id"/>
</set>
测试
public voidtest2(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
//多对多的单关联
User u1=new User();
u1.setUser_name("级联保存1");
User u2=new User();
u2.setUser_name("级联保存2");
Role r1=new Role();
r1.setRole_name("信息几包");
Role r2=new Role();
r2.setRole_name("信息几包2");
//将u1关联r1和r2
u1.getRoles().add(r1);
u1.getRoles().add(r2);
//设置u2的关联
u2.getRoles().add(r1);
session.save(u1);
session.save(u2);
tr.commit();
}
注意:如果多方的一方中是放弃外键维护的一方,请在没有放弃外键维护的一方做cascade属性,否则没有外键约束。
特别注意:要慎用级联删除,慎用。
3) 中间表操作
如果只需要修改中间表,只要修改orm的集合就可以。操作集合就可以操作中间表。
比如删除中间表的数据
public voidtest3(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
//查询要做修改的用户
User user=session.get(User.class, 3L);
//查询要删除的角色
Role role=session.get(Role.class,4L);
//执行删除
user.getRoles().remove(role);
tr.commit();
}
十二、 Hibernate的查询方式
(一) 唯一标识oid的检索方式
比如:session.get(类名.class,oid);oid:指主键。
(二) 对象的导航的方式
即通过对象的对象找到对象。
如用户中有角色属性。那么,new User().getRole().getRolename()。这就是对象的导航。
在hibernate中,根据对象的属性类的对象就可以查询这个属性类对象。
例子1:
public voidtest4(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
//使用oid查询客户
Customer c=session.get(Customer.class, 5L);
//查看这个客户的联系人,由于联系人类是客户的一个属性,对于联系人对象得到这个属性类的对象,可以查询这个属性类(也是一个持久化类)。
int a=c.getLinkmans().size();
System.out.println(a);
tr.commit();
}
例子2:
public voidtest5(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
//使用oid查询联系人
Linkman l=session.get(Linkman.class, 9L);
//查看这联系人所属的客户,由于联系人类是客户的一个属性,对于联系人对象得到这个属性类的对象,可以查询这个属性类(也是一个持久化类)。
Customer c=l.getCustomer();
System.out.println(c);
tr.commit();
}
(三) HQL的检索方式
1. 简介
HibernateQuery Language。Hibernate的查询语言。是面向对象的查询语言。和sql语言有些类似。为hibernate所独有。使用hibernate的query接口。
HQL和SQL的关系:
1) HQL面向对象,hibernate负责解析hql语句,然后根据orm文件中的信息,把hql翻译成相应的sql语句。
2) hql查询语句中的主体是域模型中的类及类的属性
3) Sql语句与关系型数据库绑定在一起,sql查询的主体是数据库表及表的字段
2. 基本查询
1) 支持方法链,调用list()方法
例子:
public voidtestHql1(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
Query query=session.createQuery("from Customer");
List<Customer> clist=query.list();
for(Customer c:clist){
System.out.println(c);
}
tr.commit();
}
方法链的方式:
public voidtestHql2(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
List<Customer> clist=session.createQuery("from Customer").list();
for(Customer c:clist){
System.out.println(c);
}
tr.commit();
}
2) 使用别名的方式
Hql支持别名。多表的查询使用别名会很方便。
例子:
public voidtestHql3(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
List<Customer> clist=session.createQuery("select c from Customer c").list();
for(Customer c:clist){
System.out.println(c);
}
tr.commit();
}
3) 排序查询
语法同sql语句。
Sql:orderby 字段 asc,字段 desc
Hql:orderby 属性 asc,属性 desc
asc可以省略。
例子:
public voidtestHql4(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
List<Linkman> clist=session.createQuery("from Linkman order by lkm_id").list();
for(Linkman c:clist){
System.out.println(c);
}
tr.commit();
}
4) 分页查询
Sql语法:limit 起始记录,查询条数 起始记录从0开始,索引0表示第一条记录。
Hibernate提供了分页的方法,如下:
setFirstResult(a) 从哪条记录开始,如果查询是从第一条开启,值是0;
setMaxResults(b) 每页查询的记录数。
例子:
public voidtestHql5(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
Query query=session.createQuery("from Linkman order bylkm_id");
query.setFirstResult(0);
query.setMaxResults(2);
List<Linkman> clist=query.list();
for(Linkman c:clist){
System.out.println(c);
}
tr.commit();
}
方法链的方法:
public voidtestHql6(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
List<Linkman> clist=session.createQuery("from Linkman order by lkm_id").setFirstResult(0).setMaxResults(2).list();
for(Linkman c:clist){
System.out.println(c);
}
tr.commit();
}
5) 带条件的查询
Hql语法1:where属性=(like)?
Hql语法2:where属性=(like):自定义名称
两种都可以。如果是?,比如属性的类型为Long,那么在setLong(0,1L)中,索引从0开始。
如果是自定义名称,那么在setLong(“自定义名称”,值)。
例子:
public voidtestHql7(){
Session session=HibernateUtil.getCurrentSession();
Transactiontr=session.beginTransaction();
//:自定义名称的方式
Query query=session.createQuery("from Linkman l where l.lkm_id = :id");
query.setLong("id",2);
//使用?的方式
// Queryquery=session.createQuery("from Linkman l where l.lkm_id = ?");
// query.setLong(0, 2);
List<Linkman> clist=query.list();
for(Linkman c:clist){
System.out.println(c);
}
tr.commit();
}
通用方法:使用setParameter(),可以不需要判断传入参数的类型。
例子:
public voidtestHql8(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
//:自定义名称的方式
Query query=session.createQuery("from Linkman l where l.lkm_id > ? and l.lkm_gender =:gender");
query.setParameter(0, 2L);
query.setParameter("gender", "1");
List<Linkman> clist=query.list();
for(Linkman c:clist){
System.out.println(c);
}
tr.commit();
}
3. HQL的投影查询
投影查询就是按照设置查询某一个字段或某几个字段。
查询多个字段是,list()方法返回的是object对象。
1) 查询多个字段例子:
public voidtestHql9(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
//:自定义名称的方式
Query query=session.createQuery("select lkm_id,lkm_name fromLinkman");
List<Object[]> clist=query.list();
for(Object c:clist){
System.out.println(c);
}
tr.commit();
}
2) 将查询结果封装到对象中
首先,在持久化类中提供相应字段的构造方法;然后,在hql语句中new这个构造方法的实例。
在持久化类中提供对应的构造方法。注意持久化类必须要有空的构造方法,如果自定义了有参构造方法,定义无参构造方法,否则报错。
public Linkman(Long lkm_id, String lkm_name) {
super();
this.lkm_id = lkm_id;
this.lkm_name = lkm_name;
}
public Linkman(){
}
编写hql语句测试
public voidtestHql10(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
//:自定义名称的方式
Query query=session.createQuery("select newLinkman(lkm_id,lkm_name) from Linkman");
List<Object[]> clist=query.list();
for(Object c:clist){
System.out.println(c);
}
tr.commit();
}
4. HQL的聚合函数查询
Hql可以直接使用聚合函数。
例子1:
public voidtestHql11(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
//:自定义名称的方式
Query query=session.createQuery("select count(*) fromLinkman");
List<Number> clist=query.list();
Long num=clist.get(0).longValue();
System.out.println(num);
tr.commit();
}
例子2:
public voidtestHql12(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
//:自定义名称的方式
Query query=session.createQuery("select sum(lkm_id) fromLinkman");
List<Number> clist=query.list();
Long num=clist.get(0).longValue();
System.out.println(num);
tr.commit();
}
(四) QBC的检索方式
Query By Criteria 按条件查询。适合数据查询中按条件查询。
1. 基本查询
使用list()方法
例子:
public voidtestCriteria1(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
Criteria criteria=session.createCriteria(Linkman.class);
List<Linkman> list=criteria.list();
for(Linkman l:list){
System.out.println(l);
}
tr.commit();
}
方法链的方式:
@Test
public voidtestCriteria2(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
List<Linkman> list=session.createCriteria(Linkman.class).list();
for(Linkman l:list){
System.out.println(l);
}
tr.commit();
}
2. 排序查询
使用addOrder()方法
例子:
public voidtestCriteria3(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
Criteria criteria=session.createCriteria(Linkman.class);
criteria.addOrder(Order.desc("lkm_id"));
List<Linkman> list=criteria.list();
for(Linkman l:list){
System.out.println(l);
}
tr.commit();
}
3. 分页查询
也是使用两个方法:setFirstResult(); setMaxResults();
例子:
public voidtestCriteria4(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
Criteria criteria=session.createCriteria(Linkman.class);
criteria.addOrder(Order.desc("lkm_id"));
criteria.setFirstResult(0);
criteria.setMaxResults(3);
List<Linkman> list=criteria.list();
for(Linkman l:list){
System.out.println(l);
}
tr.commit();
}
4. 条件查询
Criterion是查询条件的接口,Restriction类是Hibernate框架提供的工具类,使用该工具类来设置查询条件。
使用Criteria接口的add方法,来传入条件。使用Restrictions来添加条件。
Restrictions的方法列表:
Restrictions.eq | 相等 |
Restrictions.gt | 大于 |
Restrictions.ge | 大于等于 |
Restrictions.lt | 小于 |
Restrictions.le | 小于等于 |
Restrictions.between | 在之间 |
Restrictions.like | 模糊查询 |
Restrictions.in | 范围 |
Restrictions.and | 并且 |
Restrictions.or | 或者 |
例子1:
public voidtestCriteria5(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
Criteria criteria=session.createCriteria(Linkman.class);
criteria.addOrder(Order.desc("lkm_id"));
criteria.add(Restrictions.eq("lkm_gender", "1"));
criteria.add(Restrictions.gt("lkm_id", 2L));
List<Linkman> list=criteria.list();
for(Linkman l:list){
System.out.println(l);
}
tr.commit();
}
例子2:
add的用法
public voidtestCriteria6(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
Criteria criteria=session.createCriteria(Linkman.class);
criteria.addOrder(Order.desc("lkm_id"));
criteria.add(Restrictions.and(Restrictions.eq("lkm_gender","1"),Restrictions.gt("lkm_id",2L)));
List<Linkman> list=criteria.list();
for(Linkman l:list){
System.out.println(l);
}
tr.commit();
}
例子3:
注意between的用法,包括左和右的值。
public voidtestCriteria7(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
Criteria criteria=session.createCriteria(Linkman.class);
criteria.addOrder(Order.desc("lkm_id"));
criteria.add(Restrictions.between("lkm_id", 1L,10L));
List<Linkman> list=criteria.list();
for(Linkman l:list){
System.out.println(l);
}
tr.commit();
}
例子4:
注意in的用法,先创建一个集合或者数组,把需要in的值放入集合或者数组。
public voidtestCriteria8(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
Criteria criteria=session.createCriteria(Linkman.class);
criteria.addOrder(Order.desc("lkm_id"));
List<Long> idlist=new ArrayList<Long>();
idlist.add(2L);
idlist.add(3L);
idlist.add(4L);
criteria.add(Restrictions.in("lkm_id", idlist));
List<Linkman> list=criteria.list();
for(Linkman l:list){
System.out.println(l);
}
tr.commit();
}
例子5:
or的用法
public voidtestCriteria9(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
Criteria criteria=session.createCriteria(Linkman.class);
criteria.addOrder(Order.desc("lkm_id"));
criteria.add(Restrictions.or(Restrictions.eq("lkm_gender","1"),Restrictions.gt("lkm_id",2L)));
List<Linkman> list=criteria.list();
for(Linkman l:list){
System.out.println(l);
}
tr.commit();
}
例子6:
判断值是否为空的方法isNull();
5. 聚合函数查询
Projection是聚合函数的接口。hibernate也提供了Projections工具类,使用这个工具类可以设置聚合函数查询。
具体是是使用criteria的setProjection()方法。
例子:
public voidtestCriteria11(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
Criteria criteria=session.createCriteria(Linkman.class);
criteria.setProjection(Projections.count("lkm_id"));
List<Number> list=criteria.list();
Long num=list.get(0).longValue();
System.out.println(num);
tr.commit();
}
注意:如果做了聚合函数查询之后,又想查询所有的,需要在查询所有的之前,清空setProjection中的内容。
例子:
public voidtestCriteria12(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
Criteria criteria=session.createCriteria(Linkman.class);
criteria.setProjection(Projections.count("lkm_id"));
List<Number> list=criteria.list();
Long num=list.get(0).longValue();
criteria.setProjection(null);
List<Linkman> listman=criteria.list();
for(Linkman l:listman){
System.out.println(l);
}
System.out.println(num);
tr.commit();
}
6. 离线条件查询
离线条件查询使用的是DetachedCriteria接口,离线条件查询对象在创建的时候,不需要使用session对象,在查询的时候直接使用session对象即可。
离线条件查询的意义:一般在三层的开发,开启业务是在service层,开启业务需要session接口,criteria通过session对象创建,而查询条件也是在criteria对象创建后,通过criteria对象设置。但是,一般查询条件是通过web层传到service层的,如果在条件比较复杂的情况下,传参的形式就显得比较繁琐,为了简化代码,优化维护,考虑在web层就设置好查询条件,将criteria对象传到service层,由于session是在service层创建的,以便于开启事务,没有离线之前,criteria的创建依赖session,为了达到在不依赖session的目的,所以有了离线条件设置的概念,所以有了DetachedCriteria的接口,使用这个接口可以创建criteria对象。
例子:
DetachedCriteria的使用
public voidtestCriteria13(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
DetachedCriteria criteria=DetachedCriteria.forClass(Linkman.class);
criteria.setProjection(Projections.count("lkm_id"));
List<Number> list=criteria.getExecutableCriteria(session).list();
Long num=list.get(0).longValue();
criteria.setProjection(null);
List<Linkman> listman=criteria.getExecutableCriteria(session).list();
for(Linkman l:listman){
System.out.println(l);
}
System.out.println(num);
tr.commit();
}
(五) SQL的查询方式
Hibernate中可以使用sql语句来做操作。然后,框架的意义是高内聚,低耦合。在代码中使用sql就削弱了这个效果,比如使用sql就不好使用离线查询。一般在开发中,不是sql语句的方式。除非不使用sql就不行,那就用了。
在hibernate中提供了SQLQuery接口做sql语句的查询。
例子:
不使用封装对象,直接使用sqlquery对象的list()方法的返回值—数组。
public void testsq1(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
SQLQuery sqlquery=session.createSQLQuery("select * fromcst_linkman where lkm_id = ?");
sqlquery.setParameter(0, 2L);
List<Object[]> list=sqlquery.list();
for(Object[] l:list){
System.out.println(Arrays.toString(l));
}
tr.commit();
}
使用封装对象的方式
public voidtestsq2(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
SQLQuery sqlquery=session.createSQLQuery("select * fromcst_linkman where lkm_id = ?");
sqlquery.setParameter(0, 2L);
sqlquery.addEntity(Linkman.class);
List<Linkman> list=sqlquery.list();
for(Linkman l:list){
System.out.println(l);
}
tr.commit();
}
(六) HQL的多表查询
关于sql的多表查询,见:http://blog.csdn.net/xingzhishen/article/details/76944442
HQL的多表查询分为:迫切和非迫切。
非迫切返回的是Object[]
迫切返回的是对象,比如把客户的信息封装到客户对象中。
例子:
多表非迫切查询
public voidtestHQLMultyT1(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
Query query=session.createQuery("from Customer c inner joinc.linkmans");
List<Object[]> list=query.list();
for(Object[] o:list){
System.out.println(Arrays.toString(o));
}
tr.commit();
}
多表迫切查询。请使用fetch关键字将数据封装到对象。使用手动创建set集合,重装数据,过滤重复项。
例子:
public voidtestHQLMultyT2(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
Query query=session.createQuery("from Customer c inner joinfetch c.linkmans");
List<Customer> list=query.list();
Set<Customer> set=newHashSet<Customer>(list);
for(Customer o:set){
System.out.println(o);
}
tr.commit();
}
十三、 Hibernate框架查询的性能优化—延迟加载
(一) 介绍
延迟加载时hibernate框架提高性能的方法。底层采用动态代理技术,生成一个动态代理对象。当使用这个代理对象的属性的时候,才会发送sql语句。减少对系统资源可能产生的不必要的占用。两种类型的延迟加载,类级别和关联级别的。
(二) 类级别的延迟加载
使用这个类的属性的时候,产生的延迟加载。使用session的load()方法。
例子:
public voidtest2(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
Customer c=session.load(Customer.class, 2L);
System.out.println(c.getCust_name());
tr.commit();
}
设置类级别的延迟加载。
在设置延迟加载的持久化类的orm文件中,<class>标签,有一个lazy属性,lazy=”true”,使用延迟加载,lazy=”false”,不使用延迟加载。不设置lazy,默认是true。例子:
<class name="com.hibernate.pojo.Customer"table="cst_customer" lazy="false" >
……
</class>
第二种设置延迟加载的方法,使用Hibernate.initialize(Object o)方法使延迟加载失效。
例子:
public voidtest2(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
Customer c=session.load(Customer.class, 2L);
Hibernate.initialize(c);
System.out.println("-------------");
System.out.println(c.getCust_name());
tr.commit();
}
(三) 关联级别的延迟加载
比如在客户下面有很多联系人,一般在持久化类下面有联系人的集合。在使用延迟加载的情况下,当查询客户的时候,只发送查客户的sql语句,当查询联系人的时候,才发送联系人的sql语句。默认下是设置了使用延迟加载的。
例子:
public voidtest3(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
Customer c=session.load(Customer.class, 5L);
System.out.println("-------------");
System.out.println(c.getLinkmans().size());
tr.commit();
}
(四) 关联级别延迟加载的优化策略
使用lazy和fetch两个属性。
如果是一对多的映射,这两个属性都是在持久化类的orm文件的<set>标签里设置和<many-to-one>标签里设置。如果是多对多,
1. 在set标签中设置
fetch的值 | 作用 |
select | 默认值,发送查询语句 |
join | 连接查询,发送一条迫切左外连接。也就是将一对多以左外连接执行sql。如果配置join。则lazy的设置失效。 |
subselect | 子查询,发送子查询查询其关联对象(使用返回值为list的方法测试) |
lazy的值 | 作用 |
true | 默认值,延迟加载 |
false | 不延迟 |
extra | 极其懒惰 |
例子:
<set name="linkmans"inverse="true" fetch="select"lazy="false">
<key column="lkm_cust_id" />
<one-to-many class="com.hibernate.pojo.Linkman"/>
</set>
测试
public voidtest5(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
Customer c=session.get(Customer.class, 5L);
System.out.println(c.getLinkmans().size());
tr.commit();
}
2. 在一对多的多方的orm的<many-to-one>的标签中设置fetch和lazy的值,以设置查询多方时的延迟加载策略。
fetch的值 | 作用 |
select | 默认值,发送查询语句 |
join | 连接查询,发送一条迫切左外连接。也就是将一对多以左外连接执行sql。如果配置join。则lazy的设置失效。 |
lazy的值 | 作用 |
false | 不延迟 |
proxy | 默认值,代理,现在是否采用延迟。有一方的orm的<lazy>的lazy确定,如果那边的lazy=”true”,proxy的值为true,代表延迟加载;如果那边的lazy=”false”。Proxy的值就是false,代表不采用延迟。 |
例子:
在<many-to-one>中设置延迟加载策略。
<many-to-one name="customer"class="com.hibernate.pojo.Customer" column="lkm_cust_id" fetch="select" lazy="proxy"/>
测试
public voidtest6(){
Session session=HibernateUtil.getCurrentSession();
Transaction tr=session.beginTransaction();
Linkman c=session.get(Linkman.class, 2L);
System.out.println(c.getCustomer().getCust_name());
tr.commit();
}
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)