We are facing Unique constraint violation issue when concurrent clients are trying to insert data into child table.
Suppose We have tables below 1.Users user_id, first_name, last_name. 2.Projects project_id project_name and project_description.
And both are having many-to-many relation ship.
When two clients are trying to create a new users. Suppose client1 created user1(id=aa1),child record project(id=1).Client2 also created user2(id=aa2), child record is project(id=1). When two clients concurrently saving record user1, and child also inserting and while second client is trying as project(id-1) already available, it is throwing unique key constraint violation.
We are checking findby(projectx) before associating child record.
We are facing this issue rarely. We have implemented retry operation when we have an exception. Even with retry operation issue is not resolving consistently. Please guide me on this.
Exception is: HHH000346: Error during managed flush [org.hibernate.exception.ConstraintViolationException: could not execute statement. ERROR [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] (ForkJoinPool.commonPool-worker-1) ERROR: duplicate key value violates unique constraint "x_key"
We are using this sample code.
@Entity
@Table(name = "tbl_users", schema = "abcd")
public class user implements Serializable {
private static final long serialVersionUID = -8153443692098431986L;
@Id
@Column(name = "userId", nullable = false)
private String userid;
@Column(name = "last_name", nullable = false)
private String lastName;
@Column(name = "first_name", nullable = false)
private String firstName;
@ManyToMany(mappedBy = "projectsSet", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Project> projectsSet;
}
@Entity
@Table(name = "tbl_projects", schema = "abcd")
public class Project implements Serializable {
private static final long serialVersionUID = -8153443692098431986L;
@Id
@Column(name = "project_id", nullable = false)
private String projectId;
@Column(name = "project_Name", nullable = false)
private String projectName;
@Column(name = "projectDescription", nullable = false)
private String projectDescription;
@ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST}, fetch = FetchType.LAZY)
@JoinTable(name = "users_projects", schema = "abcd", joinColumns = {@JoinColumn(name = "_projectId")},
inverseJoinColumns = {@JoinColumn(name = "_userId")})
private Set<User> usersSet;
}
@Transactional
public void saveProject(ProjectDTO projectDTO, Set<UserDTO> userDTOSet) {
Optional<Project> optionalProject = getProjectFromDB(projectDTO.getProjectId());
List<User> existUsers = null;
if (!optionalProject.isPresent()) {
Map<String, UserDTO> userDTOMap = userDTOSet.stream().collect(Collectors.toMap(UserDTO::getUserId, userDTO -> userDTO));
if (MapUtils.isNotEmpty(userDTOMap)) {
existingUsers = getUsersFromDB(userDTOMap.keySet());
}
Project project = new Project();
try {
BeanUtils.copyProperties(project, projectDTO)
updateExistingUsers(project, userDTOMap, existingUsers);
addNewUsers(project, userDTOMap);
} catch (IllegalAccessException e) {
throw new BeanCopyException("Exception raised while copying the data", e);
} catch (InvocationTargetException e) {
throw new BeanCopyException("Exception raised while copying the data", e);
}
try {
ProjectRepository.save(user);
} catch (Exception e) {
throw new CustomException("Exception raised while saving the new Project", e);
}
}
private void updateExistingUsers (Project
newProject, Map < String, UserDTO > newUsers, List < User > existingUsers){
if (CollectionUtils.isNotEmpty(existingUsers)) {
existingUsers.stream().forEach(existinguser -> {
newProject.addNewUser(existinguser);
newUsers.remove(existinguser.getUserId());
});
}
}
private void addNewUsers(Project newProject, Map < String, UserDTO > userDTOMap){
userDTOMap.values().stream().forEach(userDTO -> {
User user = convertToModel(userDTO);
newProject.addNewUser(newUser);
});
}