我遇到了hibernate和映射相同类型的多个对象的问题,也许我只是愚蠢。
我有一个产品
@Entity
@Table(name = "product")
public class Product {
@Id
private int id;
@ManyToMany(cascade = CascadeType.ALL)
private List<LanguageBasedText> name;
@ManyToMany(cascade = CascadeType.ALL)
private List<LanguageBasedText> longDesc;
(...)
}
和基于语言的文字
@Entity
@Table(name = "languageBasedText")
public class LanguageBasedText {
@EmbeddedId
private LanguageTextEmbeddedKey embeddedID;
@Column(name = "text")
private String text;
}
带有嵌入式密钥
@Embeddable
public class LanguageTextEmbeddedKey implements Serializable {
@Column(name = "productID")
private int productID;
@Column(name = "lang")
private String lang;
@Column(name = "type")
private String type;
//i get the type from an other source and it is the information where
//the text goes (name or longDesc or 100 other posibil multi
//language text fields)
}
问题是:我如何让hibernate映射正确
我想要一张像
这样的表格TEXT
product_id |郎|类型|文字
和
产品
product_id |其他一些东西|
答案 0 :(得分:1)
总结:您实际上并不想要多对多,您希望在多方面实现一对多的继承(使用单表每层次结构策略),或者在多方面使用过滤器使用@Where
的OneToMany属性。前一种方法可能更多OO&#34;后者更少打字,如果你有许多不同的i18n文本字段,可能会阻止类爆炸。 @DiscriminatorOptions
和@Where
都是hibernate注释,因此您无论如何都偏离了标准的JPA注释。
如果您考虑采用ManyToMany
方法,可以看到如果没有指定type='name'
条件的过滤条件,name
可以撤回longDesc
文字以及name
同一种语言的文字 - 这可能不对。因此,如果您应用过滤器,那么现在只有OneToMany
,因为每name
只有一个{productID, lang}
。
如果您在纯建模术语中考虑这一点:
Product
可以包含多个Name
个,但lang
只有一个名称; Name
对于候选键{ProductId, Lang}
; LongDesc
; Name
和LongDesc
视为单独的业务实体。因此,这是[{1}}中每个OneToMany
属性的LanguageBasedText
模型。 Product
,Name
(以及您提到的其他类似属性)碰巧共享相同的属性:它们实现相同的接口,因此可以将它们存储在一个带有鉴别器的表中。
继承策略
您可以利用文本实体的通用接口并实现继承策略。在您的情况下,使用discriminator列,您可以跳过并自然地使用Single-Table-Per-Hierarchy继承模型。在您的代码中,您实际上有:
LongDesc
用JPA&amp; amp;表达这一点使它适用于Hibernate:
public abstract class LanguageBasedText {}
public class NameText extends LanguageBasedText {}
public class LongDescText extends LanguageBasedText {}
public class Product {
private Set<NameText> names;
private Set<LongDescText> longDescs;
}
和abstract
,以防止直接存储。为清晰起见,请指定@Entity
以及@Table
。@DiscriminatorFormula
- 这解决了将错误类型加载到@org.hibernate.annotations.DiscriminatorOptions(force=true)
Product
类@Entity
中继承它。 NameText extends LanguageBasedText
属性添加到Product中,键入为文本类子类型参数化的集合(例如@OneToMany
)。在代码中:
Set<NameText>
N.B。上面还有很多其他(不太相关)的代码,包括所有类型的强制性no-args ctors,mutators / accessors等。我还在文本类的ctors中使用了@Entity @Table(name = "product")
public class Product {
@Id @GeneratedValue(strategy = GenerationType.AUTO)
Integer id;
private Product() {}
public Product(String sku) {
this.sku = sku;
}
// omitting the other properties
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "productID", insertable = false, updatable = false)
private Set<NameText> name = new HashSet<>();
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "productID", insertable = false, updatable = false)
private Set<LongDescText> longDesc = new HashSet<>();
private String sku;
...
}
@Embeddable public class LanguageTextEmbeddedKey implements Serializable {
private LanguageTextEmbeddedKey(){}
public LanguageTextEmbeddedKey(int productID, String lang, String type) {
this.productID = productID;
this.lang = lang;
this.type = type;
}
@Column(name = "productID") private int productID;
@Column(name = "lang") private String lang;
@Column(name = "type") private String type;
...
}
@Entity @Table(name = "languageBasedText")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorFormula(value="type")
@org.hibernate.annotations.DiscriminatorOptions(force=true)
public abstract class LanguageBasedText {
@EmbeddedId private LanguageTextEmbeddedKey embeddedID;
@ManyToOne() @JoinColumn(name="productID", insertable=false,updatable=false, nullable=false)
protected Product p;
private String text;
protected LanguageBasedText(){}
protected LanguageBasedText(int productID, String lang, String type, String text) {
this.embeddedID = new LanguageTextEmbeddedKey(productID, lang, type);
this.text = text;
}
...
}
@Entity @DiscriminatorValue("name")
public class NameText extends LanguageBasedText {
private NameText(){}
public NameText(int productID, String lang, String text) {
super(productID, lang, "name", text);
}
}
// similarly for LongDescText
,但我建议你用DDD方法替换那些ctors,例如productID
来封装那个id。
答案 1 :(得分:1)
首先,Product
中的关联不是@ManyToMany
。 LanguageBasedText
实体只能引用一个Product
,因此Product
中的关联必须为@OneToMany
。
但是,我认为您要实现的目标是让name
中的Product
列表包含引用LanguageBasedText
并且具有Product
的{{1}}个对象的列表a Type
&#34; name&#34;。 longDesc
列表应包含LanguageBasedText
个type
对象&#34; longDesc&#34;。
由于您使用的是Hibernate,因此最简单的解决方案是在@Where
列表中使用@OneToMany
注释,因此您需要Product
:
@OneToMany(cascade = CascadeType.ALL)
@Where(clause="type='name'")
private List<LanguageBasedText> names;
@OneToMany(cascade = CascadeType.ALL)
@Where(clause="type='longDesc'")
private List<LanguageBasedText> longDescs;
注意我还将您的列表更改为复数,因为这更有意义(英文)。
另一个(更复杂的)解决方案是使用继承并为每种类型定义LanguageBasedText
的子类,使用单个表策略并使用type
列作为鉴别器值。因此,您将拥有以下子类:
@Entity
@DiscriminatorValue("name")
public class NameLanguageBasedText {
... etc
但是你最终会得到数百个这样的子类,而且我不确定鉴别器列如何在嵌入式Id中工作。