2009-07-13 121 views
8

我有麻烦映射一个类的嵌入式属性。我创建了一些类似于我试图说明的类。基本上,我有一个使用继承的@Embeddable类层次结构。顶级类“零件号”只有一个属性,扩展类不会为“零件号”类添加任何属性,它们只会添加一些验证/逻辑。JPA /休眠 - 嵌入属性

这里是我的意思是:

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; 

    } 
} 

当然,这是不行的,因为Hibernate忽略了继承层次结构和不喜欢的事实,部分号码是抽象的。 有没有办法使用JPA或Hibernate Annotations来做到这一点?我曾尝试使用@Inheritance JPA注释。

我无法重构层次结构的“PartNumber”部分,因为原始开发人员希望能够使用N个XXXXPartNumber类扩展PartNumber。

有没有人知道这样的事情是否会成为JPA 2.0或新版本Hibernate的一部分?

回答

15

组件(例如@Embeddable)继承不被支持,并且很可能永远不会。这有一个很好的理由 - 实体标识符在Hibernate支持的所有继承策略中起着关键作用,组件没有(映射)标识符。

你有三种选择:

A)地图PARTNUMBER(及其所有后代)的实体。部分号码可能仍然摘要:

@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)根据你的榜样,这一切似乎PARTNUMBER后裔行为的区别仅(他们没有引入任何新的属性存储)。如果确实如此,则可以将PartNumber属性和您自己的鉴别符值(因此您知道实例化哪个类)映射为@Embedded私有属性,并在Part类的marshall/unmarshall适当子类中具有get/setPartNumber()访问器。你甚至可以编写你自己的Hibernate自定义类型来为你做(这很简单)。 C)如果PartNumber后代不同于必须存储的属性并将它们映射为实体,无论出于何种原因都不可接受,那么可以使用marshall/unmarshall将它们串起来(如XML或任何符合条例草案的内容)和存储该。为了达到这个目的,我使用了XStream,并且编写了一个简单的Hibernate类型来与它一起使用。你的零件映射看起来像

@Type(type="xmlBean") 
public PartNumber getPartNumber() { 
    return partNumber; 
} 
public void setPartNumber(PartNumber partNumber) { 
    this.partNumber = partNumber; 
} 

和PartNumber后裔将不必映射。不利的一面是,在数据库中处理XML更麻烦一点,因此可能不是您可能需要报告的理想方法。 OTOH,我用这个来存储插件设置,它为我节省了映射/数据库维护的麻烦很多

1

嗯,不知道。您是否尝试在子类上放置@MappedSuperclasson PartNumber和@Embeddable 。请参阅 - https://forum.hibernate.org/viewtopic.php?t=966129

作为一个节点 - 您是否考虑过使用Hibernate验证器而不是本地生成的验证方法?

+0

尼斯通话。我很好奇如何发现子类,因为配置没有提及它们,并且JVM不允许你发现它们。 – skaffman 2009-07-13 19:20:21

+0

“JVM不允许你发现它们”是什么意思? – Hardy 2009-07-14 08:39:56

+0

@skaffman - 我想你需要将@Embedded标签移动到父类并使用@MappedSuperclass ...可能吗? – Petriborg 2009-07-14 19:19:12

1

它看起来像你试图做的是使用“嵌入式继承”,我不相信hibernate支持。

@Embeddable 
@Inheritance(strategy=InheritanceType.SINGLE_TABLE) 
@DiscriminatorColumn(name="partType", discriminatorType=DiscriminatorType.STRING) 
public PartNumber getPartNumber() 
{ 
    return partNumber; 
} 

因为它看起来像福特和雪佛兰之间的dicriminating价值看起来像一种格式。我可能会隐藏映射并添加一个不同的函数来返回你想要的特定类型。

@Embeddable 
public PartNumber getPartNumber() 
{ 
    return partNumber; 
} 

@Transient 
public SpecificPartNumber getSpecificPartNumber() 
{ 
    return PartNumberFactory.create(getPartNumber()); 
} 

