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

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

问题描述

我有以下实体:Question 有 OneToOne Config.而Config有很多Option.全部配置为CASCADE.ALL(s. appendix)

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

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

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!

附录

作为参考,这是我的 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) 返回实体的永久副本.

问题是你调用 requestConfig.getNewOptions() 之前调用 questionRepo.save().在您描述的第一种情况下并不重要,因为分配给 question 的原始实例(即使用 Question question = new Question(...); 创建的实例)code>),以及使用 .forEach(o -> config.getOptions().add(o)) 行添加到 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))不会持久化.相反,只有 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() 之后简单地构造 idMapping 映射 (使用 question.getConfig().getNewOptions()).那应该处理这两种情况.

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

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

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