Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Saving entity with composite key get ConversionNotSupportedException

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

like image 302
robert trudel Avatar asked Aug 04 '18 03:08

robert trudel


1 Answers

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);
    }
}
like image 78
peterh Avatar answered Sep 22 '22 07:09

peterh