Hibernate拦截器:当集合元素更新时拦截实体
我需要知道如何设置 Hibernate (...) 来实现以下问题:
我有一个双向(组合)一对多关联(a.bs 是 Set 对象,ba 是 A 对象)。当我从数据库加载“a”并更新其“bs”之一时,我需要 Hiernate 在 saveOrUpdate A 时拦截 A 实体。
代码:
public class A {
Set<B> bs = new HashSet<B>();
// ... other fields and setters/getters
}
public class B {
A a = null;
// ... other fields and setters/getters
}
用例:
A a = load("idA"); // load A from DB
B b = s.getBById("idB"); // get a B element of A
b.setName("blablabla"); // update a field of B
saveOrUpdate(a); // persist A entity with its Bs (including modified B)
执行更改是因为(迷你)模型已正确注释。
问题是我的拦截器只检测到 B 实体的更改,而不检测到 A 实体的更改。我需要检测 A 更改,因为我需要更新审核信息。
另一种观点是:我需要通过B获取A实体并更新它。事实上,我可以从B得到A,但是改变没有持久化......
简化问题: 当我的拦截器拦截 B 实体时,我必须修改 A 实体(设置日期)。它在 onSave 中工作正常,但在 onFlushDirty 中则不然。为什么?
这是: 当B更新时,被拦截(onFlushDirty)。 onFlushDirty 方法的主体,除其他外,执行以下操作:
b.getA().setLastModifyDate(new Date());
因此,在那一刻,附加到会话的实体应该变脏,因此它应该引发拦截操作...我的意思是, onFlushDirty 方法应该是再次呼叫,他的 A 实体时间到了。我错了吗? 但是,无论如何,A.lastModifyDate 应该更新......而这没有发生!
下面我展示了我的应用程序的实际行为:
- 我创建了一个 A 对象
- 我创建了一个 B 对象并将其关联到 A
I persist A => A.lastModifyDate 是正确的日期(确定)
我创建一个 A 对象
- 我创建一个 B 对象并将其关联到 A
- 我保留 A => ; A.lastModifyDate 是正确的日期(确定)
我加载 B 对象,更新它并保留 B -> A.lastModifyDate 是正确的日期(确定)
我创建一个 A 对象
- 我创建一个 B 对象并将其关联到 A
- 我保留 A => ; A.lastModifyDate 是正确的日期(确定)
我加载 A 对象,更新其 B 对象并保留 A -> A.lastModifyDate 不是正确的日期 (KO)
我创建一个 A 对象
- 我创建一个 B 对象并关联它对A
- 我坚持A => A.lastModifyDate 是正确的日期(确定)
我加载 A 对象,更新任何 A 的字段及其 B 对象,并且保留 A -> A.lastModifyDate 不是正确的日期 (KO)
我创建一个 A 对象并保留它。
我加载 A 对象,将其关联到一个新的 B 对象,然后保留 A => A.lastModifyDate 是正确的日期(确定)
我创建一个 A 对象并保留它。
- 我加载 A 对象,更新任何 A 的字段,将其关联到一个新的 B 对象,然后保留 A => A.lastModifyDate 是正确的日期(确定)
有什么想法吗?
谢谢!
I need to know how can I set Hibernate (...) to achieve the following issue:
I have a bidirectional (composition) one-to-many association (a.bs is a Set object and b.a is a A object). When I load "a" from DB and I update one of its "bs" I need Hiernate intercept A entity when saveOrUpdate A.
Code:
public class A {
Set<B> bs = new HashSet<B>();
// ... other fields and setters/getters
}
public class B {
A a = null;
// ... other fields and setters/getters
}
Use case:
A a = load("idA"); // load A from DB
B b = s.getBById("idB"); // get a B element of A
b.setName("blablabla"); // update a field of B
saveOrUpdate(a); // persist A entity with its Bs (including modified B)
The change is performed because the (mini)model has been annotated properly.
The problem is that my interceptor only detects the change in B entity, but not A. I need to detect A change because I need to update audit info.
Other point of view is: I need to get A entity via B and update it. In fact, I can get A from B, but the change is not persisted...
Simplifying the question:
I have to modify A entity (set a date) when my interceptor intercepts B entity. It worksfine in onSave but not in onFlushDirty. Why?
This is:
When B is updated, is intercepted (onFlushDirty). The body of onFlushDirty method, among other things, do this:
b.getA().setLastModifyDate(new Date());
So, in that moment, A entity , that is attached to session, should became dirty, hence it should raise an interception action... I mean, the onFlushDirty method should be called again, his time for A entity. Am I wrong?
But, in any case, A.lastModifyDate should be updated... and this is not happening!!!
Following I show the actual behaviour of my application:
- I create an A object
- I create a B object and I associate it to A
I persist A => A.lastModifyDate is the correct date (OK)
I create an A object
- I create a B object and I associate it to A
- I persist A => A.lastModifyDate is the correct date (OK)
I load the B object, I update it and I persist B -> A.lastModifyDate is the correct date (OK)
I create an A object
- I create a B object and I associate it to A
- I persist A => A.lastModifyDate is the correct date (OK)
I load A object, I update its B object and I persist A -> A.lastModifyDate is not the correct date (KO)
I create an A object
- I create a B object and I associate it to A
- I persist A => A.lastModifyDate is the correct date (OK)
I load A object, I update any A's field and also its B object and I persist A -> A.lastModifyDate is not the correct date (KO)
I create an A object and I persist it.
I load A object, I associate to it a new B object and I persist A => A.lastModifyDate is the correct date (OK)
I create an A object and I persist it.
- I load A object, I update any A's field, I associate to it a new B object and I persist A => A.lastModifyDate is the correct date (OK)
Any idea?
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您必须使用 onCollectionUpdate 来解决问题
You have to use onCollectionUpdate to solve the issue
好的,所以如果(正如您在评论中所说)您使用的是实际的 拦截器 那么我不太清楚你所说的“只检测 B 的变化,而不检测 A 的变化”是什么意思。由您拦截器的实现来检测您想要检测的任何内容。
以上面的示例为例,当您调用
session.saveOrUpdate(a)
时,您的拦截器的onSave()
方法将为 A 和 B 调用(假设适当的级联设置大概就在那里,因为你已经说过 b 正在被保存)。当然,这是假设 A 实际上已被修改(因此被发现是脏的)。 单独修改 B 不会使 A 变脏,除非关联尚未声明为逆关联。在这种情况下,您可以在为 B 调用onSave()
时执行需要完成的操作,或者您可以挂钩到将为 B 调用的findDirty()
方法当前正在会话的所有实体。但请注意,它可能会被多次调用 - 基本上是在每次刷新时而不是在saveOrUpdate()
上。Ok, so if (as you said in your comment) you're using an actual Interceptor then I'm not quite sure what you mean by "only detects change in B, not A". It's up to your implementation of the Interceptor to detect whatever you want to detect.
Taking your above example, when you call
session.saveOrUpdate(a)
your interceptor'sonSave()
method is going to be invoked both for A and B (assuming appropriate cascade setting which is presumably there since you've said b is being saved). That, of course, assumes that A has, in fact, been modified (and thus was found to be dirty). Modifying B alone will not make A dirty unless the association has not been declared as inverse. In such a case you can either do what needs to be done whenonSave()
is invoked for B OR your can hook into thefindDirty()
method which will be invoked for all entities currently in session. Note, however, that it may be called more than once - basically on each flush rather than onsaveOrUpdate()
.如果你想让它使用 onFlushDirty 工作,你必须在 saveOrUpdate(a) 之后手动刷新会话;这将确保持久对象中的任何更新都会进入数据库提交;如果您处于自动刷新模式,提交将触发另一次刷新,这将发现父实体为脏。否则该对象将不会被保存,因为拦截器只会保存当前修改的实体。
我有一个类似的情况,我需要从子集合中更新父实体的审核字段。区别在于我的服务仅保存子集合列表,不包括父实体。
if you want to make it work using onFlushDirty, you must manually flush the session after saveOrUpdate(a); this will ensure any update in a persistent object will get into db commit; if your on auto flush mode, commit will trigger another flush which will find the parent entity as dirty. else the object wont get save becoz the interceptor will only save the current modified entity only.
i have a similar case where i need to update the audit fields of the parent entity from a child collection. the difference is im my service is only saving the list of child collection excluding the parent entity.