Дубликаты в JPA

263
03 ноября 2017, 08:49

Как в spring data jpa бороться с дубликатами? Например мне нужно сохранить профиль с листом тегов и со связью многие ко многим. При сохранении отдельно одинаковых тегов ,которые уже есть или профиля с каскадом - теги дублируются в таблице. Column(unique = true) или NaturalID при дублировании тега по аннотированному полю выкидывает эксепшн о дубликате, но мне нужно игнорить дубликат или перезаписывать. Реализовал следующий пример через спринговский JpaRepository, но опять же эксепшн при сохранении post2, тогда как с post1 всё отлично. Вроде как тривиальная задача, как решить

Answer 1

Т.к. Тег - сущность независимая (она существует независимо от профилей), то и создается она самостоятельно, независимо от профилей, так ведь? Если так, то перед сохранением профиля, сначала, нужно сохранить список тегов.

Чтобы, при этом, избежать ошибки с дубликатами, список тегов сохраняется не сразу списком (например, repo.save(tagList)), а в цикле, сохраняя результаты в отдельную коллекцию:

for (Tag tag : tagList) {
    try {
        resultList.add(repo.save(tag));   
    } catch (Exception e) {
        // обработка ошибки
    }
}

Затем возвращаем клиенту коллекцию с результатами (или комбинированный ответ с двумя списками - удачно сохраненными тегами и дубликатами).

Answer 2

Column(unique = true) и try {} catch(){}

Update

try{
//your add to db code
}catch(Exception e) {
//do nothing
}

Update 2 Вот так

public void addTag(Tag tag) {
    try{
    tags.add(tag);
    tag.getPosts().add(this);
    } catch(Exception e) {}
}
Answer 3

На сколько мне известно, решить эту проблему на уровне JPA нельзя. Но можно нагородить свой велосипед:

@Service
public class TagService {
    private final Map<String, Tag> tags = new ConcurrentHashMap<>();
    private Lock lock = new Lock();
    @Autowired
    private TagRepository tagRepository;
    @PostConstruct
    private void init() {
        tagRepository.findAll()
          .forEach(i -> tags.put(i.getName(), i));
    }
    public Tag newTag(String name) {
        return tags.computeIfAbsent(name, name -> new Tag(name));
    }
    public Tag getTag(String name) {
        return tags.computeIfAbsent(name, name -> tagRepository.findByName(name));
    }
    public boolean save() {
        boolean locked = false;
        try {
            locked = lock.tryLock();
            if (locked) {
                tagRepository.saveAll(tags.values()
                                       .stream()
                                       .filter(i -> i.getId() == null)
                                       .collect(Collectors.toList()))
                  .forEach(i -> tags.put(i.getName(), i));
                return true;
            }
        }
        finally {
            if (locked)
                lock.unlock();
        }
        return false;
    }
}

Остаётся только получать экземпляры Tag из сервиса и вызывать tagService.save() до сохранения сущностей типа Post.

P.S. Буду очень рад, если кто-нибудь сможет подсказать более изящное решение, но не дёргающее БД чаще.

READ ALSO
Как преобразовать xml конфигурацию &lt;login-config&gt; в web.xml в java class?

Как преобразовать xml конфигурацию <login-config> в web.xml в java class?

Как преобразовать xml конфигурацию в webxml в java class?

237
Как подружить маркеры с кластерами?

Как подружить маркеры с кластерами?

В проекте есть макеры и кластерыНужно чтобы работало нажатие у обоих

239