Сохранение данных Spring создает каскадных потомков, только если родитель не существует - PullRequest
0 голосов
/ 01 июня 2018

У меня есть следующие объекты: Question имеет OneToOne ConfigConfig имеет много Option с.Все настроены на CASCADE.ALL (с. Приложение)

На основе RequestDTO (requestConfig) я создаю новые Option сущности с id=null для НОВОГО вопроса или СУЩЕСТВУЮЩЕГО вопроса.

В обоих случаях Я хочу получить доступ к сгенерированным идентификаторам нового Options. .Однако он работает для новых вопросов, но не для существующих:

Новый вопрос (ОК)

// RequestDTO requestConfig is a controller parameter
Question question = new Question(...);
Config config = requestDTO.createConfig(Optional.empty());
question.setConfig(config);

LinkedHashMap<String, Option> idMapping = requestConfig.getNewOptions();

idMapping.forEach((foo, option) -> System.out.println(option.getId())); // all null

question = questionRepo.save(question);

idMapping.forEach((foo, option) -> System.out.println(option.getId())); // 675, 676, ... etc

Существующий вопрос (Сломаны, см. Последнюю строку, идентификаторы равны нулю)

// RequestDTO requestConfig is a controller parameter
Question question = questionRepo.find(...);
Config config = requestDTO.getConfig(Optional.of(question.getConfig()));
question.setConfig(config);

LinkedHashMap<String, Option> idMapping = requestConfig.getNewOptions();

idMapping.forEach((foo, option) -> System.out.println(option.getId())); // all null

question = questionRepo.save(question);

idMapping.forEach((foo, option) -> System.out.println(option.getId())); // all null

Почему это происходит?Я ожидал бы, что LinkedHashMap idMapping будет содержать вновь созданные Option с их созданными идентификаторами, так как они были каскадированы из операции сохранения вопроса.Я проверил БД, и они вставлены!


ПРИЛОЖЕНИЕ

Для справки, вот мои RequestDTO и сущности:

public class RequestDTO {
    private LinkedHashMap<String, OptionDTO> optionDTOs;
    @JsonIgnore
    private LinkedHashMap<String, Option> newOptions = new LinkedHashMap<>();

    public Config getConfig(Optional<Config> oldConfig) {
        Config config = new Config();
        if (oldConfig.isPresent()) {
            config = oldConfig.get();
        }
        // update the options based on our OptionDTOs
        config.getOptions().clear();
        optionDTOs.stream()
                .map(o -> {
                    try { // to find the existing option
                        Option theOption = config.getOptions().stream()
                                // try to find in given config
                                .filter(existing -> o.getId().equals(existing.getId()))
                                .findAny()
                                // fallback to db
                                .orElse(optionRepo.findOne(Long.parseLong(o.getId())));
                        if (null != theOption) {
                            return theOption;
                        }
                    } catch (Exception e) {
                    }
                    // handle as new one by creating a new one with id=null
                    Option newOption = new Option(null, config);
                    newOptions.add(newOption);
                    return newOption;
                })
                .forEach(o -> config.getOptions().add(o));
        return config;
    }
    // getters
}

Объект: Вопрос

@Entity
public class Question {
    @OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "config_id", nullable = false)
    private Config config;

    // ...
}

Объект: Конфиг

@Entity
public class Config {

    @OneToOne(mappedBy = "config")
    @JoinColumn(name = "question_id", nullable = true)
    private Question question;

    @OneToMany(mappedBy = "config", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Option> options = new ArrayList<>();
    // ...
}

Объект: Опция

@Entity
public class Option {
    @ManyToOne
    @JoinColumn(name = "config_id", nullable = false)
    private Config config;

    public Option(Long id, Config config) {
        super();
        this.id = id;
        this.config = config;
    }
    // ...
}

Ответы [ 2 ]

0 голосов
/ 08 июня 2018

Я проверил БД, и они вставлены!

Это происходит с помощью entityManager, который просто возвращает новый идентификатор текущего объекта, а не связанных объектов, сохраненных каскадом.После сохранения вашего вопроса попробуйте найти снова по идентификатору вопроса.

0 голосов
/ 05 июня 2018

Когда вы вызываете questionRepo.save() для нового Question, Spring Data распознает, что вы пытаетесь сохранить новую сущность, и вызывает EntityManager.persist() для внутреннего использования.

EntityManager.persist(entity) делает сущность, переданную в качестве постоянного аргумента .

Однако, когда вы вызываете questionRepo.save() на существующем Question, Spring Data вызывает EntityManager.merge() внутри.

EntityManager.merge(entity) возвращает постоянную копию объекта .

Проблема в том, что вы звоните requestConfig.getNewOptions() до и звоните questionRepo.save().Это не имеет значения в первом случае, который вы описали, поскольку исходный экземпляр присваивается question (т. Е. Тот, который создан с использованием Question question = new Question(...);), а также дочерние экземпляры, добавляемые к Option с использованием строки .forEach(o -> config.getOptions().add(o)), становятся постоянными и получают автоматически сгенерированный идентификатор.

Однако во втором случае значение имеет значение , поскольку новые дочерние экземпляры, добавленные к Option с использованием строки .forEach(o -> config.getOptions().add(o)), , не становятся постоянными .Вместо этого, только дочерние объекты, на которые ссылается копия , возвращаемая questionRepo.save() (которая, в свою очередь, возвращает результат EntityManager.merge()), являются постоянными.

Вы должны просто построить карту idMapping после , вызвав questionRepo.save() (используя question.getConfig().getNewOptions()).Это должно обрабатывать оба случая.

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