hibernate总结(二)

hibernate持久化类和对象标识符

什么是持久化类:

Hibernate是持久层的ORM映射框架,专注于数据的持久化工作。所谓的持久化,就是将内存中的数据永久存储到关系型数据库中。

持久化类的编写规则:

(1)持久化类需要提供无参数的构造方法。因为在Hibernate的底层需要使用反射生成类的实例。

(2)持久化类的属性需要私有,对私有的属性提供公有的get和set方法。因为在Hibernate底层会将查询到的数据进行封装。

(3)持久化类的属性要尽量使用包装类的类型。因为包装类和基本数据类型的默认值不同,包装类的类型语义描述更清晰而基本数据类型不容易描述。举个例子:

假设表中有一列员工工资,如果使用double类型,如果这个员工工资忘记录入到系统中,系统会将默认值0存入到数据库,如果这个员工工资被扣完了,也会向系统中存入0.那么这个0有了多重含义,而如果使用包装类类型就会避免以上情况,如果使用Double类型,忘记录入工资就会存入null,而这个员工工资被扣完了,就会存入0,不会产生歧义。

(4)持久化类要有一个唯一标识OID与表的主键对应。因为Hibernate中需要通过这个唯一标识OID区分在内存中是否是同一个持久化类。在Java中通过地址区分是否是同一个对象的,在关系型数据库的表中是通过主键区分是否同一条记录。那么Hibernate就是通过这个OID来进行区分的。Hibernate是不允许在内存中出现两个OID相同的持久化对象的。

(5)持久化类尽量不要使用final进行修饰。因为Hibernate中有延迟加载的机制,这个机制中会产生代理对象,Hibernate产生代理对象使用的是字节码的增强技术完成的,其实就是产生了当前类的一个子类对象实现的。如果使用了final修饰持久化类。那么就不能产生子类,从而就不会产生代理对象,那么Hibernate的延迟加载策略(是一种优化手段)就会失效。.

持久化类我们已经可以正常编写了,但是在持久化类中需要有一个唯一标识OID与表的主键去建立映射关系。而且主键一般我们是不会让客户手动录入的,一般我们是由程序生成主键。Hibernate中也提供了相应的主键生成的方式。

hibernate中对象标识符(OID)

OID全称是Object Identifier,又叫做对象标识符。

它是hibernate用于区分两个对象是否是同一个对象的标识。

扫描二维码关注公众号,回复: 5667542 查看本文章

我们都知道,虚拟机内存区分两个对象看的是内存的地址是否一致。数据库区分两个对象,靠的是表的主键。hibernate负责把内存中的对象持久化到数据库表中,靠的就是对象标识符来区分两个对象是否是同一个。实体类中映射主键的字段就是OID,如下图所示:

hibernate的主键生成策略

在讲解Hibernate的主键生成策略之前,先来了解两个概念,即自然主键和代理主键,具体如下:

自然主键:把具有业务含义的字段作为主键,称之为自然主键。例如在customer表中,如果把name字段作为主键,其前提条件必须是:每一个客户的姓名不允许为null,不允许客户重名,并且不允许修改客户姓名。尽管这也是可行的,但是不能满足不断变化的业务需求,一旦出现了允许客户重名的业务需求,就必须修改数据模型,重新定义表的主键,这给数据库的维护增加了难度。

代理主键:把不具备业务含义的字段作为主键,称之为代理主键。该字段一般取名为“ID”,通常为整数类型,因为整数类型比字符串类型要节省更多的数据库空间。在上面例子中,显然更合理的方式是使用代理主键。

Hibernate的一级缓存和对象状态

hibernate中的一级缓存:

Hibernate的一级缓存就是指Session缓存,Session缓存是一块内存空间,用来存放相互管理的java对象,在使用Hibernate查询对象的时候,首先会使用对象属性的OID值在Hibernate的一级缓存中进行查找,如果找到匹配OID值的对象,就直接将该对象从一级缓存中取出使用,不会再查询数据库;如果没有找到相同OID值的对象,则会去数据库中查找相应数据。当从数据库中查询到所需数据时,该数据信息也会放置到一级缓存中。Hibernate的一级缓存的作用就是减少对数据库的访问次数。

 

