Hibernate零碎知识点

双向的many2one/one2many的问题:
一定有额外的SQL发出;
在数据库里面,外键标识many2one或者one2many的关系,外键是没有方向的,但是在面向对象里面,关系有方向,所以两个方向都要去管理
这个外键,造成额外的SQL(这些多余的SQL都是ONE方想去管理many方造成的);
我们既想保留对象中的双向关系,又想减少额外的SQL;
<set name="emps" inverse="true">
<key column="DEPT_ID"/>
<one-to-many class="Employee"/>
</set>
对象的删除:
1.many方都可以直接删除
2.删除one方:
1.inverse="false":可以正常删除
Hibernate:update Employee set DEPT_ID=null where DEPT_ID=?
Hibernate:delete from Department where id=?
2.inverse="true":不能正常删除
外键约束错误
选择:
1.90%的情况,使用单向的many2one
2.9%的情况使用双向的many2one/one2many;(使用HQL删除)
1.自连接(树状结构)
2.组合关系
3.1%的情况使用单向的one2many(使用两个one2many来实现many2many)
hibernate中的集合:
1.在hibernate中,集合只能使用接口
2.List:集合中的对象可以重复,集合中的对象是有顺序的
1.要让one方知道many方的顺序,必须在many方的表中添加一列用来保存顺序
2.这个顺序只有one方知道,所以这个列应该由one方来维护
3.在<List>元素中添加<list-index>来影射这个咧
4.不管list的inverse=true,总要发送额外的SQL去维护这个many方的顺序
<list name="emps" inverse="true">
<key column="DEPT_ID"/>
<list-index column="SEQ"/>
<one-to-many class="Employee"/>
</list>
5.第二种映射list的方式:
1.如果只是想把list作为一个集合,而不让hibernate来帮我们维护顺序,就可以用<bag>
2.bag元素不关心one中many方的顺序
<bag name="emps" inverse="true">
<key column="DEPT_ID"/>
<one-to-many class="Employee"/>
</bag>
3.选择:
1.确定集合中的对象是否能重复
2.如果使用list,尽量使用bag来影射
3.如果必须要让many方有顺序,在many方中添加一个表示顺序的属性
1.在many方添加一个顺序的属性
public class Employee{
private Long id;
private String name;
private Integer sequence;
private Department dept;
}
<class name="Employee">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<property name="sequence"/>
<many-to-one name="dept"/>
</class>
2.在One方的集合上面添加order-by属性
<bag name="emps" inverse="true" order-by="sequence">
<key column="DEPT_ID"/>
<one-to-many class="Employee"/>
</bag>
order-by属性的意思是,在加载mnay方集合的时候,使用order by语句按照many方的指定属性来查询
级联:
组合关系:强聚合关系,代表整体和部分之间不能分开,整体和部分单独存在没有意义。
分析:
1.组合关系中的两个对象都是在一个模块中管理的,都是 在整体那个对象的模块中
2.对于销售订单来说:
1.存在一个列表
2.添加:在添加页面需要添加销售订单对象上的属性,还需要添加这个销售当单的明细数据
3.修改:仍然在销售订单的编辑页面中完成
4.删除:在销售订单列表中删除销售订单
3.对于销售订单明细对象来说:
1.对于销售订单明细对象,不需要列表
2.添加:
1.在销售订单对象的添加的时候,可以添加销售订单明细对象
2.在销售订单对象的保存,可以添加销售订单明细对象
3.修改:在销售订单对象的修改方法中完成的
4.删除:在销售订单对象的修改方法中完成的,在删除销售订单的时候,也要去删除这个销售订单的明细数据
分析执行流程:
1.只需要有销售订单对象的DAO接口就可以了,销售订单明细的CRUD都是在销售订单对象的DAO中完成的
2.save:
1.保存销售订单对象
2.保存销售订单对象对应的销售订单明细对象
3.update:
1.修改销售订单对象(游离)
2.保存新的销售订单明细对象(临时对象)+
3.修改修改过的销售订单明细对象(游离)
4.删除去掉了的销售订单明细对象(删除和主对象没有关系的销售订单明细对象)
4.delete:
1.删除销售订单对象的明细
2.删除主对象
级联策略:
1.save-update:在保存或者修改主对象的时候,去级联的持久化临时的子对象,修改游离的子对象
2.delete;在删除主对象的时候,去级联的删除所有的子对象
3.all:save-update+delete
4.delete-orphan:删除和主对象打破关系的子对象
many2many进一步研究:
1.在映射文件里面,set需要table属性来告知中间表
2.set中的key子元素代表,在中间表里面那个列作为外键关联到我自己
3.many-to-many元素:和集合中的对象是多对多的关系
4.many-to-many的column属性代表:在中间表里面那个列作为外键关联到对方表的主键
5.many-to-many双方的配置其实是相反的
6.get的时候使用延迟加载
 1.必须在session关闭之前初始化
 2.不能使用ifnull来判定集合中是否有对象,只能使用size()来判断
