hibernate学习笔记

持久化对象状态

Transient(临时对象)
没有被session管理
在数据库中没有与之匹配的记录;

Persistent(持久化对象)
纳入session管理
在数据库中有与之匹配的记录
当属性发生改变,在清理缓存时(脏数据检查)会自动和数据库同步;

Detached(游离对象)
没有被session管理
在数据库中存在与之匹配的记录;

get

加载上来的对象为持久对象;
执行get会马上发出查询语句;
get方法加载数据,如果不存在返回null;

load

load支持lazy(延迟加载/懒加载);
用load查询不存在的数据,hibernate会抛出ObjectNotFoundException异常;
hibernate的lazy是如何实现的?
采用代理对象实现,代理对象主要采用的是CGLIB库生成的,而不是JDK的动态代理,
因为JDK的动态代理只能对实现了接口的类生成代理,CGLIB可以对类生成代理,它采用的是继承方式.
所以返回的实际对象是一个 proxy对象,通过 instanceof 获得的就是类似 $$$proxy对象。

class

typename可以是如下几种:
Hibernate基本类型名(比如:integer, string, character,date, timestamp, float, binary, serializable, object, blob)。注意小写
一个Java类的名字,这个类属于一种默认基础类型 (比如: int, float,char, java.lang.String, java.util.Date, java.lang.Integer, java.sql.Clob)。
一个可以序列化的Java类的名字。
一个自定义类型的类的名字。(比如: com.illflow.type.MyCustomType)。

[hibernate多对一关联映射]

关联映射,就是将关联关系映射到数据库中,所谓的关联关系在对象模型中就是一个或多个引用
多对一关联映射原理:在多的一端加入一个外键,指向一的一端
在多的一端采用如下标签映射:

	<many-to-one name="classes" column="classesid" cascade="save-update"/>
在一的一端加入如下标签:
```
		<set name="students" inverse="true">
			 <key column="classesid"/>
			<one-to-many class="com.bjpowernode.hibernate.Student"/>
		</set>
```
掌握级联的含义?
 * 级联是对象之间的连锁操作,它只影响添加、删除和修改

[一对一关系映射]

一对一关联映射原理:让两个实体的主键一样,这样就不需要加入多余的字段了

hibernate一对一主键关联映射(双向关联Person<----->IdCard)

<class name="Person" table="t_person">
				<id name="id">
					<!-- 采用foreign生成策略,forgeign会取得关联对象的标识 -->
					<generator class="foreign">
						<!-- property只关联对象 -->
						<param name="property">idCard</param>
					</generator>
				</id>
				<property name="name"/>
				<!-- 
			one-to-one指示hibernate如何加载其关联对象,默认根据主键加载
				也就是拿到关系字段值,根据对端的主键来加载关联对象

			constrained="true表示,当前主键(person的主键)还是一个外键
			参照了对端的主键(IdCard的主键),也就是会生成外键约束语句
			-->
			<one-to-one name="idCard" constrained="true"/>
		</class>
			
       需要在IdCard加入<one-to-one>标签,指示hibernate将关联对象 Person 根据主键加载上来.
		<one-to-one name="person"/>
       <one-to-one>不影响存储,只影响加载

hibernate一对一唯一外键关联映射(单向关联Person----->IdCard)

        一对一唯一外键关联映射其实是多对一的特例
        采用<many-to-one>标签来映射,指定多的一端unique为true,这样就限制了多的一端的多重性为一,就是这样来映射的。
		
		<class name="Person" table="t_person">
			<id name="id">
				<generator class="native"/>
			</id>
			<property name="name"/>
			<many-to-one name="idCard" unique="true"/>
		</class>
    
    hibernate一对一唯一外键关联映射(双向关联Person<----->IdCard)
        一对一唯一外键关联双向采用<one-to-one>标签映射,必须指定 <one-to-one> 标签中的property-ref属性为关系字段的名称。
        property-ref: (可选) 指定关联类的属性名,这个属性将会和本类的主键相对应。如果没有指定,会使用对方关联类的主键。
		<one-to-one name="person" property-ref="idCard"/>

