【问题标题】:JPA Mapping with basic class inheritance具有基本类继承的 JPA 映射
【发布时间】:2014-10-30 14:36:48
【问题描述】:

我有一个用 JPA 映射的简单类:

@Entity
@Table(name = "SPONSOR")
public class Sponsor extends PersistableBusinessObjectBase implements SponsorContract {

@PortableSequenceGenerator(name = "SEQ_SPONSOR_CODE")
@GeneratedValue(generator = "SEQ_SPONSOR_CODE")
@Id
@Column(name = "SPONSOR_CODE")
private String sponsorCode;

@Column(name = "ACRONYM")
private String acronym;

@Column(name = "AUDIT_REPORT_SENT_FOR_FY")
private String auditReportSentForFy;

@Column(name = "CAGE_NUMBER")
private String cageNumber;

@Column(name = "COUNTRY_CODE")
private String countryCode;

@Column(name = "DODAC_NUMBER")
private String dodacNumber;

@Column(name = "DUN_AND_BRADSTREET_NUMBER")
private String dunAndBradstreetNumber;

@Column(name = "DUNS_PLUS_FOUR_NUMBER")
private String dunsPlusFourNumber;

@Column(name = "OWNED_BY_UNIT")
private String ownedByUnit;

@Column(name = "POSTAL_CODE")
private String postalCode;

@Column(name = "ROLODEX_ID")
private Integer rolodexId;

@Column(name = "SPONSOR_NAME")
private String sponsorName;

@Column(name = "SPONSOR_TYPE_CODE")
private String sponsorTypeCode;

@Column(name = "STATE")
private String state;

@Column(name = "CREATE_USER")
private String createUser;

@Column(name = "ACTV_IND")
@Convert(converter = BooleanYNConverter.class)
private boolean active;

@ManyToOne(cascade = { CascadeType.REFRESH })
@JoinColumn(name = "SPONSOR_TYPE_CODE", referencedColumnName = "SPONSOR_TYPE_CODE", insertable = false, updatable = false)
private SponsorType sponsorType;

@ManyToOne(cascade = { CascadeType.REFRESH })
@JoinColumn(name = "OWNED_BY_UNIT", referencedColumnName = "UNIT_NUMBER", insertable = false, updatable = false)
private Unit unit;

@ManyToOne(cascade = { CascadeType.REFRESH })
@JoinColumn(name = "ROLODEX_ID", referencedColumnName = "ROLODEX_ID", insertable = false, updatable = false)
private Rolodex rolodex;
....

我已经将该类扩展为一个非常简单的类

@Entity
public class SponsorMaintainableBo extends Sponsor {
}

这正是我对 SponsorMaintainableBo 所需要的。它是 Sponsor 的副本,从与 Sponsor 相同的表中读取。这是通过一些 xml 文档控制的查询框架所需要的,并且对于我正在做的事情需要是一个单独的对象。 SponsorMaintainableBo 需要插入到查询/维护框架中,以实现我们不希望在主父文档中出现的自定义详细信息。当我尝试使用 SponsorMaintainableBo 时,出现以下错误:

org.springframework.orm.jpa.JpaSystemException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.MySQLSyntaxErrorException: Unknown column 'DTYPE' in 'field list'
Error Code: 1054
Call: SELECT SPONSOR_CODE AS a1, DTYPE AS a2, ACRONYM AS a3, ACTV_IND AS a4, AUDIT_REPORT_SENT_FOR_FY AS a5, CAGE_NUMBER AS a6, COUNTRY_CODE AS a7, CREATE_USER AS a8, DODAC_NUMBER AS a9, DUN_AND_BRADSTREET_NUMBER AS a10, DUNS_PLUS_FOUR_NUMBER AS a11, OBJ_ID AS a12, OWNED_BY_UNIT AS a13, POSTAL_CODE AS a14, ROLODEX_ID AS a15, SPONSOR_NAME AS a16, SPONSOR_TYPE_CODE AS a17, STATE AS a18, UPDATE_TIMESTAMP AS a19, UPDATE_USER AS a20, VER_NBR AS a21 FROM SPONSOR WHERE (DTYPE = ?) LIMIT ?, ?
bind => [3 parameters bound]
Query: ReadAllQuery(referenceClass=SponsorMaintainableBo sql="SELECT SPONSOR_CODE AS a1, DTYPE AS a2, ACRONYM AS a3, ACTV_IND AS a4, AUDIT_REPORT_SENT_FOR_FY AS a5, CAGE_NUMBER AS a6, COUNTRY_CODE AS a7, CREATE_USER AS a8, DODAC_NUMBER AS a9, DUN_AND_BRADSTREET_NUMBER AS a10, DUNS_PLUS_FOUR_NUMBER AS a11, OBJ_ID AS a12, OWNED_BY_UNIT AS a13, POSTAL_CODE AS a14, ROLODEX_ID AS a15, SPONSOR_NAME AS a16, SPONSOR_TYPE_CODE AS a17, STATE AS a18, UPDATE_TIMESTAMP AS a19, UPDATE_USER AS a20, VER_NBR AS a21 FROM SPONSOR WHERE (DTYPE = ?) LIMIT ?, ?"); nested exception is javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.MySQLSyntaxErrorException: Unknown column 'DTYPE' in 'field list'
Error Code: 1054
Call: SELECT SPONSOR_CODE AS a1, DTYPE AS a2, ACRONYM AS a3, ACTV_IND AS a4, AUDIT_REPORT_SENT_FOR_FY AS a5, CAGE_NUMBER AS a6, COUNTRY_CODE AS a7, CREATE_USER AS a8, DODAC_NUMBER AS a9, DUN_AND_BRADSTREET_NUMBER AS a10, DUNS_PLUS_FOUR_NUMBER AS a11, OBJ_ID AS a12, OWNED_BY_UNIT AS a13, POSTAL_CODE AS a14, ROLODEX_ID AS a15, SPONSOR_NAME AS a16, SPONSOR_TYPE_CODE AS a17, STATE AS a18, UPDATE_TIMESTAMP AS a19, UPDATE_USER AS a20, VER_NBR AS a21 FROM SPONSOR WHERE (DTYPE = ?) LIMIT ?, ?
bind => [3 parameters bound]
Query: ReadAllQuery(referenceClass=SponsorMaintainableBo sql="SELECT SPONSOR_CODE AS a1, DTYPE AS a2, ACRONYM AS a3, ACTV_IND AS a4, AUDIT_REPORT_SENT_FOR_FY AS a5, CAGE_NUMBER AS a6, COUNTRY_CODE AS a7, CREATE_USER AS a8, DODAC_NUMBER AS a9, DUN_AND_BRADSTREET_NUMBER AS a10, DUNS_PLUS_FOUR_NUMBER AS a11, OBJ_ID AS a12, OWNED_BY_UNIT AS a13, POSTAL_CODE AS a14, ROLODEX_ID AS a15, SPONSOR_NAME AS a16, SPONSOR_TYPE_CODE AS a17, STATE AS a18, UPDATE_TIMESTAMP AS a19, UPDATE_USER AS a20, VER_NBR AS a21 FROM SPONSOR WHERE (DTYPE = ?) LIMIT ?, ?")
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:321)
at org.springframework.orm.jpa.DefaultJpaDialect.translateExceptionIfPossible(DefaultJpaDialect.java:120)

