【问题标题】:jpa criteriabuilder join mapsjpa criteriabuilder 加入地图
【发布时间】:2011-04-07 11:55:54
【问题描述】:

我有以下条件生成器查询

    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Object> critQuery = cb.createQuery();

    Root<Role> role = critQuery.from(Role.class);

    //create a join between role and permission
    MapJoin<Role,String,Permission> perm = role.joinMap("permissions");
    critQuery.multiselect(role.get("label"), perm.key(), perm.value());

    //this line throws NPE
    Query query = em.createQuery(critQuery);

最后一行抛出空指针异常。

java.lang.NullPointerException
at org.hibernate.ejb.criteria.path.AbstractPathImpl.prepareAlias(AbstractPathImpl.java:246)
at org.hibernate.ejb.criteria.path.AbstractPathImpl.render(AbstractPathImpl.java:253)
at org.hibernate.ejb.criteria.path.AbstractPathImpl.renderProjection(AbstractPathImpl.java:261)

【问题讨论】:

  • 非常有趣!检查source code,我认为这是一些类路径问题。

标签: jpa


【解决方案1】:

我也有同样的问题。经过几个小时的处理,调试了 Hibernate 源代码,一遍又一遍地检查了书籍和 JPA 2.0 规范中的示例,我决定在 EclipseLink 中尝试一下。

所以,我创建了一个非常简单的示例:一个员工有一个电话号码地图,其中键是电话类型(家庭、办公室、手机),值是电话号码。

@ElementCollection(fetch=FetchType.EAGER)
@CollectionTable(name="emp_phone")
@MapKeyColumn(name="phone_type")
@Column(name="phone_num")
private Map<String, String> phoneNumbers;

我可以验证这与 EclipseLink 2.1 和 OpenJPA 2.1.0 完美配合,但在 Hibernate 3.5.3、3.6.1.、3.6.3 中失败

CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Employee> criteria = builder.createQuery(Employee.class);
Root<Employee> employeeRoot = criteria.from(Employee.class);
criteria.select(employeeRoot);
MapJoin<Employee, String, String> phoneRoot = employeeRoot.joinMap("phoneNumbers");

criteria.where(builder.equal(phoneRoot.key(), "HOME"));

System.out.println(entityManager.createQuery(criteria).getResultList());

我想,如果 Criteria API 失败,也许我可以使用命名查询来完成。有趣的是,Hibernate 不支持 KEY、VALUE 或 ENTRY 关键字,因此查询被证​​明是错误的。

http://opensource.atlassian.com/projects/hibernate/browse/HHH-5396

这就是运行:

String query = "SELECT e FROM Employee e JOIN e.phoneNumbers p WHERE KEY(p) IN ('HOME')";
System.out.println(entityManager.createQuery(query, Employee.class).getResultList());

在休眠中它会生成以下 SQL 查询:

   select
        employee0_.id as id0_,
        employee0_.name as name0_ 
    from
        Employee employee0_ 
    inner join
        emp_phone phonenumbe1_ 
            on employee0_.id=phonenumbe1_.Employee_id 
    where
        KEY(phonenumbe1_.phone_num) in (
            'HOME'
        )

这显然是错误的。

同样,在 EclipseLink 和 OpenJPA 中,这是可行的。

所以,很明显,Hibernate 一定有问题。我在 Hibernate Jira 问题跟踪器中提交了一个错误

http://opensource.atlassian.com/projects/hibernate/browse/HHH-6103

并已在 Hibernate 用户论坛中发布问题

https://forum.hibernate.org/viewtopic.php?f=1&t=1010411

【讨论】:

  • 感谢您的所有努力。我相信很多人会发现它很有用
  • 我遇到了同样的问题,如果有合适的休眠解决方案,我会非常高兴。欢迎每一个提示:)
  • @Tobias 如果您查看 Hibernate 论坛,您会注意到我在那里提出了两种解决方法。
  • @edalorzo 你的意思是 Hibernate Bug 跟踪页面。我想我会考虑使用选项#2 -> 2。不要使用地图,使用另一种集合。在我的例子中,我创建了一个类(一种具有键和值的条目类),然后使用一个集合来保存这个条目对象的信息。正确实现 equals() 和 hashCode 让我只使用键值来查找集合中的项目。
  • 这个休眠错误现已修复!
【解决方案2】:

对于那些坚持使用 Hibernate 3.6 的人,我能够通过映射加入并使用谓词限制结果。这个解决方案有一个重要的警告。此技术仅在您感兴趣的映射键存在于每条记录时才有效。否则,您将丢失结果集中可能出现的记录。

我的情况与 Edwin 的例子非常相似。我们有一个带有当前名称映射的 Person 实体,其中映射键是名称类型,值是名称对象。就我而言,我想检索一个人的当前标准名称。

Root<Person> person = query.from(Person.class);
Join currentName = person.join("currentNames");

NameLookup standardName = NameLookup.lookup("ST");

Predicate useridMatches = criteria.equal(person.get("userid"), "user1");
Predicate isStandardName = criteria.equal(currentName.get("nameType"), standardName);
Predicate useridAndStandardName = criteria.and(useridMatches, isStandardName);
query.where(useridAndStandardName);

在 JPQL 中,我们通常使用 WITH 子句来限制连接。

inner join person.currentNames currentStandardName 
with currentStandardName.nameType.id = :standardNameLookupId

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-22
    • 1970-01-01
    • 2020-06-22
    • 2014-08-15
    相关资源
    最近更新 更多