缓存

一级缓存
和session生命周期一致,一级缓存也叫session级缓存或事务级缓存;
支持一级缓存的方法:get/load/iterate 查询实体对象;
在同一个session中发出两次load查询 : 一次查询
在同一个session中发出两次get查询: 一次查询
在同一个session中发出两次iterate查询,查询实体对象:
第一次发出查询所有满足条件的对象,第二次发出查询id的语句
== 在同一个session中发出两次iterate查询,查询普通属性: ==
发出两次查询所有满足条件的对象的语句
iterate查询普通属性,一级缓存不会缓存,所以发出查询语句
一级缓存是缓存实体对象的
在两个session中发load查询:
两次查询语句。session间不能共享一级缓存数据;
在同一个session中先调用save,再调用load查询刚刚save的数据:
不会发出查询语句,save支持缓存
大批量的数据添加:
批量更新,save N条之后执行一次flush和clear

二级缓存
进程级的缓存或SessionFactory级的缓存,二级缓存可以被所有的session共享;
二级缓存的生命周期和SessionFactory的生命周期一致,SessionFactory可以管理二级缓存;
缓存原则:读远远大于写的数据进行缓存;
二级缓存的配置和使用:
Hibernate 使用EHCache 进行JVM级别的缓存;

      <property name="hibernate.show_sql">true</property>
  	<!-- 配置缓存提供商 -->
  	<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
  	<!-- 启用二级缓存,这也是它的默认配置 -->
  	<property name="hibernate.cache.use_second_level_cache">true</property>
  	<mapping resource="com/bjpowernode/hibernate/Student.hbm.xml"/>
  	<mapping resource="com/bjpowernode/hibernate/Classes.hbm.xml"/>
  	<!-- 指定哪些类使用二级缓存-->
  	<class-cache class="com.bjpowernode.hibernate.Student" usage="read-only"/>

一级缓存和二级缓存的交互 :
//禁止将一级缓存中的数据放到二级缓存中
session.setCacheMode(CacheMode.IGNORE);
这样两个session都会发出查询语句;
对于大批量数据更新,建议禁用一级缓存和二级缓存的交互

[flush]

    session flush方法主要做了两件事:
        * 清理缓存
        * 执行sql
        
    session在什么情况下执行flush
        * 默认在事务提交时
        * 显示的调用flush
        * 在执行查询前,如:iterate
        
    hibernate按照save(insert),update、delete顺序提交相关操作	
	
	uuid主键生成策略
		//因为user的主键生成侧路采用的是uuid,所以调用完成save后,只是将user纳入到了session的管理
		//不会发出insert语句,但是id已经生成,session中existsInDatebase状态为false
		session.save(user);
		
	native主键生成策略
		//因为user的主键生成策略为native,所以调用session.save后,将执行insert语句,返回有数据库生成的id
		//纳入了session的管理,修改了session中existsInDatebase状态为true
		//如果数据库的隔离级别设置为为提交读,那么我们可以看到save过的数据
	
	uuid主键生成策略
		//因为user的主键生成侧路采用的是uuid,所以调用完成save后,只是将user纳入到了session的管理
		//不会发出insert语句,但是id已经生成,session中existsInDatebase状态为false
		session.save(user);
		
		//将user对象从session中逐出,即session的EntityEntries属性中逐出
		session.evict(user);
		
		//无法成功提交,因为hibernate在清理缓存时,在session的insertions集合中取出user对象进行insert操作后
		//需要更新entityEntries 中session的existsInDatabase为true,而我们采用evict已经将user从session的entityEntries
		//中逐出了,所以找不到相关数据,无法更新,抛出异常
		tx.commit();

