0

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);
        });
    }
  • Welcome to the SO Community. The community will help you with your issues, but there are some expectations you need to meet. Please take a few minuets to take the [Tour](https://stackoverflow.com/tour) and also review [ask]. In particular you need to post table definitions (ddl), and sample data, as text - **no images**. – Belayer Sep 01 '21 at 22:09
  • Also the actual code of the entities and how they get filled with data and persisted, please. – Jens Schauder Sep 02 '21 at 07:33

1 Answers1

0

You could try to use @SQLInsert to use a custom "upsert" statement for User as I have answered a few times already. See here Hibernate Transactions and Concurrency Using attachDirty (saveOrUpdate)

Christian Beikov
  • 15,141
  • 2
  • 32
  • 58