我不确定映射子类和继承所有 JPA 映射的最佳方式。请指教。

【问题讨论】:

  • 请提供有关SponsorMaintainableBo的更多详细信息。不清楚它有哪些额外的数据库字段,是否为它添加了另一个表以及它的用途。
  • 向我们展示 ReadAllQuery
  • 我以完全不同的方式解决了这个问题。我将通过记录清除 JPA 相关问题的正确答案来解决此问题。

标签: java jpa


【解决方案1】:

Here是一些关于JPA实体继承的信息。

您必须在实体上配置继承,仅扩展类是不够的。如果您想将SponsorMaintainableBo 放在单独的表中,请使用@Inheritance(strategy=TABLE_PER_CLASS) 注释Sponsor 实体。网上有很多配置不同类型继承的例子。

public enum InheritanceType {
    SINGLE_TABLE,
    JOINED,
    TABLE_PER_CLASS
};

Unknown column 'DTYPE' in 'field list' 表示SINGLE_TABLE 是默认继承类型,并且您的JPA 提供程序正在目标表中查找(默认命名)列DTYPE,以确定实体的确切类型。

【讨论】:

    【解决方案2】:

    您没有在SponsorMaintainableBo 中指定您所拥有内容的详细信息,因此很难理解其中的内容。

    你没有以正确的方式扩展你的实体。您需要提示您将如何扩展数据库中的原始表(所有内容都聚集在同一个表中,称为SINGLE_TABLE?一个具有一对一引用的新表,称为JOINED?副本具有称为TABLE_PER_CLASS的新字段的表?)

    您应该查看http://en.wikibooks.org/wiki/Java_Persistence/Inheritance 以了解如何正确实施。

    更新

    虽然还不太清楚SponsorMaintainableBO到底在做什么,但从你添加的细节来看,我怀疑原因是你在这里使用了错误的设计模式。如果您扩展 JPA 实体 Bean,系统会期望您这样做是为了添加其他字段,因此它还添加了区分数据库中两个实体(超类和子类)的机制。默认情况下,如@Predrag 所示,它需要一个名为DTYPE 的字段到您的表中,以便可以识别行是超类的实例还是子类的实例(标准SQL 不支持继承)。您可以使用其他机制,例如完全独立的表,或通过外键将 2 个表连接在一起,而第二个表仅添加子类添加的字段。

    现在从你所说的来看,你似乎没有做(或不需要)任何这些。如果我理解正确,那么您正在超载实体 bean 的责任,而您的框架预期会出现进一步的行为。实体 Bean 被设计为只是 Bean,即数据的载体。如果您扩展它们,JPA 知道您这样做是有原因的,以添加更多数据(即更多字段)。您似乎想要的是 Entity 的包装器,以便它也插入您的框架。这样,两个关注点保持独立,但仍然联系在一起。

    我不知道您的框架对SponsorMaintainableBo 的期望是什么,但我倾向于您应该使用不同的方法。而不是让SponsorMaintainableBo 扩展Sponsor 您可能应该放置一个Sponsor inside SponsorMaintainableBo 的实例,从而改变SponsorMaintainableBo 的关系-is-a- SponsorSponsorMaintainableBo -has-a- Sponsor。如果您希望两个实例绝对耦合,您可以将 Sponsor 设为构造函数参数并且像这样不可变:

    public class SponsorMaintainableBo 
    {
       private final Sponsor sponsor;
    
       public SponsorMaintainableBo(Sponsor sponsor)
       {
          this.sponsor = sponsor;
       }
    
       public Sponsor getSponsor()
       {
          return this.sponsor;
       }
    
       //... rest of the methods expected by the framework
    }
    

    如果以上内容还不够,并且框架需要SponsorMaintainableBo 中的某些字段与Sponsor 相同,那么您应该使用Decorator pattern。我想你已经在SponsorContract 中有这个,所以很可能你需要做的是:

    public class SponsorMaintainableBo implements SponsorContract
    {
       private final Sponsor sponsor;
    
       public SponsorMaintainableBo(Sponsor sponsor)
       {
          this.sponsor = sponsor;
       }
    
       public Sponsor getSponsor()
       {
          return this.sponsor;
       }
    
       //... rest of the methods specified by `SponsorContract`
    
       //... rest of the methods expected by the framework
    }
    

    SponsorContract 所需的方法只是将函数调用委托给底层的Sponsor 实例。

    (请注意,完整的装饰器模式旨在将多个实例链接在一起,因此它会将SponsorContract sponsor 而不是Sponsor sponsor 添加到您的SponsorMaintainableBo,这样您甚至可以将其他行为链接到您的实体周围,但我不'认为你不需要那种复杂程度)

    【讨论】:

    • 您能否提供更多关于 SponsorMaintainableBO 使用示例代码所做的事情的信息?我怀疑你在这里使用了错误的模式,但在我说之前我需要了解更多。
    猜你喜欢
    • 2012-12-08
    • 2016-01-25
    • 2015-06-07
    • 2015-08-02
    • 2019-07-05
    • 2011-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多