14

我有一个以下 Java 类,它也是一个 Hibernate 实体:

@Entity
@Table(name = "category")
public class Category {

    @ManyToOne
    @JoinColumn(name="parent_id")
    private Category parent;

    public Category getParent() {
        return parent;
    }

    public void setParent(Category parent) {
        this.parent = parent;
    }

类别表示类别树中的一个节点。我正在实现一个允许 CRUD 类别的网络服务。例如,该接口能够创建类别树节点,并将类别 ID 作为参数传递。

我只想创建一个新的 Category 对象并将其保存到数据库中而不获取父对象。我的数据提供者类如下所示:

public void createCategory(int parent_id, String name, CategoryType type) {
    Category category = new Category();
    category.setName(name);
    // category.setParent(?); <- I don't have this object here
    // category.setParentId(id); <- but I do have the id
    category.setType(type);
    this.categoryDao.save(category);
}

我的问题是:如果假设我不会调用 hibernate 来为我获取父对象(这将是愚蠢的),我该怎么做才能创建一个带有 parent_id 集的新类别对象?我可以为 Category 类提供 setParentId/getParentId 方法吗?它会有哪些休眠注释?

4

3 回答 3

21

Session.load()Hibernate为这种场景提供了一个名为(相当混乱)的方法。

Session.load()返回具有给定标识符的惰性代理而不查询数据库(如果具有给定标识符的对象已加载到 currentSession中,则它返回一个对象本身)。

您可以使用该代理来初始化正在保存的实体中的关系:

category.setParent(session.load(Category.class, parent_id));

请注意,此代码不会Category使用给定的 id 检查是否存在。但是,如果您的 DB 模式中有外键约束,则在传入无效 id 时会出现约束冲突错误。

这种方法的 JPA 等效称为EntityManager.getReference().

于 2013-04-16T18:09:02.453 回答
1

还有一个解决问题的方法 - 使用您想要引用的实体的默认构造函数并设置 id 和版本(如果它是版本化的)。

        Constructor<S> c = entityClass.getDeclaredConstructor();
        c.setAccessible(true);

        S instance = c.newInstance();
        Fields.set(instance, "id", entityId.getId());
        Fields.set(instance, "version", entityId.getVersion());
        return instance;

我们可以使用这种方法,因为我们不使用延迟加载,而是在 GUI 上使用实体的“视图”。这使我们能够摆脱 Hibernate 用来填充所有急切关系的所有连接。视图始终具有实体的 ID 和版本。因此,我们可以通过创建一个在 Hibernate 看来不是瞬态的对象来填充引用。

我尝试了这种方法和使用 session.load() 的方法。他们都工作得很好。我在我的方法中看到了一些优势,因为 Hibernate 不会在代码的其他地方使用它的代理泄漏。如果使用不当,我只会得到 NPE 而不是“没有会话绑定到线程”异常。

于 2014-01-14T12:38:45.277 回答
-1

你可以定义惰性初始化

@ManyToOne(fetch=FetchType.LAZY)

猜测该列可以为空,因此在没有父级(根)的情况下保存类别不是问题

于 2013-04-16T18:11:40.650 回答