本总结参考尚学堂视频《Hibernate》
一、Start Step
1、新建java项目
2、创建User Library,加入如下jar
* HIBERNATE_HOME/hibernate3.jar
* HIBERNATE_HOME/lib/*.jar
* MySql jdbc驱动
3、创建hibernate配置文件hibernate.cfg.xml,为了便于调试最好加入log4j配置文件
4、定义实体类
5、定义User类的映射文件User.hbm.xml
6、将User.hbml.xml文件加入到hibernate.cfg.xml文件中
7、编写hbm2ddl工具类,将实体类生成数据库表
8、开发客户端
为了方便跟踪sql执行,在hibernate.cfg.xml文件中加入<property name="hibernate.show_sql">true</property>
二、实体对象的生命周期
1. junit简介:
* 编写测试类xxxTest,需要继承TestCase
* 编写单元测试方法,测试方法必须以test开头,测试方法不能含有参数和返回值,如:
public void testHello1() {}
* 最好单元测试的代码单独建立一个目录
2. 了解Hibernate中CRUD操作
3. 了解get和load的区别?
* get不支持lazy,load支持lazy
* 采用get加载数据,如果没有匹配的数据,返回null,而load则抛出异常
4. transient状态的特征?
* 在数据库中没有与之匹配的数据
* 没有纳入session的管理
5. persistent状态的特征?
* persistent状态的对象在数据库中有与之匹配的数据
* 纳入了session的管理
* 在清理缓存(脏数据检查)的时候,会和数据库同步
6. detached状态的特征?
* 在数据库中有与之匹配的数据
* 没有纳入session的管理
三、hibernate基本映射
1、 实体类---表
实体类中的普通属性---表字段
采用<class>标签映射成数据库表,通过<property>标签将普通属性映射成表字段,所谓普通属性指不包括自定义类、集合和数组等
注意:如果实体类和实体类中的属性和sql中的关键字重复,必须采用table或column重新命名
2、 实体类的设计原则:
* 实现一个默认的(即无参数的)构造方法(constructor)
* 提供一个标识属性(identifier property)(可选)
* 使用非final的类 (可选)
* 为持久化字段声明访问器(accessors)
3、 主键生成策略:
uuid、native和assigned
四、hibernate多对一关联映射
1、 关联映射的本质:
* 将关联关系映射到数据库,所谓的关联关系是对象模型在内存中的一个或多个引用
2、<many-to-one>会在多的一端加入一个外键,指向一的一端,这个外键是由<many-to-one>中的column属性定义的,如果忽略了这个属性那么默认的外键与实体的属性一致
3、 <many-to-one>标签的定义示例:
* <many-to-one name="group" column="groupid"/>
4、 理解级联的含义?
* 是对象的连锁操作
5. 将属性 hibernate.hbm2ddl.auto设置为update时,如果检测到表的数据结构发生变化时,将自动更新原来的表,且数据不会丢失。
五、一对一主键关联映射(单向关联Person---->IdCard)
1. 一对一主键关联映射:让两个实体对象的id保持相同,这样可以避免多余的字段被创建
2. 具体映射:
<id name="id">
<!-- person的主键来源idCard,也就是共享idCard的主键 -->
<generator class="foreign">
<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"/>
六、一对一主键关联映射(双向关联Person<---->IdCard)
需要在idcard映射文件中加入<one-to-one>标签指向person,指示hibernate如何加载person默认根据主键加载
七、一对一唯一外键关联映射(单向关联Person---->IdCard)
一对唯一外键关联映射是多对一关联映射的特例
可以采用<many-to-one>标签,指定多的一端的unique=true,这样就限制了多的一端的多重性为一通过这种手段映射一对一唯一外键关联
八、一对一唯一外键关联映射(双向关联Person<---->IdCard)
一对一唯一外键关联双向,需要在另一端(idcard),添加<one-to-one>标签,指示hibernate如何加载其关联对象,默认根据主键加载person,外键关联映射中,因为两个实体采用的是person的外键维护的关系,所以不能指定主键加载person,而要根据person的外键加载,所以采用如下映射方式:
<one-to-one name="person" property-ref="idCard"/>
九、session flush测试:
1、 session flush方法主要做了两件事:
* 清理缓存
* 执行sql
2、 session在什么情况下执行flush
* 默认在事务提交时
* 显示的调用flush
* 在执行查询前,如:iterate
3、 hibernate按照save(insert),update、delete顺序提交相关操作
4、 隔离级别
隔离级别 |
是否存在脏读 |
是否存在不可重复读 |
是否存在幻读 |
Read Uncommited |
Y |
Y |
Y |
Read Commited |
N |
Y |
Y |
Repeatable Read |
N |
N |
Y |
Seriazable |
N |
N |
N |
脏读:没提交事务就可以读出数据。
不可重复读:由于更新等操作,使重复读的结果不一样。
幻读:由于插入操作,出现查询结果的记录不一样。
Read Uncommited(未提交读):没提交就可以读出数据,采用悲观锁可以避免不可重复读。
Read Commited(提交读):可以避免脏读。
Repeatable Read(可重复读,相当于增加了悲观锁):不可避免幻读。
Seriazable(序列化读):串行化,导致并发行很差。
5、 Mysql的默认隔离级别为可重复读,查询语句为:select @@tx_isolation;
更改隔离级别:set transaction isolation level read uncommitted;
6、 如果的主键生成策略采用的是uuid,调用完成save后,只是将user纳入到了session的管理不会发出insert语句,但是id已经生成,session中existsInDatebase状态为false。调用flush,hibernate会清理缓存,执行sql如果数据库的隔离级别设置为为提交读,那么我们可以看到flush过的数据,并且session中existsInDatebase状态为true。默认情况下commit操作会先执行flush清理缓存,所以不用显示的调用flush
7、 如果主键生成策略为native,调用session.save后,将执行insert语句,返回有数据库生成的id,纳入了session的管理,修改了session中existsInDatebase状态为true,如果数据库的隔离级别设置为为提交读,那么我们可以看到save过的数据。
8、 uuid主键生成策略,session.evict(user);
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的状态,如果没又执行flush方法,则无法成功提交,因为hibernate在清理缓存时,在session的insertions集合中取出user对象进行insert操作后需要更新entityEntries属性中的existsInDatabase为true,而我们采用evict已经将user从session的entityEntries中逐出了,所以找不到相关数据,无法更新,抛出异常。
tx.commit();
9、 native主键生成策略,session.evict(user);
session.save(user);
将user对象从session中逐出,即session的EntityEntries属性中逐出
session.evict(user);
可以成功提交,因为hibernate在清理缓存时,在session的insertions集合中无法找到user对象,所以就不会发出insert语句,也不会更新session中的existsInDatabase的状态
tx.commit();
十、一对多关联映射(单向Classes----->Student)
1、 一对多关联映射利用了多对一关联映射原理
多对一关联映射:在多的一端加入一个外键指向一的一端,它维护的关系是多指向一,一对多关联映射:在多的一端加入一个外键指向一的一端,它维护的关系是一指向多,也就是说一对多和多对一的映射策略是一样的,只是站的角度不同。
2、 在一的端维护关系的缺点:
* 如果将t_student表里的classesid字段设置为非空,则无法保存
* 因为不是在student这一端维护关系,所以student不知道是哪个班的,
所以需要发出多余的update语句来更新关系
3、 在一端加入的代码如下:
<set name="students">
<key column="class_id" />
<one-to-many class="com.feiqiang.one2many.Student"/>
</set>