1、 left outer join 左外连接:
左外连接的查询结果集中包括指定左表(主表)中的所有行,
* 而不仅仅是连接列的所有行。如果左表的某行在右表(从表)中没有找到匹配的行,则结果
* 集中的右表的相对的位置为 NULL
2、关于hibernate.hbm2ddl.auto
<property name="hibernate.hbm2ddl.auto">update</property>
将这个属性设为 update 只有我们发生修改的时候才会重新创建表 否则不会被覆盖掉(未理解)
,在SessionFactory创建时,自动检查数据库结构,或者将数据库schema的DDL导出到数据库。使用
create-drop时,在显式关闭SessionFactory时,将drop掉数据库schema.
例如:我们将所有的表都删除了当我们执行插入操作时,SessionFactory被创建那么会自动检
查数据库scheme的DDL,建立数据表,并且会保留原来的数据,所以我们不用 Export 配置中的数据到数
据库,那样会覆盖掉所有的数据。
3、 关于对象关系信息导出
这里必须调用 configure 方法 否则会映射 properties 文件
Configuration cfg = new Configuration().configure();
//用于导出用户定义的 xml 配置信息,在数据库中生成对应的Table
SchemaExport export = new SchemaExport(cfg);
export.create(true, true);
4、* 测试lazy在class上的策略 这里默认为true
我们在这里应当搞清session.get()和session.load()的区别
方法load支持 lazy(在Session缓存中生成一个User的子类对象返回,只有当调用方法是才查询) 如果
不存在查询的结果则抛异常
session = HibernateUtil.getSession();
tx = session.beginTransaction();
Classes c1 = (Classes)session.load(Classes.class, 1);
System.out.println("c1.getId()"+c1.getId());
System.out.println("c1.getName()"+c1.getName());
这里不会发出sql语句,因为这里就采用lazy策略,所以这里采用了第三方的组件返回了一个,一个新的
Classes 的实例(代理),只有当我们需要查询属性时才会发出算sql语句,在这里不会发出sql语句因为
id 是我们输入的。
hibernate 支持的lazy 策略只在session的生命周期内有效。
5、lazy在collection上的策略
很显然这些lazy都是默认的
Classes c1 = (Classes)session.load(Classes.class, 1);
//如果我们将集合上的lazy设为false,这里会发出两条sql除了将要查询的
普通属性查出来外,还会将非普通属性
//将集合上的lazy设为extra和true是一样的
System.out.println("c1.name= " + c1.getName());
//没有发出sql 但是会生成一个 persistent 的set作为代理
Set students = c1.getStudents();
for(Iterator it = students.iterator();it.hasNext();){
Student s = (Student)it.next();
System.out.println("student.name= " + s.getName());
}
//System.out.println("student.number= " + student.size());
//这里会将所有的数据读取出来并生成对象存放在set中
/*这里Classes的lazy策略只对普通的属性有关,而collection的lazy策略才对
* collection对象生效*/
*测试在标签上的lazy 策略和集合上的lazy策略一样。
6、one2one
<class name="org.n2535.hibernate.Person" table="t_person">
<id name="id">
<!-- 这里表示了Person的主键是作为一个外键关联了IdCard的主键 -->
<generator class="foreign">
<!-- 表示主键的生成是根据 属性 idCard 的 id -->
<param name="property">idCard</param>
</generator>
</id>
<property name="name" />
<!-- 表示了Person与Idcard是一对一的关系 默认为根据主键加载对象(主键关联) -->
<!-- constrained(约束) (可选) 表明该类对应的表对应的数据库表,
和被关联的对象所对应的数据库表之间,通过一个外键引用对主键进行约束。
而且它们之间的约束条件为true
在这里默认了两者的级联操作,否则的话一方的主键就为空没有任何意义了 -->
<one-to-one name="idCard" constrained="true"/>
</class>
关于idCard的xml配置可以用
<!-- 默认为主键关联 即加载cardNo 时根据其主键将 关联的Person表中 主键相同的 元组
取出
默认的抓取策略为 Join -->
<one-to-one name="person"/>
当然也可以不用。
7、extends
<!-- 测试每个具体的类映射一种表,这里Animal应该是抽象的,所以不应该被生成出来,将
abstract="true"-->
<class name="Animal" abstract="true">
<id name="id">
<generator class="assigned"/>
</id>
<!-- 此处不需要鉴别器,因为每个具体的类都生产了一张表 -->
<property name="name" />
<property name="sex" />
<union-subclass name="Pig" table="t_pig">
<property name="weight"/>
</union-subclass>
<union-subclass name="Bird" table="t_bird">
<property name="height"/>
</union-subclass>
</class>
<!-- 测试单表继承映射 -->
<class name="Animal" table="t_animal" lazy="false">
<id name="id">
<generator class="native"/>
</id>
<!-- discriminator 鉴别器,用来识别不同的子类 -->
<discriminator column="type" type="string"/>
<property name="name" />
<property name="sex" />
<!-- Pig -->
<!-- discriminator-value的默认是完整的类名 -->
<subclass name="Pig" discriminator-value="P">
<property name="weight"/>
</subclass>
<!-- Bird -->
<subclass name="Bird" discriminator-value="B">
<property name="height"/>
</subclass>
</class>
8、component 组件映射测试
component类不是一个实体类,在hibernate中的实体是指的 一个Pojo+一个映射文件
component类是一个辅助类
component 组件映射
<component name="contact" class="org.n2535.hibernate.Contact">
<property name="email"/>
<property name="address"/>
<property name="zipCode"/>
<property name="contactTel"/>
</component>
9、复合主键
因为主键是不可重复的,所以在复合主键的类中我们实现了Serializable接口,
和hashcode、equals方法,能够确保主键的唯一性。
<!-- 复合主键映射我们也可以认为是一种 component 组件映射的一种 -->
<composite-id name="pk">
<key-property name="productYear"/>
<key-property name="productMonth"/>
</composite-id>
<property name="name"/>
<property name="factory"/>
</class>
10、集合映射
<set name="setValue" table="t_set_value">
<key column="set_id"/>
<element type="string" column="set_value"/>
</set>
<list name="listValue" table="t_list_value">
<key column="list_id"/>
<!-- 这里必须给出排序的索引,因为list是有序的(下同) -->
<list-index column="list_index"/>
<element type="string" column="list_value"/>
</list>
<array name="arrayValue" table="t_array_value">
<key column="array_id"/>
<list-index column="array_index"/>
<element type="string" column="array_value"/>
</array>
<map name="mapValue" table="t_map_value">
<key column="map_id"/>
<map-key type="string" column="map_key"/>
<element type="string" column="map_value"/>
</map>
11、关于Hibernate的锁
1)、悲观锁:先获得数据库的锁的线程,知道该线程放弃提交,其他的线程将无法修改该数据
(LockMode.UPGRADE :利用数据库的for update子句加锁。)
2)、乐观锁:通常利用一个 version 的版本冲突来实现,实际上不是数据库的锁,一个线程的version
必须大于数据库中的version值才能被存储,否则报错。这样提供了比悲观锁宽松的条件,只要 version
大于数据库中的version就可以被存储,而不会因为是否死一个线程先获得锁,因为乐观锁根本就不是一
种锁。
* 未使用悲观锁的时候,不同的操作可以访问相同的数据,那么会造成数据的错误
* 使用悲观锁的时候,可以避免这个问题
可以不用显示的调用,如果为调用update那么在提交的时候会自动的update,当第一个操作将数据锁住
的时候,(所有的其他访问将被禁止)第二个select操作将会阻塞,知道第一个操作释放了锁,第二个
对象的选择操作会根据第一个对像的update的结果来读取数据,如果两个对象都从数据库中读取了相同
的数据,那么第一次的update操作,将会被第二次的覆盖,造成错误的数据。
session.update(inv);
要使用悲观锁请使用指定的数据库的命令,或者使用hibernate中的配置。
* 乐观锁采用的version的冲突机制,如果update的version小于或数据库中的version
* 将会产生错误插入失败,一位update的限制条件是根据 主键和version 所以会失败
* 我们还能仿照 Hibernate 的乐观锁机制,用Jdbc实现乐观锁
12、hibernate查询语言hql
在hql中关键字不区分大小写,但是属性和类名区分大小写
1)、简单属性查询【重要】
* 单一属性查询,返回结果集属性列表,元素类型和实体类中相应的属性类型一致
* 多个属性查询,返回的集合元素是对象数组,数组元素的类型和对应的属性在实体类中的类型
一致
数组的长度取决与select中属性的个数
* 如果认为返回数组不够对象化,可以采用hql动态实例化Student对象
参见:SimplePropertyQueryTest.java
2)、实体对象查询【重要】
* N + 1问题,在默认情况下,使用query.iterate查询,有可以能出现N+1问题
所谓的N+1是在查询的时候发出了N+1条sql语句
1: 首先发出一条查询对象id列表的sql
N: 根据id列表到缓存中查询,如果缓存中不存在与之匹配的数据,那么会根据id发出相应的
sql语句
* list和iterate的区别?
* list每次都会发出sql语句,list会向缓存中放入数据,而不利用缓存中的数据
* iterate:在默认情况下iterate利用缓存数据,但如果缓存中不存在数据有可以能
出现N+1问题
参见:SimpleObjectQueryTest1.java/SimpleObjectQueryTest2.java
3)、条件查询【重要】
* 可以采用拼字符串的方式传递参数
* 可以采用 ?来传递参数(索引从0开始)
* 可以采用 :参数名 来传递参数
* 如果传递多个参数,可以采用setParamterList方法
* 在hql中可以使用数据库的函数,如:date_format
参见:SimpleConditionQueryTest.java
4)、hibernate也支持直接使用sql进行查询
参见:SqlQueryTest.java
5)、外置命名查询
* 在映射文件中采用<query>标签来定义hql
* 在程序中采用session.getNamedQuery()方法得到hql查询串
参见:Student.hbm.xml、NameQueryTest.java
6)、查询过滤器
* 在映射文件中定义过滤器参数
* 在类的映射中使用这些参数
* 在程序中启用过滤器
参见:Student.hbm.xml、FilterQueryTest.java
7)、分页查询【重要】
* setFirstResult(),从0开始
* setMaxResults,每页显示多少条数据
参见:PageQueryTest.java
8)、对象导航查询,在hql中采用 . 进行导航【重要】
参见:ObjectNavQueryTest.java
9)、连接查询【重要】
* 内连
* 外连接(左连接/右连接)
参见:JoinQueryTest.java
10)、统计查询【重要】
参见:StatQueryTest.java
11)、DML风格的操作(尽量少用,因为和缓存不同步)
参见:DMLQueryTest.java
13、Hibernate中的缓存
1)、一级缓存
session 中一级缓存的生命周期和session的相同
由于load使用了session中的一级缓存 所以第二次的load 并不会发sql 因为在session的缓存中还存在
相同的数据。
由于get使用了session中的一级缓存 所以第二次的get 并不会发sql 因为在session的缓存中还存在相
同的数据。
会发出查询 id 的 sql(使用一级缓存才会发出) 但是不会发出查询对象的 sql 因为Iterate
查询支持缓存。
在save的时候会在缓存中保存一份当前对象的引用。
session.clear();//全部清除
2)、二级缓存
<!-- 指定二级缓存的提供者 -->
<property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
<!-- 指定是否使用二级缓存 默认为true -->
<property name="cache.use_second_level_cache">true</property>
使用ehcache.xml这个配置文件来配置二级缓存。
session 中二级缓存的生命周期和sessionFactory的相同,所以又称为 sessionFactory 级缓存,二级
缓存只能用于存储实体。
3)、三级缓存
<!-- 使用查询缓存 默认为false-->
<property name="cache.use_query_cache">true</property>
queryCache生命周期和sessionFactory的相同。
但是在数据库里面的数据改变的时候,queryCache中的对象失效。
QueryCache的生命周期与session无关。
Iterate 接口不支持 QueryCache。
这里使用list 来查询实体对象 并开启了QueryCache 由于QueryCache 会存储实体的主键值,而list 在
查询实体的时候不会使用缓存 所以list会使用QueryCache的实体的主键值 去查询相应的,实体,由于
它会现在缓存中查找实体对象 如果不存在则会发出sql到数据库中查询 这里没有配置二级缓存 ,又重
开了session所以在缓存中不存在实体对象 所以会根据在QueryCache 中的实体主键值发出sql到数据库
中查询。
因为开启了二级缓存,QueryCache 会存储查询出来的实体的主键,而list会根据在QueryCache中的 主
键值到二级缓存中查找相应的实体,所以不会发出sql(list接口不会使用一级缓存但是能够利用这种方
法使用QueryCache 和 二级缓存)。
14、抓取策略
1)、fetch = "select" 抓取策略 发两个select语句;fetch = "join" 抓取策略 使用外连接
查询。
2)、在集合中的抓取策略
•使用 load
* fetch = "select" 抓取策略 发两条sql
* fetch = "join" 发一条sql 采用外连接查询
•使用 HQL
* fetch = "select" 抓取策略 发两条sql 和加载对象方式一样,每次每个实体的集合时会发sql
* fetch = "subselect" 将会在查询实体的集合时将所有查询的实体的集合发一次sql 全部查询出来
3)、测试 batch – size
batch-size="3"在集合中设置。