OiO.lk Blog java @CascadeType.MERGE won't work on child entity
java

@CascadeType.MERGE won't work on child entity


I am trying to implement a @ManyToMany relationship between 2 entities: user and authority. Supposing we have userA with authorities READ and WRITE and userB with authority READ, I want to be able to save both users and their authorities without having the READ authority saved twice in the authority table.

These are my tables:

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

And this is my code:


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

The desired output after saving both users is:

USER table

d, username, password

1,  userA,    1234

2,   userB,    4321

AUTHORITY table

id, authority

1,   READ

2,   WRITE

USER_AUTHORITY table

user_id, authority_id

1,        1

1,        2

2,        1

From past experience but also after doing some research I found out that my problem is related to the CascadeType variable inside the @ManyToMany annotation. Even though MERGE is the most suggested solution for the problem I’m having it doesn’t seem to work. I tried various ways of using CascadeType but I have the following outputs:

  1. CascadeType.ALL – after saving userA, when I try saving userB I get following error:

Duplicate entry ‘READ’ for key ‘authority….

  1. CascadeType.MERGE, CascadeType.ALL – same result as I got at point 1.

  2. CascadeType.MERGE – after saving userA, when I try saving userB I get following error:

org.hibernate.TransientObjectException: object references an unsaved transient instance – save the transient instance before flushing: com.example.AuthService.repository.entity.AuthorityEntity

I have also been trying with PERSIST in different variations. Still no luck.



You need to sign in to view this answers

Exit mobile version