7.一般来说,我们使用单向的mnay2many
8.不管集合是list还是set,都需要让一边放弃对主键的维护inverse=true
9.如果配置的集合是set,hibernate会为中间表创建一个复合主键
10.在项目中,我们应该让哪边来维护关系?需要看具体的设计,页面里面哪个模块在管理关系,我们就把关系配置在哪边




1.需要把所有的表看成一个整体, 
2.关系是在一对属性上面的;如果一个对象上面有多个关系,请一个一个的单独考虑每个关系
3.把数据库-----》模型
 1.外键:many2one
 2,中间表:many2many
4.关注点在结构上;
hibernate中的查询方式:
1.使用HQL,使用hibernate提工的面向对象的查询语句
2.使用SQL:在hibernate中允许使用原生的SQL直接查询
3.使用Criteria:hibernate提供的完全面向对象的查询方式
1.HQL:
HQL的学习方法:HQL是面向对象的,但是HQL借鉴了SQL的语法结构,把SQL中关系模型的概念替换成面向对象的概念
2.SQL:使用session.createQuery来创建基于SQL的查询
查询出来的结构是Object[]的集合
3.Criteria:完全的面向对象的查询,所有的查询及条件的拼装都是通过criteria对象的方法完成的
选择:
1.HQL:面向对象的查询,查询出来的实体都是持久化的,hibernate为HQL做了很多查询相关的优化,一般来说,对于简单的查询,我们都可以使用HQL
2.SQL:对于性能要求较高的查询,我们一般使用SQL来完成查询
3.criteria:完全面向对象的,学习非常简单,对于某些简单的查询可以直接使用criteria
分页:
1.分页需要些什么东西?总条数,每一页需要多少条数据,当前是第几页,当前页的数据
2.查询当前页的数据,对于mysql来说,limit ?,?
3,使用query.setFirstResult()方法来设置从第几条数据开始查询
4.使用query.setMaxResults()方法来说设置查询多少条数据
5.setFirstResult和setMaxResult对于SQL和Criteria的查询都有效
String hql="select e from Employee e where e.id between ? and ? and e.name like ?";
List<Employee>es=session.createQuery(hql).setParameter(0, 1L).setParameter(1, 10L).setParameter(2, "%a%")
.setFirstResult((currentPage-1)*pageSize)//query.setFirstResult==limit的第一个参数,代表从第几个参数开始查询
.setMaxResults(pageSize)//query.setMaxResult==limit的第二个参数,代表最大查询多少条数据
.list();
System.out.println(es);
查询总条数:
1.List<Long> ret=session.createQuery("select count(e) from Employee e").list();
System.out.println(ret.get(0));
但是使用这种方式非常的不方便,因为我们知道我们查询出来的结构就只有一行数据
2.Long count=(Long)session.createQuery("select count(e) from Employee e").uniqueResult();
System.out.println(count);
使用uniqueResult,这个方法可以真正的去执行查询
注意:这个方法仅仅只能用在我们确定结果集只有一行数据的时候,如果查询结果多于一条数据,报错
uniqueResult方法对于SQL和Criteria都有效
查询参数设置:
位置占位符:就是使用?来代表查询参数,通过setParameter(index,object)来根据?的位置来设置参数的;
 1.写HQL的时候很方变
 2.如果参数值不多,还是比较容易识别
 3.如果参数值过多,会造成索引不容易识别,如果调整参数位置,所有的设置参数的位置都要变,如果一个参数在多个条件中使用,必须重复设置
