具有复合(2列)主键的JPA @JoinTable



在一个spring-boot应用程序中,我有以下实体定义:

@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'具有单列主键。如。"用户名"。因此,如果在连接表('userrole')中插入{'username', 'user_role'}和{'username', 'admin_role'}之类的记录,则由于主键'重复'而导致下一次插入失败。

我试过在上面的定义中使用这两列,以及下面的变化:

@OneToMany(
cascade = CascadeType.ALL, 
orphanRemoval = true
)
@JoinColumns({
@JoinColumn(name = "username"),
@JoinColumn(name = "role") })
private List<Role> roles;`

但是它们会导致相同或更严重的问题,例如,在后者中,即使表创建失败,因为只有一个列被用作jointable的主键。角色只是另一个表,有"角色"one_answers"描述"两列,基本上是一个角色目录。

我们如何向JPA指定@JoinTable应该同时使用'username'和'role'列作为复合主键?

编辑:我尝试按照建议使用独立的表/实体,感谢@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()方法来检索与用户相关的角色,可能与' mapappedby '子句相关。"黑魔法"!还有很多,我仍然需要找到我的方式围绕JPA, Spring, Spring-data

edit2:进一步更新:原来的@JoinTable也可以工作。但是这些关系需要指定为@ manymany

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

按预期为'users-roles'表创建2列主键

感谢@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
}

在User实体

@OneToMany(mappedBy = "user") // user is Field's name, not a column
List<Role> roles = new ArrayList<>();

进一步阅读

最新更新