JPA / Hibernate - 嵌入一个属性 [英] JPA/Hibernate - Embedding an Attribute

查看:81
本文介绍了JPA / Hibernate - 嵌入一个属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在映射类的嵌入属性时遇到了麻烦。我创建了一些类似于我试图说明的类。基本上,我有一个使用继承的@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自定义类型来为你做这件事(这很简单)。



如果PartNumber后代在必须存储的属性上有所不同,将它们映射为实体是不可接受的,您可以使用marshall / unmarshall将它们串起来(如XML或任何其他符合法案的)并存储它们。为了达到这个目的,我使用了XStream,并且编写了一个简单的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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