使用JOINED继承策略时Spring JPA如何处理多级继承有限制吗?

时间:2016-01-17 10:11:48

标签: java spring postgresql jpa inheritance

我正在使用PostgreSQL数据库处理Spring Boot应用程序。在使用具有多级继承的JPA 2.1时,我遇到了问题。

我试图了解我是否正在尝试做一些不受支持的事情,或者我做错了什么。

为了说明,我使用JOINED继承策略将基本的三级继承结构放在一起:

A类

@Entity
@Inheritance(strategy=InheritanceType.JOINED)
@DiscriminatorColumn(name="dtype")
public class ClassA {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private long id;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }
}

B类

@Entity
@DiscriminatorValue("B")
public class ClassB extends ClassA {

    private String fieldInB;

    public String getFieldInB() {
        return fieldInB;
    }

    public void setFieldInB(String fieldInB) {
        this.fieldInB = fieldInB;
    }
}

C类

@Entity
@DiscriminatorValue("C")
public class ClassC extends ClassB {

    private String fieldInC;

    public String getFieldInC() {
        return fieldInC;
    }

    public void setFieldInC(String fieldInC) {
        this.fieldInC = fieldInC;
    }

    @Override
    public String toString() {
        return "[id: " + getId() + "; fieldInB: " + getFieldInB() + "; fieldInC: " + getFieldInC() + "]";
    }
}

存储库都很简单:

存储库

public interface ClassARepository extends CrudRepository<ClassA, Long> { }

public interface ClassBRepository extends CrudRepository<ClassB, Long> { }

public interface ClassCRepository extends CrudRepository<ClassC, Long> { }

然后我编写了以下简单的代码部分来检查它是否正常工作:

要测试的代码

    log.info("Checking inheritance structure ...");

    // Create object
    ClassC classC = new ClassC();
    classC.setFieldInB("bbbbb");
    classC.setFieldInC("ccccc");
    classCRepository.save(classC);
    log.info("--- Saved instance of class C.");

    // Test repository for Class C
    long countC = classCRepository.count();
    log.info("--- Result of classCRepository.count(): " + countC);
    for (ClassC  c : classCRepository.findAll()) {
        log.info("--- Item returned from classCRepository.findAll(): " + c.toString());
    }

    // Test repository for Class A
    long countA = classARepository.count();
    log.info("--- Result of classARepository.count(): " + countA);
    for (ClassA  a : classARepository.findAll()) {
        log.info("--- Item returned from classARepository.findAll(): " + a.toString());
    }

    // Test repository for Class B
    long countB = classBRepository.count();
    log.info("--- Result of classBRepository.count(): " + countB);
    for (ClassB  b : classBRepository.findAll()) {
        log.info("--- Item returned from classBRepository.findAll(): " + b.toString());
    }

    log.info("Inheritance structure check complete.");

我从上面的代码得到的输出如下:

输出

Checking inheritance structure ...
--- Saved instance of class C.
--- Result of classCRepository.count(): 1
--- Item returned from classCRepository.findAll(): [id: 10; fieldInB: bbbbb; fieldInC: ccccc]
--- Result of classARepository.count(): 1
--- Item returned from classARepository.findAll(): [id: 10; fieldInB: bbbbb; fieldInC: ccccc]
--- Result of classBRepository.count(): 1
SQL Error: 0, SQLState: 42703
ERROR: column classb0_.dtype does not exist

从输出中可以看出,我可以从根类(ClassA)的存储库或继承结构(ClassC)另一端的存储库中成功检索我的对象,但是当我尝试使用中产阶级的存储库(ClassB),抛出异常。

似乎某些组件期望ClassB的数据库表具有discriminator列,而只有ClassA的表应该需要它。 (为了完整起见,将@DiscriminatorColumn注释添加到ClassB什么都不做。)

实际的异常堆栈跟踪(为简洁起见减少)如下:

异常

org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:242) ~[spring-orm-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:225) ~[spring-orm-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:417) ~[spring-orm-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:
    ...
Caused by: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
    at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:123) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
    at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:126) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:112) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
Caused by: org.postgresql.util.PSQLException: ERROR: column classb0_.dtype does not exist
  Position: 106
    at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2182) ~[postgresql-9.4-1206-jdbc42.jar:9.4]
    at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1911) ~[postgresql-9.4-1206-jdbc42.jar:9.4]
    at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:173) ~[postgresql-9.4-1206-jdbc42.jar:9.4]
    at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:645) ~[postgresql-9.4-1206-jdbc42.jar:9.4]
    ...

对于所有这一切有点令人费解的是,如果我只改变继承策略,并使用SINGLE_TABLE或TABLE_PER_CLASS运行相同的代码,则代码运行完美。

我花了相当多的时间浏览各种文档(从Spring到PostgreSQL到JPA 2.1 Spec),但到目前为止还没有运气。

问题

总之,我想知道:

  1. 这是JPA 2.1的正常记录行为(如果是这样,请指出我所记录的位置)?
  2. 这是JPA 2.1的正常但未记录的行为吗?
  3. 有什么我做错了吗?
  4. Spring JPA实现有什么问题吗?
  5. 是否有替代解决方法允许我仍然使用JOINED策略进行多级继承?
  6. (请注意,我已经考虑过@MappedSuperclass,但它不适合我的需要。)

0 个答案:

没有答案