JPA / Hibernate - 嵌入一个属性 [英] JPA/Hibernate - Embedding an Attribute
问题描述
我在映射类的嵌入属性时遇到了麻烦。我创建了一些类似于我试图说明的类。基本上,我有一个使用继承的@Embeddable类层次结构。顶级类零件号只有一个属性,扩展类不会为零件号类添加任何属性,它们只会添加一些验证/逻辑。
这是我的意思:
PART
@Entity
@Table(name =PART)
public class Part {
private Integer id;
私人字符串名称;
私人PartNumber partNumber;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
public Integer getId(){
return id;
}
public void setId(Integer id){
this.id = id;
@Column(name =PART_NAME)
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
@Embedded
public PartNumber getPartNumber(){
return partNumber;
}
public void setPartNumber(PartNumber partNumber){
this.partNumber = partNumber;
}
}
PARTNUMBER
@Embeddable
公共抽象类PartNumber {
protected String partNumber;
私人字符串generalPartNumber;
private String specificPartNumber;
private PartNumber(){
}
public PartNumber(String partNumber){
this.partNumber = partNumber;
$ b @Column(name =PART_NUMBER)
public String getPartNumber(){
return partNumber;
}
public void setPartNumber(String partNumber){
this.partNumber = partNumber;
$ b $ **
* @param partNumber
* @return
* /
public boolean validate(String partNumber){
//做一些验证
返回true;
}
/ **
*返回零件编号的前半部分
*
* @return generalPartNumber
* /
@Transient
public String getGeneralPartNumber(){
return generalPartNumber;
}
/ **
*返回零件号码
*的后半部分,这是每个汽车品牌特定的
*
* @return specificPartNumber
* /
@Transient
public String getSpecificPartNumber(){
return specificPartNumber;
$ b
FORD PARTNUMBER
public class FordPartNumber extends PartNumber {
/ **
*福特零件号格式为1234-#1234
*
* @param partNumber
* /
public FordPartNumber(String partNumber){
super(partNumber) ;
validate(partNumber);
$ b $ * b $ b *(非Javadoc)
*
* @see com.test.PartNumber#validate(java.lang.String )
* /
@Override
public boolean validate(String partNumber){
//做一些验证
return true;
$ b $ * b $ b *(非Javadoc)
*
*查看com.test.PartNumber#getGeneralPartNumber()
* /
@Override
public String getGeneralPartNumber(){
return partNumber;
$ * b $ b *(非Javadoc)
*
*参见com.test.PartNumber#getSpecificPartNumber()
* /
@Override
public String getSpecificPartNumber(){
return partNumber;
$ b
CHEVY PARTNUMBER
公共类ChevyPartNumber扩展PartNumber {
/ **
* Chevy Part Number格式为1234- $ 1234
*
* @param partNumber
* /
public ChevyPartNumber(String partNumber){
super(partNumber);
validate(partNumber);
$ b $ * b $ b *(非Javadoc)
*
* @see com.test.PartNumber#validate(java.lang.String )
* /
@Override
public boolean validate(String partNumber){
//做一些验证
return true;
$ b $ * b $ b *(非Javadoc)
*
*查看com.test.PartNumber#getGeneralPartNumber()
* /
@Override
public String getGeneralPartNumber(){
return partNumber;
$ * b $ b *(非Javadoc)
*
*参见com.test.PartNumber#getSpecificPartNumber()
* /
@Override
public String getSpecificPartNumber(){
return partNumber;
$ b
当然这不起作用,因为Hibernate忽略了继承层次结构,并且不喜欢PartNumber是抽象的事实。 有没有办法使用JPA或Hibernate Annotations来做到这一点?我已经尝试使用@Inheritance JPA注解。
我无法重构层次结构的PartNumber部分,因为原始开发人员希望能够使用N个XXXXPartNumber类扩展PartNumber。
有没有人知道这将成为JPA 2.0或Hibernate的新版本的一部分? 解决方案
组件(例如@Embeddable )不支持继承,很可能永远不会。这有一个很好的理由 - 实体标识符在Hibernate支持的所有继承策略中起着关键作用,组件没有(映射)标识符。
您有三种选择:a)将PartNumber(及其所有后代)映射为实体。 PartNumber可能仍然是抽象的:
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn (name =part_type,discriminatorType = DiscriminatorType.STRING)
公共抽象类PartNumber {
...
}
@实体
@ DiscriminatorValue (Ford)
公共类FordPartNumber扩展PartNumber {
...
}
$ b)根据你的例子,似乎所有PartNumber后代只有行为不同(它们不会引入任何要存储的新属性)。如果确实如此,则可以将PartNumber属性和您自己的鉴别符值(因此您知道实例化哪个类)映射为@Embedded私有属性,并在Part类的marshall / unmarshall适当子类中具有get / setPartNumber()访问器。你甚至可以编写你自己的Hibernate自定义类型来为你做这件事(这很简单)。
@Type(type =xmlBean)
public PartNumber getPartNumber(){
返回partNumber;
}
public void setPartNumber(PartNumber partNumber){
this.partNumber = partNumber;
}
和PartNumber后代将不必映射。不利的一面是,在数据库中处理XML更麻烦一点,因此可能不是您可能需要报告的理想方法。 OTOH,我使用它来存储插件设置,它为映射/数据库维护保存了很多的麻烦。
I am having a trouble mapping an embedded attribute of a class. I have created some classes that are similar to what I am trying to do to illustrate. Basically, I have an @Embeddable class hierarchy that uses Inheritance. The top level class "Part Number" has only one attribute, and the extending classes add no attributes to the "Part Number" class, they only add some validation/logic.
Here is what I mean:
PART
@Entity
@Table(name="PART")
public class Part {
private Integer id;
private String name;
private PartNumber partNumber;
@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(name="PART_NAME")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Embedded
public PartNumber getPartNumber() {
return partNumber;
}
public void setPartNumber(PartNumber partNumber) {
this.partNumber = partNumber;
}
}
PARTNUMBER
@Embeddable
public abstract class PartNumber {
protected String partNumber;
private String generalPartNumber;
private String specificPartNumber;
private PartNumber() {
}
public PartNumber(String partNumber) {
this.partNumber = partNumber;
}
@Column(name = "PART_NUMBER")
public String getPartNumber() {
return partNumber;
}
public void setPartNumber(String partNumber) {
this.partNumber = partNumber;
}
/**
* @param partNumber
* @return
*/
public boolean validate(String partNumber) {
// do some validation
return true;
}
/**
* Returns the first half of the Part Number
*
* @return generalPartNumber
*/
@Transient
public String getGeneralPartNumber() {
return generalPartNumber;
}
/**
* Returns the last half of the Part Number
* which is specific to each Car Brand
*
* @return specificPartNumber
*/
@Transient
public String getSpecificPartNumber() {
return specificPartNumber;
}
}
FORD PARTNUMBER
public class FordPartNumber extends PartNumber {
/**
* Ford Part Number is formatted as 1234-#1234
*
* @param partNumber
*/
public FordPartNumber(String partNumber) {
super(partNumber);
validate(partNumber);
}
/*
* (non-Javadoc)
*
* @see com.test.PartNumber#validate(java.lang.String)
*/
@Override
public boolean validate(String partNumber) {
// do some validation
return true;
}
/*
* (non-Javadoc)
*
* @see com.test.PartNumber#getGeneralPartNumber()
*/
@Override
public String getGeneralPartNumber() {
return partNumber;
}
/*
* (non-Javadoc)
*
* @see com.test.PartNumber#getSpecificPartNumber()
*/
@Override
public String getSpecificPartNumber() {
return partNumber;
}
}
CHEVY PARTNUMBER
public class ChevyPartNumber extends PartNumber {
/**
* Chevy Part Number is formatted as 1234-$1234
*
* @param partNumber
*/
public ChevyPartNumber(String partNumber) {
super(partNumber);
validate(partNumber);
}
/*
* (non-Javadoc)
*
* @see com.test.PartNumber#validate(java.lang.String)
*/
@Override
public boolean validate(String partNumber) {
// do some validation
return true;
}
/*
* (non-Javadoc)
*
* @see com.test.PartNumber#getGeneralPartNumber()
*/
@Override
public String getGeneralPartNumber() {
return partNumber;
}
/*
* (non-Javadoc)
*
* @see com.test.PartNumber#getSpecificPartNumber()
*/
@Override
public String getSpecificPartNumber() {
return partNumber;
}
}
Of course this does not work, because Hibernate ignores the Inheritance Hierarchy and doesn't like the fact that PartNumber is abstract. Is there some way to do this using JPA or Hibernate Annotations? I have tried using the @Inheritance JPA annotation.
I am not able to refactor the "PartNumber" part of the hierarchy because the original Developer wants to be able to extend PartNumber with N many XXXXPartNumber classes.
Does anyone know if anything like this will be a part of the JPA 2.0 or a new version of Hibernate?
Component (e.g. @Embeddable) inheritance is not supported and most likely never will be. There is a good reason for that - entity identifier plays a critical role in all inheritance strategies supported by Hibernate and components don't have (mapped) identifiers.
You have three choices:
A) Map PartNumber (and all its descendants) as entities. PartNumber may remain abstract:
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="part_type", discriminatorType=DiscriminatorType.STRING)
public abstract class PartNumber {
...
}
@Entity
@DiscriminatorValue("Ford")
public class FordPartNumber extends PartNumber {
...
}
B) Based on your example it seems that all PartNumber descendants differ in behavior only (they don't introduce any new properties to be stored). If that's indeed the case, you can map PartNumber properties plus your own discriminator value (so you know which class to instantiate) as @Embedded private property and have get/setPartNumber() accessors in Part class marshall / unmarshall appropriate subclasses. You can even write your own Hibernate custom type to do that for you (it's pretty straightforward).
C) If PartNumber descendants DO differ in properties that have to be stored and mapping them as entities is unacceptable for whatever reason, you can use marshall / unmarshall them to string (as XML or anything else that fits the bill) and store that. I'm using XStream for this exact purpose and I wrote a simple Hibernate type to go with it. Your Part mapping would look something like
@Type(type="xmlBean")
public PartNumber getPartNumber() {
return partNumber;
}
public void setPartNumber(PartNumber partNumber) {
this.partNumber = partNumber;
}
and PartNumber descendants won't have to be mapped at all. The downside, of course, is that dealing with XML in the database is a bit more of a hassle so that may not be the ideal approach for something you would potentially need to report on. OTOH, I'm using this for storing plugin settings and it saved me a lot of trouble with mappings / DB maintenance.
这篇关于JPA / Hibernate - 嵌入一个属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!