acs*_*404 3 java hibernate jpa jpa-2.0
我正在使用JPA 2.0和Hibernate 4.2.5.我有一个双向映射到相同的实体类型.
@OneToMany(mappedBy = "parent", cascade = { CascadeType.PERSIST, CascadeType.DETACH }, orphanRemoval = true, fetch = FetchType.EAGER)
@Fetch(FetchMode.JOIN)
@OrderColumn(name = "position", nullable=false)
private List<MenuItem> children = new ArrayList<MenuItem>();
@ManyToOne(optional = false, fetch = FetchType.EAGER)
@JoinTable(name = "MenuItem_MenuItem", joinColumns = { @JoinColumn(name = "child_id") }, inverseJoinColumns = { @JoinColumn(name = "parent_id") })
private MenuItem parent;
Run Code Online (Sandbox Code Playgroud)
此映射使用parent_id,child_id和position列创建连接表.当我想将一个孩子添加到父母时,我会执行以下操作:
MenuItem newItem = service.persist(..);
parent.getChildren().add(newItem);
newItem.setParent(parent);
service.merge(newItem);
service.merge(parent);
Run Code Online (Sandbox Code Playgroud)
生成以下内容:
Hibernate: select nextval ('hibernate_sequence')
Hibernate: insert into MenuItem (menu_id, message, messageId, params, id) values (?, ?, ?, ?, ?)
Hibernate: insert into MenuItem_MenuItem (parent_id, child_id) values (?, ?)
Run Code Online (Sandbox Code Playgroud)
这里的问题是第二个插入不包含position属性,因此它保持为null.我究竟做错了什么?
编辑 我正在使用spring声明式事务管理,我所有的merge和persist方法都使用@Transactional注释.这可能是个问题吗?
您的问题是,您将@OrderColumn注释放在关联的映射旁边.JPA和hibernate都不支持此功能.Hibernate children在实体加载时填充列表,但没有监视列表中是否有可能的更改.将新子项添加到此类列表不会触发休眠方面的任何操作.您必须从其他(非mappedBy)方面管理此类关联:在您的情况下,您必须使用setParent()方法.
现在,如果从hibernate的角度来看情况,你会发现,在持久化一个新的Child实体(向MenuItem_MenuItem表中添加新记录)期间,hibernate没有机会让索引列正确.实际上,要获得新实体的顺序,您需要查看父项的映射列表.但这并不总是可行的.请考虑以下代码段:
MenuItem newItem = new MenuItem();
newItem.setParent(parent);
entityManager.persist(newItem);
Run Code Online (Sandbox Code Playgroud)
position这里的栏目应该保存什么?
存档目标的正确方法是在此处添加position以下列MenuItem:
private Integer position;
Run Code Online (Sandbox Code Playgroud)
然后用以下内容替换@OrderColumn注释@OrderBy("position"):
@OneToMany(mappedBy = "parent", ...)
...
@OrderBy("position")
private List<MenuItem> children = new ArrayList<MenuItem>();
Run Code Online (Sandbox Code Playgroud)
然后添加一个位置重新排序方法,如下所示:
public void reorder() {
for (int i = 0; i < getChildren().size(); i++) {
MenuItem menuItem = getChildren().get(i);
menuItem.setMyOrder(i);
}
}
Run Code Online (Sandbox Code Playgroud)
然后您可以尝试使用@PrePersist或@PreUpdate挂钩自动调用此方法.但这有点棘手.因为您必须在正在创建/更新的子级的父级上调用此方法.如果添加/更新多个子项,则最终会多次调用此方法.
这是一个已知错误HHH-5732,将在 4.3 中修复。一般来说,此功能存在一些问题,请查看这些最近的错误报告:
最初,讨论说从概念的角度来看,您提到的使用场景被认为是无效的,因为这意味着有关 MenuItem 的信息position仅在父项上可用,而在子项上不可用。
后来提到会显示更好的错误消息来解释问题,而不是默默地填充positionnull。
还提到 Hibernate 文档本身提供了与您提供的示例类似的示例,并且 JPA 没有提到任何反对它的内容,因此它应该是一个有效的用例。
HHH-5732 上的错误修复似乎解决了这个问题。但是,如果您想在当前版本上应用修复程序,有一种方法可行。
上周我在使用 Hibernate 4.1 时遇到了同样的问题并且无法升级,所以我应用了以下解决方案:
添加position为 MenuItem 中的私有属性字段,而不必为其提供 getter 和 setter。自己填写这个属性并让 Hibernate 持久化它:
public class MenuItem {
public MenuItem(... other parameters ..., int position) {
}
@Column("position")
private int position;
.... no getters or setters ...
}
Run Code Online (Sandbox Code Playgroud)
在添加子菜单的代码中:
int counter = 0; // some counter keeping the current position being added
MenuItem newItem = service.persist(.., counter);
parent.getChildren().add(newItem);
Run Code Online (Sandbox Code Playgroud)
这也使得有关元素位置的信息在关系的两侧都可用。
编辑:请参阅此处此解决方案的 Hibernate 文档
| 归档时间: |
|
| 查看次数: |
4909 次 |
| 最近记录: |