如有转载,请申明:
转载自 IT天宇: http://www.jianshu.com/p/50964e92c5fb
前言
博客正式转到简书,实在忍受不了 CSDN 的模版,有广告或界面难看还可以忍,但有些模版还有 bug,作为程序员忍无可忍,修个 bug 真的有那么难吗,这么多年了。
接着上篇,先说个段子。
三四年前如果有人问我 Android/Ios 如何入门,我可能会推荐上百 G 的资料,但如果现在问我,我只会推荐一本书《app开发从上架到上吊》
你可能觉得我危言耸听,但今年的移动开发行情真的很差,看到很多几年经验的一个月才收到几个面试通知,没有经验的就更绝望了。
好吧,今天不是来讨论行情的。
前段时间写了一篇 struts2 的笔记,有好心的老司机告诉我,struts2 已经被抛弃了。但话是这么说,面试的时候,难免碰到问 struts2,如果这个时候表现得一脸懵逼,那估计 offer 就没有了。所以虽然现在 hibernate 用得不多了,但还是得复习一下。
目录
- 环境搭建
- 实体类映射
- 核心配置详解
- 一级缓存
- 关系映射
- 抓取策略
- HQL
- QBC
- 其他配置
- 事务
- 二级缓存
正文
<a id="1"></a>
1.环境搭建
导包
根据需要选择手动导入 jar 包,或者用依赖管理工具。
- 下载
http://hibernate.org/orm/downloads/
必须导入的是lib\required
下面 jar 包 - Gradle
compile "org.hibernate:hibernate-core:5.2.9.Final"
此外,为了连接数据库,还需要数据库的驱动包。
配置
核心配置文件以 hibernate.cfg.xml 命名,放在类路径下(Idea 放在 resources 下)
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<!-- SessionFactory,相当于之前学习连接池配置 -->
<session-factory>
<!-- 1 基本4项 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///db01</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">1234</property>
</session-factory>
</hibernate-configuration>
<a id="2"> </a>
2.实体类映射
数据库
create database db01;
use db01;
实体类
public class User {
private Integer uid;
private String username;
private String password;
// 省略 get set 方法
}
映射实体类
可以用 xml 或者 注解来映射
xml
放在实体类同目录下,名字和实体类相同,扩展名为 .hbm.xml
例如: ser.hbm.xml
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.ittianyu.a_hello.User" table="t_user">
<!-- 主键 -->
<id name="uid">
<generator class="native"></generator>
</id>
<!-- 普通属性 -->
<property name="username"></property>
<property name="password"></property>
</class>
</hibernate-mapping>
核心配置文件中加上映射文件位置
<mapping resource="com/ittianyu/hibernate/helloworld/User.hbm.xml"/>
注解
// name 对应表名称
@Entity
@Table(name = "t_user")
public class User {
// 主键
@Id
@GeneratedValue
private Integer uid;
private String username;
private String password;
// 省略 get set 方法
}
在核心配置文件中加上映射文件位置
<mapping class="com.ittianyu.hibernate.helloworld.User" />
测试
public class HelloWorld {
@Test
public void hello() {
// username, password
User user = new User("123456", "123");
// 1.加载配置文件
Configuration configure = new Configuration().configure();
// 2.获得session factory对象
SessionFactory sessionFactory = configure.buildSessionFactory();
// 3.创建session
Session session = sessionFactory.openSession();
// 4.开启事务
Transaction transaction = session.beginTransaction();
// 5.保存并提交事务
session.save(user);
transaction.commit();
// 6.释放资源
session.close();
sessionFactory.close();
}
}
<a id="3"> </a>
3.核心配置详解
核心 api
Configuration
常用方法
- 构造方法:默认加载 hibernate.properties
- configure() 方法:默认加载 hibernate.cfg.xml
- configure(String) 方法:加载指定配置文件
手动添加映射
// 手动加载指定的配置文件
config.addResource("com/ittianyu/a_hello/User.hbm.xml");
// 手动加载指定类,对应的映射文件 User--> User.hbm.xml
config.addClass(User.class);
SessionFactory
- SessionFactory 相当于java web连接池,用于管理所有session
- 获得方式:config.buildSessionFactory();
- sessionFactory hibernate缓存配置信息 (数据库配置信息、映射文件,预定义HQL语句 等)
- SessionFactory线程安全,可以是成员变量,多个线程同时访问时,不会出现线程并发访问问题
- 开启一个 session:
factory.openSession();
- 获取和当前线程绑定的会话(需要配置):
factory.getCurrentSession();
<property name="hibernate.current_session_context_class">thread</property>
Session
- Session 相当于 JDBC的 Connection -- 会话
- 通过session操作PO对象 --增删改查
- session单线程,线程不安全,不能编写成成员变量。
- Api:
save 保存 update 更新 delete 删除 get 通过id查询,如果没有 null load 通过id查询,如果没有抛异常 createQuery("hql") 获得Query对象 createCriteria(Class) 获得Criteria对象
Transaction
开启事务 beginTransaction()
获得事务 getTransaction()
提交事务:commit()
回滚事务:rollback()
和 spring 整合后,无需手动管理
Query
- hibernate执行hql语句
- hql语句:hibernate提供面向对象查询语句,使用对象(类)和属性进行查询。区分大小写。
- 获得
session.createQuery("hql")
- 方法:
list() 查询所有 uniqueResult() 获得一个结果。如果没有查询到返回null,如果查询多条抛异常。 setFirstResult(int) 分页,开始索引数startIndex setMaxResults(int) 分页,每页显示个数 pageSize
Criteria
- QBC(query by criteria),hibernate提供纯面向对象查询语言,提供直接使用PO对象进行操作。
- 获得方式:Criteria criteria = session.createCriteria(User.class);
- 条件
criteria.add(Restrictions.eq("username", "tom")); Restrictions.gt(propertyName, value) 大于 Restrictions.ge(propertyName, value) 大于等于 Restrictions.lt(propertyName, value) 小于 Restrictions.le(propertyName, value) 小于等于 Restrictions.like(propertyName, value) 模糊查询,注意:模糊查询值需要使用 % _
工具类
public class HibernateUtils {
private static SessionFactory sessionFactory;
static {
Configuration configuration = new Configuration().configure();
sessionFactory = configuration.buildSessionFactory();
Runtime.getRuntime().addShutdownHook(new Thread(){
@Override
public void run() {
sessionFactory.close();
}
});
}
public static Session openSession() {
return sessionFactory.openSession();
}
public static Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
public static void main(String[] args) {
Session session = openSession();
System.out.println(session);
session.close();
}
}
核心配置
基本配置
<!-- SessionFactory,相当于之前学习连接池配置 -->
<session-factory>
<!-- 1 基本4项 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///h_day01_db</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">1234</property>
<!-- 2 与本地线程绑定 -->
<property name="hibernate.current_session_context_class">thread</property>
<!-- 3 方言:为不同的数据库,不同的版本,生成sql语句(DQL查询语句)提供依据
* mysql 字符串 varchar
* orcale 字符串 varchar2
-->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
<!-- 4 sql语句 -->
<!-- 显示sql语句 -->
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<!-- 5 自动创建表(了解) ,学习中使用,开发不使用的。
* 开发中DBA 先创建表,之后根据表生产 PO类
* 取值:
update:【】
如果表不存在,将创建表。
如果表已经存在,通过hbm映射文件更新表(添加)。(映射文件必须是数据库对应)
表中的列可以多,不负责删除。
create :如果表存在,先删除,再创建。程序结束时,之前创建的表不删除。【】
create-drop:与create几乎一样。如果factory.close()执行,将在JVM关闭同时,将创建的表删除了。(测试)
validate:校验 hbm映射文件 和 表的列是否对应,如果对应正常执行,如果不对应抛出异常。(测试)
-->
<property name="hibernate.hbm2ddl.auto">create</property>
<!-- 6 java web 6.0 存放一个问题
* BeanFactory 空指针异常
异常提示:org.hibernate.HibernateException: Unable to get the default Bean Validation factory
* 解决方案:取消bean校验
-->
<property name="javax.persistence.validation.mode">none</property>
<!-- 添加映射文件
<mapping >添加映射文件
resource 设置 xml配置文件 (addResource(xml))
class 配置类 (addClass(User.class)) 配置的是全限定类名
-->
<mapping resource="com/ittianyu/a_hello/User.hbm.xml"/>
</session-factory>
主键种类
- 自然主键: 在业务中,某个属性符合主键的三个要求.那么该属性可以作为主键列.
- 代理主键: 在业务中,不存符合以上3个条件的属性,那么就增加一个没有意义的列.作为主键.
类型对应
Java数据类型 | Hibernate数据类型 | 标准SQL数据类型(不同DB有差异) |
---|---|---|
byte、java.lang.Byte | byte | TINYINT |
short、java.lang.Short | short | SMALLINT |
int、java.lang.Integer | integer | INGEGER |
long、java.lang.Long | long | BIGINT |
float、java.lang.Float | float | FLOAT |
double、java.lang.Double | double | DOUBLE |
java.math.BigDecimal | big_decimal | NUMERIC |
char、java.lang.Character | character | CHAR(1) |
boolean、java.lang.Boolean | boolean | BIT |
java.lang.String | string | VARCHAR |
boolean、java.lang.Boolean | yes_no | CHAR(1)('Y'或'N') |
boolean、java.lang.Boolean | true_false | CHAR(1)('Y'或'N') |
java.util.Date、java.sql.Date | date | DATE |
java.util.Date、java.sql.Time | time | TIME |
java.util.Date、java.sql.Timestamp | timestamp | TIMESTAMP |
java.util.Calendar | calendar | TIMESTAMP |
java.util.Calendar | calendar_date | DATE |
byte[] | binary | VARBINARY、BLOB |
java.lang.String | text | CLOB |
java.io.Serializable | serializable | VARBINARY、BLOB |
java.sql.Clob | clob | CLOB |
java.sql.Blob | blob | BLOB |
java.lang.Class | class | VARCHAR |
java.util.Locale | locale | VARCHAR |
java.util.TimeZone | timezone | VARCHAR |
java.util.Currency | currency | VARCHAR |
普通属性
<hibernate-mapping>
package 用于配置PO类所在包
例如: package="com.ittianyu.d_hbm"
<class> 配置 PO类 和 表 之间对应关系
name:PO类全限定类名
例如:name="com.ittianyu.d_hbm.Person"
如果配置 package,name的取值可以是简单类名 name="Person"
table : 数据库对应的表名
dynamic-insert="false" 是否支持动态生成insert语句
dynamic-update="false" 是否支持动态生成update语句
如果设置true,hibernate底层将判断提供数据是否为null,如果为null,insert或update语句将没有此项。
普通字段
<property>
name : PO类的属性
column : 表中的列名,默认name的值相同
type:表中列的类型。默认hibernate自己通过getter获得类型,一般情况不用设置
取值1: hibernate类型
string 字符串
integer 整形
取值2: java类型 (全限定类名)
java.lang.String 字符串
取值3:数据库类型
varchar(长度) 字符串
int 整形
<property name="birthday">
<column name="birthday" sql-type="datetime"></column>
</property>
javabean 一般使用类型 java.util.Date
jdbc规范提供3中
java类型 mysql类型
java.sql.Date date
java.sql.time time
java.sql.timestamp timestamp
null datetime
以上三个类型都是java.util.Date子类
length : 列的长度。默认值:255
not-null : 是否为null
unique : 是否唯一
access:设置映射使用PO类属性或字段
property : 使用PO类属性,必须提供setter、getter方法
field : 使用PO类字段,一般很少使用。
insert 生成insert语句时,是否使用当前字段。
update 生成update语句时,是否使用当前字段。
默认情况:hibernate生成insert或update语句,使用配置文件所有项
注意:配置文件如果使用关键字,列名必须使用重音符
主键
<id>配置主键
name:属性名称
access="" 设置使用属性还是字段
column="" 表的列名
length="" 长度
type="" 类型
<generator> class属性用于设置主键生成策略
1.increment 由hibernate自己维护自动增长
底层通过先查询max值,再+1策略
不建议使用,存在线程并发问题
2.identity hibernate底层采用数据库本身自动增长列
例如:mysql auto_increment
3.sequence hibernate底层采用数据库序列
例如:oracle 提供序列
4.hilo
</generator>
5.native 根据底层数据库的能力选择 identity、sequence 或者 hilo 中的一个。【】
##以上策略使用整形,long, short 或者 int 类型
6.uuid 采用字符串唯一值【】
##以上策略 代理主键,有hibernate维护。
7.assigned 自然主键,由程序自己维护。【】
<a id="4"> </a>
4.一级缓存
对象状态
三种状态
- 瞬时态:transient,session没有缓存对象,数据库也没有对应记录。
OID特点:没有值 - 持久态:persistent,session缓存对象,数据库最终会有记录。(事务没有提交)
OID特点:有值 - 脱管态:detached,session没有缓存对象,数据库有记录。
OID特点:有值
转换
一级缓存
一级缓存:又称为session级别的缓存。当获得一次会话(session),hibernate在session中创建多个集合(map),用于存放操作数据(PO对象),为程序优化服务,如果之后需要相应的数据,hibernate优先从session缓存中获取,如果有就使用;如果没有再查询数据库。当session关闭时,一级缓存销毁。
@Test
public void demo02(){
//证明一级缓存
Session session = factory.openSession();
session.beginTransaction();
//1 查询 id = 1
User user = (User) session.get(User.class, 1);
System.out.println(user);
//2 再查询 -- 不执行select语句,将从一级缓存获得
User user2 = (User) session.get(User.class, 1);
System.out.println(user2);
session.getTransaction().commit();
session.close();
}
可以调用方法清除一级缓存
//清除
//session.clear();
session.evict(user);
快照
与一级缓存一样的存放位置,对一级缓存数据备份。保证数据库的数据与 一级缓存的数据必须一致。如果一级缓存修改了,在执行commit提交时,将自动刷新一级缓存,执行update语句,将一级缓存的数据更新到数据库。
当缓存和数据库数据不一样且在提交之前,可以调用 refresh 强制刷新缓存。
<a id="5"> </a>
5.关系映射
一对一
一对一关系一般是可以整合成一张表,也可以分成两张表。
维护两张表的关系可以选择外键也可以选择让主键同步。
实体类
Address.java
public class Address {
private Integer id;
private String name;
private Company company;
// 省略 get set
}
Company.java
public class Company {
private Integer id;
private String name;
private Address address;
// 省略 get set
}
外键维护关系
Address.hbm.xml
<!--
dynamic-insert 和 dynamic-update 为 true 时,sql语句中只有值变化或者不为空的属性才会加上,用于更新部分属性
-->
<class name="Address" table="t_address_sync" dynamic-insert="true" dynamic-update="true">
<id name="id" column="id">
<!--
主键与外键表的主键同步
-->
<generator class="foreign">
<param name="property">company</param>
</generator>
</id>
<property name="name" column="name"/>
<!--
需要在同步主键的一方加上 constrained="true" 使用给主键加上外键约束
-->
<one-to-one name="company" class="Company" constrained="true" />
</class>
Company.hbm.xml
<!--
dynamic-insert 和 dynamic-update 为 true 时,sql语句中只有值变化或者不为空的属性才会加上,用于更新部分属性
-->
<class name="Company" table="t_company_ref" dynamic-insert="true" dynamic-update="true">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name" column="name"/>
<!--
one-to-one 中使用了 property-ref :当前类哪个属性是引用外键
放弃维护外键
-->
<one-to-one name="address" class="Address" property-ref="company" />
</class>
主键同步关系
Address.hbm.xml
<!--
dynamic-insert 和 dynamic-update 为 true 时,sql语句中只有值变化或者不为空的属性才会加上,用于更新部分属性
-->
<class name="Address" table="t_address_sync" dynamic-insert="true" dynamic-update="true">
<id name="id" column="id">
<!--
主键与外键表的主键同步
-->
<generator class="foreign">
<param name="property">company</param>
</generator>
</id>
<property name="name" column="name"/>
<!--
需要在同步主键的一方加上 constrained="true" 使用给主键加上外键约束
-->
<one-to-one name="company" class="Company" constrained="true" />
</class>
Company.hbm.xml
<!--
dynamic-insert 和 dynamic-update 为 true 时,sql语句中只有值变化或者不为空的属性才会加上,用于更新部分属性
-->
<class name="Company" table="t_company_sync" dynamic-insert="true" dynamic-update="true">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name" column="name"/>
<!--
在另一个表需要修改主键生成策略为 外键
-->
<one-to-one name="address" class="Address" />
</class>
一对多
实体类
Customer.java
public class Customer {
private Integer id;
private String name;
private Set<Order> orders = new HashSet<>();
// 省略 get set
}
Order.java
public class Order {
private Integer id;
private String name;
private Customer customer;
// 省略 get set
}
映射文件
Customer.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!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.ittianyu.hibernate.onetomany">
<!--
dynamic-insert 和 dynamic-update 为 true 时,sql语句中只有值变化或者不为空的属性才会加上,用于更新部分属性
-->
<class name="Customer" table="t_customer" dynamic-insert="true" dynamic-update="true">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name" column="name"/>
<!--
inverse 为 true 表示放弃维护关系,留给对方来维护,
一般是一对多中 一的一方放弃,由多的一放维护,
这个时候删除对象时,需要手动将关联的对象外键引用移除
-->
<set name="orders" inverse="true">
<key column="cid"></key>
<one-to-many class="Order" />
</set>
</class>
</hibernate-mapping>
Order.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!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.ittianyu.hibernate.onetomany">
<!--
dynamic-insert 和 dynamic-update 为 true 时,sql语句中只有值变化或者不为空的属性才会加上,用于更新部分属性
-->
<class name="Order" table="t_order" dynamic-insert="true" dynamic-update="true">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name" column="name"/>
<many-to-one name="customer" column="cid" class="Customer" />
</class>
</hibernate-mapping>
多对多
实体类
Course.java
public class Course {
private Integer id;
private String name;
private Set<Student> students = new HashSet<>();
// 省略 get set
}
Student.java
public class Student {
private Integer id;
private String name;
private Set<Course> courses = new HashSet<>();
// 省略 get set
}
映射文件
Course.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!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.ittianyu.hibernate.manytomany">
<!--
dynamic-insert 和 dynamic-update 为 true 时,sql语句中只有值变化或者不为空的属性才会加上,用于更新部分属性
-->
<class name="Course" table="t_course" dynamic-insert="true" dynamic-update="true">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name" column="name"/>
<!--
many to many 中,需要给 set 加上 table 名
放弃维护外键
-->
<set name="students" table="t_student_course" inverse="true">
<key column="cid"></key>
<many-to-many class="Student" column="sid" />
</set>
</class>
</hibernate-mapping>
Student.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!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.ittianyu.hibernate.manytomany">
<!--
dynamic-insert 和 dynamic-update 为 true 时,sql语句中只有值变化或者不为空的属性才会加上,用于更新部分属性
-->
<class name="Student" table="t_studennt" dynamic-insert="true" dynamic-update="true">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name" column="name"/>
<!--
many to many 中,需要给 set 加上 table 名
外键表由 student 维护,并启用级联,所以 course 中要放弃维护
-->
<set name="courses" table="t_student_course" cascade="save-update">
<key column="sid"></key>
<many-to-many class="Course" column="cid" />
</set>
</class>
</hibernate-mapping>
级联
cascade 表示指定级联操作的类型。
- save-update : 增加或更新 A 时,自动增加或更新 B。
- delete : 删除 A 时,自动删除 B
- all : 上面两项效果叠加
- delete-orphan (孤儿删除) : 删除所有和当前对象解除关联关系的对象
- all-delete-orphan : all + delete-orphan 效果叠加
<set name="courses" table="t_student_course" cascade="save-update">
<key column="sid"></key>
<many-to-many class="Course" column="cid" />
</set>
<a id="6"> </a>
6.抓取策略
检索方式
- 立即检索:立即查询,在执行查询语句时,立即查询所有的数据。
- 延迟检索:延迟查询,在执行查询语句之后,在需要时在查询。(懒加载)
检索策略
- 类级别检索:当前的类的属性获取是否需要延迟。
- 关联级别的检索:当前类 关联 另一个类是否需要延迟。
类级别检索
- get:立即检索。get方法一执行,立即查询所有字段的数据。
- load:延迟检索。默认情况,load方法执行后,如果只使用OID的值不进行查询,如果要使用其他属性值将查询。可以配置是否延迟检索:
<class lazy="true | false"> lazy 默认值true,表示延迟检索,如果设置false表示立即检索。
关联级别检索
容器<set> 提供两个属性:fetch、lazy,用于控制关联检索。
- fetch:确定使用sql格式
- join:底层使用迫切左外连接
- select:使用多个select语句(默认值)
- subselect:使用子查询
- lazy:关联对象是否延迟。
- false:立即
- true:延迟(默认值)
- extra:极其懒惰,调用 size 时,sql 查询 count。(用于只需要获取个数的时候)
批量查询
一次加载多行数据,用于减少 sql 语句数量
<set batch-size="5">
比如: 当客户关联查询订单时,默认给每一个客户生产一个select语句查询订单。开启批量查询后,使用in语句减少查询订单语句个数。
默认:select * from t_order where customer_id = ?
批量:select * from t_order where customer_id in (?,?,?,?)
检索总结
检索策略| 优点| 缺点| 优先考虑使用的场合
----| ----| ----
立即检索| 对应用程序完全透明,不管对象处于持久化状态还是游离状态,应用程序都可以从一个对象导航到关联的对象| (1)select语句多
(2)可能会加载应用程序不需要访问的对象,浪费许多内存空间。| (1)类级别
(2)应用程序需要立即访问的对象
(3)使用了二级缓存
延迟检索| 由应用程序决定需要加载哪些对象,可以避免执行多余的select语句,以及避免加载应用程序不需要访问的对象。因此能提高检索性能,并节省内存空间。| 应用程序如果希望访问游离状态的代理类实例,必须保证她在持久化状态时已经被初始化。| (1)一对多或者多对多关联
(2)应用程序不需要立即访问或者根本不会访问的对象
表连接检索| (1)对应用程序完全透明,不管对象处于持久化状态还是游离状态,都可从一个对象导航到另一个对象。
(2)使用了外连接,select语句少| (1)可能会加载应用程序不需要访问的对象,浪费内存。
(2)复杂的数据库表连接也会影响检索性能。| (1)多对一或一对一关联
(2)需要立即访问的对象
(3)数据库有良好的表连接性能。
<a id="7"> </a>
7.HQL
查询所有
//1 使用简单类名 , 存在自动导包
// * Customer.hbm.xml <hibernate-mapping auto-import="true">
// Query query = session.createQuery("from Customer");
//2 使用全限定类名
Query query = session.createQuery("from com.ittianyu.bean.Customer");
// 获取结果
List<Customer> allCustomer = query.list();
条件查询
//1 指定数据,cid OID名称
// Query query = session.createQuery("from Customer where cid = 1");
//2 如果使用id,也可以(了解)
// Query query = session.createQuery("from Customer where id = 1");
//3 对象别名 ,格式: 类 [as] 别名
// Query query = session.createQuery("from Customer as c where c.cid = 1");
//4 查询所有项,mysql--> select * from...
Query query = session.createQuery("select c from Customer as c where c.cid = 1");
Customer customer = (Customer) query.uniqueResult();
投影查询
//1 默认
//如果单列 ,select c.cname from,需要List<Object>
//如果多列,select c.cid,c.cname from ,需要List<Object[]> ,list存放每行,Object[]多列
// Query query = session.createQuery("select c.cid,c.cname from Customer c");
//2 将查询部分数据,设置Customer对象中
// * 格式:new Customer(c.cid,c.cname)
// * 注意:Customer必须提供相应的构造方法。
// * 如果投影使用oid,结果脱管态对象。
Query query = session.createQuery("select new Customer(c.cid,c.cname) from Customer c");
List<Customer> allCustomer = query.list();
排序
Query query = session.createQuery("from Customer order by cid desc");
List<Customer> allCustomer = query.list();
分页
Query query = session.createQuery("from Customer");
// * 开始索引 , startIndex 算法: startIndex = (pageNum - 1) * pageSize;
// *** pageNum 当前页(之前的 pageCode)
query.setFirstResult(0);
// * 每页显示个数 , pageSize
query.setMaxResults(2);
List<Customer> allCustomer = query.list();
绑定参数
Integer cid = 1;
//方式1 索引 从 0 开始
// Query query = session.createQuery("from Customer where cid = ?");
// query.setInteger(0, cid);
//方式2 别名引用 (:别名)
Query query = session.createQuery("from Customer where cid = :xxx");
// query.setInteger("xxx", cid);
query.setParameter("xxx", cid);
Customer customer = (Customer) query.uniqueResult();
聚合函数和分组
//1
// Query query = session.createQuery("select count(*) from Customer");
//2 别名
// Query query = session.createQuery("select count(c) from Customer c");
//3 oid
Query query = session.createQuery("select count(cid) from Customer");
Long numLong = (Long) query.uniqueResult();
连接查询
//左外连接
// List list = session.createQuery("from Customer c left outer join c.orderSet ").list();
//迫切左外链接 (默认数据重复)
// List list = session.createQuery("from Customer c left outer join fetch c.orderSet ").list();
//迫切左外链接 (去重复)
List list = session.createQuery("select distinct c from Customer c left outer join fetch c.orderSet ").list();
命名查询
Custom.hbm.xml
...
<!--局部 命名查询-->
<query name="findAll"><![CDATA[from Customer ]]></query>
</class>
<!--全局 命名查询-->
<query name="findAll"><![CDATA[from Customer ]]></query>
测试
//全局
//List list = session.getNamedQuery("findAll").list();
//局部
List list = session.getNamedQuery("com.ittianyu.a_init.Customer.findAll").list();
<a id="8"> </a>
8.QBC
查询所有
List<Customer> list = session.createCriteria(Customer.class).list();
分页查询
Criteria criteria = session.createCriteria(Order.class);
criteria.setFirstResult(10);
criteria.setMaxResults(10);
List<Order> list = criteria.list();
排序
Criteria criteria = session.createCriteria(Customer.class);
// criteria.addOrder(org.hibernate.criterion.Order.asc("age"));
criteria.addOrder(org.hibernate.criterion.Order.desc("age"));
List<Customer> list = criteria.list();
条件查询
// 按名称查询:
/*Criteria criteria = session.createCriteria(Customer.class);
criteria.add(Restrictions.eq("cname", "tom"));
List<Customer> list = criteria.list();*/
// 模糊查询;
/*Criteria criteria = session.createCriteria(Customer.class);
criteria.add(Restrictions.like("cname", "t%"));
List<Customer> list = criteria.list();*/
// 条件并列查询
Criteria criteria = session.createCriteria(Customer.class);
criteria.add(Restrictions.like("cname", "t%"));
criteria.add(Restrictions.ge("age", 35));
List<Customer> list = criteria.list();
离线查询
// service 层 封装与 session 无关的 criteria
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Customer.class);
detachedCriteria.add(Restrictions.eq("id", 4));
// dao 层
Session session = HibernateUtils.openSession();
Criteria criteria = detachedCriteria.getExecutableCriteria(session);
List list = criteria.list();
<a id="9"> </a>
9.其他配置
c3p0(spring 整合后直接配 dataSource)
- 导入 c3p0 包
- hibernate.cfg.xml 配置
<property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
log4j
- 导入包
- log4j 核心包:log4j-1.2.17.jar
- 过渡jar:slf4j-log4j12-1.7.5.jar
- 导入配置文件
- log4j.properties ,此配置文件通知log4j 如何输出日志
<a id="10"> </a>
10.事务
隔离级别
- read uncommittd,读未提交。存在3个问题。
- read committed,读已提交。解决:脏读。存在2个问题。
- repeatable read ,可重复读。解决:脏读、不可重复读。存在1个问题。
- serializable,串行化。单事务。没有问题。
hibernate 中配置
<property name="hibernate.connection.isolation">4</property>
对照上面的分别是 1 2 4 8,0表示没有事务级别
锁
悲观锁
采用数据库锁机制。丢失更新肯定会发生。
- 读锁:共享锁。
select .... from ... lock in share mode;
- 写锁:排他锁。(独占)
select ... from .... for update
Hibernate 中使用
Customer customer = (Customer) session.get(Customer.class, 1 ,LockMode.UPGRADE);
乐观锁
在表中提供一个字段(版本字段),用于标识记录。如果版本不一致,不允许操作。丢失更新肯定不会发生
Hibernate 中使用
- 在PO对象(javabean)提供字段,表示版本字段。
... private Integer version; ...
- 在配置文件中增加 version
<class ...> ... <version name="version" /> ...
<a id="11"> </a>
11.二级缓存
sessionFactory 级别缓存,整个应用程序共享一个会话工厂,共享一个二级缓存。
由4部分构成:
- 类级别缓存
- 集合级别缓存
- 时间戳缓存
- 查询缓存(二级缓存的第2大部分,三级缓存)
并发访问策略
||
---|---
transactional| 可以防止脏读和不可重复读,性能低
read-write| 可以防止脏读,更新缓存时锁定缓存数据
nonstrict-read-write| 不保证缓存和数据库一致,为缓存设置短暂的过期时间,减少脏读
read-only| 适用于不会被修改的数据,并发性能高
应用场景
- 适合放入二级缓存中的数据:
很少被修改
不是很重要的数据, 允许出现偶尔的并发问题 - 不适合放入二级缓存中的数据:
经常被修改
财务数据, 绝对不允许出现并发问题
与其他应用数据共享的数据
二级缓存提供商
- EHCache: 可作为进程(单机)范围内的缓存, 存放数据的物理介质可以是内存或硬盘, 对 Hibernate 的查询缓存提供了支持。--支持集群。
- OpenSymphony `:可作为进程范围内的缓存, 存放数据的物理介质可以是内存或硬盘, 提供了丰富的缓存数据过期策略, 对 Hibernate 的查询缓存提供了支持
- SwarmCache: 可作为集群范围内的缓存, 但不支持 Hibernate 的查询缓存
- JBossCache:可作为集群范围内的缓存, 支持 Hibernate 的查询缓存
开启二级缓存
- 导包 hibernate-ehcache-5.2.8.Final.jar
- 配置
<!--二级缓存 #hibernate.cache.region.factory_class org.hibernate.cache.internal.EhCacheRegionFactory --> <property name="hibernate.cache.use_second_level_cache">true</property> <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
使用二级缓存
类缓存
<!--
类缓存
-->
<class-cache class="com.ittianyu.hibernate.onetomany.Order" usage="read-only"/>
<class-cache class="com.ittianyu.hibernate.onetomany.Customer" usage="read-only"/>
集合缓存
<collection-cache collection="com.ittianyu.hibernate.onetomany.Customer.orders" usage="read-only" />
查询缓存
将HQL语句 与 查询结果进行绑定。通过HQL相同语句可以缓存内容。
-
配置
#hibernate.cache.use_query_cache true 启用 HQL查询缓存 <property name="hibernate.cache.use_query_cache">true</property>
-
使用
Query query = session.createQuery("from Customer"); query.setCacheable(true);// 标记为缓存