java - 可选的多对一与 hibernate

标签 java hibernate jpa

我正在尝试使用 JavBeans + Hibernate 映射创建开源库,该库使用提供的数据库来读取值(它只读取,不写入)。遗憾的是,这个数据库设计得不是很好。

我的问题是可选的 ManyToOne 关系 - 即它可以为空。

这是两个表(第一个是types,第二个是metaTypes):

+--------+---------------+
| typeID |   typeName    |
+--------+---------------+
|      1 | Chair         |
|      2 | Table         |
|      3 | Picnic Table  |
|      4 | Bedside Table |
+--------+---------------+

+--------+--------------+
| typeID | parentTypeID |
+--------+--------------+
|      3 |            2 |
|      4 |            2 |
+--------+--------------+

所以,现在的问题是这一切如何结合在一起。在类型表中有各种类型,就像它们可以存在的事物的列表一样。

在第二个表中,这些内容被分组在一起。正如您所看到的,“野餐 table ”在 metaTypes 表中有一个条目作为类型表的子项。

如果类型是基类型,则 metaTypes 表中没有相应的条目。在设计良好的数据库中,至少会存在一个 parentTypeID 为 NULL 的条目,但事实并非如此。我通过在 Type bean 中使用 @NotFound(action = NotFoundAction.IGNORE) 解决了这个问题。

在真实的数据库中,metaType 表中有更多包含附加相关信息的列,因此我必须有一个 MetaType bean。

在这个冗长的介绍之后,真正的问题出现了:
如何将类型映射到它的变体(MetaType)?

这是我尝试过的(缩短,大部分省略 getter 和 setter):

@Entity
@Table(name = "types")
public class Type {

    @Id
    private int typeID;     
    private String typeName, description;

    // meta types/ variations

    @OneToOne
    @JoinColumn(name = "typeID", insertable = false, updatable = false, nullable = true)
    @NotFound(action = NotFoundAction.IGNORE)
    private MetaType metaType; // -> this works as expected

    @OneToMany
    @JoinColumn(name = "typeID", referencedColumnName = "parentTypeID")
    @NotFound(action = NotFoundAction.IGNORE)
    private List<MetaType> variations; // this doesn't work

    /**
     * This method is quite easy to explain: Either this type is a base
     * type, in which case it has no metaType but holds the variations directly,
     * or it is a dervied type, in which case we get it's parent type and list the
     * variations of the parent type.
     */
    public List<MetaType> getVariations () {
        if (metaType != null) {
            return metaType.getParentType().getVariations();
        }
        return variations;
    }
}

@Entity
@Table (name = "metaTypes")
public class MetaType {

    @Id
    private int typeID;

    private Integer parentTypeID;

    // id <> object associations

    @OneToOne (mappedBy = "metaType")
    @JoinColumn(name = "typeID", insertable = false, updatable = false)
    private Type type;

    @OneToOne
    @JoinColumn(name = "parentTypeID", referencedColumnName = "typeID", insertable = false, updatable = false)
    private Type parentType;
}

目标应该明确。 假设我查询“床头柜”。那么metaType不为空,但变体列表应该为空。

假设我查询“Table”类型。在这种情况下,没有 MetaType,并且由于 @NotFound 注释,它会默默地失败,并且 metaType 字段中存在 null。到目前为止,一切都很好。但由于“Table”的 typeID2,我希望 variations 列表包含两个条目。但相反,我得到了下面的异常。

但不知怎的,这不起作用,我得到以下异常:

Exception in thread "main" java.lang.NullPointerException
    at org.hibernate.cfg.annotations.CollectionBinder.bindCollectionSecondPass(CollectionBinder.java:1456)
    at org.hibernate.cfg.annotations.CollectionBinder.bindOneToManySecondPass(CollectionBinder.java:864)
    at org.hibernate.cfg.annotations.CollectionBinder.bindStarToManySecondPass(CollectionBinder.java:779)
    at org.hibernate.cfg.annotations.CollectionBinder$1.secondPass(CollectionBinder.java:728)
    at org.hibernate.cfg.CollectionSecondPass.doSecondPass(CollectionSecondPass.java:70)
    at org.hibernate.cfg.Configuration.originalSecondPassCompile(Configuration.java:1695)
    at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1424)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1844)

那么,这种方法有什么问题以及如何让它发挥作用?

最佳答案

好的,以下是我的方法,看起来效果很好:

类型实体

@Entity
public class Type {

    @Id
    @Column(name = "typeId")
    private Integer id;

    @Column(name="typeName")
    private String name;


    @OneToMany
    @JoinColumn(name="parentTypeId")
    List<MetaType> metaTypes;
}

请注意,我的 @OneToMany 未使用 mappedBy 属性。在本例中,我使用 @JoinColumn 代替。这是一种单向关系。

元类型实体

@Entity
public class MetaType {

    @Id
    private Integer id;

    @MapsId
    @OneToOne
    @JoinColumn(name = "typeId")
    private Type type;

    @ManyToOne
    @JoinColumn(name = "parentTypeId")
    private Type parentType;
}

现在,我恢复给定的类型,就像您给出的示例一样,我得到了正确的数据:

TypeService service = ...;
Type t = service.getTypeById(2);

System.out.println(t.getName());
for(MetaType mt : t.getMetaTypes()){
    System.out.println("\t" + mt.getType().getName() + "-> " + mt.getParentType().getName());
}

这会产生输出

Table
    Picnic Table-> Table
    Bedside Table-> Table

它也适用于没有变化的类型。如果您查询类型 2(椅子),它将带来一个带有空变体集合的椅子,我希望这正是您所期望的。

关于java - 可选的多对一与 hibernate ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25461413/

相关文章:

spring - 注入(inject)两个@PersistenceContext 之一

java - 为什么在此示例中我没有收到 java.util.ConcurrentModificationException?

翻译时出现 java.lang.NoClassDefFoundError

java - 在 html 上,表格形式的数据,我如何使用 selenium web-driver java 单击并打印第二个位置的数据

java - Hibernate 在 Oracle 中使用序列生成器和序列

java - Jackson 对特定属性使用 getter

java - 使用parent.setChilds(null)显式删除子项

java - 不同switch case中的变量不能重名?

java - hibernate 工具 : persistence unit not found

java - 如果不存在 JPA 插入对象,如果存在则不执行任何操作