在 Session 接口的实现中包含一系列的 Java 集合, 这些 Java 集合构成了 Session 缓存。只要 Session 实例没有结束生命周期,存放在它缓存中的对象也不会结束生命周期。固一级缓存也被称为是Session基本的缓存。

 

Hibernate的一级缓存有如下特点:

  1. 当应用程序调用Session接口的save()、update()、saveOrUpdate时,如果Session缓存中没有相应的对象,Hibernate就会自动的把从数据库中查询到的相应对象信息加入到一级缓存中去。
  2. 当调用Session接口的load()、get()方法,以及Query接口的list()、iterator()方法时,会判断缓存中是否存在该对象,有则返回,不会查询数据库,如果缓存中没有要查询对象,再去数据库中查询对应对象,并添加到一级缓存中。
  3. 当调用Session的close()方法时,Session缓存会被清空。

快照机制:

Hibernate 向一级缓存放入数据时,同时复制一份数据放入到Hibernate快照中,当使用commit()方法提交事务时,同时会清理Session的一级缓存,这时会使用OID判断一级缓存中的对象和快照中的对象是否一致,如果两个对象中的属性发生变化,则执行update语句,将缓存的内容同步到数据库,并更新快照;如果一致,则不执行update语句。Hibernate快照的作用就是确保一级缓存中的数据和数据库中的数据一致。

对象的三种状态

了解了主键的生成策略之后,我们可以进一步来了解持久化类了。Hibernate为了更好的来管理持久化类,特将持久化类分成了三种状态。在Hibernate中持久化的对象可以划分为三种状态,分别是瞬时态、持久态和脱管态,一个持久化类的实例可能处于三种不同状态中的某一种,三种状态的详细介绍如下。

瞬时态(transient)

瞬时态也称为临时态或者自由态,瞬时态的实例是由new命令创建、开辟内存空间的对象,不存在持久化标识OID(相当于主键值),尚未与Hibernate Session关联,在数据库中也没有记录,失去引用后将被JVM回收。瞬时状态的对象在内存中是孤立存在的,与数据库中的数据无任何关联,仅是一个信息携带的载体。

持久态(persistent)

 持久态的对象存在持久化标识OID ,加入到了Session缓存中,并且相关联的Session没有关闭,在数据库中有对应的记录,每条记录只对应唯一的持久化对象,需要注意的是,持久态对象是在事务还未提交前变成持久态的。

脱管态(detached)

脱管态也称离线态或者游离态,当某个持久化状态的实例与Session的关联被关闭时就变成了脱管态。脱管态对象存在持久化标识OID,并且仍然与数据库中的数据存在关联,只是失去了与当前Session的关联,脱管状态对象发生改变时Hibernate不能检测到。

区分状态只有两个标识

一是否有OID

二是否和Session建立的关系

临时状态:

没有OID,和Session没有关系。

持久化状态:

有OID,和Session有关系。

脱管状态:

有OID,和Session没有关系

Hibernate的事务控制

配置Session和线程绑定

在Hibernate中,可以通过代码来操作管理事务

除了在代码中对事务开启,提交和回滚操作外,还可以在Hibernate的配置文件中对事务进行配置。配置文件中,可以设置事务的隔离级别。其具体的配置方法是在hibernate.cfg.xml文件中的<session-factory>标签元素中进行的。配置方法如下所示。

<!—

 事务隔离级别

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>

到这我们已经设置了事务的隔离级别,那么我们在真正进行事务管理的时候,需要考虑事务的应用的场景,也就是说我们的事务控制不应该是在DAO层实现的,应该在Service层实现,并且在Service中调用多个DAO实现一个业务逻辑的操作

配置步骤:

细节:

当我们把Session绑定到当前线程之后,关闭session就是hibernate来做的,我们就不用关了。


 

1、在hibernate.cfg.xml文件中配置

<!-- 把session绑定到当前线程上 -->

