I've got a JPA @MappedSuperClass
and an @Entity
extending it:
@MappedSuperclass public class BaseClass { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Column private Boolean active; //getters & setters } @Entity public class Worker extends BaseClass{ @Column private String name; //getters & setters }
The active
field of the base class is a flag for the children entities. Only the active ones should be loaded in the application. Then I've written a generic Spring Data Proxy interface:
public interface Dao<T extends BaseClass, E extends Serializable> extends CrudRepository<T, E> { Iterable<T> findByActive(Boolean active); }
And this one is the interface that should be for Worker
data access, properly extending the previous one:
@Transactional public interface WorkerDao extends Dao<Worker, Long>{}
Well, now in my logic layer I've implemented an abstract class which will wrap the common code for CRUD operations over my entities. I'll have a service for each of them, but I want just to inherit from the abstract
one. I want to wire the specific repository for each of the services and provide it to the superclass using an abstract
method. That's how my superclass is implemented:
public abstract class GenericService<E extends BaseClass>{ public abstract Dao<E, Long> getDao(); //Here I've got some common operations for managing //all my application classes, including Worker }
The problem is that the getDao()
method uses the E
class parameter, which is guaranteed only to be a child of BaseClass
and not a javax.persistence.Entity
. When I try to access the DAO from my custom service implementation I get this error:
Caused by: java.lang.IllegalArgumentException: Could not create query metamodel for method public abstract java.lang.Iterable com.mycompany.model.daos.interfaces.Dao.findByActive(java.lang.Boolean)! at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:93)
Caused by: java.lang.IllegalArgumentException: Not an entity: class com.mycompany.model.BaseClass at org.hibernate.jpa.internal.metamodel.MetamodelImpl.entity(MetamodelImpl.java:203)
Which makes sense, because E
is defined as a child of BaseClass
. The compiler allows me to write this too:
public abstract class GenericService<E extends BaseClass && Entity>
However I get an error in the child Service that says Worker
class is not compatible with the signature for E
. Does anybody know how to solve this?
In the repository interfaces, we can add the methods like findByCustomerNameAndPhone() (assuming customerName and phone are fields in the domain object). Then, Spring provides the implementation by implementing the above repository interface methods at runtime (during the application run).
The goal of Spring Data repository abstraction is to significantly reduce the amount of boilerplate code required to implement data access layers for various persistence stores.
It's just a matter of annotating the abstract Repository
as @NoRepositoryBean
:
@NoRepositoryBean public interface Dao<T extends BaseClass, E extends Serializable> extends CrudRepository<T, E> { Iterable<T> findByActive(Boolean active); }
This way Spring relies on the underlying repository implementation to execute the findByActive
method.
Regarding to the annotation type restriction issue, it's not possible to declare an annotation restricted type. See the referenced answers below.
See also:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With