我正在尝试在两个实体之间实现 @ManyToMany 关系:用户和权限。假设我们有具有 READ 和 WRITE 权限的 userA,以及具有 READ 权限的 userB,我希望能够保存用户及其权限,而无需在权限表中保存两次 READ 权限。
这些是我的桌子:
CREATE TABLE user (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
password VARCHAR(50) NOT NULL,
enabled BOOLEAN DEFAULT TRUE
);
CREATE TABLE authority (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
authority VARCHAR(50) UNIQUE NOT NULL
);
CREATE TABLE user_authority (
user_id BIGINT,
authority_id BIGINT,
PRIMARY KEY (user_id, authority_id),
FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE,
FOREIGN KEY (authority_id) REFERENCES authority(id) ON DELETE CASCADE
);
这是我的代码:
@Entity
@Data
@Builder
@Table(name = "user", schema = "user_database")
@NoArgsConstructor
@AllArgsConstructor
public class UserEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String username;
private String password;
private boolean enabled;
@ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.ALL})
@JoinTable(
name = "user_authority",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "authority_id")
)
private Set<AuthorityEntity> authority;
}
@Data
@Entity
@Builder
@Table(name = "authority", schema = "user_database")
@NoArgsConstructor
@AllArgsConstructor
public class AuthorityEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true)
private String authority;
@ToString.Exclude
@ManyToMany(mappedBy = "authority")
private Set<UserEntity> users;
}
SERVICE:
public Response<String> createUser(UserEntity user) {
UserEntity saveUserEntity = userRepository.save(userEntity);
log.info(saveUserEntity.toString());
return new Response<>(200, "SUCCESS", saveUserEntity.toString());
}
REPOSITORY:
@Repository
public interface UserRepository extends JpaRepository<UserEntity, Long> {
UserEntity findByUsername(String username);
}
保存两个用户后所需的输出是:
用户表
d, username, password
1, userA, 1234
2, userB, 4321
权限表
id, authority
1, READ
2, WRITE
USER_AUTHORITY 表
user_id, authority_id
1, 1
1, 2
2, 1
根据过去的经验,经过一些研究,我发现我的问题与 @ManyToMany 注释中的 CascadeType 变量有关。尽管 MERGE 是针对我遇到的问题最建议的解决方案,但它似乎不起作用。我尝试了使用 CascadeType 的各种方法,但我有以下输出:
关键“权限”的重复条目“阅读”....
CascadeType.MERGE、CascadeType.ALL - 与我在第 1 点得到的结果相同。
CascadeType.MERGE - 保存用户A后,当我尝试保存用户B时,出现以下错误:
org.hibernate.TransientObjectException:对象引用未保存的瞬态实例 - 在刷新之前保存瞬态实例:com.example.AuthService.repository.entity.AuthorityEntity
我也一直在尝试不同形式的坚持。还是没有运气。
发生
Duplicate entry 'READ' for key 'authority....
错误是因为您尝试在 READ
字段中保存具有相同 authority
值的两个不同的 AuthorityEntity 对象,而您已将该字段声明为唯一: authority VARCHAR(50) UNIQUE NOT NULL
。您的 userA
和 userB
包含不同的 AuthorityEntity 实例,因为您没有为它们指定 id。如果您删除 authority
字段上的唯一约束,保存将会成功,但您将得到如下内容:
AUTHORITY
id, authority
1, READ
2, WRITE
3, READ
USER_AUTHORITY
user_id, authority_id
1, 1
1, 2
2, 3
要获取你想要的内容,你必须先创建你想要的AuthorityEntities,然后在创建UserEntity时,为其选择现有的AuthorityEntities。