@CascadeType.MERGE 不适用于子实体

问题描述 投票:0回答:1

我正在尝试在两个实体之间实现 @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 的各种方法,但我有以下输出:

  1. CascadeType.ALL - 保存 userA 后,当我尝试保存 userB 时,出现以下错误:

关键“权限”的重复条目“阅读”....

  1. CascadeType.MERGE、CascadeType.ALL - 与我在第 1 点得到的结果相同。

  2. CascadeType.MERGE - 保存用户A后,当我尝试保存用户B时,出现以下错误:

org.hibernate.TransientObjectException:对象引用未保存的瞬态实例 - 在刷新之前保存瞬态实例:com.example.AuthService.repository.entity.AuthorityEntity

我也一直在尝试不同形式的坚持。还是没有运气。

java spring hibernate jpa
1个回答
0
投票

发生

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。

© www.soinside.com 2019 - 2024. All rights reserved.