<property name="hibernate.current_session_context_class">thread</property>

2、获取Session时使用的方法:

/**

 * 每次都是从当前线程上获取Session

 * @return

 */

public static Session getCurrentSession(){

return factory.getCurrentSession();

}

Hibernate查询对象的API

Query

Query代表面向对象的一个Hibernate查询操作。在Hibernate中,通常使用session.createQuery()方法接受一个HQL语句,然后调用Query的list()或uniqueResult()方法执行查询。所谓的HQL是Hibernate Query Language缩写,其语法很像SQL语法,但它是完全面向对象的。

在Hibernate中使用Query对象的步骤,具体所示:

(1)获得Hibernate的Session对象。

(2)编写HQL语句。

(3)调用session.createQuery 创建查询对象。

(4)如果HQL语句包含参数,则调用Query的setXxx设置参数。

(5)调用Query对象的方法执行查询。

HQL的说明:

把表的名称换成实体类名称。把表字段名称换成实体类属性名称。

例如:

SQL:select * from cst_customer where cust_name like ?

HQL:select * from Customer where custName = ?

  其中select * 可以省略,写为:from Customer where custName = ?

了解了使用Query对象的步骤后,接下来,通过具体示例来演示Query对象的查询操作。

查询所有:

/**
	 * 查询所有
	 */
	@Test
	public void test1(){
		Session s = HibernateUtil.getCurrentSession();
		Transaction tx = s.beginTransaction();
		//1.获取Query对象
		Query query = s.createQuery("from Customer");
		//2.执行对象的方法,获取结果集
		List list = query.list();
		for(Object o : list){
			System.out.println(o);
		}
		tx.commit();
	}	

条件查询:

