一、多表设计
①一对多
public class Customer {
private Integer cid;
private String cname;
//一对多:一个客户(当前客户) 拥有 【多个订单】
// * 需要容器存放多个值,一般建议Set (不重复、无序)
// * 参考集合:List、Map、Array等
// ** 建议实例化--使用方便
private Set<Order> orderSet = new HashSet<Order>();
配置文件
<class name="com.itheima.b_onetomany.Customer" table="t_customer">
<id name="cid">
<generator class="native"></generator>
</id>
<property name="cname"></property>
<!-- 一对多:一个客户(当前客户) 拥有 【多个订单】
1 确定容器 set <set>
2 name确定对象属性名
3 确定从表外键的名称
4 确定关系,及另一个对象的类型
注意:
在hibernate中可以只进行单向配置
每一个配置项都可以完整的描述彼此关系。
一般情况采用双向配置,双方都可以完成描述表与表之间关系。
-->
<!-- 一对多:一个客户(当前客户) 拥有 【多个订单】 -->
<set name="orderSet" cascade="delete-orphan">
<key column="customer_id"></key>
<one-to-many class="com.itheima.b_onetomany.Order"/>
</set>
</class>
②多对一
public class Order {
private Integer xid;
private String price;
//多对一:多个订单属于【一个客户】
private Customer customer;
配置文件
<class name="com.itheima.b_onetomany.Order" table="t_order">
<id name="xid">
<generator class="native"></generator>
</id>
<property name="price"></property>
<!-- 多对一:多个订单属于【一个客户】
* name 确定属性名称
* class 确定自定义类型
* column 确定从表的外键名称
-->
<many-to-one name="customer" class="com.itheima.b_onetomany.Customer" column="customer_id"></many-to-one>
</class>
③多对多
Student:
public class Student {
private Integer sid;
private String sname;
// 学生选择多门课程.
private Set<Course> courses = new HashSet<Course>();
...
}
Course:
public class Course {
private Integer cid;
private String cname;
// 课程可以被多个学生选择:
private Set<Student> students = new HashSet<Student>();
...
}
配置文件
Student.hbm.xml
<hibernate-mapping>
<class name="cn.itcast.demo3.Student" table="student">
<id name="sid" column="sid">
<generator class="native"/>
</id>
<property name="sname" column="sname"/>
<!-- 配置多对多关联关系 -->
<set name="courses" table="stu_cour">
<key column="sno"/>
<many-to-many class="cn.itcast. demo3.Course" column="cno"/>
</set>
</class>
</hibernate-mapping>
Course.hbm.xml
<hibernate-mapping>
<class name="cn.itcast. demo3.Course" table="course">
<id name="cid" column="cid">
<generator class="native"/>
</id>
<property name="cname" column="cname"/>
<!-- 配置多对多关联关系映射 -->
<set name="students" table="stu_cour">
<key column="cno"/>
<many-to-many class="cn.itcast. demo3.Student" column="sno"/>
</set>
</class>
</hibernate-mapping>
二、级联操作
save-update:A保存,同时保存B
delete:删除A,同时删除B,AB都不存在
delete-orphan:孤儿删除,解除关系,同时将B删除,A存在的。
如果需要配置多项,使用逗号分隔。
all : save-update 和 delete 整合
all-delete-orphan : 三个整合
三、抓取策略(优化)
1.检索方式:
1.1 立即检索(get):立即查询,在执行查询语句时,立即查询所有的数据
1.2 延迟查询(load):延迟查询,在执行查询语句之后,在需要时在查询(懒加载)
<class lazy="true | false">
lazy 默认值true,表示延迟检索,如果设置false表示立即检索
2.检索策略
2.1 类级别检索:当前的类的属性获取是否需要延迟。
2.2 关联级别的检索:当前类 关联 另一个类是否需要延迟。
3.关联级别检索
①一对多或多对多:
3.1 容器 提供两个属性:fetch、lazy
fetch:确定使用sql格式
lazy:关联对象是否延迟。
3.2 fetch:join、select、subselect
join:底层使用迫切左外连接
fetch="join" ,lazy无效。底层使用迫切左外连接,使用一条select将所有内容全部查询。
select:使用多个select语句(默认值)
当前对象 和 关联对象 使用多条select语句查询。
lazy="false" , 立即,先查询客户select,立即查询订单select
lazy="true",延迟,先查询客户select,需要订单时,再查询订单select
lazy="extra",极其懒惰(延迟),先查询客户select, 如果只需要订单数,使用聚合函数(不查询详情)
subselect:使用子查询
将使用子查询。注意:必须使用Query否则看不到效果。
lazy= 同上
3.3 lazy:false、true、extra
false:立即
true:延迟(默认值)
extra:极其懒惰
②多对一:
3.5介绍:
<many-to-one fetch="" lazy=""> (<one-to-one>)
fetch取值:join、select
join:底层使用迫切左外连接
select:多条select语句
lazy取值:false、proxy、no-proxy
false:立即
proxy:采用关联对象 类级别检索的策略。
订单 关联 客户 (多对一)
订单 立即获得 客户,需要在客户Customer.hbm.xml
订单 延迟获得 客户,需要在客户Customer.hbm.xml
四、HQL语句
HQL语句:描述对象操作的一种查询语句,Hibernate特有。(关键字不区分大小写,但类名和属性名区分大小写)
public void demo01(){
/*查询所有*/
//1 查询所有
Session session = factory.openSession();
session.beginTransaction();
//1 使用简单类名 , 存在自动导包
// * Customer.hbm.xml <hibernate-mapping auto-import="true">
Query query = session.createQuery("from Customer");
//2 使用全限定类名
Query query = session.createQuery("from com.itheima.a_init.Customer");
/*选择查询*/
//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");
/*投影查询*/
//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");
/*排序*/
Query query = session.createQuery("from Customer order by cid desc");
/*分页*/
Query query = session.createQuery("from Customer");
// * 开始索引 , startIndex 算法: startIndex = (pageNum - 1) * pageSize;
// *** pageNum 当前页(之前的 pageCode)
query.setFirstResult(0);
// * 每页显示个数 , pageSize
query.setMaxResults(2);
/*绑定参数*/
Integer cid = 1;
//方式1
// 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);
/*聚合函数和分组*/
//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();
/*连接查询*/
/*
1.交叉连接 ,等效 sql 笛卡尔积
2.隐式内连接,等效 sql 隐式内连接
3.内连接,等效sql内连接
4.迫切内连接,hibernate底层使用 内连接。
5.左外连接,等效sql左外连接
6.迫切左外连接,hibernate底层使用 左外连接
7.右外连接,等效sql右外连接
*/
//左外连接
// 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();
/*命名查询*/
//全局
//List list = session.getNamedQuery("findAll").list();
//局部
List list = session.getNamedQuery("com.itheima.a_init.Customer.findAll").list();
session.getTransaction().commit();
session.close();
}
五、常见配置
5.1事务
事务:一组业务操作,要么全部成功,要么全部不成功。
特性:ACID
原子性:整体
一致性:数据
隔离性:并发
持久性:结果
隔离问题:
脏读:一个事务读到另一个事务未提交的内容
不可重复读:一个事务读到另一个事务已提交的内容(insert)
虚读(幻读):一个事务读到另一个事务已提交的内容(update)
隔离级别--解决问题
read uncommittd,读未提交。存在3个问题。
read committed,读已提交。解决:脏读。存在2个问题。
repeatable read ,可重复读。解决:脏读、不可重复读。存在1个问题。
serializable,串行化。单事务。没有问题
5.2Hibernate设置隔离区级别
<property name="hibernate.connection.isolation">4</property>
5.3丢失更新
悲观锁:丢失更新肯定会发生。
采用数据库锁机制。
读锁:共享锁。
select .... from ... lock in share mode;
写锁:排他锁。(独占)
select ... from .... for update
乐观锁:丢失更新肯定不会发生
在表中提供一个字段(版本字段),用于标识记录。如果版本不一致,不允许操作。
六、二级缓存
①定义:
hibernate 提供缓存机制:一级缓存、二级缓存
一级缓存:session级别缓存,在一次请求中共享数据。
二级缓存:sessionFactory级别缓存,整个应用程序共享一个会话工厂,共享一个二级缓存。
SessionFactory的缓存两部分:
1.内置缓存:使用一个Map,用于存放配置信息,预定义HQL语句等,提供给Hibernate框架自己使用,对外只读的。不能操作。
2.外置缓存:使用另一个Map,用于存放用户自定义数据。默认不开启。外置缓存hibernate只提供规范(接口),需要第三方实现类。外置缓存有成为二级缓存。
②构成:
类级别缓存
集合级别缓存
时间戳缓存
查询缓存(二级缓存的第2大部分,三级缓存)
③应用场景:
适合放入二级缓存中的数据:
很少被修改
不是很重要的数据, 允许出现偶尔的并发问题
不适合放入二级缓存中的数据:
经常被修改
财务数据, 绝对不允许出现并发问题
与其他应用数据共享的数据
④配置操作
1.导入jar包:ehcache-1.5.0.jar/ commons-logging.jar/ backport-util-concurrent.jar
2.开启二级缓存(我要使用二级缓存)
<!-- 9.1 开启二级缓存 -->
<property name="hibernate.cache.use_second_level_cache">true</property>
3.确定二级缓存提供商(我要使用哪个二级缓存)
<!-- 9.2 提供商 -->
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
4.确定需要缓存内容
1>配置需要缓存的类
2>配置需要缓存的集合
<!-- 9.3 确定缓存内容 -->
<!-- 类缓存 -->
<class-cache usage="read-write" class="com.itheima.a_init.Customer"/>
<class-cache usage="read-write" class="com.itheima.a_init.Order"/>
<!-- 集合缓存 -->
<collection-cache usage="read-write" collection="com.itheima.a_init.Customer.orderSet"/>
5.配置ehcache自定义配置文件
类缓存:
public void demo02(){
//2 类缓存:只存放数据,散装数据。
// * 使用默认的toString();
Session s1 = factory.openSession();
s1.beginTransaction();
//1 查询id=1 -- 执行select
Customer c1 = (Customer) s1.get(Customer.class, 1);
System.out.println(c1);
//2 查询id=1 -- 从一级缓存获取,一级缓存存放对象本身
Customer c2 = (Customer) s1.get(Customer.class, 1);
System.out.println(c2);
s1.getTransaction().commit();
s1.close();
System.out.println("----------");
Session s2 = factory.openSession();
s2.beginTransaction();
//3 查询id=1 -- 对象不一样,数据一样
Customer c3 = (Customer) s2.get(Customer.class, 1);
System.out.println(c3);
s2.getTransaction().commit();
s2.close();
}
集合缓存:
public void demo03(){
//3 集合缓存:只存放关联对象OID的值,如果需要数据,从类缓存中获取。
// * 3.1 默认:第一条select 查询客户,第二条 select 查询客户所有订单
// * 3.2 操作:在hibernate.cfg.xml 将 Order 类缓存删除
// *** <!-- <class-cache usage="read-write" class="com.itheima.a_init.Order"/>-->
// *** 多了10条select,通过订单的id查询订单
Session s1 = factory.openSession();
s1.beginTransaction();
//1 查询id=1
Customer c1 = (Customer) s1.get(Customer.class, 1);
System.out.println(c1);
//2 获得订单
for (Order o1 : c1.getOrderSet()) {
System.out.println(o1);
}
s1.getTransaction().commit();
s1.close();
System.out.println("----------");
Session s2 = factory.openSession();
s2.beginTransaction();
//3 查询id=1
Customer c3 = (Customer) s2.get(Customer.class, 1);
System.out.println(c3);
//4 获得订单
for (Order o2 : c3.getOrderSet()) {
System.out.println(o2);
}
s2.getTransaction().commit();
s2.close();
}
时间戳:任何操作都在时间戳中记录操作时间。
public void demo04(){
//4 时间戳: 所有的操作都会在时间戳中进行记录,如果数据不一致,将触发select语句进行查询
// * 修改toString()
Session s1 = factory.openSession();
s1.beginTransaction();
//1 查询id=1
Integer cid = 1;
Customer c1 = (Customer) s1.get(Customer.class, cid);
System.out.println(c1);
//2 绕过一级和二级缓存,修改数据库,修改客户cname=大东哥
s1.createQuery("update Customer set cname = ? where cid = ?")
.setString(0, "大东哥")
.setInteger(1, cid)
.executeUpdate();
//3打印
System.out.println(c1);
s1.getTransaction().commit();
s1.close();
System.out.println("----------");
Session s2 = factory.openSession();
s2.beginTransaction();
//4 查询id=1 -- ?
Customer c3 = (Customer) s2.get(Customer.class, 1);
System.out.println(c3);
s2.getTransaction().commit();
s2.close();
}