1. 概述
1.1 Hibernate介绍
Hibernate框架应用在三层架构中的dao层,对JDBC进行封装,完成CRUD。Hibernate是开源的轻量级框架。
1.2 ORM 思想
ORM:object relational mapping 对象关系映射。
- 让实体类和数据库表进行一一对应关系
让实体类首先和数据库表对应
让实体类属性 和 表里面字段对应 - 不需要直接操作数据库表,而操作表对应实体类对象。
1.3 搭建Hibernate环境
-
导入jar包
-
创建实体类:使用Hibernate的时候不需要自己手动创建表,hibernate会自动创建。
注意:- 要求必须有唯一的属性;
- 属性类型不建议为基本数据类型,使用对应的包装类。
-
创建映射配置文件:
-
创建核心配置文件
- 配置数据库信息
- 配置hibernate信息:非必须的
- 加载映射文件
- 注意:
-
名称和位置必须固定:必须在resource下,名称必须为hibernate.cfg.xml;
-
配置文件中:
true
true<!--只有配置了之后,Hibernate会自动创建表--> <property name="hibernate.hbm2ddl.auto">update</property> <!--配置数据库方言:让Hibernate框架自动识别不同数据库的特有的语言--> <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
-
-
编写测试类。
第一步 加载hibernate核心配置文件:到resources下面找到名称hibernate.cfg.xml配置文件,创建对象,把配置文件放到对象里面(加载核心配置文件)
第二步 创建SessionFactory对象:根据核心配置文件中,有数据库配置,有映射文件部分,到数据库里面根据映射关系把表创建。不过该过程特别耗资源,一般一个项目只加载一个SessionFactory对象。
第三步 使用SessionFactory创建session对象:session类似于jfbc中的connection,可以使用session实现crud操作。
注意:session对象是单线程对象,不能被共用,只能自己使用。
第四步 开启事务
第五步 写具体逻辑 crud操作
第六步 提交事务
第七步 关闭资源
public void testAdd()
{
//加载核心配置文件
Configuration configuration = new Configuration();
configuration.configure();//创建SessionFactory对象 SessionFactory factory = configuration.buildSessionFactory(); //使用SessionFactory创建session对象 Session session = factory.openSession(); //开启事务 Transaction tx = session.beginTransaction(); User user = new User(); user.setName("李淼"); user.setPassword("123"); user.setAddress("临汾"); session.save(user); //提交事务 tx.commit(); //关闭资源 session.close(); factory.close(); }
1.4 主键生成策略
<id name="id" column="id">
<generator class="native"></generator>
</id>
class属性中有很多值,例如native,increment,identity,sequence,uuid等,使用最多的是native和uuid。
(1)native:根据使用的数据库选择哪个值。适合跨数据库开发。
(2)uuid:
UUID的使用:
1. 实体类id属性类型 必须 字符串类型;
2.
1.5 实体类的状态
- 瞬时态
瞬时态的实例是由new命令创建,开辟内存空间的对象,不存在持久化标识符OID(相当于主键),尚未与Hibernate Session关联。 - 持久态
持久态的对象存在持久化标识符OID,加入到了session的缓存中,并且相关联的session没有关闭,在数据库中有对应的记录,每条记录只对应唯一的持久化对象。持久态对象是在事务还未提交之前变成的。 - 托管态
也称离线态或脱离态,当某个持久态的实例与session的连接被关闭后,就成为了托管态。托管态对象存在持久化标识符OID,并且仍然与数据库的数据相关联,只是失去了和session的关联。
2. 缓存
2.1 一级缓存
2.1.1 特点
- 一级缓存默认打开
- 使用范围:session。session从打开到关闭
- 存储数据必须为持久态数据。
2.1.2 特性
持久态自动更新数据库。
Session session = factory.openSession();
//开启事务
Transaction tx = session.beginTransaction();
User user2 = session.get(User.class,2);
user2.setName("马窦桂梅");
//提交事务
tx.commit();
//关闭资源
session.close();
factory.close();
以上代码中并没有session.update(),但是仍然会修改数据库中的数据。
2.2二级缓存
2.1.2 特点
- 目前已经不在使用,使用redis代替
- 默认不打开 ,需要进行配置
- 使用范围:sessionFactory
3. 事务操作
3.1 代码规范
try {
开启事务
提交事务
}catch() {
回滚事务
}finally {
关闭
}
4. Hibernate三个对象
4.1 Query
使用query对象,不需要写sql语句,但是写hql语句
(1)hql:hibernate query language,hibernate提供查询语言,这个hql语句和普通sql语句很相似
(2)hql和sql语句区别:
-
使用sql操作表和表字段
-
使用hql操作实体类和属性
2 查询所有hql语句:
(1)from 实体类名称
Query query = session.createQuery("from User");
List<User> users = query.list();
for(User user: users)
{
System.out.println(user);
}
4.2 Criteria对象
使用这个对象查询操作,但是使用这个对象时候,不需要写语句,直接调用方法实现。
Criteria criteria = session.createCriteria(User.class);
List<User> users = criteria.list();
for(User user: users)
{
System.out.println(user);
}
4.3 SQLQuery对象
使用hibernate时候,调用底层sql实现.
SQLQuery sqlQuery = session.createSQLQuery("select * from t_user");
sqlQuery.addEntity(User.class);
List<User> users = sqlQuery.list();
for(User user: users)
{
System.out.println(user);
}
5. Hibernate的一对多操作
5.1 创建两个实体类
一个客户有多个联系人。在客户实体类中添加private Set linkManSet = new HashSet<>();
一个联系人对应一个客户:在联系人实体类中添加private Customer customer;
5.2 创建映射文件
创建客户实体类对应的映射文件,与之前的不同在于:
<set name="linkManSet">
<key column="clid"></key>
<one-to-many class="com.nuc.domain.LinkMan"></one-to-many>
</set>
创建联系人实体类对应的映射文件,与之前的不同在于:
<many-to-one name="customer" class="com.nuc.domain.Customer" column="clid"></many-to-one>
5.3 创建核心配置文件
引入以上两个映射配置文件。
5.4 测试
分别创建两个对象,然后添加到表中。
session = factory.openSession();
tx = session.beginTransaction();
Customer customer = new Customer();
customer.setCustLevel("vip");
customer.setCustName("腾讯");
customer.setCustMobile("123456");
customer.setCustPhone("1384949");
customer.setCustSource("山西");
LinkMan linkMan = new LinkMan();
linkMan.setLkm_name("小王");
linkMan.setLkm_phone("1381446");
linkMan.setLkm_gender("男");
customer.getLinkManSet().add(linkMan);
linkMan.setCustomer(customer);
session.save(customer);
session.save(linkMan);
tx.commit();
5.5 级联操作
首先进行如下配置:
<set name="linkManSet" cascade="save-update,delete"/>
级联添加:
Customer customer = new Customer();
customer.setCustLevel("普通");
customer.setCustName("nba");
customer.setCustMobile("4568");
customer.setCustPhone("1384949");
customer.setCustSource("美国");
LinkMan linkMan = new LinkMan();
linkMan.setLkm_name("小科");
linkMan.setLkm_phone("1381446");
linkMan.setLkm_gender("男");
customer.getLinkManSet().add(linkMan);
session.save(customer);
级联删除:
session = factory.openSession();
tx = session.beginTransaction();
Customer customer = session.get(Customer.class,2);
session.delete(customer);
tx.commit();
级联修改:
Customer customer = session.get(Customer.class,2);
LinkMan linkMan = session.get(LinkMan.class,1);
customer.getLinkManSet().add(linkMan);
linkMan.setCustomer(customer);
tx.commit();
因为hibernate双向维护外键,在客户和联系人里面都需要维护外键,修改客户时候修改一次外键,修改联系人时候也修改一次外键,造成效率问题。
解决方式:让其中的一方不维护外键
<set name="linkManSet" inverse="true">
6. Hibernate的多对多操作
6.1 多对多映射配置
- 编写user和role两个实体类,在各自的属性中使用set引用对方,添加get,set方法。
- 配置映射文件:主要是set的配置。
User对应的映射文件:
role对应的映射文件:
- 在核心配置文件中引入以上两个配置文件。
- 编写测试类:实现级联添加,删除等。
6.2 维护第三张表的关系
给某一个用户添加角色,只需要在该用户的set集合中添加一个role即可。
User user = session.get(User.class,3);
Role role = session.get(Role.class,3);
user.getRoleSet().add(role);
给某一个用户删除角色,只需要在该用户的set集合中删除role即可。
User user = session.get(User.class,3);
Role role = session.get(Role.class,3);
user.getRoleSet().remove(role);
7. Hibernate查询方式
7.1 对象导航方式
根据id查询某个客户,再查询这个客户里面所有的联系人
Customer customer = session.get(Customer.class,2);
Set<LinkMan> linkMEN = customer.getLinkManSet();
for (LinkMan l:linkMEN
) {
System.out.println(l);
}
7.2 OID查询
调用session里面的get方法实现.
Customer customer = session.get(Customer.class,2);
7.3 HQL查询
- hql:hibernate query language,hibernate提供一种查询语言,hql语言和普通sql很相似,区别:普通sql操作数据库表和字段,hql操作实体类和属性
- 常用的hql语句
(1)查询所有: from 实体类名称
(2)条件查询: from 实体类名称 where 属性名称=?
(3)排序查询: from 实体类名称 order by 实体类属性名称 asc/desc
- 使用hql查询操作时候,使用Query对象
(1)创建Query对象,写hql语句
(2)调用query对象里面的方法得到结果
7.4 QBC查询
1 使用hql查询需要写hql语句实现,但是使用qbc时候,不需要写语句了,使用方法实现
2 使用qbc时候,操作实体类和属性
3 使用qbc,使用Criteria对象实现
7.6 本地sql查询
8. 多表查询
-
内连接
Query query = session.createQuery(“from Customer c inner join c.linkManSet”);
List customers = query.list();
System.out.println(customers.size()); -
迫切内连接:
Query query = session.createQuery(“from Customer c inner join fetch c.linkManSet”);List<Customer> customers = query.list(); for (Customer c:customers ) { System.out.println(c); }
二者的区别在于:内连接返回的是数组,迫切内连接返回的是对象。
左连接,左外连接,右连接,右外连接与之类似。
9. 检索策略
9.1 立即查询
根据id查询,调用get方法,一调用get方法马上发送语句查询数据库
9.2 延迟查询
根据id查询,还有load方法,调用load方法不会马上发送语句查询数据,只有得到对象里面的值时候才会发送语句查询数据库。延迟查询分为两类:
- 类级别延迟:根据id查询返回实体类对象,调用load方法不会马上发送语句
- 关联级别延迟:查询某个客户,再查询这个客户的所有联系人,查询客户的所有联系人的过程是否需要延迟,这个过程称为关联级别延迟。实现方法如下:
在set标签上使用属性
(1)fetch:值select(默认)
(2)lazy:值- true:延迟(默认)
- false:不延迟
- extra:极其延迟
9.3 批量抓取
查询所有的客户,返回list集合,遍历list集合,得到每个客户,得到每个客户的所有联系人。之前的方式会发送多次sql语句。
解决:在客户的映射文件中,set标签配置:batch-size值,值越大发送语句越少