/**
	 * 条件查询
	 * 	hibernate的参数占位符索引是从0开始的
	 */
	@Test
	public void test2(){
		Session s = HibernateUtil.getCurrentSession();
		Transaction tx = s.beginTransaction();
		//1.获取Query对象
		Query query = s.createQuery("from Customer where custName like ? and custLevel = ?");
		//2.给参数占位符赋值
		query.setString(0, "%集%");
		query.setString(1, "普通客户");
		//3.执行对象的方法,获取结果集
		List list = query.list();
		for(Object o : list){
			System.out.println(o);
		}
		tx.commit();
	}
	
	/**
	 * 条件查询
	 *  给参数占位符提供一个具体的名称
	 *  参数占位符的写法:
	 *  	:名称
	 *  赋值的时候不需要写冒号,直接写名称
	 */
	@Test
	public void test3(){
		Session s = HibernateUtil.getCurrentSession();
		Transaction tx = s.beginTransaction();
		//1.获取Query对象
		Query query = s.createQuery("from Customer where custName like :custName and custLevel = :custLevel");
		//2.给参数占位符赋值
		query.setString("custName", "%集%");
		query.setString("custLevel", "普通客户");
		//3.执行对象的方法,获取结果集
		List list = query.list();
		for(Object o : list){
			System.out.println(o);
		}
		tx.commit();

分页查询:

	/**
	 * mysql的分页关键字
	 * 		limit
	 * limit的参数含义
	 * 	第一个:查询的开始记录索引
	 * 	第二个:每次查询的条数
	 * hibernate中针对分页提供了两个方法
	 * 		setFirstResult(int firstResult);设置开始记录索引
	 * 		setMaxResults(int maxResults);设置每次查询的记录条数
	 */
	@Test
	public void test4(){
		Session s = HibernateUtil.getCurrentSession();
		Transaction tx = s.beginTransaction();
		//1.获取Query对象
		Query query = s.createQuery("from Customer");
		//2.设置分页的方法
		query.setFirstResult(2);
		query.setMaxResults(2);
		//3.执行对象的方法,获取结果集
		List list = query.list();
		for(Object o : list){
			System.out.println(o);
		}
		tx.commit();
	}	

排序查询:

	
4.1.2.4排序查询
/**
	 * 排序查询
	 * 	使用的关键字:
	 * 		order by
	 * 	升序:
	 * 	  asc	默认值
	 *  	降序:
	 *    desc
	 */
	@Test
	public void test2(){
		Session s = HibernateUtil.getCurrentSession();
		Transaction tx = s.beginTransaction();
		Query query = s.createQuery("from LinkMan order by lkmId desc ");
		List list = query.list();
		for(Object o : list){;
			System.out.println(o);
		}
		tx.commit();
	}				

统计查询:

/**
	 * HQL使用聚合函数:
	 * 		统计查询
	 * 聚合函数:
	 * 	 count sum max min avg
	 * 
	 * sql语句使用聚合函数时,在不使用group by子句的情况下,返回的结果,永远只有一行一列的情况。
	 * 
	 * 在SQL语句时:
	 * 		select count(*) from table		它是统计所有字段,效率没有只统计主键字段高
	 * 		select count(主键) from table	它和第一个的结果是一样的,但是效率更高
	 * 		select count(非主键) from table	只统计不为null的字段
	 */
	@Test
	public void test1(){
		Session s = HibernateUtil.getCurrentSession();
		Transaction tx = s.beginTransaction();
		Query query = s.createQuery("select count(lkmId) from LinkMan");//它最终仍然换转成SQL语句
//		List list = query.list();
//		for(Object o : list){;
//			System.out.println(o);
//		}
		
		Long total = (Long)query.uniqueResult();//返回的是一个唯一的结果集。 只有确定结果集唯一时,才能使用
		System.out.println(total);
		tx.commit();
	}		

投影查询:

/**
 * 投影查询:
 * 	 投影:使用一个实体的部分字段信息,来构建实体类对象,叫做对象的投影(在hibernate中的叫法)
 * 	使用HQL的方式查询实体类的部分字段信息,并且封装到实体类中。(QBC也能实现投影查询,但是不如hql的好用,所以使用投影查询,一般都用HQL)
 * HQL语句的写法:
 * 	 select  new Customer() from Customer  
 * 	 如果工程只有一个唯一的类,可以不写全限定类名,否则必须写全限定类名。
 * 实体类要求:
 * 	必须提供一个相同参数列表的构造函数
 */
@Test
public void test3(){
	Session s = HibernateUtil.getCurrentSession();
	Transaction tx = s.beginTransaction();
	Query query = s.createQuery("select new Customer(custId,custName) from Customer ");
	List<Object[]> list = query.list();
	for(Object o : list){
		System.out.println(o);
	}
	tx.commit();
}				
/**
 * 客户的实体类
*/
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;
	}
 	
	public Long getCustId() {
		return custId;
	}
	public void setCustId(Long custId) {
		this.custId = custId;
	}
	public String getCustName() {
		return custName;
	}
	public void setCustName(String custName) {
		this.custName = custName;
	}
	public String getCustSource() {
		return custSource;
	}
	public void setCustSource(String custSource) {
		this.custSource = custSource;
	}
	public String getCustIndustry() {
		return custIndustry;
	}
	public void setCustIndustry(String custIndustry) {
		this.custIndustry = custIndustry;
	}
	public String getCustLevel() {
		return custLevel;
	}
	public void setCustLevel(String custLevel) {
		this.custLevel = custLevel;
	}
	public String getCustAddress() {
		return custAddress;
	}
	public void setCustAddress(String custAddress) {
		this.custAddress = custAddress;
	}
	public String getCustPhone() {
		return custPhone;
	}
	public void setCustPhone(String custPhone) {
		this.custPhone = custPhone;
	}
	public Set<LinkMan> getLinkmans() {
		return linkmans;
	}
	public void setLinkmans(Set<LinkMan> linkmans) {
		this.linkmans = linkmans;
	}
	@Override
	public String toString() {
		return "Customer [custId=" + custId + ", custName=" + custName + ", custSource=" + custSource
				+ ", custIndustry=" + custIndustry + ", custLevel=" + custLevel + ", custAddress=" + custAddress
				+ ", custPhone=" + custPhone + "]";
	}	
}

Query中的方法说明:

