I want to use the find method with an interface, instead of using with the implementation.
That said here is my Code:
public Goods findGoods(Long goodsId) {
return this.jpaApi.withTransaction(()->{
Goods goods = null;
try{
EntityManager em = this.jpaApi.em();
Query query = em.createQuery("select g from Goods g where id=:id", Goods.class);
query.setParameter("id", goodsId);
goods = (Goods) query.getSingleResult();
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return goods;
});
}
My Entity:
@Entity(name = "Goods")
@Table(name = "GOODS")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class GoodsImp implements Goods, Serializable {
..
}
My Interface:
@JsonTypeInfo(include = JsonTypeInfo.As.PROPERTY, property = "type",
use = Id.NAME, defaultImpl = GoodsImp.class, visible = true)
@JsonSubTypes({ @Type(value = ProductImp.class, name = "product"),
@Type(value = ServiceImp.class, name = "service") })
@ImplementedBy(GoodsImp.class)
public interface Goods {
..
}
Error:
java.lang.IllegalArgumentException: Unable to locate persister: interfaces.Goods at org.hibernate.internal.SessionImpl.find(SessionImpl.java:3422) at org.hibernate.internal.SessionImpl.find(SessionImpl.java:3365) at repository.JPAGoodsRepository.lambda$deleteGoods$2(JPAGoodsRepository.java:58) at play.db.jpa.DefaultJPAApi.lambda$withTransaction$3(DefaultJPAApi.java:197)
My doubt is why i have this issue, if when I use a Query statement works fine with the interface.
This works:
@Override
public Collection<Goods> getAllGoods() {
return this.jpaApi.withTransaction(() -> {
Collection<Goods> goods = null;
try {
EntityManager em = this.jpaApi.em();
Query query = em.createQuery("Select g from Goods g", Goods.class);
goods = query.getResultList();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return goods;
});
}
The EntityManager
method createQuery
you used is declared as:
<T> TypedQuery<T> createQuery(String qlString, Class<T> resultClass)
The only reason for the Class<T> resultClass
parameter is to infer the type of T
, so that you could write:
List<Goods> listOfGoods = em.createQuery("select g from Goods g where id=:id", Goods.class).getResultList();
without getting compiler warnings. The resultClass
parameter is certainly not telling the entityManager
which entity type you are querying for. This is done by the select g from Goods g
part of the query, and incidentally, Goods
is an alias for the entity GoodsImpl
(you could annotate the GoodsImpl
entity with @Entity(name = "Bads")
and select b from Bads b
would also work).
Now, if I understand correctly, you're asking why the call em.find(entityClass, primaryKey)
fails when Goods.class
is used for entityClass
. You can find the answer in the javadoc for EntityManager
, in which find
is said to throw:
IllegalArgumentException - if the first argument does not denote an entity type
An entity type is, unsurprisingly, a class annotated with @Entity
.
If you're asking why this is the way it is implemented, then the answer is pretty simple. An interface can be implemented by several classes. Suppose you had multiple entity classes implementing Goods
, each with its own table and its own id. There would be no reason for ids not overlap across these different entities. How is JPA supposed to know which of these entities you're hoping to get?