我坚持实体和字符串之间的M:N关系。用户可以拥有多个角色,每个角色可以分配给多个用户。角色只是一个字符串。角色包含在表格中,其中包含两列:roleId
和roleName
。
我创建了两个实体,但我绝对无法使它工作。第一个实体是用户:
@Entity
@Table(name="appUsers")
public class UserEntity {
@Id
private String login;
private String password;
@OneToMany(fetch=FetchType.EAGER,mappedBy="user") //we always need to load user's roles
private Collection<UsersToRoles> roles;
@Transient
private Collection<String> roleNames;
public String getLogin() {
return login;
}
public String getPassword() {
return password;
}
@PostLoad
void prepareRoleNames() {
roleNames = new HashSet<String>(roles.size());
for (UsersToRoles mapping : roles)
roleNames.add(mapping.getNameOfRole());
}
public Collection<String> getRoles() {
return roleNames;
}
}
第二个是与连接表关联的实体:
@Entity
@IdClass(UsersToRolesId.class)
public class UsersToRoles {
@Id
@SuppressWarnings("unused")
@Column(name="login")
private String login;
@Id
@SuppressWarnings("unused")
@Column(name="roleId")
private int roleId;
@ElementCollection(fetch=FetchType.EAGER)
@CollectionTable(name="userRoles", joinColumns={@JoinColumn(name="roleId")})
private List<String> roleName;
@ManyToOne
@JoinColumn(name="login")
@SuppressWarnings("unused")
private UserEntity user;
public String getNameOfRole() {
if (roleName.isEmpty())
throw new CommonError("Role name for roleId=" + roleId, AppErrors.ACCESSOR_UNAVAILABLE);
return roleName.get(0);
}
}
class UsersToRolesId {
private String login;
private int roleId;
/**
* Implicit constructor is not public. We have to
* declare public non-parametric constructor manually.
*/
public UsersToRolesId() {
}
@Override
public int hashCode() {
return 17*login.hashCode() + 37*roleId;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof UsersToRolesId))
return false;
UsersToRolesId ref = (UsersToRolesId)obj;
return (this.login.equals(ref.login) && this.roleId == ref.roleId);
}
}
问题是,roleName
集合始终为空。我无法让它发挥作用。当我在@CollectionTable
注释中的表名中出错时,它仍然有效。 JPA根本没有获取子集合。它使用表UsersToRoles
加入的用户表进行选择,但缺少对表userRoles
的连接。
我能做到吗?我是否可以热切地收集包含另一个急切获取的集合的实体?
答案 0 :(得分:1)
JPA提供程序通常具有表示默认的预先获取深度的配置属性,即Hibernate的hibernate.max_fetch_depth
。检查增加时是否可以看到更多信息。
另外,请考虑一下您的设计。仅在有限的情况下(性能方面),热切地获取集合的子集合可能是一个好主意。当您像这样注释您的实体时,您将在所有用例中使用急切提取。也许你最好使用“懒惰”并且只是明确地使用JOIN FETCH子句查询来获取它?
答案 1 :(得分:1)
您的映射完全错误。 UsersToRoles
有一个roleId
列。因此,它指的是单一角色。怎么会有角色名称的集合?登录列在实体中映射两次。此外,这看起来像一个简单的连接表,没有除roleId和login之外的任何其他属性,它们分别是User和Role的ID的外键。
您应该有两个实体:User
和Role
,ManyToMany
关联使用UsersToRoles
表作为连接表。而已。 UsersToRoles表不应映射为实体:它是纯连接表。