在 Spring 启动应用程序中,我有以下实体定义:
@Data
@Entity
@Table(name = "users")
public class User {
@Id
@Column(nullable = false, name = "username", length = 100)
private String username;
@JoinTable(name = "userrole",
joinColumns = { @JoinColumn(name = "username") },
inverseJoinColumns = { @JoinColumn(name = "role") }
)
@OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<Role> roles;`
我用的是Spring-data-jpa,Hibernate用H2做数据库。问题是spring-data-jpa,hibernate总是生成/创建带有单个列主键的连接表(DDL) 'userrole '。例如“用户名”。因此,如果将诸如{'username ',' user_role'}和{'username ',' admin_role'}之类的记录插入到连接表(' userrole ')中,则下一次插入会因主键“重复”而失败并出错。
我已经尝试在上面的定义中使用这两列,以及下面的变体:
@OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true
)
@JoinColumns({
@JoinColumn(name = "username"),
@JoinColumn(name = "role") })
private List<Role> roles;`
但是它们导致了相同或更严重的问题,例如,在后者中,即使表创建也会失败,因为只有一列用作可连接的主键。角色只是另一个包含 2 列“角色”和“描述”的表,基本上是一个角色目录。
我们如何向JPA指定@JoinTable应该同时使用“用户名”和“角色”列作为复合主键?
编辑:我试着按照建议使用一个独立的表格/实体,谢谢@ Kamil bóBen
@Data
@Entity
@Table(name = "users")
public class User {
@Id
@Column(nullable = false, name = "username", length = 100)
private String username;
@OneToMany(
fetch = FetchType.EAGER,
cascade = CascadeType.ALL,
mappedBy = "username",
orphanRemoval = true
)
@ElementCollection
private List<UserRole> roles;
UserRole是这样定义的
@Data
@NoArgsConstructor
@Entity
@Table(name = "userrole")
public class UserRole {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "userrole_seq")
Long id;
@Column(nullable = false, name = "username", length = 100)
private String username;
@Column(nullable = false, name = "role", length = 50)
private String role;
用户-角色连接表存储库定义为
@Repository
public interface UserRoleRepository extends CrudRepository<UserRole, Long> {
UserRole findByUsernameAndRole(String username, String role);
List<UserRole> findByUsername(String username);
List<UserRole> findByRole(String role);
}
诚然,很丑,但它是有效的。不知何故,它似乎使用了正确的findByUsername()方法来检索与用户相关的角色,可能与“mappdBy”子句有关。“黑魔法”!我还需要在JPA、Spring、Spring数据中找到更多方法
edit2:进一步更新:原来的@JoinTable也可以。但是这些关系需要被指定为@ManyToMany
@ManyToMany(
fetch = FetchType.EAGER,
cascade = CascadeType.MERGE
)
@JoinTable(name = "usersroles",
joinColumns = { @JoinColumn(name = "username") },
inverseJoinColumns = { @JoinColumn(name = "role") }
)
private List<Role> roles = new ArrayList<Role>();
这将为“用户-角色”表创建两列主键
感谢@Roman
如果role只有两列,例如user_id和Role,那么在jpa中的映射方式如下
@ElementCollection
@CollectionTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id"))
@Column(name = "role")
List<String> roles = new ArrayList<>();
否则,jpa实际上要求每个实体的标识符和连接列是单独的列,因此角色实体必须有id、user_id和role_name等列。
class Role {
@Id
Long id;
@ManyToOne
@JoinColumn(name = "user_id", referencedColumnName = "id");
User user;
String roleName;
// Other fields
}
并且在用户实体中
@OneToMany(mappedBy = "user") // user is Field's name, not a column
List<Role> roles = new ArrayList<>();
进一步阅读