1. Hibernate持久化类和对象标识符
1.1 Hibernate持久化类
1.1.1 什么是持久化类
Hibernate是持久层的ORM映射框架,专注于数据持久化工作。持久化即将内存中的数据永久存储到数据库中。持久化类就是与数据库建立映射关系的JAVA类。
1.1.2 持久化类编写规范
持久化类编写规范符合JavaBean编写规范。
JavaBean:Bean指可重用组件(一个类,一个业务层,Dao层等都可以称为组件)。JavaBean就是指java语言的可利用组件。
JavaBean编写规范:
1)类是public修饰的
2)类具有无参构造
3)类的属性是private修饰的
4)类的属性具有public修饰的getter、sertter方法
5)实现Serializable接口
JavaBean本质上还是个java类,当不含有构造器的时候,系统默认无参构造;当存在有参构造时,需要创建无参构造方法,否则系统创建无参对象时报错。在Hibernate中,底层需要使用反射机制生成类的实例,所以持久化类需要无参构造。成员变量是private修饰保证数据安全,getter、setter方法保证属性读取,且getter、setter方法可以添加操作(如权限控制)。对于Hibernate,底层会将查询到的数据进行封装,所以需要getter,setter方法。实现可序列化接口目的是将对象转换成一组byte,这样日后要用这个对象的时候,可以反序列化将这些数据恢复出来,并可以重新构建这个对象,可以跨网络传输,自动补偿操作系统的差异.这只是一个接口标记,没有任何方法实现.Bean的状态信息在设计时配置,并且保存下来,供程序启动时使用,序列化就是这个功能。
除了javabean的编写规范以外,持久化类的编写还具备以下规范:
- 持久化类的属性尽量使用包装类的类型。因为包装类和基本数据类型的默认值不同,包装类的类型语义描述更加清晰。例如考试零分和缺考可以用0和null表示,但是基础类型只有0。
- 持久化类需要有一个和数据库表主键对应的对象标识符(OID)。
- 持久化类尽量不要用final进行修饰。因为hibernate中有延迟加载机制,这个机制中会产生代理对象,该行为是采用字节码增强技术完成,其实就是产生了当前类的一个子类对象实现的。如果使用final修饰持久化类,该持久化类就不能创建子类,延迟加载机制(一种优化手段)就失效了。
1.2 Hibernate对象标识符(OID)
OID(Object Identifier),又叫做对象标识符。
虚拟机区分两个对象是根据内存地址是否一致。数据库区分两个对象是根据表的主键。Hibernate靠OID,实体类的OID映射数据库表中的主键。
1.3 Hibernate主键生成策略
持久化类中需要唯一的OID映射数据库表中的主键字段,而主键一般不让客户手动输入,而由程序自动生成。那么程序生成主键的方式有哪些呢?
两个概念:
自然主键:具有业务含义的字段作为主键,称为自然主键。如:把用户名字作为主键。
代理主键:不具有业务含义的字段作为主键,称为代理主键。如:id。
2. Hibernate一级缓存和对象状态
2.1 Hibernate一级缓存
2.1.1 Session缓存
Hibernate的一级缓存是指Session缓存。Session缓存是内存中的一块区域,用来存放一些快速访问的java对象的。当使用Hibernate进行对象查询时,程序首先根据OID在Session中寻找对应java对象 ,有则返回,没有就向数据库中寻找。找到后会将查找到的java对象存入Session缓存,以便下次读取。Session缓存的目的就是减少对数据库的访问。
Session缓存由一系列java集合构成。只要Session实例没有结束生命周期,缓存中的对象也不会结束生命周期。当调用close()方法时,Session缓存也会被清空。
2.1.2 快照机制
当Session缓存中存入数据时,该数据也会被拷贝到Hibernate快照中。当使用commit()进行提交事务时,同时会清理Session缓存,这时会使用OID判断缓存中的对象和快照中的对象是否一致,如果两个对象中的属性发生变化,则执行update语句,将缓存中的内容同步到数据库中,并更新快照;如果一致,则不执行update()。Hibernate快照作用是保持一级缓存中的数据和数据库一致。
2.2 Hibernate对象的三种状态
Hibernate将持久化类分为三种状态。
1、瞬时态(transient)=临时态=自由态。
该状态的持久化对象没有OID标识,也没有和Session建立关联,在数据库中没有记录。
2、持久态(persistent)
该状态的持久化对象存在OID标识,且加入了Session缓存中,且Session对象没有关闭,在数据库中有相应记录。
3、脱离态(detached)=离线态=游离态
该状态的持久化对象存在OID标识,与Session失去关联,在数据库中存在记录。
3. hibernate事务管理
3.1 配置session和线程绑定
除了在代码中对事务进行开启、提交、回滚之外,还可以在hibernate.cfg.xml文件中的标签中对事务进行配置。
设置事务隔离级别:
<!—
事务隔离级别
hibernate.connection.isolation = 4
1—Read uncommitted isolation
2—Read committed isolation
4—Repeatable read isolation
8—Serializable isolation
-->
<property name="hibernate.connection.isolation">4</property>
Service层需要使用Session获得事务对象,DAO层需要Session对数据库进行增删改查操作。如何保证Service层和Dao层的Session对象保持一致呢?方法有二:
1.Session对象向下传递。(Service层获得Session并通过传参的方式将Session传送给DAO层)
2.将Session绑定到当前线程,DAO层通过当前线程获取Session。(多用该方式)
Hibernate5自身提供三种方式管理Session对象:
1)Session对象的生命周期和当前线程绑定。
2)Session对象的生命周期和JTA事务绑定。
3)Hibernate委托程序管理Session对象的生命周期。
1.在hibernate.cfg.xml文件中的标签中,通过标签进行Session管理方式配置。
<!--将Session绑定到当前线程中-->
<property name="hibernate.current_session_context_class">thread</property>
2.获得Session的方式。
/**
*从当前线程获取Session
*/
public static Session getCurrentSession(){
return sessionFactory.getCurrentSession();
}
//该方式获得的Session,当事务提交时,Session自动关闭
4.Hibernate查询对象的API
4.1 Query
Query对象代表Hibernate的一个查询操作。在Hibernate中,使用Session.createQuery()方法接收一个HQL(Hibernate Query Language)语句,然后调用Query对象的List()和uniqueResult()执行查询操作。
HQL是Hibernate面向对象查询语言,语法和SQL类似。HQL把数据库表的名称换成了类名称,字段名称换成了属性名称。例如:
SQL:select * from cst_customer where cust_name like ?;
HQL:select * from Customer where custName like ?;
其中,HQL的select * 可以省略。写为: from Customer where custName like ?;
4.1.1 Query查询流程
Query查询的基本步骤为:
获得Hibernate的Session对象;
由Session.createQuery("HQL")创建Query对象,并输入查询语句;
如果HQL包含参数,则调用Query的setXXX方法设参;
调用List或uniqueRequest()方法得到查询结果。
4.1.2 基本查询
/**查询所有*/
public void test1(){
//获得session
Session s = HibernateUtil.openSession();
//得到Query
Query query = s.createQuery("from Customer");
//调用查询
List list = query.list();
//分析结果
for(Object o : list){
System.out.println(o);
}
}
4.1.3 条件查询
/**条件查询
*Hibernate的参数占位符索引从0开始
*/
public void test2(){
//获得session
Session s = HibernateUtil.openSession();
//得到Query
Query query = s.createQuery("from Customer where custName like ? and custLevel = ?");
//设置query查询参数
query.setString(0,"%集%");
query.setString(1,"普通客户");
//调用查询
List list = query.list();
//分析结果
for(Object o : list){
System.out.println(o);
}
}
//条件查询的另一种形式
public void test3(){
//获得session
Session s = HibernateUtil.openSession();
//得到Query
Query query = s.createQuery("from Customer where custName like :custName and custLevel = :custLevel");
//设置query查询参数
query.setString("custName","%集%");
query.setString("custLevel","普通客户");
//调用查询
List list = query.list();
//分析结果
for(Object o : list){
System.out.println(o);
}
}
4.1.4 分页查询
/**
*分页查询
*SQL通过limit关键字进行分页查询,limit有两个参数:查询开始的起始索引,查询条数
*Hibernate通过两个方法进行分页查询,参数类似
* setFistResult(int fist);设置其实查询位置
* setMaxResults(int max);设置每次查询的条数
*/
public void test4(){
//获得session
Session s = HibernateUtil.openSession();
//得到Query
Query query = s.createQuery("from Customer");
//设置query查询参数
query.setFirstResult(2);
query.setMaxResults(10);
//调用查询
List list = query.list();
//分析结果
for(Object o : list){
System.out.println(o);
}
}
4.1.5 排序查询
/**
*排序查询
*使用关键字:order by
* 升序:asc(默认值)
* 降序:desc
*/
public void test5(){
//获得session
Session s = HibernateUtil.openSession();
//得到Query
Query query = s.createQuery("from Customer order by custId desc");
//调用查询
List list = query.list();
//分析结果
for(Object o : list){
System.out.println(o);
}
}
4.1.6 统计查询
/**
*统计查询(利用聚合函数)
* 聚合函数:count sum avg max min
* 在SQL语句时:
* select count(*) from table 它是统计所有字段,效率没有只统计主键字段高
* select count(主键) from table 它和第一个的结果是一样的,但是效率更高
* select count(非主键) from table 只统计不为null的字段
*
*/
public void test6(){
//获得session
Session s = HibernateUtil.openSession();
//得到Query
Query query = s.createQuery("select count(custId) from Customer");
//调用查询
Long total = (Long)query.uniqueResult();
//分析结果
System.out.println(total);
}
4.1.7 投影查询
/**
*投影查询:
* 投影:将实体类的部分信息映射成完整的实体类对象,叫做对象的投影
* QBC也能实现投影查询,不如HQL好用
*HQL的语法:
* select new Customer() from Customer
* 注意:1.如果工程中该类(被投影类)唯一,可以不写全限定类名,否则要写全限定类名、
* (例如:Hibernate内部存在一个Order类,但是工程中也有一个Order订单类,对订单类不写全限定类名,hibernate会报错)
* 2.要求该类(被投影类)必须存在一个相同参数类表的构造方法
* (原因:hibernate通过构造方法进行投影)
*/
public void test5(){
//获得session
Session s = HibernateUtil.openSession();
//得到Query
Query query = s.createQuery("select new cn.itcast.domain.Customer(custId,custName) from Customer");
//调用查询
List list = query.list();
//分析结果
for(Object o : list){
System.out.println(o);
}
}
/******************实体类*********************/
/**
* 客户的实体类
*/
public class Customer implements Serializable {
private Long custId;
private String custName;
private String custSource;
private String custIndustry;
private String custLevel;
private String custAddress;
private String custPhone;
public Customer(){
}
//提供对应参数列表的构造函数
public Customer(Long custId, String custName) {
this.custId = custId;
this.custName = custName;
}
.......
4.2 Criteria
Criteria是Hibernate核心查询对象。Criteria是完全面向对象的,可扩展的条件查询API。Criteria查询和Query查询不同,Criteria完全不必关心数据库底层实现和SQL,HQL语句。通过对Criteria对象的add(“Criterion条件对象”)方法进行条件设定,list()或uniqueResult()方法进行查询。
使用Criteria进行查询步骤如下:
通过Hibernate获得Session对象
通过Session.createCriteria()获得Criteria对象。
通过使用Restriction的静态方法创建Criterion条件对象。Restriction类提供一系列静态方法设定查询条件,这些方法都会返回Criterion条件对象。每一个Criterion实例都是一个查询条件对象。
Criteria对象通过add()方法进行添加Criterion对象。
执行list()或uniqueResult()获得查询结果。
4.2.1 基本查询
/**
*查询所有
*
*/
public void test1(){
//获得Session对象
Session s = HibernateUtil.openSession();
//获得Criteria对象
Criteria c = s.createCriteria(Customer.class);//相当于from Customer
//通过Restriction对象的静态方法设置查询条件,返回Criterion对象
//添加条件对象
//完成查询
List list = c.list();
for(Object o : list){
System.out.println(o);
}
}
4.2.2 条件查询
/**
*条件查询
*
*/
public void test2(){
//获得Session对象
Session s = HibernateUtil.openSession();
//获得Criteria对象
Criteria c = s.createCriteria(Customer.class);//相当于from Customer
//通过Restriction对象的静态方法设置查询条件,返回Criterion对象
//添加条件对象
c.add(Restriction.like("custName","集"));
c.add(Restriction.eq("custLevel","普通客户"));
//完成查询
List list = c.list();
for(Object o : list){
System.out.println(o);
}
}
4.2.3 分页查询
/**
*分页查询(和HQL一模一样)
*
*/
public void test3(){
//获得Session对象
Session s = HibernateUtil.openSession();
//获得Criteria对象
Criteria c = s.createCriteria(Customer.class);//相当于from Customer
//设置查询条件
c.setFirstResult(2);
c.setMaxResults(2);
//完成查询
List list = c.list();
for(Object o : list){
System.out.println(o);
}
}
4.2.4 排序查询
/**
*排序查询
*
*/
public void test4(){
//获得Session对象
Session s = HibernateUtil.openSession();
//获得Criteria对象
Criteria c = s.createCriteria(Customer.class);//相当于from Customer
//设置排序
c.addOrder(Order.desc("custId"));
//完成查询
List list = c.list();
for(Object o : list){
System.out.println(o);
}
}
4.2.5 统计查询,投影查询
/**
* QBC使用聚合函数
* 统计查询
* 涉及的对象:
* Criteria
* 涉及的方法:
* setProjection(Projection p);
* 参数的含义
* Projection:要添加的查询投影
*/
public void test5(){
//获得Session对象
Session s = HibernateUtil.openSession();
//获得Criteria对象
Criteria c = s.createCriteria(Customer.class);//相当于from Customer
//设置统计查询条件
c.setProjection(Projections.count("custId"));
//投影查询的例子如下
/*
*c.setProjection(Projections.projectionList()
* .add(Projections.property("custId"))
* .add(Projections.property("custName")));
*/
//完成查询
Long long = c.uniqueResult();
System.out.println(long);
}
4.3 离线查询
三层架构中,Hibernate的Seesion应该出现在Dao层,在Web层和Service层都不应该出现。但当使用QBC查询时,我们需要在WEB或者Service层调用Session,创建Criteria对象,这就造成了矛盾。解决矛盾的方法有二:1、将参数向下传递(不可取,参数多的时候不好处理)。2、使用Criteria离线查询。
在WEB层和Service层可以使用DetachedCriteria对象来设置查询条件。该对象的获取不需要Session对象,可后期与Criteria对象进行转换,被称为离线对象。
获得该对象的方式:DetachedCriteria dCriteria = DetachedCriteria.forClass(“要查询的实体类字节码”);
public void test3(){
//模拟一次web操作: 浏览器发送请求——调用servlet——调用service——调用dao——拿到结果到jsp上展示
List list = servletFindAllCustomer();
for(Object o : list){
System.out.println(o);
}
}
//模拟servlet
public List<Customer> servletFindAllCustomer(){
//离线对象
DetachedCriteria dCriteria = DetachedCriteria.forClass(Customer.class);
//设置条件:和Criteria是一样的
dCriteria.add(Restrictions.like("custName","%集%"));
return serviceFindAllCustomer(dCriteria);
}
public List<Customer> serviceFindAllCustomer(DetachedCriteria dCriteria) {
return daoFindAllCustomer(dCriteria);
}
public List<Customer> daoFindAllCustomer(DetachedCriteria dCriteria) {
Session s = HibernateUtil.getCurrentSession();
Transaction tx = s.beginTransaction();
//把离线对象使用可用Session激活
Criteria c = dCriteria.getExecutableCriteria(s);
List<Customer> list = c.list();
tx.commit();
return list;
}