I use spring boot 2 and some of my entities have composite key
When I try to save an entity, I get this error
Failed to convert request element: org.springframework.beans.ConversionNotSupportedException: Failed to convert property value of type 'java.lang.Integer' to required type 'com.lcm.model.SamplingsPK' for property 'sampling'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.Integer' to required type 'com.lcm.model.SamplingsPK' for property 'sampling': no matching editors or conversion strategy found
I get my entity with that method
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);
My entity
@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;
}
}
edit
no problem to save sample, when I pass from sampling
The problem is that since the IDs are set manually and there's no @Version
property on these entities then Spring Data has no good way of knowing if the entity is a brand new one or an existing one. In this case it decides it is an existing entity and attempts a merge
instead of a persist
. This is obviously a wrong conclusion.
You can read more about how Spring Data decides if an entity is new or not here.
The best solution I've found is to always let entity classes with manually set IDs implement Persistable interface. This solves the problem. I make this a rule for myself for any such case. Most of the time I do not have to implement Persistable
because my entity either has an auto-generated key or my entity uses a "@Version" annotation. But this is special case.
So, as per the recommendation in the Spring official documentation, for example the Samplings
class would become:
@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);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With