Hibernate merge() сохраняет дубликаты

118
27 августа 2019, 16:00

В Spring MVC приложении я имею упрощенную модель следующего вида. Класс User, у которого есть список файлов:

    @Entity
    @Table(name = "users")
    public class User {
        @Id
        @GeneratedValue
        private int id;
        @OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY, mappedBy = "owner")
        private Set<StoredFile> storedFiles = new HashSet<>();
        //.........//
    }

Файлы с одинаковым именем не должны добавляться в базу. Класс файл:

@Entity
@Table(name = "stored_files")
public class StoredFile {
    @Id
    @GeneratedValue
    private int id;
    @Column(unique = false)
    private String name;
    private LocalDateTime time;
    @ManyToOne
    private User owner;
    private boolean shared;
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof StoredFile)) return false;
        StoredFile that = (StoredFile) o;
        return  getName().equals(that.getName());
    }
    @Override
    public int hashCode() {
        int hash = Objects.hash(getName());
        return hash;
    }
}

Я добавляю первый файл user.getStoredFiles().add(new StoredFile(...)), Обновляю юзера, CascadeType.MERGE обновляет и коллекцию entityManger.merge(user) Файл появился в базе. Добавляю так же второй файл, но в базу попадает снова первый файл и второй: Если добавить третий файл, то их в базе будет уже 6. В списке storedFiles Элементы не дублируются, т.е. это уже происходит в недрах метода merge(). В чём может быть проблема? Пробовал ставить на имени @Column(unique=true) - в postgresql это срабатывает, записываются только новые файлы, на старые вылетает эксепшн, а вот в mysql после эксепшена файлы вообще не записываются в базу.

UPDATE:

добавление файла в методе контроллера:

@PostMapping("/savefile")
    @ResponseBody
    public void saveFile(@RequestParam(name = "file")MultipartFile file, @RequestParam String shared, HttpSession session){
        User user = (User) session.getAttribute("user");
        String name = file.getOriginalFilename();
        try {
            FileUtils.writeByteArrayToFile(new File(GeneralSettings.STORED_FILES_PATH + user.getLogin() + "/" + name), file.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
        user.getStoredFiles().add(new StoredFile(name, LocalDateTime.now(), user, shared.equals("1")));
        userDAO.updateUser(user);
    }

UserDAO:

@Repository("user_dao")
@Transactional
public class UserDAO {
    @PersistenceContext
    private EntityManager em;

    @Transactional
    public void updateUser(User user) {
        em.merge(user);
    }
}
Answer 1

При втором и последующем merge User вы мерджите старое значение с пустыми id в StoredFile.

Соответственно вам нужно:

  1. UserDAO.updateUser возвращал новую сущность которую вернул merge и подменять ею user в сессии.

Или

  1. Получать текущее состояние User из БД, добавлять записи и после записывать.
READ ALSO
Общее кратное число, оптимизация

Общее кратное число, оптимизация

Есть программа, которая работает, но числа long очень долго думает, а иногда и не все выписываетУсловия : программа должна работать пока не вводится...

132
Как подставить имя метода программно?

Как подставить имя метода программно?

Смысл в том, что в зависимости от получаемых методом GET параметрах сервлетом необходимо выполнить тот или иной метод - getN1FromDB, getN2FromDB и тд

103
Start/Stop SVG анимация

Start/Stop SVG анимация

Я хотел бы использовать эту красивую SVG-анимацию, размещенную на CodePen, но я не могу понять, как запустить или перезапустить анимацию:

119