具有基本类继承的JPA映射

时间:2014-10-30 14:36:48

标签: java jpa

我有一个用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所需要的。它与赞助商重复,并与赞助商在同一张表中阅读。这是我们通过一些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映射的最佳方法。请指教。

2 个答案:

答案 0 :(得分:3)

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

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

public enum InheritanceType {
    SINGLE_TABLE,
    JOINED,
    TABLE_PER_CLASS
};

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

答案 1 :(得分:0)

您还没有详细说明SponsorMaintainableBo中的内容,因此很难理解您的内容。

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

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

<强>更新

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

现在从你所说的话来看,你似乎并没有做(或需要)任何这些。如果我理解正确,那么您正在使用您的框架预期的进一步行为来重载Entity bean的责任。实体Bean被设计为Beans,即数据的载体。如果您扩展它们,JPA会理解您出于某种原因这样做,以添加更多数据(即更多字段)。你似乎想要的是你的实体的包装器,以便它也插入你的框架。这样两种担忧都是分开但仍然紧密相连。

我不知道你的框架对SponsorMaintainableBo的期望是什么,但我倾向于你应该采用不同的方法。您可能应该将SponsorMaintainableBo 的实例放在 Sponsor中,从而改变Sponsor <之间的关系,而不是使SponsorMaintainableBo扩展SponsorMaintainableBo。 em> -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实例。

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