问题
Suppose I have 2 JPA classes which model 2 entities in datastore (Google app engine) like these:
@Entity
public class Clazz {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Key classKey;
@Basic
private String classId;
@Basic
private String className;
@ManyToOne
private Subject subject;
}
@Entity
public class Subject {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Key subjectKey;
@Basic
private String subjectId;
@Basic
private String subjectName;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "subject")
private Set<Clazz> classes = new HashSet<Clazz>();
}
So, how to get Clazz objects which have classId and subjectId equal to given values using JPA criteria. I used this code but got an Exception like this:
em = EMF.get().createEntityManager();
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Clazz> criteriaQuery = criteriaBuilder.createQuery(Clazz.class);
Root<Clazz> root = criteriaQuery.from(Clazz.class);
List<Predicate> predicates = new ArrayList<Predicate>();
if (searchObj.getClassId() != null && searchObj.getClassId().length() > 0) {
Expression<String> classIdExpression = root.get("classId");
predicates.add(criteriaBuilder.equal(classIdExpression, searchObj.getClassId()));
}
if (searchObj.getSubjectId() != null && searchObj.getSubjectId().length() > 0) {
Join<Clazz, Subject> join = root.join("subject");
predicates.add(criteriaBuilder.equal(join.get("subjectId"), searchObj.getSubjectId()));
}
if (predicates.isEmpty()) {
criteriaQuery.select(root);
} else {
criteriaQuery.select(root).where(predicates.toArray(new Predicate[predicates.size()]));
}
TypedQuery<Clazz> query = em.createQuery(criteriaQuery);
return query.getResultList();
Exception:
javax.persistence.PersistenceException: SELECT DN_THIS FROM thesis.filesharing.model.Clazz DN_THIS JOIN DN_THIS.subject WHERE (DN_THIS.classId = '44444') AND (DN_THIS.subject.subjectId = 'IT5834'): Can only reference properties of a sub-object if the sub-object is embedded.
at org.datanucleus.api.jpa.NucleusJPAHelper.getJPAExceptionForNucleusException(NucleusJPAHelper.java:302)
at org.datanucleus.api.jpa.JPAQuery.getResultList(JPAQuery.java:202)
at thesis.filesharing.dao.impl.ClassDAOImpl.countFoundClasses(ClassDAOImpl.java:203)
at thesis.filesharing.bo.impl.ClassBOImpl.countFoundClasses(ClassBOImpl.java:84)
at thesis.filesharing.test.TestController.searchClasses(TestController.java:143)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.google.appengine.tools.development.agent.runtime.Runtime.invoke(Runtime.java:115)
at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:219)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:746)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:687)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:915)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:811)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:796)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
Thanks in advance
回答1:
Can only reference properties of a sub-object if the sub-object is embedded.
is pretty explicit enough. GAE/Datastore can't cope easily with "joining". Whether using JPA Criteria of JPQL is not of relevance, since they equate to the same requirements on the datastore
回答2:
Actually you should read some about GAE Datastore
since datastore based on No-SQL database
which is based mainly on data structure there aren't any relationships here (No Tables)
(Only Classes)
@OneToMany..etc not supported any more
From Wikipedia: In computing, NoSQL (commonly interpreted as "not only SQL"[1]) is a broad class of database management systems identified by non-adherence to the widely used relational database management system model. NoSQL databases are not built primarily on tables, and generally do not use SQL for data manipulation.
回答3:
Since GAE doesn't play well with criteria joins, but accepts statements of the following type:
SELECT f FROM Foo f JOIN f.bar b WHERE b.id = "42"
I managed to implement a hacky solution of this issue using aliases, and it works perfectly. I cannot guarantee the safety of this method though, so use at your own risk.
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Foo> q = cb.createQuery(Foo.class);
Root<Foo> foo = q.from(Foo.class);
foo.join("bar").alias("b1"); // Comment 1
foo.alias("b1"); // Comment 2
q.select(foo).where(cb.equal(foo.get("id"), "42"));
foo.alias("f1"); // Comment 3
TypedQuery<Foo> query = em.createQuery(q);
List<Foo> foos = query.getResultList();
Few things to note:
- Comment 1: This alias represents only the first
b
in the JPQL statement. - Comment 2: This alias represents the
b
inb.id
. - Comment 3: This alias represents the
f
in the JPQL statement
The resulting statement in em.createQuery(q)
is equivalent to the JPQL statement above.
来源:https://stackoverflow.com/questions/15029539/jpa-join-criteria-with-datastore-in-google-app-engine