【问题标题】:JPA/Hibernate - Embedding an AttributeJPA/Hibernate - 嵌入属性
【发布时间】:2010-11-10 07:38:45
【问题描述】:

我在映射类的嵌入属性时遇到问题。我创建了一些类似于我试图说明的类。基本上,我有一个使用继承的@Embeddable 类层次结构。顶级类“Part Number”只有一个属性,扩展类没有为“Part Number”类添加属性,它们只是添加了一些验证/逻辑。

这就是我的意思:

部分

@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;
    }

}

零件编号

@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;

    }

}

福特零件编号

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;

    }

}

雪佛兰零件编号

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 忽略了继承层次结构并且不喜欢 PartNumber 是抽象的这一事实。 有没有办法使用 JPA 或 Hibernate Annotations 来做到这一点?我已经尝试使用 @Inheritance JPA 注释。

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

有谁知道这样的东西是否会成为 JPA 2.0 或新版 Hibernate 的一部分?

【问题讨论】:

    标签: java hibernate jpa jakarta-ee


    【解决方案1】:

    不支持组件(例如@Embeddable)继承,而且很可能永远不会支持。这是有充分理由的——实体标识符在 Hibernate 支持的所有继承策略中都扮演着关键角色,并且组件没有(映射的)标识符。

    你有三个选择:

    A) 将 PartNumber(及其所有后代)映射为实体。 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 类编组/解组适当的子类中具有 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,我用它来存储插件设置,它为我节省了很多映射/数据库维护的麻烦。

    【讨论】:

    • 对于 B,您知道使用自定义休眠类型编组/解组适当子类的任何好的教程吗?
    • 你只需要实现 Hibernate 的 UserType 接口——它相当简单。这是 Hibernate 文档中的一个示例:(hibernate.org/172.html)。在您的情况下,您将从“returnedClass()”方法返回 PartNumber,并根据您存储在列中的值(并从 ResultSet 获取)在“nullSafeGet()”中动态实例化适当的后代类。
    【解决方案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;
    

    你可能会滥用它,以至于你不会在意......

    【讨论】:

      【解决方案3】:

      嗯,不确定。你试过把@MappedSuperclasson PartNumber 和@Embeddable 关于子类。另见 - https://forum.hibernate.org/viewtopic.php?t=966129

      作为一个侧节点 - 您是否考虑过使用 Hibernate Validator 而不是本地开发的 validate 方法?

      【讨论】:

      • 很好的电话。我很好奇如何发现子类,因为配置没有提到它们,而且 JVM 不允许你发现它们。
      • “JVM 不允许你发现它们”是什么意思?
      • @skaffman - 我认为您需要将@Embedded 标签移动到父类并使用@MappedSuperclass ...可能吗?
      【解决方案4】:

      看起来你想要做的是使用我不相信hibernate支持的“可嵌入继承”。

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

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

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

      查看 cmets 中的链接...

      【解决方案5】:

      【讨论】:

        【解决方案6】:

        正如在其他答案中提到的那样,Hibernate 似乎不支持可嵌入的继承(至少我未能使其工作)。 我想出了一些解决方法(感谢@ChssPly76 的启发):

        1) 将@Embaddable 替换为@Entity 并将PartNumber 层次结构存储在专用表中。当然,它需要@OneToOne 关系才能与复合类(Part)一起使用。

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

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

        对于我的任务,我选择了第三种方法。我使用Hibernate Types 将对象序列化为JSON 并将结果存储在PostgreSQL 的jsonb 列中。 Hibernate Types 在后台使用 Jackson,因此它的所有注释(包括控制多态行为的注释)都可供您使用。在您的情况下,代码将是这样的:

        @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
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-02-17
          • 2015-01-24
          • 2017-04-19
          • 1970-01-01
          • 2021-04-26
          • 1970-01-01
          • 2014-02-07
          • 2016-07-06
          相关资源
          最近更新 更多