Избегайте множественных операторов удаления с помощью Spring Data JPA на ManyToMany - PullRequest
0 голосов
/ 25 апреля 2018

У меня есть некоторые сущности, которые связаны с @ManyToMany следующим образом (нерелевантные поля, конструкторы и методы удалены для краткости):

@Entity
public class Profile {
    @Id
    @GeneratedValue
    private Long id;

    @ManyToMany(cascade = {
        CascadeType.PERSIST,
        CascadeType.MERGE
    })
    @JoinTable(
        name = "profile_skills",
        joinColumns = @JoinColumn(name = "profile_id", referencedColumnName = "id"),
        inverseJoinColumns = @JoinColumn(name = "skill_id", referencedColumnName = "id")
    )
    private Set<Skill> skills;

    public void addSkill(Skill skill) {
        this.skills.add(requireNonNull(skill));
    }

    public Set<Skill> getSkills() {
        return unmodifiableSet(skills);
    }

    public void removeSkill(Skill skill) {
        this.skills.remove(skill);
    }
}

@Entity
@EqualsAndHashCode
public class Skill {
    @Id
    private String id;
    @Column(nullable = false)
    private String name;

    public String getId() {
        return id;
    }
}

И в классе обслуживания я обновляю Профиль новыми навыками, такими как:

@Service
@Transactional
public class ProfileUpdaterService implements ProfileUpdater {
    private final ProfileRepository profileRepository;
    private final SkillRepository skillRepository;

    ProfileUpdaterService(ProfileRepository profileRepository, SkillRepository skillRepository) {
        this.profileRepository = profileRepository;
        this.skillRepository = skillRepository;
    }

    @Override
    public WebResponse updateData(Supplier<User> userSupplier, ProfileRequest request) {
        Profile profile = profileRepository.findByOwner(userSupplier.get())
            .map(updateProfileValues(request))
            .orElseGet(() -> makeProfile(userSupplier, request));
        Profile save = profileRepository.save(profile);
        return save != null ? () -> OperationResult.PROFILE_UPDATED : () -> OperationResult.OPERATION_FAILED;
    }

    private Function<Profile, Profile> updateProfileValues(ProfileRequest request) {
        return profile -> {
            updateSkills(request, profile);
            return profile;
        };
    }

    private void updateSkills(ProfileRequest request, Profile profile) {
        Set<Skill> requestSkills = extractSkillsFromRequest(request).collect(toSet());
        requestSkills.forEach(profile::addSkill);
        Set<Skill> removedSkills = profile.getSkills().stream()
            .filter(skill -> !requestSkills.contains(skill))
            .collect(toSet());
        removedSkills.forEach(profile::removeSkill);
    }

    private Stream<Skill> extractSkillsFromRequest(ProfileRequest request) {
        if (request.getSkills().isEmpty()) {
            return Stream.empty();
        }
        return request.getSkills().stream()
            .filter(s -> !s.isEmpty())
            .map(this::findOrSaveSkill)
            .distinct();
    }

    private Skill findOrSaveSkill(String s) {
        return skillRepository.findById(s)
            .orElseGet(() -> skillRepository.save(Skill.of(s)));
    }
}

TL; DR: я получаю новый набор навыков из запроса, и мне нужно добавить те, которых нет в профиле, и удалить те, которых нет в запросе.

При этом способе выполняется один оператор удаления в таблице соединений для каждого навыка, удаляемого из профиля. Каков был бы (чистый) способ объединить это в один оператор удаления?

ПРИМЕЧАНИЕ. Это приложение Spring Boot 1.5.12 с Spring Data JPA 1.11.11

1 Ответ

0 голосов
/ 30 апреля 2018

Проблема была, конечно, в моих недостаточных знаниях Hibernate. Эта проблема может быть легко исправлена ​​установкой свойства jdbc.batch_size в Hibernate. В Spring Boot на application.properties добавьте эту строку:

spring.jpa.properties.hibernate.jdbc.batch_size=20

И готово, теперь Hibernate объединяет до 20 операторов.

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