данные весенней загрузки jpa DataIntegrityViolationException EntityExistsException - PullRequest
0 голосов
/ 05 мая 2020

есть две сущности Permission и Role с отношением @ManyToMany; Я хочу добавить дополнительное поле в их таблицу соединений, и я закодировал после этой статьи введите описание ссылки здесь , вот мой код:
Роль

@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Entity
@GenericGenerator(name = "jpa-uuid", strategy = "uuid")
public class Role {

    @Id
    @GeneratedValue(generator = "jpa-uuid")
    private String id;

    @JsonIgnore @ToString.Exclude
    @OneToMany(mappedBy = "role",cascade = CascadeType.ALL,orphanRemoval = false,fetch = FetchType.EAGER)
    private Set<RolePermission> role_permission;

    public void addPermission(Permission permission){
        RolePermission rolePermission  = new RolePermission(this,permission);
        role_permission.add(rolePermission);
        permission.getRole_permission().add(rolePermission);
    }
    public void removePermission(Permission permission){
        for(Iterator<RolePermission> iterator = role_permission.iterator();iterator.hasNext();){
            RolePermission rolePermission = iterator.next();
            if(rolePermission.getRole().equals(this) && rolePermission.getPermission().equals(permission)){
                iterator.remove();
                rolePermission.getRole().role_permission.remove(rolePermission);
                rolePermission.setRole(null);
                rolePermission.setPermission(null);
            }
        }
    }


    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Role role = (Role) o;
        return Objects.equals(id, role.id);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }
}


Разрешающая сущность


@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Entity
@GenericGenerator(name = "jpa-uuid", strategy = "uuid")
public class Permission {
    @Id
    @GeneratedValue(generator = "jpa-uuid")
    private String id;

    @JsonIgnore @ToString.Exclude
    @OneToMany(mappedBy = "permission",cascade = CascadeType.ALL,orphanRemoval = true,fetch = FetchType.EAGER)
    private Set<RolePermission> role_permission;

    public void addRole(Role role){
        RolePermission rolePermission  = new RolePermission(role,this);
        role_permission.add(rolePermission);
        role.getRole_permission().add(rolePermission);
    }
    public void removeRole(Role role){
        for(Iterator<RolePermission> iterator = role_permission.iterator(); iterator.hasNext();){
            RolePermission rolePermission = iterator.next();
            if(rolePermission.getRole().equals(role) && rolePermission.getPermission().equals(this)){
                iterator.remove();
                rolePermission.getRole().getRole_permission().remove(rolePermission);
                rolePermission.setRole(null);
                rolePermission.setPermission(null);
            }
        }
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Permission that = (Permission) o;
        return Objects.equals(id, that.id);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }
}

Ключ разрешения роли


@Embeddable
@Setter
@Getter
@NoArgsConstructor
public class RolePermissionKey implements Serializable {
    private static final long serialVersionUID = 4686642987484483168L;
    @Column(name = "role_id")
    private String role_id;
    @Column(name = "permission_id")
    private String permission_id;

    public RolePermissionKey(String roleId, String permissionId) {
        this.role_id=roleId;
        this.permission_id=permissionId;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        RolePermissionKey that = (RolePermissionKey) o;
        return role_id.equals(that.role_id) &&
                permission_id.equals(that.permission_id);
    }

    @Override
    public int hashCode() {
        return Objects.hash(role_id, permission_id);
    }
}

таблица отношений с дополнительным полем

@Entity
@Setter
@Getter
@NoArgsConstructor
public class RolePermission implements Serializable {
    private static final long serialVersionUID = 8274025418699729303L;

    @EmbeddedId
    RolePermissionKey id;

    @ManyToOne(fetch = FetchType.LAZY)
    @MapsId("role_id")
    Role role;

    @ManyToOne(fetch = FetchType.LAZY)
    @MapsId("permission_id")
    Permission permission;

    Date create_date  =new Date();


    public RolePermission(Role role,Permission permission) {
        this.role=role;
        this.permission=permission;
        this.id = new RolePermissionKey(role.getId(),permission.getId());
        this.create_date=new Date();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        RolePermission that = (RolePermission) o;
        return Objects.equals(role, that.role) &&
                Objects.equals(permission, that.permission);
    }

    @Override
    public int hashCode() {
        return Objects.hash(role, permission);
    }
}

тестовый код

        Role role5 = roleRepository.findByTitle("Role5");
        Permission permission6 = permissionRepository.findByTitle("Permission6");
        role5.addPermission(permission6);
        permission6.addRole(role5);
        roleRepository.save(role5);
        permissionRepository.save(permission6);

Затем я использовал тот же тестовый код в четырех местах для теста:
1 . класс реализует ApplicationRunner; инициализировать данные перед запуском весенней загрузки, no exception
2. класс весенней загрузки @SpringBootTest; no exception
3. класс контроллера @Controller; exception thrown
4. класс обслуживания @Service; exception thrown
Здесь исключение: 2020-05-05 09:11:35.952 WARN 17000 --- [nio-8080-exec-1] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.dao.DataIntegrityViolationException: A different object with the same identifier value was already associated with the session : [com.xxx.model.authentication.RolePermission#com.xxx.model.authentication.RolePermissionKey@9e2c8771]; nested exception is javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [com.xxx.model.authentication.RolePermission#com.xxx.model.authentication.RolePermissionKey@9e2c8771]]
теперь я не понимаю, является ли проблема кодом или чем-то еще. Я использовал тестовый код в 1,2, чтобы добавить связь между ролью и разрешением успешно, и получил ожидаемый результат. Но код не работает в 3,4.
Мне нужны подсказки и советы; Кстати, есть ли какие-нибудь реальные проекты с открытым исходным кодом, которые стоит изучить, в которых используются данные spring jpa, спасибо.

#################################################################
спасибо за помощь @ Olivier Depriester. Я исследовал элементы набора RolePermission как в ролевой, так и в разрешающей сущности в модели отладки; тип коллекции, поддерживающий связь двух сущностей, - set, и я перезаписал методы equals() и hashCode(), которые могли гарантировать, что сущность отношения (RolePermission) с тем же идентификатором не появится в одном наборе. перед выполнением save() фактически есть только один объект RolePermission, содержащийся как в Set of Role, так и в Permision. В тестовом коде есть два действия .save(), даже если я прокомментирую одно из них, исключение также будет сгенерировано. Мне интересно, есть ли проблема с файлом cascade. Странно, что тестовый код может где-то успешно выполняться.

1 Ответ

0 голосов
/ 05 мая 2020

Когда вы вызываете role5.get().addPermission(permission6);, вы создаете и сохраняете (*) RolePermission для role5 и permission6.

Когда вы вызываете permission6.addRole(role5);, вы создаете и сохраняете другой RolePermission для role5 и permission6.

==> Исключение: 2 экземпляра одной и той же сущности с одинаковым хэш-кодом в сеансе.

(*) Когда вы добавляете элемент в атрибут постоянного объекта, этот элемент сохраняется автоматически. И поскольку вы получаете role5 и permission6 из их репозитория, они являются постоянными объектами

Таким образом, вам нужно изменить свой код, чтобы создать 1 одиночный RolePermission, который вы можете назначить как Role, так и Permission

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...