名称占位符:
String hql="select e from Employee e where e.name like:name and e.id between :low and :hi";
List<Employee> ret=session.createQuery(hql).setParameter("name", "%a%").setParameter("low", 1L).setParameter("hi", 10L).list();
System.out.println(ret);
1.使用:参数名称 格式来添加名称占位符
2、使用setParameter(String name,Object)这个方法为名称占位符添加参数
3.可以为多个参数起相同名字的名称占位符,在设置参数的时候只需要设置一次值,就可以在所有的位置设置好参数
4.使用名称占位符可以给参数传列表值进去,很容易的完成In等查询,但是使用位置占位不行
String hql="select e from Employee e where e.id in(:ids)";
List<Employee> ret=session.createQuery(hql).setParameterList("ids", new Long[]{1L,10L,20L,30L}).list();
System.out.println(ret);
二级缓存概念:
1.生命周期为整个应用的缓存(二级缓存是sessionFactory上的缓存,能提供整个应用中所有的session使用)
2.所有的get,load方法,总是先查一级缓存,再查二级缓存,如果都没有再去数据库里面查询
3.不是所有对象都适合放到二级缓存(读>>>写)
4.二级缓存有些性能的指标
 1.命中率(总的从二级缓存中取得的数量/总的取得数量)
 2.最大对象数量
 3.最大空闲时间
5、二级缓存实际上是一个缓存,所以,hibernate并没有实现自己的二级缓存框架,而是用的开源的
对象缓存策略:
1.usage="read-only":放到二级缓存里面的对象是只读(性能最高)
2.Usage="read-write":允许读写(对并发支持较好)
3.usage="nonstrict-read-write":允许读写,但是在病发事务情况下会产生脏数据
4、usage="transactional":允许读写,并且支持全事务(只能在applicationServer环境下有用)
二级缓存的操作:
//得到二级缓存对象
Cache cache=sf.setCache();
//剔除一个实例
cache.evictEntity(User.class,1L);
//剔除某种类型的所有实例
cache.evictEntityRegion(User.class);
//剔除所有二级缓存实例
cache.evictEntityRegions();
查询缓存:(使用非常少,又叫三级缓存,因为可能带来非常大的负面性能影响)
1.默认情况下,hibernate没有打开查询缓存
2、使用查询缓存:
 1.打开查询缓存
 2.在查询的时候,使用Query对象的.setCacheable(true)方法
3.查询缓存使用的条件:
 1.两条查询的HQL和查询参数必须完全一致;
事务并发5类问题(如果数据库没有做任何并发处理的情况下):
第一类丢失更新:两个事务更新相同数据,如果一个事务提交,另一个事务回滚,第一个事务的更新会被回滚
脏读:第二个事务查询到第一个事务来提交的更新的数据,第二个事务根据该数据执行,但第一个事务回滚,第二个事务操作脏数据
虚读:一个事务查询到了另一个事务已经提交的新数据,导致多次查询数据不一致
不可重复读:一个事务查询到另一个事务已经修改的数据,导致多次查询数据不一致
第二类丢失更新:多个事务同时读取相同数据,并完成各自的事务提交,导致最后一个事务提交会覆盖前面所有事务对数据的改变
事务隔离级别:
READ_UNCOMMITED:允许你读取还未提交的改变了的数据,可能导致脏.幻、不可重复读(相当于没有做任何事务隔离)
READ_COMMITTED:允许在并发事务已经提交后读取,可防止脏读
REPEATABLE_READ:对相同字典的多次读取是一只的,除非数据被事务本身改变,可防止不可重复读、脏读(MYSQL默认级别)
SERIALIZABLE:完全服从ACID的隔离级别,确保不发生脏、幻、不可重复读,这在所有的隔离级别中是最慢的
所以,数据库的隔离级别除了SERIALIZABLE,都不能处理第一类丢失更新和第二类丢失更新
所以,数据库提供了锁机制来防止第一类丢失更新和第二类丢失更新

猜你喜欢

转载自blog.csdn.net/qq_36594703/article/details/79617599