如果父级不存在,spring数据保存只会创建级联的子级 [英] spring data save does only create cascaded children if parent did not exist

查看:86
本文介绍了如果父级不存在,spring数据保存只会创建级联的子级的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我具有以下实体:Question具有OneToOne Config.并且Config有许多Option.全部配置为CASCADE.ALL(附录)

I have the following Entities: Question has OneToOne Config. And Config has many Options. All are configured to CASCADE.ALL (s. appendix)

基于RequestDTO(requestConfig),我用id=null为新问题或现有问题创建新的Option实体.

Based on a RequestDTO (requestConfig) I create new Option entities with id=null for either a NEW question or an EXISTING question.

在两种情况下我都想访问新的Options的生成的ID..但是,它确实适用于新问题,但不适用于现有问题:

In both cases I want to access the generated ids of the new Options.. However, it does work for new questions, but not for existing ones:

新问题(确定)

// 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

现有问题(破损,请参见最后一行,id为空)

Existing Question (Broken, see last line, ids are null)

// 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及其创建的ID,因为它们是从问题保存操作中级联的.我检查了数据库并将其插入!

Why is this happening? I would expect the LinkedHashMap idMapping to contain the newly created Options with their created ids since they were cascaded from the question save operation. I checked the DB and they are inserted!

APPENDIX

作为参考,这是我的RequestDTO和实体:

For reference, here is my RequestDTO and the entities:

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;
    }
    // ...
}

推荐答案

在新的Question上调用questionRepo.save()时,Spring Data会识别出您正在尝试保存新实体,并在内部调用EntityManager.persist() .

When you call questionRepo.save() on a new Question, Spring Data recognizes that you're trying to save a new entity, and invokes EntityManager.persist() internally.

EntityManager.persist(entity)使作为参数传递的实体持久化.

但是,当您在现有的Question上调用questionRepo.save()时,Spring Data在内部调用EntityManager.merge().

However, when you call questionRepo.save() on an existing Question, Spring Data invokes EntityManager.merge() internally.

EntityManager.merge(entity)返回实体的持久副本.

问题是您在调用questionRepo.save()之前先调用requestConfig.getNewOptions() .在您描述的第一种情况下,这无关紧要,因为分配给question的原始实例(即使用Question question = new Question(...);创建的实例)以及使用行Option的子实例>,成为持久性,并获取一个自动生成的ID.

The problem is that you call requestConfig.getNewOptions() before calling questionRepo.save(). It doesn't matter in the first case you've described, since the original instance assigned to question (i.e. the one created using Question question = new Question(...);), as well as the child instances added to Option using the line .forEach(o -> config.getOptions().add(o)), become persistent, and obtain an autogenerated id.

但是,在第二种情况下没关系很重要,因为使用行.forEach(o -> config.getOptions().add(o))将新的子实例添加到Option时,不会持久化.取而代之的是,只有questionRepo.save()返回的副本 引用的子实体实例是持久的(后者又返回EntityManager.merge()的结果).

However, it does matter in the second case, since the new child instances added to Option using the line .forEach(o -> config.getOptions().add(o)), do not become persistent. Instead, only the child entity instaces referenced by the copy returned by questionRepo.save() (which in turn returns the result of EntityManager.merge()) are persistent.

您只需在调用questionRepo.save()(使用question.getConfig().getNewOptions())之后构造idMapping映射 .那应该处理两种情况.

You should simply construct the idMapping map after calling questionRepo.save() (using question.getConfig().getNewOptions()). That should handle both cases.

这篇关于如果父级不存在,spring数据保存只会创建级联的子级的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