首先理解 关系维护 cascade.
得先看两篇官方文章.
1. Chapter 21. Example: Parent/Child
摘抄2:
inverse和cascade都配置的结果:
You need to explicitly delete() the Child.
Parent p = (Parent) session.load(Parent.class, pid);
Child c = (Child) p.getChildren().iterator().next();
p.getChildren().remove(c);
session.delete(c);
session.flush();
In our case, a Child cannot exist without its parent. So if we remove a Child from the collection, we do want it to be deleted. To do this, we must use cascade="all-delete-orphan".
<set name="children" inverse="true" cascade="all-delete-orphan">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
Even though the collection mapping specifies
inverse="true"
,
cascades
are still processed by iterating the collection elements. If you need an object be saved, deleted or updated by cascade, you must add it to the collection. It is not enough to simply call setParent().
|
2.3.2.5.3.2.1. Bidirectional of Hibernate Annotations Reference Guide
To map a bidirectional one-to-many, with the one-to-many side as the owning side, you have to remove the
mappedBy
element and set the many-to-one @JoinColumn
as
insertable and updatable
to false. This solution is obviously
not optimized
and will produce some
additional UPDATE statements(更新外键)
.
@Entity
public
class
Troop {
@OneToMany
@
JoinColumn
(name=
"troop_fk"
)
//we need to duplicate the physical information
public Set<Soldier>
getSoldiers
() {...
}
@Entity
public
class
Soldier {
@ManyToOne
@
JoinColumn
(name=
"troop_fk"
, insertable=false, updatable=false
)
public
Troop
getTroop
() {
...
}
3. @OneToMany() 默认表,默认数据库表字段
@OneToMany(
mappedby=
)
即使不配置mappedby= fieldName , 不配置 @Joinolumn(
physical information
) 关系维护方,默认也生成一张中间关系表(One实体名_field属性名 例如 parent_the_child_list),字段为long 类型的
实体名和属性名( parent,
the_
child_list)(设置 hibernate.auto=create的话)
4. @ManyToOne 默认数据库表字段
不配置 @Joinolumn(
physical information
)
默认都会有对应的字段.(filedName为字段名 例如 the_parent)
5. 重要概念 关系维护,级联区别
(与级联是不同的,级联是针对行,关系维护是针对
外键 ).
关系维护: 赋值外键
OneToMany 级联,孤值(
orphan
)删除: 对一行的操作.
Even though the collection mapping specifies
inverse="true"
,
cascades
are still processed by iterating the collection elements. 这两个概念常常被误解了.不维护和遍历并不矛盾. 不维护的只是某个字段,但不代表不对整行进行操作.
案例分析:
洪泉需求 getList().remove(A),
期望的是
1.这一行A被删除.那么需要的是级联概念,设置为级联上的孤值删除.orphan. 对应的
EntityDeleteAction(
DefaultDeleteEventListener.deleteEntity(EventSource, Object, EntityEntry, boolean, EntityPersister, Set) line: 275 )
2. 这一行A上的外键删除(设置为null,hibernate的OneToMany的delete概念就是 update set xx=null ) ,那么就需要选择关系维护这个概念,设置为关系维护方.
数据库世界的关系维护:
首先得理解数据库世界的关系维护概念是什么.
Parent 和 Child 1对多关系是由Child表上的外键决定的.
当插入一行parent,插入一行child.
如果对child这行上的parent_foreign_id 赋值. 关系就产生了.
如果删除这个值,关系就没有了.
这个过程就叫关系维护,是通过赋值这个外键产生的.( 注意删除parent这一行,需要删除所在外键,但不一定要删除)
人的世界的关系维护:
class teacher {
List<student> studentList
}
|
class Student{
Teacher teacher
}
|
一个老师教N个学生. 一个学生知道哪个老师教他.
某一天老师去教其他M个学生了,老师下面的
studentList.clear()
studentList.addAll( newStudentList).
老师知道哪些学生是自己教的,之前那些学生自己不教了. 但是之前的那些同学并不知情.
这就需要 系统去
维护这些信息.
注意和数据库世界的区别:
数据库世界老师和学生的关系是绑定在一个外键上. 只要这个值变了, 关系的变动对双方来说都是同步知情的.
但是人的世界,两份关系分别保存在每个人的大脑里.也就是说数据是冗余的,需要有人去通知去更新,而且不保证所有人都能通知到.不是同步的,不是原子的(计算机电脑程序的优势所在).
jpa hibernate世界
映射一,单方维护:
class Teacher {
List<student> studentList
}
缺点修改了studentList,需要
手工
Student.
setTeacher
. hibernate的一个缺点,坑.
普通改进:
teacher中;
增加
public void addStudent(
Student staudent
){
student.setTeacher(this);
studentList.add(student)
}
public void addStudentAll( )
最终改进:
双方都维护
注解, 一方增加 @joinColumn ,保持和多端一致
xml , 已经有column一项. inverse=true; 默认是false, 都维护更新
|
class Student{
Teacher teacher
}
|
映射二:
class Teacher {
List<student> studentList
}
|
class Student{
数据库有 teacher外键,但是实体上无 ManyToOne
}
|
jpq世界所有的持久化信息都是通过物理层 phsical即数据库 来实现的.
hibernate从效率的角度的,就是不期望"1端的List的变动导致下数据库."
这样的坏处效率问题显而易见:
考虑更新某个学生.
先需要save 学生, save teacher ,然后在 flush阶段下一个update 关系的操作.
考虑一个不教育某个student, 要先从数据库世界里获取到所有的List<student> ,然后在remove这个学生.然后再save.
jpa世界的坑:
many端维护的问题,双向.
1.洪泉的问题.
把list原来有2个, set了一个新的list.3个.
第二次获取list有5个. 不符合原有逻辑? 期望是3个
问题原因: 一非关系维护方. 没有对原来的2个child setParent(null )
2.
add了一个,但 child没有setParent.
导致第二次获取 list 还是为0;
双向,双方维护的问题;
容易引起先被child端维护, 然后被parent端维护给覆盖.
多了很多sql语句???? 待测试
当list的值多了一个少了一个的时候, save parent ,会自动
以前用xml配置,见官方文档1.
都需要配置column, 1方维护不维护关系11
6.
Bidirectional 和 undirectional的区别;
OneTOMan
y Bidirectional 使用了@OneTOMany(mappedBy=)
OneTOMany undirectional使用了@joinColumn,和物理世界绑定 默认使用多端的主键作为另一个join键.
hibernate官方文档不建议OneTOMany使用@joinColumn(3.2.5.3.2.2. Unidirectional
A unidirectional one-to-many using a foreign key column in the owned entity is not that common and not really recommended.)
所以中间表还是字段形式.是看是否默认使用 primary key ,是否使用主键作为join的一部分.
ManyToMany没办法用一行的主键来做join的一部分. 所以必须用@joinTable要绑定到新的表上.并且把对应主键也映射上去.
ManyToMany没办法用一行的主键来做join的一部分. 所以必须用@joinTable要绑定到新的表上.并且把对应主键也映射上去.
6 hibernate的 save和flush时候对应的概念
persistent层次,
PersistentCollection层次,
Collection
Action,EntirtyAction层次,
BatchingBatcher层次
;
session.getActionQueue().
addAction 或者 .
org.hibernate.engine.ActionQueue
是执行队列保存了
org.hibernate.action.Executable,Action等等
save --对应基本persistent,内含 updateSql
1. 针对单表完成. 生成sql语句,
除了EntityIdentityInsertAction 直接在save下sql (
委托给了
AbstractEntityPersister
.class下的
private InsertGeneratedIdentifierDelegate identityDelegate;
), 其他的ActionQueue都要等到flush才会下sql.
注意:flush打印preparementSql的代码
1. prepSql hibernate entity初始化时就生成,这里里只是生成prepareStatement,顺便打印sql
2.(非log,是system.out.print) 并不是执行sql的代码.真正执行sql,hibernate没有打印日志.是在WriteKey之后.
2. save完之后会对oneToMany的list进行替换,从ArrayList替换为 PersistentBag.
这样以后便于监控. add 设置 isDirty
flush--对应基本oneToMany sql,内含 updateSql .. hibernate 初始化时期. prepareMentSql
baseDao.flush()DefaultFlushEventListener.flush()总共有3个大阶段
一.
DefaultFlushEventListener.
flushEverythingToExecutions(event);
2. prepareCollectionFlushes(session),
1. prepareEntityFlushes(),
3. flushEntities(event)
4. flushCollections(session); )
二.DefaultFlushEventListener.performExecutions(source);
三.DefaultFlushEventListener.postFlush(source);
|
对每个save的单表进行检查, 查看其他persistent, OneToMany, 级联操作.
dirty检查,根本是依赖storedSnapshot和悄悄替换的代理类 persistentBag:
hibernate如何判断一个list是否更新,更新了几条. 是完全性的list?
1.是否更新, baseDao.save后把list类型悄悄变成了代理类 persistentBag, 每次add,会设置
AbstractPersistentCollection.
Dirty();
2.在baseDao.flush()的
一阶段.
DefaultFlushEventListener.
flushEverythingToExecutions(event)的
prepareEntityFlushes阶段 ,对每个object entirty Entry对进行遍历,对立面的每个list进行判断.获取级联的信息和孤值信息.addAction(具体见后面的孤值分析)
3. 在flush的 一阶段.
DefaultFlushEventListener.
flushEverythingToExecutions(event);的
flushEntities的
execute 准备prepareStatement阶段,会判断这个
CollectionUpdateAction
list 是属于 完全新建还是被更新
else
if
( collection.isDirty() ) { // else if it's elements changed
entry.setDoupdate(true);
}
Collections.
prepareCollectionForUpdate
(PersistentCollection, CollectionEntry, EntityMode, SessionFactoryImplementor) line: 256
4.
一阶段
.
DefaultFlushEventListener.
flushEverythingToExecutions(event)的
flushCollections()
阶段对每个collections进行判断.
AbstractFlushingEventListener.
flushCollections(EventSource)
if ( ce.isDoupdate() ) {
session.getInterceptor().onCollectionUpdate( coll, ce.getLoadedKey() );
actionQueue.addAction(
new CollectionUpdateAction(
coll,
ce.getLoadedPersister(),
ce.getLoadedKey(),
ce.isSnapshotEmpty(coll),
session
)
);
}
5.在 二.AbstractFlushingEventListener.
performExecutions阶段的
public Class ActionQueue{
private void executeActions(List list) throws HibernateException {
int size = list.size();
//阶段1:
for ( int i = 0; i < size; i++ ) {
execute( ( Executable ) list.get( i ) );
}
list.clear();
//阶段2:
session
.getBatcher().executeBatch(); //最终下sql,在数据库日志里可见
}
5.1 //阶段1:
内有重要调用的函数
public Class
CollectionUpdateAction
{
public
void
execute
()
throws
HibernateException {
persister.deleteRows( collection, id, session )
persister.updateRows( collection, id, session );
persister.insertRows( collection, id, session ); // 遍历collection item 判断是否需要生成 prepareStatment,插入id=?的参数值. 注意即使多个item符合,也不会智能生成多个 in( ? ? ? .),还是多个update prepareStatment sql语句.} |
每个方法都
遍历一遍(算法复杂度不好,但是业务逻辑清晰)每个
clollection item是否和
storedSnapshot(
这个是dirty检查的关键
)
相符合. 判断是否需要添加 insert update delete Rows sql;
不过不太名称和自己sql不太相符;
insert对关系维护而言是 update chile set parent_id= ? where id=?
delete 是 update child set parent_id=null
where id=?
update 同 delete
where id=?
;
打印sql, 立马对所有的collection进行遍历,生成prepareStatement 获取初始化期早已生成的带?sql ,
并打印
(注意:一个update固定一个id=?)
,在
OneToManyPersister(AbstractCollectionPersister).
writeKey
(PreparedStatement, Serializable, int, SessionImplementor) line: 806处
赋值"?"参数值.(具体堆栈见下面附件)
5.2
//阶段2:
执行
BatchingBatcher(AbstractBatcher)是比较重要的执行prepareStatement类;),
还有个疑惑:
如果hibernate的One端不维护List, 上面的操作还会执行吗???
答:会执行.
1.
官方文档上有句话:
Even though the collection mapping specifies
inverse="true"(xml配置表示 一方不是关系维护方,注解配置使用的是mappedBy)
,
cascades
are still processed by iterating the collection elements.
也说明了即使One端不是关系维护方,也会主动地遍历所有的collection 元素.
2. 关系维护和遍历行无关,维护的并不是list,而是每行上的外键.
OneToMany等返回的是hibernate自己封装
代理List类 (巧妙利用了接口编程,class不变,利于debug检查,无侵入的侵入), add的时候能够知道设置dirty=true了.
那如果是全部=另外一个List,如何判断是更新操作??
else if ( collection.isDirty() ) { // else if it's elements changed
entry.setDoupdate(true);
}
Collections.prepareCollectionForUpdate(PersistentCollection, CollectionEntry, EntityMode, SessionFactoryImplementor) line: 256
Collections.processReachableCollection(PersistentCollection, CollectionType, Object, SessionImplementor) line: 207
FlushVisitor.processCollection(Object, CollectionType) line: 60
FlushVisitor(AbstractVisitor).processValue(Object, Type) line: 122
FlushVisitor(AbstractVisitor).processValue(int, Object[], Type[]) line: 83
FlushVisitor(AbstractVisitor).processEntityPropertyValues(Object[], Type[]) line: 77
DefaultFlushEntityEventListener.onFlushEntity(FlushEntityEvent) line: 165
DefaultFlushEventListener(AbstractFlushingEventListener).flushEntities(FlushEvent) line: 219
DefaultFlushEventListener(AbstractFlushingEventListener).flushEverythingToExecutions(FlushEvent) line: 99
DefaultFlushEventListener.onFlush(FlushEvent) line: 50
SessionImpl.flush() line: 1216
}
dirty检查之孤值检查 orphan 检查
*
DefaultFlushEventListener.
flushEverythingToExecutions(event)的
AbstractFlushingEventListener的1.
prepareEntityFlushes阶段.
(
总共有 1.
prepareEntityFlushes(),
2.
prepareCollectionFlushes(session),
3.
flushEntities(event)
4. flushCollections(session);
)
*
执行代码
getOrphans (
AbstractPersistentCollection.getOrphans(Collection, Collection, String, SessionImplementor) line: 901 ),内部实现是利用 persistentBag里的snapshot进行orphans检查;
*获取之后对在
Cascade.deleteOrphans()遍历每个孤值进行删除.. 做的其实是伪删除. 异步事件删除.
DeleteEvent 概念只是简单的封装.
.
void org.hibernate.event.def.DefaultDeleteEventListener.onDelete(DeleteEvent event, Set
transientEntities) throws HibernateException
Handle the given delete event. This is the cascaded form.(级联形式,递归形式)
DefaultDeleteEventListener.onDelete(DeleteEvent, Set) line: 162
SessionImpl.delete(String, Object, boolean, Set) line: 948
Cascade.deleteOrphans(String, PersistentCollection) line: 486
*把事件转换为Action
在
DefaultDeleteEventListener.deleteEntity(EventSource, Object, EntityEntry, boolean, EntityPersister, Set) line: 275
最终实现了变成一个Action,加入到 异步事件队列里面;
// Ensures that containing deletions happen before sub-deletions
session.getActionQueue().addAction(
new EntityDeleteAction(
entityEntry.getId(),
deletedState,
version,
entity,
persister,
isCascadeDeleteEnabled,
session
)
);
注意: 在其前面后调用cascadeBeforeDelete(),cascadeAfterDelete().级联获取级联
new Cascade( CascadingAction.DELETE, Cascade.BEFORE_INSERT_AFTER_DELETE, session )
.cascade( persister, entity, transientEntities );
附件详细见后文
sql的打印, prepareStatement语句准备,?参数数据(其实就是每个需要操作的object的值,难点还是dirty检查),执行
赋值"?"参数值具体堆栈:
关于
prepareStatement的准备, 和执行到数据库引擎.(真正生成mysql的sql日志,binlog)
*对entity类型的使用,
*对参数值的设定.类型转换
*
AbstractCollectionPersister
对list的变动,更新几个bean的决策
BigIntTypeDescriptor$1.doBind(PreparedStatement, X, int, WrapperOptions) line: 52
BigIntTypeDescriptor$1(BasicBinder<J>).bind(PreparedStatement, J, int, WrapperOptions) line: 91
LongType(AbstractStandardBasicType<T>).nullSafeSet(PreparedStatement, Object, int, WrapperOptions) line: 283
LongType(AbstractStandardBasicType<T>).nullSafeSet(PreparedStatement, Object, int, SessionImplementor) line: 278
OneToManyPersister(AbstractCollectionPersister).
writeKey(PreparedStatement, Serializable, int, SessionImplementor) line: 806
这个就是把生成的PreparedStatement,赋值参数. 之前已经打印了固定xx=?的sql.
OneToManyPersister(AbstractCollectionPersister).remove(Serializable, SessionImplementor) line: 1106
CollectionRemoveAction.execute() line: 107
ActionQueue.execute(Executable) line: 273
ActionQueue.executeActions(List) line: 265
ActionQueue.
executeActions() line: 186
DefaultFlushEventListener(AbstractFlushingEventListener).
performExecutions(EventSource) line: 321
{
performExecutions
分成 prepareAction()和
executeAction()
两部分
}
DefaultFlushEventListener.onFlush(FlushEvent) line: 51
SessionImpl.flush() line: 1216
|
Pasted from: <http://write.blog.csdn.net/postedit/24337863>
获取孤值堆栈代码
AbstractPersistentCollection.getOrphans(Collection, Collection, String, SessionImplementor) line: 901
PersistentSet.getOrphans(Serializable, String) line: 113
CollectionEntry.getOrphans(String, PersistentCollection) line: 373
Cascade.deleteOrphans(String, PersistentCollection) line: 471
Cascade.cascadeCollectionElements(Object, Object, CollectionType, CascadeStyle, Type, Object, boolean) line: 455
Cascade.cascadeCollection(Object, Object, CascadeStyle, Object, CollectionType) line: 362
Cascade.cascadeAssociation(Object, Object, Type, CascadeStyle, Object, boolean) line: 338
Cascade.cascadeProperty(Object, Object, Type, CascadeStyle, String, Object, boolean) line: 204
Cascade.cascade(EntityPersister, Object, Object) line: 161
DefaultAutoFlushEventListener(AbstractFlushingEventListener).cascadeOnFlush(EventSource, EntityPersister, Object, Object) line: 154
DefaultAutoFlushEventListener(AbstractFlushingEventListener).prepareEntityFlushes(EventSource) line: 145
DefaultAutoFlushEventListener(AbstractFlushingEventListener).flushEverythingToExecutions(FlushEvent) line: 88
DefaultAutoFlushEventListener.onAutoFlush(AutoFlushEvent) line: 58
SessionImpl.autoFlushIfRequired(Set) line: 1185 | ||||
拦截器的onDelete堆栈,真正的增加Action在同一函数的(
DefaultDeleteEventListener.deleteEntity(EventSource, Object, EntityEntry, boolean, EntityPersister, Set) line: 275 )
ReplaceableInterceptor.onDelete(Object, Serializable, Object[], String[], Type[]) line: 48
DefaultDeleteEventListener.deleteEntity(EventSource, Object, EntityEntry, boolean, EntityPersister, Set) line: 255
DefaultDeleteEventListener.onDelete(DeleteEvent, Set) line: 162
SessionImpl.fireDelete(DeleteEvent, Set) line: 965
SessionImpl.delete(String, Object, boolean, Set) line: 948
Cascade.deleteOrphans(String, PersistentCollection) line: 486
Cascade.cascadeCollectionElements(Object, Object, CollectionType, CascadeStyle, Type, Object, boolean) line: 455
Cascade.cascadeCollection(Object, Object, CascadeStyle, Object, CollectionType) line: 362
Cascade.cascadeAssociation(Object, Object, Type, CascadeStyle, Object, boolean) line: 338
Cascade.cascadeProperty(Object, Object, Type, CascadeStyle, String, Object, boolean) line: 204
Cascade.cascade(EntityPersister, Object, Object) line: 161
DefaultFlushEventListener(AbstractFlushingEventListener).cascadeOnFlush(EventSource, EntityPersister, Object, Object) line: 154
DefaultFlushEventListener(AbstractFlushingEventListener).prepareEntityFlushes(EventSource) line: 145
DefaultFlushEventListener(AbstractFlushingEventListener).flushEverythingToExecutions(FlushEvent) line: 88
DefaultFlushEventListener.onFlush(FlushEvent) line: 50
SessionImpl.flush() line: 1216
| ||||