JPA join criteria with datastore in google app engine

笑着哭i 提交于 2020-01-15 11:04:12

问题


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 in b.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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!