我正在使用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),但到目前为止还没有运气。
问题
总之,我想知道:
(请注意,我已经考虑过@MappedSuperclass,但它不适合我的需要。)