一. Hibernate的定义:符合Java习惯的关系型数据库持久化
-
1.对象关系映射型框架(ORM Object Realtion Mapping)
Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。
即Hibernate封装了简单java对象(pojo plain ordinary java object)转化为相应的数据库中的关系数据的操作:也就是相关的sql语句,使得程序员只需提供对象的相关属性便可操作数据库中所对应的表的相关数据
-
2.Hibernate对象的生存周期
a) 临时状态/瞬时状态
该对象是新创建的;一个持久化状态的对象被删除;一个游离状态的数据被删除特点:瞬时状态下,内存中存在这个对象,但session还未持有该对象,数据库没有该对象对应的关系值 此时的对象将会被gc清理,可通过set(),update()等方法重新进入持久化态
b) 持久化状态
对象从数据库中查询出来时,临时状态的数据被保存时,游离状态的数据被更新/
锁定特点:持久化状态下,内存中存在这个对象,且session也持有该对象,数据库有该对象对应的关系值, 直至事务提交前,session会对这个对象进行管理并进行脏数据检查等操作,使得这个对象在事务提交时 以最后一次修改的状态提交。此时的对象不会被gc清理,可通过delete()等方法重新回归瞬时态 或通过close(),clear()等方法进入游离态
c) 游离状态
持久化状态的数据被(session)清理特点:游离状态下,内存中存在这个对象,但session还未持有该对象,数据库已拥有该对象对应的关系值。 此时的对象将会被gc清理,可通过set(),update()等方法重新回归持久化态
二. Hibernate的配置
-
1.hibernate.cfg.xml配置文件:主要配置数据库信息及相关pojo类的映射文件信息
<hibernate-configuration> <!-- session 工厂的配置 --> <session-factory> <!-- jdbc 配置 --> <property name="connection.driver_class"> com.mysql.jdbc.Driver </property> <property name="connection.url"> <!-- url 的两种配置方式 --> <!-- jdbc:mysql://localhost:3306/hibernate4--> jdbc:mysql:///hibernate4 </property> <property name="connection.username">root</property> <property name="connection.password">root</property> <!-- 数据库方言 hibernate 支持多种数据库,通过设置方言 hibernate 才知道应该 生成对应数据库的 sql 语句:hibernate 支持的数据库的方言 hibernate.properties 文件中都有 --> <property name="dialect"> org.hibernate.dialect.MySQL5Dialect </property> <!-- 打印 hibernate 生成的 sql 语句 --> <property name="show_sql">true</property> <!-- 格式化打印的 sql 语句 --> <property name="format_sql">true</property> <!-- 根据不同值,进行数据表表的操作 create 每次执行 都删除原有表,然后创建新表 create-drop 执行前创建表,执行后删除表 update 如果有则不改变表,如果没有则创建 --> <property name="hbm2ddl.auto">update</property> <!-- 将所有映射文件添加到这里 --> <mapping resource="cn/sxt/pojo/User.hbm.xml"/> </session-factory> </hibernate-configuration>
-
2.*.hbm.xml 映射文件: 配置相关pojo类的映射信息
<!-- 类的映射文件信息 --> <!-- package 指定类的包名 可以不配置 如果不配置 那么在配置 class 的name 时需要指定该类所在包--> <hibernate-mapping> <!-- class 配置类 name 指类名 table 指定表名 如果不写,默认类名 为表名--> <class name="cn.sxt.pojo.User" table="t_user"> <!-- id 主键的配置 name 配置类的属性名 column 数据库字段名 不写和属性名一致 type 指定属性的类型 length 指定字段的长度 --> <id name="id" column="id"> <!-- 主键的生成策略 increment 用于为 long, short 或者 int 类型生成 唯一标识。只有在 没有其他进程往同一张表中插入数据时才能使用。 在集群下不要使用。 identity 对 DB2,MySQL, MS SQL Server, Sybase 和HypersonicSQL 的内置标识字段提供支持。 返回的标识符是 long, short 或者 int 类型的。 native -(如果是 mysql 自增,那么 native 和 identity 是一样) 根据底层数据库的能力选择 identity, sequence 或者 hilo 中的一个。 sequence 在 DB2,PostgreSQL, Oracle, SAP DB, McKoi 中使用序列(sequence),而在 Interbase 中使用生成器(generator)。返回的标识符是 long, short 或者 int 类型的。 <generator class="sequence"> <param name="sequence">user_seq</param> </generator> assigned 让应用程序在 save()之前为对象分配一个标示符。这是<generator>元素没有指定时 的默认生成策略。 --> <generator class="identity"></generator> </id> <!-- property 是配置类的属性 name 指属性名 --> <property name="name" length="40"/> <property name="age" /> </class> </hibernate-mapping>
-
3.Hibernate所需jar包导入:
导入所有lib目录下requires文件的所有jar包
三. Hibernate的映射关系
表结构的设计:
1. 多对一关系:
hibernate中,对具有多对一的关联映射表进行查询时,可将这个表中关联信息的一对一的信息同时查询出来
1.具有多对一关系对象的映射文件:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.test.vo">
<class name="Student_m1" table="t_student">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="name" column="name"></property>
<many-to-one name="clazz" column="class_id"></many-to-one>
</class>
</hibernate-mapping>
2.被多对一关系所指向的对象的映射文件:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.test.vo">
<class name="Class_m1" table="t_class">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="name" column="name"></property>
</class>
</hibernate-mapping>
3.测试类:获取多对一对象同时获取其一对一的属性:
@Test
public void testGetManyToOne() {
try {
Student_m1 student = new Student_m1();
student = session.get(Student_m1.class, 1);
//获取学生名。并获取学生所在班级名称
System.out.println(student.getName() + "----------" + student.getClazz().getName());
transaction.commit();
} catch (Exception e) {
e.printStackTrace();
if (transaction != null) {
transaction.rollback();
}
}
}
2. 一对多关系:
1.具有一对多关系对象的映射文件:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.test.vo">
<class name="Class_1m" table="t_class">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="name" column="name"></property>
<!-- 一对多的关联 -->
<set name="students">
<key column="class_id"></key>
<one-to-many class="Student_1m"/>
</set>
</class>
</hibernate-mapping>
2.被一对多关系所指向的对象的映射文件:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.test.vo">
<class name="Student_1m" table="t_student">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="name" column="name"></property>
</class>
</hibernate-mapping>
3.测试类:获取多对一对象同时获取其一对一的属性:
@Test
public void testGetOneToMany() {
try {
Class_1m clazz = new Class_1m();
clazz = session.get(Class_1m.class, 1);
Set<Student_1m> students = clazz.getStudents();
for (Student_1m student : students) {
System.out.println(student.getName()+"----------"+student.getId());
}
transaction.commit();
} catch (Exception e) {
e.printStackTrace();
if (transaction != null) {
transaction.rollback();
}
}
}
3. 双向多对一关系:
- 由多对一端管理信息
- 在一对多段映射文件的一对多对象添加字段"inverse=‘true’",即
<set name="students" inverse="true">
- 其余代码是1、2的整合,即两个映射文件都包含各自的多对一和一对多映射关系
- 单元测试方法:先查询出学生对应的班级后(即先执行多对一查询),可根据这些班级id获取对应的学生set(再执行一对多查询)
4. 一对一关系:
-
一对一的关系,相当于在多对一关系中添加唯一约束,即
<many-to-one name="clazz" column="class_id" unique="true"></many-to-one>
5. 双向一对一关系:
-
即一对一关系的扩充,两个互相指向的一对一关系的对象均持有对方的引用;
-
双向一对一关系中,原先为被指向的对象(例如人指向身份证,这里的对象就是指的是身份证)的中,添加了一对一关系的对象引用,于是其映射文件必须添加相应的字段来说明其一对一关系,即
<one-to-one name="被指向的前一个对象的完整类名" property-ref="一对一属性名"></one-to-one>
6. 主键单向一对一关系:
- 即一对一关系所对应的另一张表的主键作为该表的主键值(例如学生的id对应card表的id值)
1.具有一对一关系对象(student)的映射文件:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.test.vo">
<class name="Student_1p" table="p_student">
<id name="id" column="id">
<generator class="foreign">
<param name="property">card</param>
</generator>
</id>
<property name="name" column="name"></property>
<!-- constrained为true代表添加外键约束 -->
<one-to-one name="card" constrained="true"></one-to-one>
</class>
</hibernate-mapping>
2.被一对一关系所指向的对象(card)的映射文件:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.test.vo">
<class name="Card_1p" table="p_card">
<id name="id" column="id"/>
<property name="address" column="address"></property>
</class>
</hibernate-mapping>
3.测试类:获取一对一对象同时获取其所对应对象的相关属性:
@Test
public void testGetOneToOnePrimary() {
try {
Student_1p student = new Student_1p();
student = session.get(Student_1p.class, 1);
System.out.println(student.getId());
System.out.println(student.getCard().getId());
System.out.println(student.getCard().getAddress());
transaction.commit();
} catch (Exception e) {
e.printStackTrace();
if (transaction != null) {
transaction.rollback();
}
}
}
7. 主键双向一对一关系:
- 在被指向的一对一的对象中添加指向其的对象的引用,并在其映射文件中添加字段
<one-to-one name="前一个对象的完整类名"></one-to-one>
8. 联表单向多对多关系:
- 即由中间表搭建的,由一张表的对应另一张多个属性的关系,称联表多对多关系
1.具多对多关系对象(user)的映射文件:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.test.vo">
<class name="User_mm" table="m_user">
<id name="id" column="id">
<generator class="assigned"></generator>
</id>
<property name="name" column="name"></property>
<!--设置外联表属性,即key值对应外键为user表的id列,value值对应permission表的id值,
当set存入一个permisson的value后,映射文件将自动转化为其对应的id作为该列的值-->
<set name="permissions" table="m_user_permission">
<key column="uid"></key>
<many-to-many column="pid" class="Permission_mm"></many-to-many>
</set>
</class>
</hibernate-mapping>
2.被多对多关系所指向的对象(permission)的映射文件:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.test.vo">
<class name="Permission_mm" table="m_permission">
<id name="id" column="id"/>
<property name="content" column="content"></property>
</class>
</hibernate-mapping>
3.测试类:获取多对多对象同时获取其所对应对象的相关属性:
@Test
public void testGetManyToManyByRelationTable() {
try {
User_mm user1 = session.get(User_mm.class, 1001);
Set<Permission_mm> permissions = user1.getPermissions();
for (Permission_mm p : permissions) {
System.out.println(p.getContent());
}
transaction.commit();
} catch (Exception e) {
e.printStackTrace();
if (transaction != null) {
transaction.rollback();
}
}
}
9. 联表双向多对多关系:
- 和单向的区别:在被对应的对象的映射文件中添加
reverse="true"
字段,其余代码一致,即在被对应的对象中添加联表;
10. 组件映射关系:
- 即数据库中只存在一张表,但是由于某些属性可以被单独抽取作为一个对象进行管理,这个对象从属于一个大的用于和数据库交互的对象,则称它为交互对象的组件。
11. 组合主键映射关系:
- 即一个数据库由多个键共同构成主键,对此类表进行查询时,需将要查询的主键值赋给事先创建完成的对象,利用这个对象进行查询(也就是说查询对应的对象前,必须将其组合主键先行赋值才能进行)
1.查询代码:
@Test
public void testGetByCompositeId() {
try {
Result result = new Result();
result.setStuid(1);
result.setSubid(2);
//Result类必须实现Serializable接口,才能将result对象传入session的get查询方法
result = session.get(Result.class,result);
System.out.println(result.getScore());
transaction.commit();
} catch (Exception e) {
e.printStackTrace();
if (transaction != null) {
transaction.rollback();
}
}
}
2.映射文件代码:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.test.vo">
<class name="Result" table="result">
<!-- 配置组合外键 -->
<composite-id>
<key-property name="stuid" column="stuid"/>
<key-property name="subid" column="subid"/>
</composite-id>
<property name="score" column="score"></property>
</class>
</hibernate-mapping>
- 注: 查询组合主键关系数据时,还可以将多个主键抽取独自封装为一个主键类(pk)并且实现Serializable接口,查询前为Pk赋值,并给查询对象赋pk值。在查询时传入pk对象而不是传入整个查询对象,从而提升查询效率
四. Hibernate的相关属性
1. OpenSessioInView:
-
自定义的过滤器,用于管理连接数据库的事务,负责其创建,提交,回滚,从而简化持久层的代码。
示例代码: public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { SqlSession ss = HibernateUtil.getSession(); try { chain.doFilter(request, response); ss.commit(); } catch (Exception e) { ss.rollback(); e.printStackTrace(); } HibernateUtil.closeSession(); }
-
cascade:级联,操作一个对象可附带操作其关联对象,称为级联
1.相应值:
save-update–在操作主对象的保存或更新操作时,所引发其关联对象的保存或更新操作未被保存时,将对其进行保存操作;
多对一的多端适用,不会使一段产生过多sql语句,但delete操作不适用于多端
delete–表示在操作主对象的删除操作时,其关联对象的相应的列也将一同被删除(如果不是孤儿数据将无法删除该列并报错,因为还有值关联该列,无法删除)
多对一端的一端适用,但save-update操作会使多端造成插入多条sql语句进行更新,降低效率
-
inverse: 反转,将主键的控制权反转给与其对应的关联对象的操作,称之为反转:
对应的情况是:
1.当主查询对象在设值时,设定的关联对象的值为空;
2.关联对象后续进行设值,设定的值不为空;
3.保存查询对象和关联对象时,将导致以下两种情况:a.关联对象没有设置inverse为true,因此关联对象可以管理关系(主键),因此导致在保存查询对象时, 将导致查询对象必须进行更新操作,设定相应的关联对象的值 b.关联对象设inverse为true,则关联对象没有管理关系的权限,因而其值发生变化不会使查询对象发生 更新操作,只是单纯改变自己设定的值
a情况下将会有多条查询对象的更新sql语句;而b情况下不会有(主键控制权完全在查询对象手中,关联对象的更改值的操作不影响它),因此在实际开发中设置inverse为true,减少关联对象对主键的管理而造成生成过多sql语句的生成降低程序执行效率的问题。
-
hql:hibernate query language,hibernate框架对应的数据库查询语言:面向对象
-
利用createQuery方法操作hql语句;
-
可省略
select *
; -
若有占位符,则下标从0开始;
//查询所有 @Test public void testQuery(){ try { //注意这里的from后面的字段不是表名,而是该表对应的对象的类名 String hql ="from Result"; List<Result> list = session.createQuery(hql).list(); for (Result result : list) { System.out.println(result.getScore()); } transaction.commit(); } catch (Exception e) { e.printStackTrace(); if (transaction != null) { transaction.rollback(); } } }
-
在hibernate中亦可使用本地sql查询语句,但返回值将会被封装为object对象而非指定的对象,因此在执行sql语句后执行addEntity方法设置返回对象的类型为指定的类型;
@Test public void testSqlQuery(){ try { String sql ="select * from result"; List<Result> list = session.createSQLQuery(sql) .addEntity(Result.class) .list(); for (Result result : list) { System.out.println(result.getScore()); } transaction.commit(); } catch (Exception e) { e.printStackTrace(); if (transaction != null) { transaction.rollback(); } } }
-
-
hibernate的加载策略:
-
即时加载和延迟加载
即时加载:指当查询数据时直接将该数据从数据库中查询,占用资源多,降低效率(get,List)
延迟加载(懒加载):当真正使用数据时,才从数据库中查询数据,占用资源少,提高效率(load,Iterator)
-
-
hibernate的抓取策略:
-
抓取策略:select/join
Select 抓取:表示查询关联对象时,使用 select 语句。(在 select 抓取下,可以用 lazy);
Join 抓取:查询关联对象时,使用 join 连接语句。Lazy 没有意义(没有作用);
subselect 抓取:如果要查询关联集合的内容。会查询之前已经查出的对象的所有关联集合。(Category 对应了多个 Book)如果查询了(”文学”,”历史”);那么在使用(lazy=true)”文学”或”历史”的集合对象(”所对应的书籍信息”).会将(“文学”和”历史”)的书籍信息一起查询。如果 lazy=false;在查询“多个分类时”会将所有分类的书籍信息一起查询。
-
-
hibernate的缓存:
-
查询缓存:指查询数据后,将数据存储的缓存,基于二级缓存使用
-
一级缓存:又称为 Session 缓存,由 Session 来管理;和 Session 的生命周期相同。是事务级别的缓存(线程);
Iterator、get和load方法均会在查询数据库时查询一级缓存是否含有相应的对象; list方法不会检查一级缓存是否含有该对象,直接查询 以上方法在查询得到对象的相应结果后,会向缓存写入该对象,便于存取
-
一级缓存的管理:
session. clear 方法:清空缓存中的所有数据;清空以后当再次查询数据时会从数据库中重新查询。 flush 方法:将缓存中的数据与数据库中的数据同步。 evict 方法:清除缓存中指定的对象 close 方法:关闭 session 对象,释放缓存;
-
二级缓存:SessionFactory 缓存;和 SessionFactory 生命周期相同;是进程级别的缓存;可通过导入外源jar包来实现,但二级缓存占用较多的系统资源
-
hibernate的锁:
-
乐观锁:由更新字段控制,认为数据库的写操作属于少数,当发生写操作时对版本进行更新并阻止其他同时进行的更新操作的再提交:即设置表的某一列为版本号,hibernate将对该列的值进行管理,事务在提交更新操作时发现版本号不一致将无法提交事务,这一锁机制可以提升并发效率,但是用户体验较差,因为其他线程的写操作可能造成本线程事务提交失败;
-
悲观锁:由锁控制,即认为数据库的写操作是频繁进行的,一个线程在操作数据库的某一行数据时其他线程不能对这一行数据进行任何操作:例如在事务与数据库交互操作中添加锁选项的相关字段来实现,即
result = session.get(Result.class,result,LockOptions.UPGRADE);
-
-