使用复合键保存实体get ConversionNotSupportedException

时间:2018-08-04 03:27:00

标签: spring hibernate jpa spring-data-jpa spring-data

我使用Spring Boot 2,并且我的某些实体具有复合键

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

  

无法转换请求元素:   org.springframework.beans.ConversionNotSupportedException:失败   将类型'java.lang.Integer'的属性值转换为所需的类型   “ com.lcm.model.SamplingsPK”用于属性“ sampling”;嵌套异常   是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);

我的实体

@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;
    }
}

编辑

当我通过采样时保存样品没问题

2 个答案:

答案 0 :(得分:0)

此问题的跟踪源为https://jira.spring.io/browse/DATAJPA-1391,与@Id @ManyToOne中使用Samples有关。作为一种解决方法,您可以尝试为Samplings创建一个构造函数,该构造函数使用其两个主键,或者可以使用一个java.lang.Integer?这适用于单个级别的组合主键,但是如果您具有多个级别,则可能不起作用。

year中还有SamplingsPK被键入为int而不是Integer。这可能会导致PK识别出现问题,因为需要特别考虑才能处理可自动装箱的原始类,并且我怀疑它是否已被考虑。

答案 1 :(得分:0)

问题在于,由于ID是手动设置的,并且这些实体上没有@Version属性,因此Spring Data无法很好地知道该实体是全新实体还是现有实体。在这种情况下,它确定它是现有实体,并尝试使用merge而不是persist。这显然是错误的结论。

您可以阅读有关Spring Data如何确定实体是here的更多信息。

我发现的最佳解决方案是始终让具有手动设置ID的实体类实现Persistable interface。这样就解决了问题。对于任何此类情况,我都会以此为己任。大多数时候,我不必实施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);
    }
}