Home > Software design >  JPA - Converter class is not being invoked as expected
JPA - Converter class is not being invoked as expected

Time:07-20

I cannot figure out why my converter class is not being called. I have the following Entity class:

import org.springframework.validation.annotation.Validated;
import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.List;

@Entity
@Validated
@Table(name = "c_mark", schema="common")
public class CMark {

    @Id
    @Column(name = "c_mark_id")
    private String id;
    @Column(name = "cl_fk")
    private String cFk;
    @Column(name = "s_con")
    @Convert(converter = StringListConverterCommaDelim.class)
    private List<String> sCon;

    @Override
    public String toString() {
        return "ClassificationMarking{"  
            "id='"   id   '\''  
            ", cFk='"   cFk   '\''  
            ", s_con='"   s_con   '\''  
            '}';
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getCFk() {
        return cFk;
    }

    public void setCFk(String cFk) {
        this.cFk = cFk;
    }

    public List<String> getSCon() {
        return sCon;
    }

    public void setSCon(List<String> sCon) {
    this.sCon = sCon;
}
}

Here is the converter class:

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import java.util.Arrays;
import java.util.List;
import static java.util.Collections.emptyList;

@Converter
public class StringListConverterCommaDelim implements AttributeConverter<List<String>, String> 
{
    private static final String SPLIT_CHAR = ",";

    @Override
    public String convertToDatabaseColumn(List<String> stringList) {
        return stringList != null ? String.join(SPLIT_CHAR, stringList) : "";
    }

    @Override
    public List<String> convertToEntityAttribute(String string) {
        return string != null ? Arrays.asList(string.split(SPLIT_CHAR)) : emptyList();
    }
}

Here is the repo interface that defines the insert method:

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;

public interface CMarkRepository extends JpaRepository <ClassificationMarking, String> {

    @Transactional
    @Modifying
    @Query(value = "INSERT INTO c_mark(c_fk, s_con) "  
        "values(:c_fk, :s_con)", nativeQuery = true)
    int insertCMark(@Param("c_fk") String c_fk, @Param("s_con") String s_con);
}

, and finally the method that invokes the insert:

public int postCMark(CMark cMark) {
    CMark cm = null;
    int status = 0;
    try {
        status = cMarkingRepository.insertCMark(cMark.getCFk(), cMark.getSCon());
    } catch ( Exception e) {
        e.printStackTrace();
    }
    return status;
}

My expectation is that the conversion takes place from the insertCMark() call? I am not sure. In any event, the converter is never called. I would be grateful for any ideas. Thanks!

CodePudding user response:

You're not inserting the whole Entity. So I guess from Spring perspective you just give normal String parameters and Spring probably doesn't know the parameter should somehow be converted.

Despite that shouldn't you even get a compile error because you try to call insertCMark(String, String) as insertCMark(String, List<String>)?

Right now I would say that there is no need for some fancy Spring magic. You can just tweak the getSCon() method to return a String and convert it in there. Or when you need it for something else to create a second method getSConString():

public String getSCon() {
    return this.sCon != null ? String.join(SPLIT_CHAR, this.sCon) : "";
}

Another way is to use your current Converter by hand when calling insertCMark:

public int postCMark(CMark cMark) {
    CMark cm = null;
    int status = 0;
    AttributeConverter<List<String>, String> converter = new StringListConverterCommaDelim();
    String sCon = converter.convertToDatabaseColumn(cMark.getSCon());
    try {
        status = cMarkingRepository.insertCMark(cMark.getCFk(), sCon);
    } catch ( Exception e) {
        e.printStackTrace();
    }
    return status;
}
  • Related