spring-projects / spring-data-jpa

Simplifies the development of creating a JPA-based data access layer.
https://spring.io/projects/spring-data-jpa/
Apache License 2.0
3.02k stars 1.42k forks source link

Add support for mapped superclasses without id attributes [DATAJPA-782] #1149

Closed spring-projects-issues closed 5 years ago

spring-projects-issues commented 9 years ago

Thomas Beauvais opened DATAJPA-782 and commented

I have an extremely valid model where a couple classes extend a @MappedSuperclass. I want to create a base repository in which I can find all of the super class and return the correct instances. This is doable. Spring Data JPA is throwing cryptic errors and I can't find any documentation on getting this to work. I have also tried @Inheritence and @Enity and that is an even deeper hole. Can you please provide some insight into this?

Caused by: java.lang.IllegalArgumentException: This class [class Element] does not define an IdClass
        at org.hibernate.jpa.internal.metamodel.AbstractIdentifiableType.getIdClassAttributes(AbstractIdentifiableType.java:200)
        at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation$IdMetadata.<init>(JpaMetamodelEntityInformation.java:223)
        at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.<init>(JpaMetamodelEntityInformation.java:79)
        at org.springframework.data.jpa.repository.support.JpaEntityInformationSupport.getEntityInformation(JpaEntityInformationSupport.java:67)
        at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getEntityInformation(JpaRepositoryFactory.java:146)
        at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:90)
        at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:70)
        at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:171)
        at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.initAndReturn(RepositoryFactoryBeanSupport.java:239)
        at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:225)
        at org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactorava:92)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1633)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1570)
        ... 27 common frames omitted

Affects: 1.8.2 (Fowler SR2)

Reference URL: http://stackoverflow.com/questions/32142718/hibernate-jpa-cant-map-different-id-for-sub-class?noredirect=1#comment52175230_32142718

spring-projects-issues commented 9 years ago

Oliver Drotbohm commented

Using @MappedSuperclass currently is supported but requires the identifier property to be contained in it. See AbstractPersistable for example.

I am not quite sure in how far it makes sense to not include the identifier property in that class as that could mean different identifier types in various sub-types, which in turn raises the question how a findOne(…) is supposed to work properly (i.e. I am not even sure persistence providers support EntityManager.find(id, type) with a mapped superclass, especially with entities using a compound key like with {{@IdClass}). Happy to explore opportunities though

spring-projects-issues commented 9 years ago

Thomas Beauvais commented

Thank you for the response though I am not sure if I follow. This is perfectly legal in JPA and Hibernate. The sub-classes have the same ID typed, though unfortunately they are named different.

Please check the reference URL to understand the problem fully. Let me know if there is anything else I can provide

spring-projects-issues commented 9 years ago

Oliver Drotbohm commented

I think I understood the problem entirely. I know that this is supported by JPA, however, until now it's not for Spring Data :). I guess mostly because nobody as asked so far. Again, I suggest the workaround to declare the identifier property on the mapped superclass and use @AttributeOverride in the subclasses until this is fixed

spring-projects-issues commented 9 years ago

Thomas Beauvais commented

With the following model

@MappedSuperclass
public abstract class Element extends AbstractPersistable<Long> {
}
@Entity
@Table(name = "primary_element")
@AttributeOverride(name = "id", column = @Column(name = "eid"))
public class PrimaryElement extends Element implements Serializable {
}
@Entity
@Table(name = "working_element")
@AttributeOverride(name = "id", column = @Column(name = "weid"))
public class WorkingElement extends Element implements Serializable {
}

I get the following exception..

Caused by: java.lang.IllegalArgumentException: Not an entity: class lacuna.model.Element
        at java.lang.reflect.Method.invoke(Method.java:497)
        at org.hibernate.jpa.internal.metamodel.MetamodelImpl.entity(MetamodelImpl.java:203)
        at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:434)
        at org.hibernate.jpa.criteria.QueryStructure.from(QueryStructure.java:139)
        at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:419)
        at org.hibernate.jpa.criteria.CriteriaQueryImpl.from(CriteriaQueryImpl.java:173)
        at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.javaat org.springframework.data.jpa.repository.support.SimpleJpaRepository.applySpecificationToCriteria(SimpleJpaRepository.java:568)

        at org.springframework.data.jpa.repository.support.SimpleJpaRepository.getQuery(SimpleJpaRepository.java:526)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
        at org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll(SimpleJpaRepository.java:315)
        at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61)
        at org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll(SimpleJpaRepository.java:67)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
        at java.lang.reflect.Method.invoke(Method.java:497)
spring-projects-issues commented 9 years ago

Oliver Drotbohm commented

I am not quite how that relates to the original feature request, but a from(…) by definition requires to be used with an entity type (see the spec and JavaDoc). For polymorphic queries, Element has to be an entity. I haven't been part of the EG when this was defined but I guess this restriction is due to mapped superclasses being defined as table-less (see 2.11.2, JPA 2.0 spec) and inheritance strategies only being definable on entities.

Please let's not side track this ticket with general JPA mapping support requests

spring-projects-issues commented 9 years ago

Thomas Beauvais commented

How can I side track my own ticket?

Element doesn't have it's own table yet shares properties with the sub-classes. They are defined in each of the sub-classes tables. So, the @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) should work but it's not.

It will work when I create two separate Repository interfaces. One for PrimaryElement and one for WorkingElement but not if I want to create a generic ElementRepository.

public interface ElementRepository extends CrudRepository<Element, Long> {
}
spring-projects-issues commented 9 years ago

Oliver Drotbohm commented

Because "side-tracking" is not defined by who created the ticket but what the ticket deals with. As already stated, mapped superclasses are currently only supported if they contain the id declaration. I acknowledged that and turned this into a feature request.

This however has nothing to do with general query abilities in JPA, which doesn't support a mapped superclass in a select clause. These are unrelated issues, hence my request for not mixing up one with the other. If it's about general JPA mapping related issues, I suggest to move to StackOverflow

spring-projects-issues commented 5 years ago

Jens Schauder commented

The discussion seems to have died. Closing the issue