一、hibernate初始
第一个hibernate项目
1、创建java项目
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文件中
7、编写工具类ExoprtDB.java,将hbm生成ddl,也就是hbm2ddl,生成表
public class ExportDB {
publicstatic void main(String[] args) {
//默认读取hibernate.cfg.xml文件
Configurationcfg = new Configuration().configure();
SchemaExportexport = new SchemaExport(cfg);
export.create(true,true);
}
}
8、建立客户端类Client,添加用户数据到mysql
最好加入如下配置项,方便观察hibernatesql的生成:
<propertyname="hibernate.show_sql">true</property>
<propertyname="hibernate.format_sql">true</property>
最好加入log4j配置文件,将该配置文件拷贝到src下,便于程序的调试
注意:必须自己创建数据库(手动创建)和生成表(在用之前必须让程序帮你创建,一般用exportDB.java创建表,然后再进行数据库的操作)
public class HibernateUtils {
privatestatic SessionFactory factory;
static{
try{
//读取hibernate.cfg.xml文件
Configurationcfg = new Configuration().configure();
//建立SessionFactory
factory= cfg.buildSessionFactory();
}catch(Exceptione) {
e.printStackTrace();
}
}
publicstatic Session getSession() {
returnfactory.openSession();
}
publicstatic void closeSession(Session session) {
if(session != null) {
if(session.isOpen()) {
session.close();
}
}
}
publicstatic SessionFactory getSessionFactory() {
returnfactory;
}
}
二、Hibernate的优缺点
• 优点
– 提高生产力
– 使开发更加对象化(阻抗不匹配)
– 可移植性
– 没有侵入性,支持透明持久化
• 缺点
– 使用数据库特性的语句,将很难调优
– 对大批量数据更新存在问题
– 系统中存在大量的统计查询功能
使用hibernate可以使我们采用对象化的思维操作关系型数据库
三、持久化对象的状态
Ø 瞬时对象(Transient Objects):使用new 操作符初始化的对象不是立刻就持久的。它们的状态是瞬时的,也就是说它们没有任何跟数据库表相关联的行为,只要应用不再引用这些对象(不再被任何其它对象所引用),它们的状态将会丢失,并由垃圾回收机制回收。
Ø 持久化对象(Persist Objects):持久实例是任何具有数据库标识的实例。它有持久化管理器Session统一管理,持久实例是在事务中进行操作的——它们的状态在事务结束时同数据库进行同步。当事务提交时,通过执行SQL的INSERT、UPDATE和DELETE语句把内存中的状态同步到数据库中。
Ø 离线对象(Detached Objects):Session关闭之后,持久化对象就变为离线对象。离线表示这个对象不能再与数据库保持同步,它们不再受Hibernate管理。
持久化对象的生命周期(lifecycle)
Transeint
*没有被session管理
*在数据库中没有与之匹配的记录
Persistent
*纳入session管理
*在数据库中有与之匹配的记录
*当属性发生改变,在清理缓存时(脏数据检查)会自动和数据库同步
Detached
*没有被session管理
*在数据库中存在与之匹配的记录
Session是一个持久化管理器
Persistent状态下,当对象的属性改变时,hibernate在清理缓存(脏数据检查)的时候,会与数据库同步。意思就是说,无需显示的调用session的update()方法,因为此时的状态为持久状态。调用update()没什么明显的意义。
Detached状态下,session回收,但是数据库里面仍然存在,仍然可以调用User的setter方法来修改对象的属性,但是无法更新数据库,所以要重新new个session出来(必须new因为session是不是线程安全的,所以要尽量避免多个线程同时用一个session的情况,每线程一session)
详细情况请见代码。,。。。。。。。。。。。。。hibernate_session的SessionTest.java
四、掌握get和load方法的差别?
*get不支持lazy,load在默认情况下支持lazy(代理)
*get加载数据,如果不存在返回null,而load返回ObjectNotFoundException异常
什么叫lazy?只有真正使用这个对象的时候,再创建,对于hibernate来说
才真正的发出查询语句,主要是为了提高性能,lazy是hibernate中非常重要的特性hibernate的lazy是如何实现的?采用代理对象实现,代理对象主要采用的是CGLIB库生成的而不是JDK的动态代理,因为JDK的动态代理只能对实现了接口的类生成代理,CGLIB可以对类
生成代理,它采用的是继承方式
五、测试程序
junit简介:
*编写测试类XXTest,需要继承TestCase
*编写单元测试方法,方法名称必须为test开头,方法没有参数没有返回值,采用public修饰
*最好采用单独的目录存放测试程序
*建议使用断言
六、hibernate常见的错误及警告
1.必须自己创建数据库(手动创建)和生成表(在用之前必须让程序帮你创建,一般用exportDB.java创建表,然后再进行数据库的操作)
2.测试时报错但测试可正常运行
WARN SessionFactoryObjectFactory:98 - Could not bindfactory to JNDI
javax.naming.NoInitialContextException: Need to specify class name inenvironment or system property, or as an applet parameter, or in an applicationresource file: java.naming.factory.initial
在hibernate.cfg.xml中session-factory设置了name属性,hibernate会试图把这个sessionfacotry注册到jndi中去
将name属性去掉即可解决
3.经常会遇到TransientObjectException错误,这是因为你可能在flush之前没有保存说需要的对象
七、
Session Transaction(事务) Query
八、hibernate基本映射
实体类---表,采用<class>标签映射
实体类中的普通属性(不包括集合、自定义类和数组)---表字段,采用<property>标签映射
注意:如果实体类的名称或实体类中属性的名称和数据库关键字重复,将会出现问题
可以考虑采用table属性和column属性对其进行重新命名
实体类的设计原则:
*实现无参的默认的构造函数
*提供一个标识
*建议不要使用final修饰实体类
* 建议为实体类生成getter和setter方法
主要了解如下主键生成策略:
*identity mysql,MSsql的生成策略
*sequence DB2,oracle的生成策略
*uuid 自动生成一个32位的标识符
*native 根据地层数据库的能力选择identity,sequence或者hilo中的一次
*assigned 让程序在save()之前生成一次标识符
*foreign 使用另一个相关联的对象的标识符,通常和<one-to-one>联合起来使用
了解hibernate.cfg.xml文件中的hbm2ddl属性(hibernate.hbm2ddl.auto:validate | update | create | create-drop)
九、many to one
Many2one映射原理
t_user id name groupid 1 张三 1 2 李四 1 |
t_group id name 1 动力节点 |
hibernate多对一关联映射
关联映射,就是将关联关系映射到数据库中,所谓的关联关系在对象模型中就是一个或多个引用
多对一关联映射原理:在多的一端加入一个外键,指向一的一端
在多的一端采用如下标签映射:
<many-to-onename="group" column="groupid"/>
掌握级联的含义?
l 级联是对象之间的连锁操作,它只影响添加、删除和修改
十、one to one
1.(主键映射)
One to one 映射原理
t_idCard id cardNo 1 11111111111111111 2 2222222222222222 |
t_person id name 1 张三 2 李四 |
hibernate一对一主键关联映射(单向关联Person----->IdCard)
一对一关联映射原理:让两个实体的主键一样,这样就不需要加入多余的字段了
<classname="com.bjpowernode.hibernate.Person"table="t_person">
<idname="id">
<!--采用foreign生成策略,forgeign会取得关联对象的标识 -->
<generatorclass="foreign">
<!--property只关联对象 -->
<paramname="property">idCard</param>
</generator>
</id>
<propertyname="name"/>
<!--
one-to-one指示hibernate如何加载其关联对象,默认根据主键加载
也就是拿到关系字段值,根据对端的主键来加载关联对象
constrained="true表示,当前主键(person的主键)还是一个外键
参照了对端的主键(IdCard的主键),也就是会生成外键约束语句
-->
<one-to-onename="idCard" constrained="true"/>
</class>
hibernate一对一主键关联映射(双向关联Person<----->IdCard)
需要在IdCard加入<one-to-one>标签,指示hibernate将关联对象Person
根据主键加载上来
<one-to-one>不影响存储,只影响加载
Person.hbm.xml
<hibernate-mapping>
<classname="com.bjpowernode.hibernate.Person" table="t_person">
<id name="id">
<!-- 采用foreign生成策略,forgeign会取得关联对象的标识 -->
<generatorclass="foreign">
<!-- property只关联对象 -->
<paramname="property">idCard</param>
</generator>
</id>
<propertyname="name"/>
<!--
one-to-one指示hibernate如何加载其关联对象,默认根据主键加载
也就是拿到关系字段值,根据对端的主键来加载关联对象
constrained="true表示,当前主键(person的主键)还是一个外键
参照了对端的主键(IdCard的主键),也就是会生成外键约束语句
-->
<one-to-one name="idCard"constrained="true"/>
</class>
</hibernate-mapping>
IdCard.hbm.xml
<hibernate-mapping>
<class name="com.bjpowernode.hibernate.IdCard"table="t_idCard">
<id name="id">
<generatorclass="native"/>
</id>
<propertyname="cardNo"/>
<one-to-one name="person"/>
</class>
</hibernate-mapping>
2.One to one (唯一外键关联)
hibernate一对一唯一外键关联映射(单向关联Person----->IdCard)
一对一唯一外键关联映射其实是多对一的特例
采用<many-to-one>标签来映射,指定多的一端unique为true,这样就限制了多的一端的多重性
为一,就是这样来映射的。
hibernate一对一唯一外键关联映射(双向关联Person<----->IdCard)
一对一唯一外键关联双向采用<one-to-one>标签映射,必须指定<one-to-one>
标签中的property-ref属性为关系字段的名称
<one-to-onename="person" property-ref="idCard"/>Property-ref指定的是property-ref用于指定关联类的一个属性,这个属性将会和本外键相对应
property-ref指:person类中的idCard变量
十一、session_flush 隔离级别(mysql)
隔离级别 | 是否存在脏读 | 是否存在不可重复读 | 是否存在幻读 |
未提交读 Read uncommitted | Y | Y | Y |
提交读 Read committed | N | Y | Y |
可重复读 Repeatable Read | N | N | Y |
序列化读 Serializable Read | N | N | N |
提交读是常用的,
十二、one to many
One2many 单向原理图
t_classes id name 1 动力节点 |
t_student id name classesid 1 张三 1 2 李四 1 |
hibernate一对多关联映射(单向Classes--->Student)
一对多关联映射和多对一关联映射映射原理是一致的,都是在多的一端加入一个外键,指向一的一端
<setname="students">
<!--
<keycolumn="classesid" not-null="true"/>
-->
<key column="classesid"/>
<one-to-manyclass="com.bjpowernode.hibernate.Student"/>
</set>
它们的区别在于维护的关系不同:
* 多对一维护的关系是:多指向一的关系,有了此关系,在加载多的时候可以将一加载上来
* 一对多维护的关系是:一指向多的关系,有了此关系,在加载一的时候可以将多加载上来
在一一端维护关系存在缺陷:
* 因为多的一端Student不知道Classes的存在(也就是Student没有维护与Classes的关系)所以在保存Student的时候关系字段classesid是为null的,如果将该关系字段设置为非空,则将无法保存数据
* 另外因为Student不维护关系,而Classes维护关系,Classes就会发出多余的update语句,保证Classes和Student有关系,这样加载Classes的时候才可以把该Classes对应的学生加载上来
One2many 双向
hibernate一对多关联映射(双向Classes<--->Student)
采用一对多双向关联映射的目的主要是为了主要是为了解决一对多单向关联的缺陷
而不是需求驱动的
一对多双向关联的映射方式:
* 在一的一端的集合上采用<key>标签,在多的一端加入一个外键
<set name="students" inverse="true">
<!--
<key column="classesid"not-null="true"/>
-->//表示classesid字段不能为空
<key column="classesid"/> //
<one-to-many class="com.bjpowernode.hibernate.Student"/>
</set>
* 在多的一端采用<many-to-one>标签
<many-to-one name="classes" column="classesid"/>
!!!注意:<key>标签和<many-to-one>标签加入的字段保持一直,否则会产生数据混乱
inverse属性:
* inverse属性可以用在一对多和多对多双向关联上,inverse属性默认为false,为
false表示本端可以维护关系,如果inverse为true,则本端不能维护关系,会交给另一端维护关系,本端失效。所以一对多关联映射我们通常在多的一端维护关系,让一的一端失效,所以设置为inverse为true
inverse和cascade
* inverse是控制方向上的反转,只影响存储
* cascade是操作上的连锁反映
十三、many tomany
Many2many需要第三方表来维护他们之间的关系,因为两张表没办法把关系说的清楚
Many2many单向
t_user id name 1 张三 2 李四 3 王五 |
t_role id name 1 数据录入人员 2 商务主管 3 商务经理 4 项目会计 |
t_user_role user_id role_id 1 1 1 2 2 1 2 2 2 3 3 3 3 4 |
hibernate多对多关联映射(单向User--->Role)
具体映射:
<setname="roles" table="t_user_role">//生成第三方表t_user_role来维护User和Role之间的关系
<key column="user_id"/>
<many-to-manyclass="com.bjpowernode.hibernate.Role"column="role_id"/>
</set>
Many2many双向
和单向的差不多,只不过是在两边同时维护关系,注意的是,生成的第三方连接表的表明和字段名必须一样。
具体映射:<User----àRoles>
<setname="roles" table="t_user_role">//生成第三方表t_user_role来维护User和Role之间的关系
<key column="user_id"/>
<many-to-manyclass="com.bjpowernode.hibernate.Role"column="role_id"/>
</set>
<Roles----->User>
<set name="users"table="t_user_role">//生成第三方表t_user_role来维护User和Role之间的关系,这里的表名必须和上面的一样
<key column=" role_id "/>
<many-to-manyclass="com.bjpowernode.hibernate.User" column="user_id"/>
</set>
十四、继承映射------每棵类继承树一张表(推荐使用)
每棵类继承树一张表
t_animal
Id | Name | Sex | Weight | Height | Type |
1 | 小猪猪 | True | 200 |
| P |
2 | 小鸟鸟 | False |
| 100 | B |
每棵类继承树一张表
因为类继承树肯定是对应多个类,要把多个类的信息存放在一张表中,
必须有某种机制来区分哪些记录是属于哪个类的。这种机制就是,在表中添加一个字段,
用这个字段的值来进行区分。用hibernate实现这种策略的时候,有如下步骤:
父类用普通的<class>标签定义
在父类中定义一个discriminator,即指定这个区分的字段的名称和类型
如:<discriminatorcolumn=”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鉴别出其真正的类型
十五、继承映射-------------每个类一张表
每个类一张表
t_animal
Id | Name | Sex |
1 | 小猪猪 | True |
2 | 小鸟鸟 | False |
t_pig
Pid | Weight |
1 | 200 |
t_bird
Bid | Height |
2 | 100 |
³ 这种策略是使用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标签的内部。
<joined-subclass name="Pig" table="t_pig">
<keycolumn="pid"/>
<propertyname="weight"/>
</joined-subclass>
<joined-subclassname="Bird" table="t_bird">
<keycolumn="bid"/>
<propertyname="height"/>
</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不能重复(不能使用数据库的自增方式生成主键)
每个具体类一张表
t_pig
Id | Name | Sex | Weigth |
1 | 小猪猪 | True | 200 |
t_bird
Id | Name | Sex | Height |
2 | 小鸟鸟 | False | 100 |
<classname="Animal" table="t_animal"abstract="true">
<idname="id">
<generatorclass="assigned"/>
</id>
<propertyname="name"/>
<propertyname="sex"/>
<union-subclassname="Pig" table="t_pig">
<propertyname="weight"/>
</union-subclass>
<union-subclassname="Bird" table="t_bird">
<propertyname="height"/>90
</union-subclass>
</class>
十七、复合(联合)主键映射:
通常做法是将主键相关字段放到一个单独的类中,这样类是有要求的:
* 必须实现序列化接口
* 覆盖equals和hashcode方法
十八、component映射
t_user
Id | Name | | Address | zipCode | contactTel |
1 | 张三 | Aa | Dsss | Sss | sss |
t_employee
Id | Name | | Address | zipCode | contactTel |
1 | 李四 | Rr | ;; | kkkk | kllk |
User和Employee
Component映射
在hibernate中Component映射采用<component>标签即可
Component是某个实体的逻辑组成部分,它与实体类的主要差别在于,它没有oid(对象标识)
Component在DDD(模型驱动设计)中成为值类
采用Component的好处:实现对象模型的细粒度划分,复用率高,含义明确,层次分明
对象模型与关系模型的设计恰恰相反,对象模型一般是细粒度的,关系模型一般是粗粒度的
十九、各种集合映射
实体类中含有各种集合的映射方法:set list array map
<class name="com.bjpowernode.hibernate.CollectionMapping"table="t_collection_mapping">
<id name="id">
<generatorclass="native"/>
</id>
<propertyname="name"/>
<setname="setValues" table="t_set_values">
<keycolumn="set_id"/>
<element type="string"column="set_value" not-null="true"/>
<!--
<composite-elementclass=""></composite-element>
-->
</set>
<listname="listValues" table="t_list_values">
<keycolumn="list_id"/>
<list-indexcolumn="list_index"/>
<element type="string"column="list_value"/>
</list>
<arrayname="arrayValues" table="t_array_values">
<key column="array_id"/>
<list-indexcolumn="array_index"/>
<element type="string"column="array_value"/>
</array>
<map name="mapValues"table="t_map_values">
<keycolumn="map_id"/>
<map-keytype="string" column="map_key"/>
<elementtype="string" column="map_value"/>
</map>
</class>
二十、lazy
hibernate的lazy策略可以使用在:
* <class>标签上,可以取值:true/false
* <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特性,<class>上的lazy只会影响普通属性
<class>标签上的lazy不会影响单端关联对象的lazy策略