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:
- CascadeType.ALL – after saving userA, when I try saving userB I get following error:
Duplicate entry ‘READ’ for key ‘authority….
-
CascadeType.MERGE, CascadeType.ALL – same result as I got at point 1.
-
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
Leave feedback about this