【问题标题】:Hibernate/JPA Map of Primitives causes strange left outer joinHibernate/JPA Map of Primitives 导致奇怪的左外连接
【发布时间】:2011-06-26 15:50:17
【问题描述】:

我很难对包含基元映射 (Map) 的实体进行适当的映射。生成的 SQL 不是我所期望的(左外连接),结果是在检索所述实体的列表时,结果中应该是唯一的实体重复。

这里是实体(缩写):

@Entity(name = "ZPrincipal")
@Table(name = "users")
public class ZPrincipal implements Principal, Serializable {
    @Id
    @Column(name = "username")
    private String username;

    @ElementCollection(fetch = FetchType.EAGER)
    @CollectionTable(name = "user_metadata", joinColumns = { @JoinColumn(name = "username") })
    @MapKeyColumn(name = "meta_key")
    @Column(name = "meta_value", nullable = false)
    private Map<String, String> metadata;
}

如您所见,有一个包含 Map 的用户类(ZPrincipal)。我的表格看起来像这样(同样,从“用户”中缩写一些字段,例如电子邮件、密码等):

CREATE TABLE users (
    username VARCHAR(60) NOT NULL,
    PRIMARY KEY (username)
);

CREATE TABLE user_metadata (
    username VARCHAR(60) NOT NULL,
    meta_key VARCHAR(255) NOT NULL,
    meta_value VARCHAR(1024) NOT NULL,
    CONSTRAINT user_meta_fk FOREIGN KEY (username) REFERENCES users (username),
    PRIMARY KEY (username, meta_key)
);

一些示例内容:

用户表:

|username |
+---------+
|admin    |
|brett    |
+---------+

元数据表:

|username |meta_key  |meta_value  |
+---------+----------+------------+
|brett    |key1      |value1      |
|brett    |key2      |value2      |
+---------+----------+------------+

通过上述映射,当我使用 Hibernate 检索 ZPrincipal 列表时,如下所示:

Criteria criteria = session.createCriteria(ZPrincipal.class);
List<ZPrincipal> list = criteria.list();

Hibernate 运行以下查询:

select this_.username as username9_1_, 
       metadata2_.username as username9_3_, 
       metadata2_.meta_value as meta2_3_, 
       metadata2_.meta_key as meta3_3_
from users this_ 
left outer join user_metadata metadata2_ on this_.username=metadata2_.username

导致返回的行:

|username9_1_  |username9_3_  |meta2_3_  |meta3_3_  |
+--------------+--------------+----------+----------+
|admin         |null          |null      |null      |
|brett         |brett         |key1      |value1    |
|brett         |brett         |key2      |value2    |
+--------------+--------------+----------+----------+

这会产生一个包含 3 个实体(即“问题”)的用户列表,即“管理员”用户 对象和两个“brett”用户对象(相同)。两个“brett”实例确实包含一个正确填充的地图。对于那些想知道的人,ZPrincipal 类 确实 覆盖了 equals(),它提供了基于用户名(与主键相同)的比较,并且 覆盖 hashCode() 也对用户名进行哈希处理。

我们的商店采用“模式优先”设计,并且模式在数据库意义上似乎“正确”。可能这个问题可以通过干预映射表和在 user_metadata 表中生成的 id 来解决,但是从阅读可用的 JPA 和 Hibernate 文档 (http://en.wikibooks.org/wiki/Java_Persistence/ElementCollection) 看来,应该只使用两个表来映射原始元素集合可能。

映射中缺少什么?或者它是Hibernate中的一个错误?如果是这样,任何人都可以想到一个映射解决方法吗?我已经把注释弄得乱七八糟了。

【问题讨论】:

  • 貌似解决了。我会在这里为遇到此问题的其他人发布答案。当我将基于条件的查询替换为:Query query = session.createQuery("FROM ZPrincipal"); 时,SQL 查询本身是相同的,但结果是正确唯一的 ZPrincipal 列表。恕我直言,这是一个 Hibernate 错误,因为“FROM ZPrincipal”在语义上应该等同于:Criteria criteria = session.createCriteria(ZPrincipal.class); List&lt;ZPrincipal&gt; list = criteria.list();

标签: hibernate jpa


【解决方案1】:

这是预期和记录的行为。这些实例是完全相同的对象(因为 hibernate 保证具有相同主键的同一实体在 JVM 中只有一个实例)。您可以执行客户端 distinct 来删除它们。您可以阅读 Hibernate 常见问题解答以了解他们决定不自动执行此操作的原因。

您尝试使用“FROM ZPrincipal”是告诉 Hibernate 不要急切地获取集合,因此它必须动态加载每个实体的映射(这可能导致 N+1如果您打算读取每个提取的 ZPrincipal 的映射条目,请选择问题。(或者 Hibernate 正在发出后续查询以填充映射)。

最好的方法是自己进行去重并保存一些查询。

参考: http://community.jboss.org/wiki/HibernateFAQ-AdvancedProblems#Hibernate_does_not_return_distinct_results_for_a_query_with_outer_join_fetching_enabled_for_a_collection_even_if_I_use_the_distinct_keyword

【讨论】:

  • 优秀的答案,正是我想要的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-01-18
  • 1970-01-01
  • 2013-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多