本文介绍了JPA的地图< KEY,VALUE>由JPQL查询失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在JPA中存储了一个Map,其中存储了每种语言的关键字翻译。如一个对象存储 Locale.ENGLISH - > 父亲,Locale.CHINESE - > 爸爸。另一个对象存储 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<>();

//其他字段已跳过
}

,我可以将许多关键字翻译存储到数据库。但是当使用JPQL查询时,它有一些问题:例如,我想要找到哪个关系具有英文键值Father:



这是我的代码:

 关系r = em.createQuery(select r from Relation r加入r.langMap其中(KEY(m)=:locale和VALUE(m)=:value),Relation.class)
.setParameter(locale,locale)
.setParameter value,value)
.getSingleResult();

它产生这个奇怪的/不工作的SQL:

  Hibernate:
选择
relation0_.id作为id1_18_
from
关系0_
内部连接
RelationName langmap1_
on relation0_.id = langmap1_.relation_id
其中
langmap1_.locale =?
和(
从$ b $中选择
langmap1_.value
RelationName langmap1_
其中
relation0_.id = langmap1_.relation_id
) =?
00:16:12.038警告ohejdbc.spi.SqlExceptionHelper - SQL错误:1242,SQLState:21000
00:16:12.038错误ohejdbc.spi.SqlExceptionHelper - 子查询返回多于一行

我不知道为什么会产生奇怪的子查询。



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

  CriteriaBuilder builder = em.getCriteriaBuilder(); 
CriteriaQuery< Relation> criteria = builder.createQuery(Relation.class);
Root<关系> 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();

它生成正确的SQL(其中langmap1_.locale =?和langmap1_.value =?),效果很好。



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

谢谢。

Env:

JPA2 ,Hibernate 4.2.3,MySQL方言

解决方案

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

  select r from Relation r join r.langMap m where(KEY(m)=:locale and m =:value)


I am storing a Map in JPA , which stores a keyword translation in each language . such as one object stores Locale.ENGLISH -> "Father" , Locale.CHINESE -> "PaPa". And another object stores Locale.ENGLISH -> "Mother" , Locale.CHINESE -> "MaMa";

Here is my working design :

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
}

It works well , I can store many keyword translations to DB. But when query with JPQL , it has some problems :

For example , I want to find which Relation has English key with value "Father" :

This is my code :

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();

It generates this strange / not-working 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

I don't know why it generates that strange subquery.

I can solve this problem by 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();

It generates correct SQL ( where langmap1_.locale=? and langmap1_.value=? ) and works well.

But I feel Criteria is too complicated. And I wonder why the JPQL failed? How to correct the JPQL ?

Thanks.

Env :

JPA2 , Hibernate 4.2.3 , MySQL dialect

解决方案

I had the same problem. It looks like accessing map by ref (without VALUE()) already gives you a map entry value, i.e. the next JPQL should be transformed to a valid SQL:

select r from Relation r join r.langMap m where ( KEY(m) = :locale and m = :value )

这篇关于JPA的地图&lt; KEY,VALUE&gt;由JPQL查询失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-03 08:19