我正在尝试使用JavBeans + Hibernate映射创建和开源库,该库使用提供的数据库读取值(仅读取,不写入)。可悲的是,该数据库的设计不是很好。
我的问题是与ManyToOne关系是可选的-即它可以为null。
这是两个表(第一个是types
,第二个是metaTypes
):
+--------+---------------+
| typeID | typeName |
+--------+---------------+
| 1 | Chair |
| 2 | Table |
| 3 | Picnic Table |
| 4 | Bedside Table |
+--------+---------------+
+--------+--------------+
| typeID | parentTypeID |
+--------+--------------+
| 3 | 2 |
| 4 | 2 |
+--------+--------------+
因此,现在存在一个问题,那就是它们如何全部归为一体。在类型表中,存在各种类型,例如可以存在的事物列表。
在第二张表中,这些东西被分组在一起。如您所见,“野餐表”在
metaTypes
表中具有作为类型表的子项的条目。如果类型是基本类型,则
metaTypes
表中没有相应的条目。在一个设计良好的数据库中,至少会存在一个为parentTypeID
且为NULL的条目,但事实并非如此。我通过在@NotFound(action = NotFoundAction.IGNORE)
bean中使用Type
解决了这个问题。在实际数据库中,
metaType
表中有更多列包含其他相关信息,因此,我必须具有MetaType
bean。在冗长的介绍之后,真正的问题出现了:
如何将
Type
映射到其变体(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不为null,但变体列表应为null。
假设我查询了“表格”类型。在这种情况下,没有MetaType,并且由于有了
@NotFound
注释,它默默地失败了,并且metaType
字段中为null。到目前为止,一切都很好。但是由于“表”的typeID
为2
,所以我希望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(椅子),它将带来一个带有空变化集合的椅子,我希望这是您所期望的。