(一)第一个hibernate项目
2、创建User Library,加入依赖包
* HIBERNATE_HOME/lib/*.jar
* HIBERNATE_HOME/hibernate3.jar
* 加入数据库驱动(mysql驱动)
3、提供hibernate.cfg.xml文件,完成基本的配置
4、建立实体类User.java
5、提供User.hbm.xml文件,完成实体类的映射
6、将User.hbm.xml文件加入到hibernate.cfg.xml文件中
<mapping resource="com/bjpowernode/hibernate/User.hbm.xml"/>
7、编写工具类ExoprtDB.java,将hbm生成ddl,也就是hbm2ddl
Configuration cfg = new Configuration().configure();
SchemaExport export = new SchemaExport(cfg);
export.create(true, true);
8、建立客户端类Client,添加用户数据到mysql
最好加入如下配置项,方便观察hibernate sql的生成:
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
最好加入log4j配置文件,将该配置文件拷贝到src下,便于程序的调试
(二)基础知识
* 编写测试类XXTest,需要继承TestCase
* 编写单元测试方法,方法名称必须为test开头,方法没有参数没有返回值,采用public修饰
* 最好采用单独的目录存放测试程序
* 建议使用断言(this.assertEquals(expected, username);)
掌握HIbernate的CRUD操作
必须掌握get和load方法的差别?
* get不支持lazy,load在默认情况下支持lazy
* get加载数据,如果不存在返回null,而load返回ObjectNotFoundException异常
Transeint
* 没有被session管理
* 在数据库中没有与之匹配的记录
Persistent
* 纳入session管理
* 在数据库中有与之匹配的记录
* 当属性发生改变,在清理缓存时(脏数据检查)会自动和数据库同步
Detached
* 没有被session管理
* 在数据库中存在与之匹配的记录
Session是一个持久化管理器
new一个对象是Transient状态,save或update之后是Persistent状态,关闭session后是detached状态.
commit()之前自动调用了flush(),在flush()时会发sql并清理缓存.
get()立即发sql,load()默认会延迟加载,
//load不会马上发出查询语句,因为load支持lazy(延迟加载/懒加载)
//什么叫lazy?只有真正使用这个对象的时候,再创建,对于hibernate来说
//才真正的发出查询语句,主要是为了提高性能,lazy是hibernate中非常重要的特性
//hibernate的lazy是如何实现的?采用代理对象实现,代理对象主要采用的是CGLIB库生成的
//而不是JDK的动态代理,因为JDK的动态代理只能对实现了接口的类生成代理,CGLIB可以对类
//生成代理,它采用的是继承方式
简单了解hql
(三)hibernate基本映射
实体类中的普通属性(不包括集合、自定义类和数组)---表字段,采用<property>标签映射
注意:如果实体类的名称或实体类中属性的名称和数据库关键字重复,将会出现问题
可以考虑采用table属性和column属性对其进行重新命名
实体类的设计原则:
* 实现无参的默认的构造函数
* 提供一个标识
* 建议不要使用final修饰实体类
* 建议为实体类生成getter和setter方法
主要了解如下主键生成策略:
* identity
* sequence
* uuid
* native
* assigned
* foreign
<id name="id" column="user_id" length="32" access="field">
<generator class="uuid"/>
</id>
了解hibernate.cfg.xml文件中的hbm2ddl属性
(四)hibernate多对一关联映射
在对象模型中就是一个或多个引用
多对一关联映射原理:在多的一端加入一个外键,指向一的一端
在多的一端采用如下标签映射:
<many-to-one name="group" column="groupid" cascade="save-update"/>
掌握级联的含义?
* 级联是对象之间的连锁操作,它只影响添加、删除和修改
如果不设置cascade时必须显示调用save保存group和user,否则报错.
(五)hibernate一对一主键关联映射(单向关联Person----->IdCard)
<class name="com.bjpowernode.hibernate.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>
(六)hibernate一对一主键关联映射(双向关联Person<----->IdCard)
需要在IdCard加入<one-to-one>标签,指示hibernate将关联对象Person
根据主键加载上来
<one-to-one>不影响存储,只影响加载
(七)hibernate一对一唯一外键关联映射(单向关联Person----->IdCard)
采用<many-to-one>标签来映射,指定多的一端unique为true,这样就限制了多的一端的多重性
为一,就是这样来映射的。
Person.hbm.xml:<many-to-one name="idCard" unique="true"/>
(八)hibernate一对一唯一外键关联映射(双向关联Person<----->IdCard)
标签中的property-ref属性为关系字段的名称
Person.hbm.xml:<many-to-one name="idCard" unique="true"/>
IdCard.hbm.xml:<one-to-one name="person" property-ref="idCard"/>
(九)session flush测试:
* 清理缓存
* 执行sql
session在什么情况下执行flush
* 默认在事务提交时
* 显示的调用flush
* 在执行查询前,如:iterate
hibernate按照save(insert),update、delete顺序提交相关操作
(1).uuid生成策略:
//因为user的主键生成侧路采用的是uuid,所以调用完成save后,只是将user纳入到了session的管理
//不会发出insert语句,但是id已经生成,session中existsInDatebase状态为false
session.save(user);
//调用flush,hibernate会清理缓存,执行sql
//如果数据库的隔离级别设置为为提交读,那么我们可以看到flush过的数据
//并且session中existsInDatebase状态为true
session.flush();
//提交事务
//默认情况下commit操作会先执行flush清理缓存,所以不用显示的调用flush
//commit后数据是无法回滚的
tx.commit();
(2).native生成策略:
//因为user的主键生成策略为native,所以调用session.save后,将执行insert语句,返回有数据库生成的id
//纳入了session的管理,修改了session中existsInDatebase状态为true
//如果数据库的隔离级别设置为为提交读,那么我们可以看到save过的数据
session.save(user);
tx.commit();
(3).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属性中的existsInDatabase为true,而我们采用evict已经将user从session的entityEntries
//中逐出了,所以找不到相关数据,无法更新,抛出异常
tx.commit();
(4).uuid主键生成策略
//因为user的主键生成侧路采用的是uuid,所以调用完成save后,只是将user纳入到了session的管理
//不会发出insert语句,但是id已经生成,session中existsInDatebase状态为false
session.save(user);
//flush后hibernate会清理缓存,会将user对象保存到数据库中,将session中的insertions中的user对象
//清除,并且设置session中existsInDatebase的状态为true
session.flush();
//将user对象从session中逐出,即session的EntityEntries属性中逐出
session.evict(user);
//可以成功提交,因为hibernate在清理缓存时,在session的insertions集合中无法找到user对象
//所以就不会发出insert语句,也不会更新session中的existsInDatabase的状态
tx.commit();
(5).native主键生成策略
//因为user的主键生成策略为native,所以调用session.save后,将执行insert语句,返回有数据库生成的id
//纳入了session的管理,修改了session中existsInDatebase状态为true
//如果数据库的隔离级别设置为为提交读,那么我们可以看到save过的数据
session.save(user);
//将user对象从session中逐出,即session的EntityEntries属性中逐出
session.evict(user);
//可以成功提交,因为hibernate在清理缓存时,在session的insertions集合中无法找到user对象
//所以就不会发出insert语句,也不会更新session中的existsInDatabase的状态
tx.commit();
(十)hibernate一对多关联映射(单向Classes--->Student)
它们的区别在于维护的关系不同:
* 多对一维护的关系是:多指向一的关系,有了此关系,在加载多的时候可以将一加载上来
* 一对多维护的关系是:一指向多的关系,有了此关系,在加载一的时候可以将多加载上来
在一一端维护关系存在缺陷:
* 因为多的一端Student不知道Classes的存在(也就是Student没有维护与Classes的关系)
所以在保存Student的时候关系字段classesid是为null的,如果将该关系字段设置为非空,则
将无法保存数据
* 另外因为Student不维护关系,而Classes维护关系,Classes就会发出多余的update语句,保证
Classes和Student有关系,这样加载Classes的时候才可以把该Classes对应的学生加载上来
(十一)hibernate一对多关联映射(双向Classes<--->Student)
而不是需求驱动的
一对多双向关联的映射方式:
* 在一的一端的集合上采用<key>标签,在多的一端加入一个外键
* 在多的一端采用<many-to-one>标签
!!!注意:<key>标签和<many-to-one>标签加入的字段保持一直,否则会产生数据混乱
inverse属性:
* inverse属性可以用在一对多和多对多双向关联上,inverse属性默认为false,为
false表示本端可以维护关系,如果inverse为true,则本端不能维护关系,会交给另一端
维护关系,本端失效。
所以一对多关联映射我们通常在多的一端维护关系,让一的一端失效,所以设置为inverse为true
inverse和cascade
* inverse是控制方向上的反转,只影响存储
* cascade是操作上的连锁反映
--------------------------------------------
<class name="com.bjpowernode.hibernate.Student" table="t_student">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<many-to-one name="classes" column="classesid"/>
</class>
---------------------------------------------
<class name="com.bjpowernode.hibernate.Classes" table="t_classes">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="students" inverse="true">
<!--
<key column="classesid" not-null="true"/>
-->
<key column="classesid"/>
<one-to-many class="com.bjpowernode.hibernate.Student"/>
</set>
</class>
-------------------------------------------------
(十二)hibernate多对多关联映射(单向User--->Role)
<set name="roles" table="t_user_role">
<key column="user_id"/>
<many-to-many class="com.bjpowernode.hibernate.Role" column="role_id"/>
</set>
(十三)hibernate多对多关联映射(双向User--->Role)
<set name="users" table="t_user_role">
<key column="role_id" not-null="true"/>
<many-to-many class="com.bjpowernode.hibernate.User" column="user_id"/>
</set>
------------
<set name="roles" table="t_user_role">
<key column="user_id"/>
<many-to-many class="com.bjpowernode.hibernate.Role" column="role_id"/>
</set>
需要注意:
* 生成的中间表名称必须一样
* 生成的中间表中的字段必须一样
(十四)整棵类继承树一张表
因为类继承树肯定是对应多个类,要把多个类的信息存放在一张表中,
必须有某种机制来区分哪些记录是属于哪个类的。这种机制就是,在表中添加一个字段,
用这个字段的值来进行区分。用hibernate实现这种策略的时候,有如下步骤:
父类用普通的<class>标签定义
在父类中定义一个discriminator,即指定这个区分的字段的名称和类型
如:<discriminator column=”XXX” type=”string”/>
子类使用<subclass>标签定义,在定义subclass的时候,需要注意如下几点:
Subclass标签的name属性是子类的全路径名
在Subclass标签中,用discriminator-value属性来标明本子类的discriminator字段
(用来区分不同类的字段)的值Subclass标签,既可以被class标签所包含(这种包含关系
正是表明了类之间的继承关系),也可以与class标签平行。 当subclass标签的定义与class
标签平行的时候,需要在subclass标签中,添加extends属性,里面的值是父类的全路径名称。
子类的其它属性,像普通类一样,定义在subclass标签的内部。
关于鉴别值在存储的时候hibernate会自动存储,在加载的时候会根据鉴别值取得相关的对象
get和hql支持多态查询
load在lazy设置为false的情况下支持多态查询
多态查询:hibernate在加载数据的时候,能够采用instancof鉴别出其真正的类型,load在lazy=true时不能鉴别出真正的类型.
(十五)每个类一张表
这种策略是使用joined-subclass标签来定义子类的。父类、子类,每个类都对应一张数据库表。
在父类对应的数据库表中,实际上会存储所有的记录,包括父类和子类的记录;在子类对应的数据库表中,
这个表只定义了子类中所特有的属性映射的字段。子类与父类,通过相同的主键值来关联。
实现这种策略的时候,有如下步骤:
父类用普通的<class>标签定义即可
父类不再需要定义discriminator字段
子类用<joined-subclass>标签定义,在定义joined-subclass的时候,需要注意如下几点:
Joined-subclass标签的name属性是子类的全路径名
Joined-subclass标签需要包含一个key标签,这个标签指定了子类和父类之间是通过哪个字段来关联的。
如:<key column=”PARENT_KEY_ID”/>,这里的column,实际上就是父类的主键对应的映射字段名称。
Joined-subclass标签,既可以被class标签所包含(这种包含关系正是表明了类之间的继承关系),
也可以与class标签平行。 当Joined-subclass标签的定义与class标签平行的时候,
需要在Joined-subclass标签中,添加extends属性,里面的值是父类的全路径名称。
子类的其它属性,像普通类一样,定义在joined-subclass标签的内部。
(十六)每个具体类一张表
这种策略是使用union-subclass标签来定义子类的。每个子类对应一张表,而且这个表的信息是完备的,
即包含了所有从父类继承下来的属性映射的字段(这就是它跟joined-subclass的不同之处,
joined-subclass定义的子类的表,只包含子类特有属性映射的字段)。实现这种策略的时候,有如下步骤:
父类用普通<class>标签定义即可
子类用<union-subclass>标签定义,在定义union-subclass的时候,需要注意如下几点:
Union-subclass标签不再需要包含key标签(与joined-subclass不同)
Union-subclass标签,既可以被class标签所包含(这种包含关系正是表明了类之间的继承关系),
也可以与class标签平行。 当Union-subclass标签的定义与class标签平行的时候,需要在Union-subclass标签中,
添加extends属性,里面的值是父类的全路径名称。
子类的其它属性,像普通类一样,定义在Union-subclass标签的内部。这个时候,虽然在union-subclass
里面定义的只有子类的属性,但是因为它继承了父类,所以,不需要定义其它的属性,在映射到数据库表的时候,
依然包含了父类的所有属性的映射字段。
!!!注意:在保存对象的时候id不能重复(不能使用数据库的自增方式生成主键)
(十七)复合(联合)主键映射:
* 必须实现序列化接口
* 覆盖equals和hashcode方法
<class name="com.bjpowernode.hibernate.FiscalYearPeriod" table="t_fiscal_year_period">
<composite-id name="fiscalYearPeriodPK">
<key-property name="fiscalYear"/>
<key-property name="fiscalPeriod"/>
</composite-id>
<property name="beginDate" type="date"/>
<property name="endDate" type="date"/>
<property name="periodSts"/>
</class>
---------------------------------------------------------
session.beginTransaction();
FiscalYearPeriod fiscalYearPeriod = new FiscalYearPeriod();
//构造复合主键对象
FiscalYearPeriodPK fiscalYearPeriodPK = new FiscalYearPeriodPK();
fiscalYearPeriodPK.setFiscalYear(2009);
fiscalYearPeriodPK.setFiscalPeriod(12);
fiscalYearPeriod.setFiscalYearPeriodPK(fiscalYearPeriodPK);
fiscalYearPeriod.setBeginDate(new Date());
fiscalYearPeriod.setEndDate(new Date());
fiscalYearPeriod.setPeriodSts("Y");
session.save(fiscalYearPeriod);
session.getTransaction().commit();
(十八)Component映射
Component是某个实体的逻辑组成部分,它与实体类的主要差别在于,它没有oid
Component在DDD中成为值类
采用Component的好处:实现对象模型的细粒度划分,复用率高,含义明确,层次分明
对象模型与关系模型的设计恰恰相反,对象模型一般是细粒度的,关系模型一般是粗粒度的
<class name="com.bjpowernode.hibernate.Employee" table="t_emplyee">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<component name="employeeContact">
<property name="email"/>
<property name="address"/>
<property name="zipCode"/>
<property name="contactTel"/>
</component>
</class>
--------------------------------
<class name="com.bjpowernode.hibernate.User" table="t_user">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<component name="userContact">
<property name="email"/>
<property name="address"/>
<property name="zipCode"/>
<property name="contactTel"/>
</component>
</class>
(十九)完成set、list、array、map的映射
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="setValues" table="t_set_values">
<key column="set_id"/>
<element type="string" column="set_value" not-null="true"/>
<!--
<composite-element class=""></composite-element>
-->
</set>
<list name="listValues" table="t_list_values">
<key column="list_id"/>
<list-index column="list_index"/>
<element type="string" column="list_value"/>
</list>
<array name="arrayValues" table="t_array_values">
<key column="array_id"/>
<list-index column="array_index"/>
<element type="string" column="array_value"/>
</array>
<map name="mapValues" table="t_map_values">
<key column="map_id"/>
<map-key type="string" column="map_key"/>
<element type="string" column="map_value"/>
</map>
</class>
(二十)hibernate的lazy策略可以使用在:
* <property>标签上,可以取值:true/false,需要类增强工具,对字节码进行修改
* <set>/<list>标签上,可以取值:true/false/extra
* <many-to-one>/<one-to-one>单端关联标签上,可以取值:false/proxy/noproxy
lazy的概念:在正真使用某个对象的时候才正真的去创建,对于hibernate才会正真的发出sql语句
去加载该对象
hibernate的lazy策略必须在session打开状态下有效
<class>上的lazy只影响普通属性
(二十一)lazy策略在集合上可以取值,true/false/extra
<class>标签上的lazy不会影响集合上的lazy特性,<class>上的lazy只会影响普通属性
(二十二)hibernate在单端关联上的lazy特性:
<class>标签上的lazy不会影响单端关联对象的lazy策略
(二十三)略
(二十四)hibernate查询语言hql
1、简单属性查询【重要】
* 单一属性查询,返会属性结果集列表,元素类型和实体类中相应的类型一致
* 多个属性查询,多个属性查询返会对象数组,对象数组的长度取决于属性的个数
对象数组中元素的类型取决于属性在实体类中的类型
* 如果认为返会数组不够对象化,可以使用hql动态实例化Student对象,需要提供构造函数
参见:SimplePropertyQueryTest.java
//返回结果集属性列表,元素类型和实体类中的属性类型一致
List students = session.createQuery("select name from Student").list();
for (Iterator iter=students.iterator(); iter.hasNext();) {
String name = (String)iter.next();
System.out.println(name);
}
---------------------------------------------------
//查询多个属性,返回对象数组集合
//数组元素的类型与查询的属性类型一致
//数组的长度与select中查询的属性个数一致
List students = session.createQuery("select id, name from Student").list();
for (Iterator iter=students.iterator(); iter.hasNext();) {
Object[] obj = (Object[])iter.next();
System.out.println(obj[0] + ", " + obj[1]);
}
---------------------------------------------------
//可以使用hql返回Student对象
//需要提供构造函数
List students = session.createQuery("select new Student(id, name) from Student").list();
for (Iterator iter=students.iterator(); iter.hasNext();) {
Student student = (Student)iter.next();
System.out.println(student.getId() + ", " + student.getName());
}
2、实体对象查询【重要】
* N + 1问题,就是发出了N+1条sql语句
1:首先发出查询对象id列表的语句
N:根据id到缓存中查询,如果缓存中不存在与之匹配的数据,那么会根据id发出相应的sql语句
*list和iterate的区别?
list: 默认情况下list每次都会发出sql语句,list会将数据放到缓存中,而不利用缓存
iterate:默认情况下iterate利用缓存,如果缓存中不存在会出现N+1问题
参见:SimpleObjectQueryTest1.java,SimpleObjectQueryTest2.java
--------------------------------------------------------
/**
* 会出现N+1问题,所谓的N+1指的是发出了N+1条sql语句
*
* 1:发出一条查询id列表的语句
* Hibernate: select student0_.id as col_0_0_ from t_student student0_
*
* N:根据id发出N条sql语句,加载相关的对象
* Hibernate: select student0_.id as id0_0_, student0_.name as name0_0_,
* student0_.createTime as createTime0_0_, student0_.classesid as classesid0_0_
* from t_student student0_ where student0_.id=?
*
*/
Iterator iter = session.createQuery("from Student").iterate();
while (iter.hasNext()) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
-----------------------------------------------------
List students = session.createQuery("from Student").list();
for (Iterator iter=students.iterator(); iter.hasNext();) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
/**
* 避免了N+1问题
*
* 因为执行list操作后会将数据放到session的缓存中(一级缓存),所以采用iterate的时候
* 首先会发出一条查询id列表的语句,再根据id到缓存中加载相应的数据,如果缓存中存在与之匹配的数据
* 则不再发出根据id查询的sql语句,直接使用缓存中的数据
*
* Iterate方法如果缓存中存在数据,它可以提高性能,否则出现N+1问题
*
*/
Iterator iter = session.createQuery("from Student").iterate();
while (iter.hasNext()) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
----------------------------------------------------------
List students = session.createQuery("from Student").list();
for (Iterator iter=students.iterator(); iter.hasNext();) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
/**
* 再此发出查询语句
*
* 在默认情况下,每次执行list查询实体对象都会发出查询语句,除非配置了查询缓存
* 虽然一级缓存中存在Student数据,但list不用,所以仍然发出查询语句,
*
* 其实list就是只向缓存中放入数据,而不利用缓存中的数据
*/
students = session.createQuery("from Student").list();
for (Iterator iter=students.iterator(); iter.hasNext();) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
-----------------------------------------------------------
3、条件查询【重要】
* 可以采用拼字符串的方式传递参数
* 可以采用 ?来传递参数(索引从0开始)
* 可以采用 :参数名 来传递参数
* 如果传递多个参数,可以采用setParamterList方法
* 在hql中可以使用数据库的函数,如:date_format
参见:SimpleConditionQueryTest.java
----------------------------------------------------
//采用 ?方式,查询学号为1,2,3,4,5的学生
List students = session.createQuery("select s.id, s.name from Student s where s.id in(?, ?, ?, ?, ?)")
.setParameter(0, 1)
.setParameter(1, 2)
.setParameter(2, 3)
.setParameter(3, 4)
.setParameter(4, 5)
.list();
----------------------------------------------------
//采用 :参数名 方式,查询学号为1,2,3,4,5的学生
List students = session.createQuery("select s.id, s.name from Student s where s.id in(:ids)")
.setParameterList("ids", new Object[]{1, 2, 3, 4, 5})
.list();
----------------------------------------------------
4、hibernate直接使用sql语句查询
参见:SqlQueryTest.java
List students = session.createSQLQuery("select * from t_student").list();
5、外置命名查询
* 在映射文件中使用<query>标签来定义hql
* 在程序中使用session.getNamedQuery()方法得到hql查询串
参见:NameQueryTest.java,Student.hbm.xml
Student.hbm.xml文件:
<query name="queryStudent">
<![CDATA[
select s from Student s where s.id <?
]]>
</query>
代码:
List students = session.getNamedQuery("queryStudent").setParameter(0, 10).list();
6、查询过滤器
* 在映射文件中定义过滤器参数
* 在类的映射中使用过滤器参数
* 在程序中必须显示的启用过滤器,并且为过滤器参数赋值
参见:FilterQueryTest.java,Student.hbm.xml
Student.hbm.xml:
<hibernate-mapping>
<class name="com.bjpowernode.hibernate.Student" table="t_student">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<property name="createTime"/>
<many-to-one name="classes" column="classesid"/>
<filter name="testFilter" condition="id < :myid"></filter>
</class>
<filter-def name="testFilter">
<filter-param type="integer" name="myid"/>
</filter-def>
</hibernate-mapping>
FilterQueryTest.java代码:
session.enableFilter("testFilter").setParameter("myid", 10);
List students = session.createQuery("from Student").list();
7、分页查询【重要】
* setFirstResult(),从0开始
* setMaxResults(),每页显示的记录数
参见:PageQueryTest.java
List students = session.createQuery("from Student")
.setFirstResult(1)
.setMaxResults(2)
.list();
8、对象导航查询【重要】
参见:ObjectNavQueryTest.java
List students = session.createQuery("from Student s where s.classes.name like '%2%'")
.list();
9、连接查询【重要】
* 内连接
* 外连接(左连接/右连接)
参见:JoinQueryTest.java
List students = session.createQuery("select c.name, s.name from Student s left join s.classes c").list();
10、统计查询【重要】
参见:StatQueryTest.java
Long count = (Long)session.createQuery("select count(*) from Student").uniqueResult();
11、DML风格的操作(尽量少用,因为和缓存不同步)
参见:DMLQueryTest.java
//数据库表中更新了,但缓存不更新
//所以一般不建议使用,除非有必须,如遇到性能问题
session.createQuery("update Student s set s.name=? where s.id<?")
.setParameter(0, "王五")
.setParameter(1, 5)
.executeUpdate();
(二十五)悲观锁
那么任何用户都不能查看或修改
锁主要是解决并发性问题
Inventory inv = (Inventory)session.load(Inventory.class, "1001", LockMode.UPGRADE);
(二十六)乐观锁
在读取数据的时候将version读取出来,在保存数据的时候判断version的值是否小于数据库中的
version值,如果小于不予更新,否则给予更新
(二十七)hibernate一级缓存
哪些方法支持一级缓存:
* load/get/iterate查询实体对象
一级缓存是缓存实体对象的
了解大批量数据更新的做法
Oracle SQL Loader
import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import org.hibernate.Session;
import junit.framework.TestCase;
public class CacheTest extends TestCase {
/**
* 在同一个session中发出两次load查询
*/
public void testCache1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Student student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
//不会发出查询语句,load使用缓存
student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
/**
* 在同一个session中发出两次get查询
*/
public void testCache2() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Student student = (Student)session.get(Student.class, 1);
System.out.println("student.name=" + student.getName());
//不会发出查询语句,get使用缓存
student = (Student)session.get(Student.class, 1);
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
/**
* 在同一个session中发出两次iterate查询,查询实体对象
*/
public void testCache3() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Iterator iter = session.createQuery("from Student s where s.id<5").iterate();
while (iter.hasNext()) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
System.out.println("--------------------------------------");
//它会发出查询id的语句,但不会发出根据id查询学生的语句,因为iterate使用缓存
iter = session.createQuery("from Student s where s.id<5").iterate();
while (iter.hasNext()) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
/**
* 在同一个session中发出两次iterate查询,查询普通属性
*/
public void testCache4() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Iterator iter = session.createQuery("select s.name from Student s where s.id<5").iterate();
while (iter.hasNext()) {
String name = (String)iter.next();
System.out.println(name);
}
System.out.println("--------------------------------------");
//iterate查询普通属性,一级缓存不会缓存,所以发出查询语句
//一级缓存是缓存实体对象的
iter = session.createQuery("select s.name from Student s where s.id<5").iterate();
while (iter.hasNext()) {
String name = (String)iter.next();
System.out.println(name);
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
/**
* 在两个session中发load查询
*/
public void testCache5() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Student student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Student student = (Student)session.load(Student.class, 1);
//会发出查询语句,session间不能共享一级缓存数据
//因为他会伴随着session的消亡而消亡
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
/**
* 在同一个session中先调用save,再调用load查询刚刚save的数据
*/
public void testCache6() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Student student = new Student();
student.setName("张三");
Serializable id = session.save(student);
student = (Student)session.load(Student.class, id);
//不会发出查询语句,因为save支持缓存
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
/**
* 大批量的数据添加
*/
public void testCache7() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
for (int i=0; i<100; i++) {
Student student = new Student();
student.setName("张三" + i);
session.save(student);
//每20条更新一次
if (i % 20 == 0) {
session.flush();
//清除缓存的内容
session.clear();
}
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
}
(二十八)hibernate二级缓存
二级缓存的生命周期和SessionFactory的生命周期一致,SessionFactory可以管理二级缓存
二级缓存的配置和使用:
* 将ehcache.xml文件拷贝到src下
* 在hibernate.cfg.xml文件中加入缓存产品提供商
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
* 启用二级缓存,这也是它的默认配置
<property name="hibernate.cache.use_second_level_cache">true</property>
* 指定哪些实体类使用二级缓存
可以在映射文件中采用<cache>标签指定或在hibernate.cfg.xml文件中统一指定
注意使用的策略,通常采用read-only和read-write
缓存原则:通常读远远大于写的数据进行缓存
二级缓存主要是缓存实体对象的
了解一级缓存和二级缓存的交互(session.setCacheMode(CacheMode.IGNORE);)
注意大批量数据更新时,如果配置了二级缓存建议禁用一级缓存和二级缓存的交互
import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import org.hibernate.CacheMode;
import org.hibernate.Session;
import junit.framework.TestCase;
public class CacheTest extends TestCase {
/**
* 开启二级缓存
*
* 在两个session中发load查询
*/
public void testCache1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Student student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Student student = (Student)session.load(Student.class, 1);
//不会发出查询语句,因为配置二级缓存,session可以共享二级缓存中的数据
//二级缓存是进程级的缓存
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
/**
* 开启二级缓存
*
* 在两个session中发get查询
*/
public void testCache2() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Student student = (Student)session.get(Student.class, 1);
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Student student = (Student)session.get(Student.class, 1);
//不会发出查询语句,因为配置二级缓存,session可以共享二级缓存中的数据
//二级缓存是进程级的缓存
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
/**
* 开启二级缓存
*
* 在两个session中发load查询,采用SessionFactory管理二级缓存
*/
public void testCache3() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Student student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
//管理二级缓存
//HibernateUtils.getSessionFactory().evict(Student.class);
HibernateUtils.getSessionFactory().evict(Student.class, 1);
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Student student = (Student)session.load(Student.class, 1);
//会发出查询语句,因为二级缓存中的数据被清除了
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
/**
* 开启二级缓存
*
* 一级缓存和二级缓存的交互
*/
public void testCache4() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//禁止将一级缓存中的数据放到二级缓存中
session.setCacheMode(CacheMode.IGNORE);
Student student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Student student = (Student)session.load(Student.class, 1);
//会发出查询语句,因为禁止了一级缓存和二级缓存的交互
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
/**
* 大批量的数据添加
*/
public void testCache5() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//禁止一级缓存和二级缓存交互
session.setCacheMode(CacheMode.IGNORE);
for (int i=0; i<100; i++) {
Student student = new Student();
student.setName("张三" + i);
session.save(student);
//每20条更新一次
if (i % 20 == 0) {
session.flush();
//清除缓存的内容
session.clear();
}
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
}
(二十九)hibernate查询缓存
查询缓存缓存什么?
* 查询缓存是缓存普通属性结果集的
* 对实体对象的结果集会缓存id
查询缓存的生命周期,当关联的表发生修改,查询缓存的生命周期结束
查询缓存的配置和使用:
* 修改hibernate.cfg.xml文件,来开启查询缓存,默认是false是不起用的
<property name="hibernate.cache.use_query_cache">true</property>
* 必须在程序启用
query.setCacheable(true)
import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import org.hibernate.CacheMode;
import org.hibernate.Session;
import junit.framework.TestCase;
public class CacheTest extends TestCase {
/**
* 开启查询,关闭二级缓存,采用query.list()查询普通属性
*
* 在一个session中发query.list()查询
*/
public void testCache1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
List names = session.createQuery("select s.name from Student s")
.setCacheable(true)
.list();
for (int i=0; i<names.size(); i++) {
String name = (String)names.get(i);
System.out.println(name);
}
System.out.println("-------------------------------------------------------");
//不会发出查询语句,因为启用查询缓存
names = session.createQuery("select s.name from Student s")
.setCacheable(true)
.list();
for (int i=0; i<names.size(); i++) {
String name = (String)names.get(i);
System.out.println(name);
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
/**
* 开启查询,关闭二级缓存,采用query.list()查询普通属性
*
* 在两个session中发query.list()查询
*/
public void testCache2() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
List names = session.createQuery("select s.name from Student s")
.setCacheable(true)
.list();
for (int i=0; i<names.size(); i++) {
String name = (String)names.get(i);
System.out.println(name);
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
System.out.println("-------------------------------------------------------");
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//不会发出查询语句,因为查询缓存和session的生命周期没有关系
List names = session.createQuery("select s.name from Student s")
.setCacheable(true)
.list();
for (int i=0; i<names.size(); i++) {
String name = (String)names.get(i);
System.out.println(name);
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
/**
* 开启查询,关闭二级缓存,采用query.iterate()查询普通属性
*
* 在两个session中发query.iterate()查询
*/
public void testCache3() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Iterator iter = session.createQuery("select s.name from Student s")
.setCacheable(true)
.iterate();
while(iter.hasNext()) {
String name = (String)iter.next();
System.out.println(name);
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
System.out.println("-------------------------------------------------------");
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//会发出查询语句,query.iterate()查询普通属性它不会使用查询缓存
//查询缓存只对query.list()起作用
Iterator iter = session.createQuery("select s.name from Student s")
.setCacheable(true)
.iterate();
while(iter.hasNext()) {
String name = (String)iter.next();
System.out.println(name);
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
/**
* 关闭查询,关闭二级缓存,采用query.list()查询实体
*
* 在两个session中发query.list()查询
*/
public void testCache4() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
List students = session.createQuery("select s from Student s")
.list();
for (int i=0; i<students.size(); i++) {
Student studnet = (Student)students.get(i);
System.out.println(studnet.getName());
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
System.out.println("-------------------------------------------------------");
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//会发出查询语句,默认query.list()每次执行都会发出查询语句
List students = session.createQuery("select s from Student s")
.list();
for (int i=0; i<students.size(); i++) {
Student studnet = (Student)students.get(i);
System.out.println(studnet.getName());
}
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
/**
* 开启查询,关闭二级缓存,采用query.list()查询实体
*
* 在两个session中发query.list()查询
*/
public void testCache5() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
List students = session.createQuery("select s from Student s")
.setCacheable(true)
.list();
for (int i=0; i<students.size(); i++) {
Student studnet = (Student)students.get(i);
System.out.println(studnet.getName());
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
System.out.println("-------------------------------------------------------");
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//会发出n条查询语句,因为开启了查询缓存,关闭了二级缓存,那么查询缓存就会缓存实体对象的id
//第二次执行query.list(),将查询缓存中的id依次取出,分别到一级缓存和二级缓存中查询相应的实体
//对象,如果存在就使用缓存中的实体对象,否则根据id发出查询学生的语句
List students = session.createQuery("select s from Student s")
.setCacheable(true)
.list();
for (int i=0; i<students.size(); i++) {
Student studnet = (Student)students.get(i);
System.out.println(studnet.getName());
}
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
/**
* 开启查询,开启二级缓存,采用query.list()查询实体
*
* 在两个session中发query.list()查询
*/
public void testCache6() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
List students = session.createQuery("select s from Student s")
.setCacheable(true)
.list();
for (int i=0; i<students.size(); i++) {
Student studnet = (Student)students.get(i);
System.out.println(studnet.getName());
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
System.out.println("-------------------------------------------------------");
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//不再发出查询语句,因为配置了二级缓存和查询缓存
List students = session.createQuery("select s from Student s")
.setCacheable(true)
.list();
for (int i=0; i<students.size(); i++) {
Student studnet = (Student)students.get(i);
System.out.println(studnet.getName());
}
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
}
(三十)hibernate抓取策略,单端代理的批量抓取
<many-to-one name="classes" column="classesid" fetch="select"/>
fetch="select",另外发送一条select语句加载当前对象的关联对象或集合
fetch为select或join不影响hql,它影响的是load,get方法
(三十一)hibernate抓取策略,单端代理的批量抓取
<many-to-one name="classes" column="classesid" fetch="join"/>
fetch="join",hibernate会通过一个select语句连接(内联/外联)抓取其关联对象或集合
fetch="join",那么lazy失效
fetch="join",只影响get和load,对hql没有影响
<many-to-one>可能会出现N+1问题,
如:查询100个学生显示到列表中:
* 首先会发出查询学生的sql语句
* 然后会发出根据班级id查询班级的sql语句
这样就会导致N+1问题,也就是发出了N+1条语句,会严重影响性能
所以我们可以采用预先抓取的策略,如:
select s from Student s join fetch s.classes
(三十二)hibernate抓取策略,集合代理的批量抓取
<set name="students" order-by="id" inverse="true" cascade="all" fetch="select">
fetch="select",另外发送一条select语句加载当前对象的关联对象或集合
(三十三)hibernate抓取策略,集合代理的批量抓取
<set name="students" order-by="id" inverse="true" cascade="all" fetch="join">
fetch="join",hibernate会通过一个select语句连接(内联/外联)抓取其关联对象或集合
fetch="join",那么lazy失效
fetch="join",只影响get和load,对hql没有影响
(三十四)hibernate抓取策略,集合代理的批量抓取
<set name="students" order-by="id" inverse="true" cascade="all" fetch="subselect">
fetch="subselect",另外发送一条select语句抓取在前面查询到的所有实体的关联集合
fetch="subselect",会影响hql查询
(三十五)hibernate抓取策略,batch-size在<class>的应用
<class name="com.bjpowernode.hibernate.Classes" table="t_classes" batch-size="10">
(三十六)hibernate抓取策略,batch-size在集合上的应用
batch-size属性,可以批量加载实体类,参见Classes.hbm.xml
<set name="students" order-by="id" inverse="true" cascade="all" batch-size="3">
(三十七)略
(三十八)略
(三十九)第一个hibernate(hibernate annotation) JPA项目
2、创建User Library,加入依赖包
* HIBERNATE_HOME/lib/*.jar
* HIBERNATE_HOME/hibernate3.jar
* 加入数据库驱动(mysql驱动)
3、加入hibernate annotation支持包
* hibernate-annotations.jar
* ejb3-persistence.jar
* hibernate-commons-annotations.jar
4、提供hibernate.cfg.xml文件,完成基本的配置
5、建立实体类User.java
5、采用注解完成映射
6、将实体类加入到hibernate.cfg.xml配置文件中,注意与hbm.xml文件有区别
<mapping class="com.bjpowernode.hibernate.User"/>
7、编写工具类ExoprtDB.java,注解生成ddl,必须采用AnnotationConfiguration类,注意与hbm.xml配置文件方式不同,
Configuration cfg = new AnnotationConfiguration().configure();//annotation方式
Configuration cfg = new Configuration().configure();//文件方式
8、建立客户端类Client,添加用户数据到mysql
(四十)hibernate jpa基本映射
@Entity
@Id
@Table
@GeneratedValue
@Transient
@Column
采用如下注解会默认采用数据库的主键生成能力,相当于hibernate中的native
@GeneratedValue相当于@GeneratedValue(strategy=GenerationType.AUTO)
了解如何采用hibernate的uuid生成主键
//采用hibernate uuid生成器
@Id
@GenericGenerator(name="myIdGenerator",strategy="uuid")
@GeneratedValue(generator="myIdGenerator")
public String getId() {
return id;
}
(四十一)hibernate JPA多对一关联映射
关于关联对象在表中的维护,JPA会采用关联对象+"_" + "id"方式作为字段加入表中
@Entity
@Table(name="t_user")
public class User {
...
@ManyToOne
public Group getGroup() {
return group;
}
...
}
(四十二)hibernate JPA一对多关联映射
了解mappedBy属性
了解targetEntity属性
了解@JoinColumn注解
@Entity
@Table(name="t_student")
public class Student {
...
@ManyToOne
@JoinColumn(name="classesid")
public Classes getClasses() {
return classes;
}
...
}
-----------------
@Entity
@Table(name="t_classes")
public class Classes {
...
//mappedBy在哪一端,哪一端就不维护关系,它成为了关系的被管理端
//向当于hibernate中的inverse=true
//如果采用了泛型,可以不用采用targetEntity属性
@OneToMany(mappedBy="classes",targetEntity=Student.class)
@JoinColumn(name="classesid")
public Set getStudents() {
return students;
}
...
}
(四十三)hibernate jpa多对多关联映射
采用@JoinTable指定第三方表的名称
@Entity
@Table(name="t_role")
public class Role {
...
@ManyToMany(mappedBy="roles")
public Set<User> getUsers() {
return users;
}
...
}
----------------------------------------
@Entity
@Table(name="t_user")
public class User {
...
//因为集合使用泛型所以不再使用targetEntity来指定类型
@ManyToMany
@JoinTable(
name="t_user_role",
joinColumns={@JoinColumn(name="user_id")},
inverseJoinColumns={@JoinColumn(name="role_id")}
)
public Set<Role> getRoles() {
return roles;
}
...
}
(四十四)采用JPA映射每棵类继承树一张表
@Table(name="t_animal")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="type",discriminatorType=DiscriminatorType.STRING)
public class Animal {...
--------------------------------------------
@Entity
@DiscriminatorValue(value="P")
public class Pig extends Animal {...
----------------------------------------------
@Entity
@DiscriminatorValue(value="B")
public class Bird extends Animal {...
(四十五)采用JPA映射每个类一张表
@Table(name="t_animal")
@Inheritance(strategy=InheritanceType.JOINED)
public class Animal {...
(四十六)采用JPA映射每个具体类一张表
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class Animal {...