使用复合键保存实体获取 ConversionNotSupportedException

rob*_*del 6 spring hibernate jpa spring-data spring-data-jpa

我使用 spring boot 2,我的一些实体有复合键

当我尝试保存实体时,出现此错误

无法转换请求元素:org.springframework.beans.ConversionNotSupportedException:无法将“java.lang.Integer”类型的属性值转换为属性“sampling”所需的类型“com.lcm.model.SamplingsPK”;嵌套异常是 java.lang.IllegalStateException:无法将“java.lang.Integer”类型的值转换为属性“采样”所需的类型“com.lcm.model.SamplingsPK”:找不到匹配的编辑器或转换策略

我用那个方法得到我的实体

public Samples findById(Integer id, int year, String sampleLetter) {
    Optional<Samples> optSamples = samplesRepository.findById(new SamplesPK(new SamplingsPK(year, id), sampleLetter));

    if (optSamples.isPresent()) {
        return optSamples.get();
    }

    return null;
}


Samples samples = samplesService.findById(idSeq, year, samplesLetter);

Compressions compressionTest = null;

if (samples.getTestSamples().getAbsorptionTest() != null) {
    compressionTest = samples.getTestSamples().getCompressionTest();
} else {
    compressionTest = new Compressions();
}

samplesService.save(samples);
Run Code Online (Sandbox Code Playgroud)

我的实体

@Entity
@IdClass(SamplesPK.class)
public class Samples extends BaseEntity{
    @Id
    private String sampleLetter;

    @Embedded
    private TestSamples testSamples;

    @Id
    @ManyToOne(optional=false)
    @JoinColumns({
        @JoinColumn(name = "sampling_id", referencedColumnName = "id"),
        @JoinColumn(name = "sampling_year", referencedColumnName = "year")})
    private Samplings sampling;
}

@Entity
@IdClass(SamplingsPK.class)
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Samplings {
    @Id
    private Integer year;

    @Id
    @GeneratedValue
    private Integer id;

    @OneToMany(mappedBy = "sampling", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Samples> samples = new ArrayList<>();
}

public class SamplingsPK implements Serializable {

    private int year;

    private Integer id;

    public SamplingsPK(int year, Integer id) {
        this.id = id;
        this.year = year;
    }
}

public class SamplesPK implements Serializable {

    private SamplingsPK sampling;

    private String sampleLetter;

    public SamplesPK(SamplingsPK sampling, String sampleLetter) {
        this.sampling = sampling;
        this.sampleLetter = sampleLetter;
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑

保存样本没问题,当我从采样中通过时

pet*_*erh 7

问题是,由于 ID 是手动设置的,并且这些实体没有@Version属性,因此 Spring Data 没有好的方法来知道该实体是全新的实体还是现有的实体。在这种情况下,它决定它是一个现有实体并尝试 amerge而不是 a persist。这显然是一个错误的结论。

您可以在此处阅读有关 Spring Data 如何决定实体是否是新实体的更多信息。

我发现的最好的解决方案是始终让具有手动设置 ID 的实体类实现Persistable 接口。这解决了问题。对于任何此类情况,我都为自己制定了这条规则。大多数时候我不必实现,Persistable 因为我的实体要么有自动生成的密钥,要么我的实体使用“@Version”注释。但这是特殊情况。

因此,根据 Spring 官方文档中的建议,该类Samplings将变为:

@Entity
@IdClass(SamplingsPK.class)
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Samplings implements Persistable<SamplingsPK> {
    @Transient
    private boolean isNew = true; 

    @Id
    private Integer year;

    @Id
    @GeneratedValue
    private Integer id;

    @OneToMany(mappedBy = "sampling", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Samples> samples = new ArrayList<>();

    @Override
    public boolean isNew() {
        return isNew; 
    }

    @PrePersist 
    @PostLoad
    void markNotNew() {
        this.isNew = false;
    }

    @Override
    public SamplingsPK getId() {
        return new SamplingsPK(year, id);
    }
}
Run Code Online (Sandbox Code Playgroud)


Mit*_*ggs 0

此问题在https://jira.spring.io/browse/DATAJPA-1391@Id @ManyToOne上进行跟踪,并且与Samples. 作为一种解决方法,您可以尝试创建一个构造函数,Samplings该构造函数接受两个主键,或者一个接受java.lang.Integer? 这对于单个级别的复合主键有效,但如果您有多个级别,则可能不起作用。

您还year输入了SamplingsPKanint而不是Integer。这可能会导致 PK 识别出现问题,因为需要特别考虑来处理可自动装箱的基元类,而且我怀疑是否考虑过这一点。