查看评论中的链接...

2

我有什么,我此刻使出我自己的模式类似的问题是这样的:

父类:

@Entity 
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) 
@SequenceGenerator(name="SEQ", sequenceName="part_id_seq", initialValue=1, allocationSize=1) 
public abstract class BasePart { 
    @Id 
    @Column(name="part_id") 
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ") 
    protected Long partId; 

    @YourBusinessKeyAnnotation 
    @Column(name="part_number") 
    protected String partNumber 
    ... 
} 

子类:

@Entity 
public class FordPart extends BasePart { 
    ... 
} 

@Entity 
public class ChevyPart extends BasePart { 
    ... 
} 

现在我可以操纵biz键了,但是我需要这样做,因为每种不同的零件类型都有自己的表格(这对我们很有用)。

您也可以使用@Embedded@AttributeOverrides我认为要指定不同的列名,但是您需要......有一个annotation docs的示例。

@Entity 
public class Person implements Serializable { 

    // Persistent component using defaults 
    Address homeAddress; 

    @Embedded 
    @AttributeOverrides({ 
      @AttributeOverride(name="iso2", column = @Column(name="bornIso2")), 
      @AttributeOverride(name="name", column = @Column(name="bornCountryName")) 
    }) 
    Country bornIn; 
    ... 
} 

...

@Entity 
public class Person implements Serializable { 

    // Persistent component using defaults 
    Address homeAddress; 

    @Embedded 
    @AttributeOverrides({ 
      @AttributeOverride(name="iso2", column = @Column(name="bornIso2")), 
      @AttributeOverride(name="name", column = @Column(name="bornCountryName")) 
    }) 
    Country bornIn; 
    ... 
} 

...

@Embedded 
@AttributeOverrides({ 
     @AttributeOverride(name="city", column = @Column(name="fld_city")), 
     @AttributeOverride(name="nationality.iso2", column = @Column(name="nat_Iso2")), 
     @AttributeOverride(name="nationality.name", column = @Column(name="nat_CountryName")) 
     //nationality columns in homeAddress are overridden 
}) 
Address homeAddress; 

您可以滥用这还不够,你不会在意......

0

正如在其他答案中提到的似乎Hibernate不支持继承的嵌入式(至少我没有使它的工作)。 我想出了一些解决方法(感谢@ ChssPly76为灵感):

1)@Entity更换@Embaddable并储存在专用表格PartNumber层次。当然,它需要@OneToOne关系用于复合类(Part)。

2)引入完全反映数据库表结构的中间实体类。然后手动将实体映射到您的域模型类(并返回)。这种方法以编写和测试映射代码的手工工作为代价提供了极高的灵活性。

3)以序列化形式存储PartNumber后代,并依赖于序列化程序,它们必须能够在反序列化期间实例化正确的类。

对于我的任务,我选择了第三种方法。我使用Hibernate Types将对象序列化为JSON,并将结果存储在PostgreSQL的jsonb列中。 Hibernate Types使用了杰克逊的内幕,所以它的所有注释(包括那些控制多态行为的注释)都可以使用。你的情况的代码会是这样的:

@Entity 
public class Part { 
    // ... 

    @Type(type = "com.vladmihalcea.hibernate.type.json.JsonBinaryType") 
    private PartNumber partNumber; 

} 

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") 
@JsonSubTypes(value = [ 
    JsonSubTypes.Type(value = FordPartNumber::class, name = "FordPartNumber"), 
    JsonSubTypes.Type(value = ChevyPartNumber::class, name = "ChevyPartNumber") 
]) 
public abstract class PartNumber { /* ... */ } 

public class FordPartNumber extends PartNumber { /* ... */ } 

public class ChevyPartNumber extends PartNumber { /* ... */ } 

产生的JSON在数据库将看起来像

{ 
    "type": "FordPartNumber", 
    // ... other serialized properties of the FordPartNumber class 
}