Reputation: 5749
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
Upvotes: 12
Views: 2337
Reputation: 41240
I noticed this too. It does not happen on my IDE on Windows but it happens on the Azure build server
I was on org.springframework.data:spring-data-jpa:jar:2.4.5:compile
.
I upgraded the BOM to <spring-data-bom.version>2020.0.15</spring-data-bom.version>
so I have org.springframework.data:spring-data-jpa:jar:2.4.15:compile
Once I did that it started working correctly.
Upvotes: 0
Reputation: 19225
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);
}
}
Upvotes: 7
Reputation: 360
This issue is tracked at https://jira.spring.io/browse/DATAJPA-1391 and has to do with the use of @Id @ManyToOne
inside of Samples
. As a workaround, you can try creating a constructor for Samplings
that takes in its two primary keys, or maybe one that takes a java.lang.Integer
? That's what worked for a single level of composite primary keys, but it might not work if you have multiple levels.
You also have year
in SamplingsPK
typed as an int
rather than an Integer
. This may cause problems with PK recognition, since special consideration is needed to handle autobox-able primitive classes and I doubt it was considered.
Upvotes: 0