我很难实现包含基元Map的实体的适当映射(Map< String,String>)。生成的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检索ZPrincipals列表时,如下所示:
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个实体(即“问题”),“admin”用户的用户列表 对象和两个“brett”用户对象(相同)。两个“brett”实例确实包含一个正确填充的Map。对于那些想知道的人,ZPrincipal类做覆盖equals(),它提供基于用户名的比较(与主键相同),以及 覆盖hashCode()也在用户名上进行散列。
我们的商店从“架构优先”设计开始工作,并且架构在数据库意义上似乎是“正确的”。可能这个问题可以通过插入映射表并在user_metadata表中生成id来解决,但是通过阅读可用的JPA和Hibernate文档(http://en.wikibooks.org/wiki/Java_Persistence/ElementCollection),似乎只使用两个表来映射一组原始元素应该是可能的。
映射中缺少什么?或者它是Hibernate中的一个错误?如果是这样,任何人都可以想到一个映射解决方案吗?我已经用注释捣乱了很多没有快乐。
答案 0 :(得分:2)
这是预期和记录的行为。实例是完全相同的对象(因为hibernate保证具有相同主键的同一实体在JVM中只有一个实例)。你可以做一个客户端不同的删除它们。您可以阅读Hibernate常见问题解答,了解他们决定不自动执行此操作的原因。
您尝试使用“FROM ZPrincipal”是告诉Hibernate不要急切地获取集合,因此它必须为每个实体加载地图(这可能会导致N + 1如果您打算读取每个获取的ZPrincipals的映射条目,请选择问题。(或者Hibernate发出后续查询来填充地图)。
最好的方法是自己进行去重复数据删除并保存一些查询。