JPQL的JPL Map <KEY,VALUE>查询失败

sma*_*ufo 9 mysql hibernate jpa jpql

我在JPA中存储了一个Map,它存储了每种语言的关键字翻译.比如一个对象商店Locale.ENGLISH -> "Father" , Locale.CHINESE -> "PaPa".另一个对象存储Locale.ENGLISH -> "Mother" , Locale.CHINESE -> "MaMa";

这是我的工作设计:

public class Relation {
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private long id;

  @ElementCollection
  @MapKeyColumn(name="locale")
  @Column(name="value")
  @CollectionTable(name = "RelationName", joinColumns = @JoinColumn(name = "relation_id"))
  private Map<Locale, String> langMap = new HashMap<>();

  // other fields skipped
}
Run Code Online (Sandbox Code Playgroud)

它运行良好,我可以存储许多关键字翻译到DB.但是当用JPQL查询时,它有一些问题:

例如,我想找到哪个Relation的英文键值为"Father":

这是我的代码:

Relation r = em.createQuery("select r from Relation r join r.langMap m where ( KEY(m) = :locale and VALUE(m) = :value ) " , Relation.class)
  .setParameter("locale" , locale)
  .setParameter("value" , value)
  .getSingleResult();
Run Code Online (Sandbox Code Playgroud)

它会生成这个奇怪/无法运行的SQL:

Hibernate: 
    select
        relation0_.id as id1_18_
    from
        Relation relation0_ 
    inner join
        RelationName langmap1_ 
            on relation0_.id=langmap1_.relation_id 
    where
        langmap1_.locale=? 
        and (
            select
                langmap1_.value 
            from
                RelationName langmap1_ 
            where
                relation0_.id=langmap1_.relation_id
        )=?
00:16:12.038 WARN  o.h.e.jdbc.spi.SqlExceptionHelper - SQL Error: 1242, SQLState: 21000
00:16:12.038 ERROR o.h.e.jdbc.spi.SqlExceptionHelper - Subquery returns more than 1 row
Run Code Online (Sandbox Code Playgroud)

我不知道它为什么会生成那个奇怪的子查询.

我可以通过Criteria解决这个问题:

CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Relation> criteria = builder.createQuery(Relation.class);
Root<Relation> root = criteria.from(Relation.class);
criteria.select(root);
MapJoin<Relation , Locale , String> mapJoin = root.joinMap("langMap");
criteria.where(builder.and(
  builder.equal(mapJoin.key(), locale) ,
  builder.equal(mapJoin.value() , value))
);

return em.createQuery(criteria).getSingleResult();
Run Code Online (Sandbox Code Playgroud)

它生成正确的SQL(where langmap1_.locale=? and langmap1_.value=?)并且运行良好.

但我觉得Criteria太复杂了.我想知道为什么JPQL失败了?如何更正JPQL?

谢谢.

环境:

JPA2,Hibernate 4.2.3,MySQL方言

小智 18

我有同样的问题.看起来像通过ref访问map(没有VALUE())已经为你提供了一个map条目值,即下一个JPQL应该被转换为一个有效的SQL:

select r from Relation r join r.langMap m where ( KEY(m) = :locale and m = :value )
Run Code Online (Sandbox Code Playgroud)

  • 与“:value in”变体相比,生成的SQL恰如其妙,并且恰如其所期望地生成了SQL(其中locale =?和value =?),而后者又产生了另一个不必要的子选择。谢谢! (2认同)

小智 12

我在使用带有Hibernate的JPQL VALUE()运算符时遇到了类似的问题.似乎Hibernate实现了VALUE()运算符,就像Java中的java.util.Map.values()方法一样.它生成一个子查询,返回映射中的所有值,即映射表中与保存Map属性的实体相关的所有行.只要在映射中有多个键/值对,比较表达式(将标量表达式视为操作数)将失败.

你可以做的是转换比较表达式并将其转换为IN表达式.

代替:

select r from Relation r join r.langMap m 
  where ( KEY(m) = :locale and VALUE(m) = :value )
Run Code Online (Sandbox Code Playgroud)

你可以写:

select r from Relation r join r.langMap m 
  where ( KEY(m) = :locale and :value in (VALUE(m)) )
Run Code Online (Sandbox Code Playgroud)

我希望通过这种方式查询将起作用.