[一对多关系映射]

    一对多关联映射和多对一关联映射映射原理是一致的,都是在多的一端加入一个外键指向一的一端
    它们的区别在于维护的关系不同:
        * 多对一维护的关系是:多指向一的关系,有了此关系,在加载多的时候可以将一加载上来
        * 一对多维护的关系是:一指向多的关系,有了此关系,在加载一的时候可以将多加载上来
    在一一端维护关系存在缺陷:
        * 因为多的一端Student不知道Classes的存在(也就是Student没有维护与Classes的关系)所以在保存Student的时候关系字段classesid是为null的,
        如果将该关系字段设置为非空,则将无法保存数据
        * 另外因为Student不维护关系,而Classes维护关系,Classes就会发出多余的update语句,保证 Classes和Student有关系,这样加载Classes的时候才可以把该Classes对应的学生加载上来
        
    hibernate一对多关联映射(双向Classes<--->Student)
        采用一对多双向关联映射的目的主要是为了主要是为了解决一对多单向关联的缺陷而不是需求驱动的

        一对多双向关联的映射方式:
            * 在一的一端的集合上采用<key>标签,在多的一端加入一个外键. one-to-many 没有级联操作
            * 在多的一端采用<many-to-one>标签          
        !!!注意:<key>标签和<many-to-one>标签加入的字段保持一直,否则会产生数据混乱

        inverse属性:
            * inverse属性可以用在一对多和多对多双向关联上,inverse属性默认为false,表示本端可以维护关系,
            如果inverse为true,则本端不能维护关系,会交给另一端维护关系,本端失效。
            所以一对多关联映射我们通常在多的一端维护关系,让一的一端失效,所以设置为inverse为true
            
        inverse和cascade
            * inverse是控制方向上的反转,只影响存储
            * cascade是操作上的连锁反映. (只作用在 many-to-one 多的一端)

【查看隔离级别】

    select @@tx_isolation;
    set transaction isolation level REPEATABLE READ;

【lazy 策略】

    hibernate的lazy策略可以使用在:
    * <class>标签上,可以取值:true/false
    * <property>标签上,可以取值:true/false,需要类增强工具,对字节码进行修改
    * <set>/<list>标签上,可以取值:true/false/extra
    * <many-to-one>/<one-to-one>单端关联标签上,可以取值:false/proxy/no-proxy

    lazy的概念:在正真使用某个对象的时候才正真的去创建,对于hibernate才会正真的发出sql语句去加载该对象
    hibernate的lazy策略必须在session打开状态下有效 **
    <class>上的lazy只影响普通属性 **
	
	集合的lazy=true:
		//不会发出sql
		Classes classes = (Classes)session.load(Classes.class, 1);
		//会发出sql
		System.out.println("classes.name=" + classes.getName());
		//不会发出sql ****
		Set students = classes.getStudents();
		//会发出sql
		for (Iterator iter=students.iterator(); iter.hasNext();) {
			Student student = (Student)iter.next();
			System.out.println("student.name=" +student.getName());
		}
	
	集合的lazy=false:
		//不会发出sql
		Classes classes = (Classes)session.load(Classes.class, 1);
		//会发出两条查询语句,分别查询班级和学生***
		System.out.println("classes.name=" + classes.getName());
		//不会发出sql
		Set students = classes.getStudents();
		//不会发出sql
		for (Iterator iter=students.iterator(); iter.hasNext();) {
			Student student = (Student)iter.next();
			System.out.println("student.name=" +student.getName());
		}
	集合的lazy=extra:
		//不会发出sql
		Classes classes = (Classes)session.load(Classes.class, 1);
		//会发出sql
		System.out.println("classes.name=" + classes.getName());
		//不会发出sql
		Set students = classes.getStudents();
		//会发出一条智能的sql,如:
		//Hibernate: select count(id) from t_student where classesid =?
		//建议集合上的lazy设置为extra
		System.out.println("count=" + students.size());

【HQL】

    在hql中关键字不区分大小写,通常小写,类的名称和属性名称必须区分大小写;
    2、实体对象查询【重要】
    * N + 1问题,就是发出了N+1条sql语句
        1:首先发出查询对象id列表的语句
        N:根据id到缓存中查询,如果缓存中不存在与之匹配的数据,那么会根据id发出相应的sql语句

    *list和iterate的区别?
        list: 默认情况下list每次都会发出sql语句,list会将数据放到缓存中,而不利用缓存
        iterate:默认情况下iterate利用缓存,如果缓存中不存在会出现N+1问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值