gpt4 book ai didi

java - Play 中使用 Hibernate 的菜单树!

转载 作者:行者123 更新时间:2023-11-30 09:50:39 25 4
gpt4 key购买 nike

我的第一个 Hibernate/JPA 和 Play 项目!我有一个菜单,一旦工作,将支持更改(即轻松地向树添加节点)。努力(在 >5 小时的意义上)将基本建模放在一起:

模型:

@Entity
@Table(name = "Node")
public class Node extends Model {

@Column(nullable=false)
public String description;

@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
@JoinColumn(name="parent")
public List<Node> children = new LinkedList<Node>();

@ManyToOne
@JoinColumn(name="parent", insertable=false, updatable=false)
public Node parent;

public Node(){}

}

工具类:

public class NodeUtil {

public static void addChild(Node root, String description) {

Node child = new Node();
child.description = description;
child.parent = root;

root.children.add(child);

root.save();

}

private static final String MENU_NAME = "MainMenu";

public static Node getMenu() {
return getRoot(MENU_NAME);
}
public static Node getRoot(String name) {
Node root = Node.find("byDescription", name).first();
if (root == null) {
root = new Node();
root.description = name;
root.save();
}

return root;
}

}

测试:

public class MenuTest extends UnitTest {

private static final String TEST_MENU = "testMenu";

@Test
public void testMenu() {

// test build/get

Node root = NodeUtil.getRoot(TEST_MENU);
assertNotNull(root);

// delete all children - maybe from previous tests etc.
for(Node o : root.children)
o.delete();
root.save();

// test add

root = NodeUtil.getRoot(TEST_MENU);

NodeUtil.addChild(root, "child 1");
NodeUtil.addChild(root, "child 2");
NodeUtil.addChild(root, "child 3");

assertEquals(3, root.children.size());
assertEquals("child 3", root.children.get(2).description);

assertEquals(0, root.children.get(1).children.size());

Node node = root.children.get(1);
NodeUtil.addChild(node, "subchild 1");
NodeUtil.addChild(node, "subchild 2");
NodeUtil.addChild(node, "subchild 3");
NodeUtil.addChild(node, "subchild 4");

NodeUtil.addChild(root.children.get(2), "sub subchild");

assertEquals("sub subchild", root
.children.get(1)
.children.get(2)
.children.get(0)
.description);

assertEquals(4, root.children.get(1).children.size());

root.save();

// test delete

root = NodeUtil.getRoot(TEST_MENU); // regrab the root via hibernate, assuming there isnt it isnt cached this will get changes that have been persisted to the db (maybe?)

root.children.get(1).children.get(2).delete();
assertEquals(3, root.children.get(1).children.size());

//root.delete();

}

}

问题:

  1. 我做错了什么? (即,我只是无法将这个简单的想法建模并通过单元测试。就像我说的,Hibernate 的新手,我所做的每一次更改都会产生一个新的 Hibernate 错误变体,这对我来说毫无意义。例如,当前安装程序抛出“传递给持久化的分离实体:models.Node”)

  2. 最初我将整个 util 类作为模型类中的一堆静态方法。首先,静态方法会影响 Hibernates 建模吗?如果是这样,简而言之,在什么情况下我可以拥有对对象建模“ transient ”的静态方法(和成员,想想看)?

  3. 假设我将 util 方法保存在一个单独的公共(public) util 类中,这个类通常存储在 play 框架中的什么位置?目前它在模型包中,在 Node.java 旁边。

最佳答案

我不熟悉 Play Framework,但我可以就使用 Hibernate 提出一些观点:

  • 维护内存中对象的一致状态是您的责任。考虑以下代码:

    for(Node o : root.children)
    o.delete();
    root.save();

    您指示 Hibernate 从数据库中删除子项,但内存中的 root 对象仍然引用它们。由于关系配置为级联,Hibernate 将尝试再次保存它们(我猜这是“分离的实体传递给持久化”错误的原因)。因此,通过清除 root.children 来保持对象在内存中的状态一致。

  • Hibernate 严重依赖 Unit of Work 的概念.我不确定 Play 如何管理它,但看起来您应该在单元测试中调用 clearJPASession() 以确保现有 session 状态不会影响进一步的操作:

    root.save();

    clearJPASession();

    // test delete
    root = NodeUtil.getRoot(TEST_MENU);
  • 您定义关系的方式受支持,但不推荐(参见 2.2.5.3.1.1. Bidirectional )。请改用以下方法:

     @OneToMany(mappedBy = "parent", cascade=CascadeType.ALL, fetch=FetchType.EAGER)                           
    public List<Node> children = new LinkedList<Node>();

    @ManyToOne
    @JoinColumn(name="parent")
    public Node parent;
  • 静态方法不会干扰 Hibernate。

关于java - Play 中使用 Hibernate 的菜单树!,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5069807/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com