使用复合键保存实体获取 ConversionNotSupportedException
Posted
技术标签:
【中文标题】使用复合键保存实体获取 ConversionNotSupportedException【英文标题】:Saving entity with composite key get ConversionNotSupportedException 【发布时间】:2019-01-11 21:51:35 【问题描述】:我使用 spring boot 2,我的一些实体有复合键
当我尝试保存实体时,出现此错误
转换请求元素失败: org.springframework.beans.ConversionNotSupportedException:失败 将类型“java.lang.Integer”的属性值转换为所需类型 '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);
我的实体
@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;
编辑
保存样本没问题,当我通过采样时
【问题讨论】:
遇到同样的问题。我发现如果您直接使用 EntityManager,您可以正确地保留实体。同时,我打开了jira.spring.io/browse/DATAJPA-1391 不确定,因为我可以创建采样和采样...但可以通过压缩保存采样... 【参考方案1】:我也注意到了这一点。它不会发生在我的 Windows IDE 上,但会发生在 Azure 构建服务器上
我在org.springframework.data:spring-data-jpa:jar:2.4.5:compile
。
我将 BOM 升级到 <spring-data-bom.version>2020.0.15</spring-data-bom.version>
,所以我有 org.springframework.data:spring-data-jpa:jar:2.4.15:compile
一旦我这样做了,它就开始正常工作了。
【讨论】:
【参考方案2】:问题在于,由于 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);
【讨论】:
【参考方案3】:此问题在 https://jira.spring.io/browse/DATAJPA-1391 进行跟踪,并且与在 Samples
中使用 @Id @ManyToOne
有关。作为一种解决方法,您可以尝试为 Samplings
创建一个构造函数,该构造函数接受它的两个主键,或者一个接受 java.lang.Integer
的主键?这适用于单级复合主键,但如果您有多个级别,它可能不起作用。
SamplingsPK
中的 year
也输入为 int
而不是 Integer
。这可能会导致 PK 识别出现问题,因为处理可自动装箱的原始类需要特别考虑,我怀疑是否考虑过。
【讨论】:
以上是关于使用复合键保存实体获取 ConversionNotSupportedException的主要内容,如果未能解决你的问题,请参考以下文章
使用实体的复合键作为另一个实体中的 ManyToMany 键