list方法:该方法用于查询语句,返回的结果是一个list集合。

uniqueResult方法:该方法用于查询,返回的结果是一个Object对象。

setter方法:Query接口中提供了一系列的setter方法用于设置查询语句中的参数,针对不同的数据类型,需要用到不同的setter方法。

uniqueResult()方法:该方法用于返回唯一的结果,在确保只有一条记录的查询时可以使用该方法。

setFirstResult()方法:该方法可以设置获取第一个记录的位置,也就是它表示从第几条记录开始查询,默认从0开始计算。

setMaxResult()方法:该方法用于设置结果集的最大记录数,通常与setFirstResult()方法结合使用,用于限制结果集的范围,以实现分页功能。

 

Criteria: 

概述

Criteria是一个完全面向对象,可扩展的条件查询API,通过它完全不需要考虑数据库底层如何实现,以及SQL语句如何编写,它是Hibernate框架的核心查询对象。Criteria 查询,又称为QBC查询(Query By Criteria),它是Hibernate的另一种对象检索方式。

org.hibernate.criterion.Criterion是Hibernate提供的一个面向对象查询条件接口,一个单独的查询就是Criterion接口的一个实例,用于限制Criteria对象的查询,在Hibernate中Criterion对象的创建通常是通过Restrictions 工厂类完成的,它提供了条件查询方法。

通常,使用Criteria对象查询数据的主要步骤,具体如下:

(1)获得Hibernate的Session对象。

(2)通过Session获得Criteria对象。

(3)使用Restrictions的静态方法创建Criterion条件对象。Restrictions类中提供了一系列用于设定查询条件的静态方法,这些静态方法都返回Criterion实例,每个Criterion实例代表一个查询条件。

(4)向Criteria对象中添加Criterion 查询条件。Criteria的add()方法用于加入查询条件。

(5)执行Criterita的 list() 或uniqueResult() 获得结果。

细节:

HQL能查的,QBC都能查,反之亦然。

了解了Criteria对象的使用步骤后,接下来,通过具体示例来演示Criteria对象的查询操作。

基本查询:

/**

 * 查询所有

 */

@Test

public void test1(){

Session s = HibernateUtil.getCurrentSession();

Transaction tx = s.beginTransaction();

//1.获取Criteria对象

Criteria c = s.createCriteria(Customer.class);//它就相当于HQL的from Customer

//2.执行对象的方法获取结果集

List list = c.list();

for(Object o : list){

System.out.println(o);

}

tx.commit();

}

附录:QBC常用查询条件说明

短语 

含义 

Restrictions.eq

等于=

Restrictions.allEq

使用Map,使用key/value进行多个等于的判断 

Restrictions.gt

大于>

Restrictions.ge

大于等于>=

Restrictions.lt

小于<

Restrictions.le

小于等于<=

Restrictions.between

对应sql的between子句 

Restrictions.like

对应sql的like子句 

Restrictions.in

对应sql的in子句 

Restrictions.and

and 关系 

Restrictions.or

or关系 

Restrictions.sqlRestriction

Sql限定查询 

Restrictions.asc()

根据传入的字段进行升序排序 

Restrictions.desc()

根据传入的字段进行降序排序 

 

 

运算类型 

HQL运算符

QBC运算方法

比较运算

=

Restrictions.eq()

<>

Restrictions.not(Restrictions.eq())

>=

Restrictions.ge()

<

Restrictions.lt()

<=

Restrictions.le()

is null

Restrictions.isNull()

is not null

Restrictions.isNotNull()

范围运算符

in

Restrictions.in()

not in

Restrictions.not(Restrictions.in())

between

Restrictions.between()

not between

Restrictions.not(Restrictions.between())

 

 

运算类型 

HQL运算符

QBC运算方法

字符串模式匹配

like

Restrictions.like()

逻辑

and

Restrictions.and()|

Restrictions.conjunction()

or

Restrictions.or()|

Restrictions.disjunction()

not

Restrictions.not()

 

 

 

猜你喜欢

转载自blog.csdn.net/Gray_humor/article/details/88571817