Home > Enterprise >  Is it possible to map a JPA/Hibernate Entity field to a field in another class without @Embeddable?
Is it possible to map a JPA/Hibernate Entity field to a field in another class without @Embeddable?

Time:09-07

I have a very simple Entity (Person.java) that I am wanting to persist via JPA/Hibernate. The Entity contains two fields: ID and Identification String.

The ID is a simple Integer, and is no problem. The Identification String is currently a String, but for various reasons, I want to instead use a wrapper class for String (IDString), where there are various validation methods among other things.

I am wondering how I can get JPA/Hibernate to use the wrapped string (inside the custom class IDString) when persisting the Person table in the database. I know this can probably be solved by letting the IDString be @Embeddable and then embed IDString in the Person entity with @Embedded, but I am looking for another method, mostly because IDString is in an entirely different package, and I am reluctant to have to go there and change stuff.

Googling, I found https://www.baeldung.com/hibernate-custom-types, but it seems to be mostly about more complicated cases, where you want to convert one class into another type, and I do feel that there is probably a smarter way that I am simply overlooking.

Here is the entity (in theory)

@Entity(name="Person")
@Table(name="DB_TABLE_PERSON")
public class Person implements Serializable {
    @Id
    Integer id;
    // WHAT SHOULD I PUT HERE? I WANT TO SIMPLY USE THE STRING INSIDE IDSTRING AS THE FIELD TO PERSIST
    IDString idString;

    // getter and setter for ID.
    
    public void getIdString() {
        return idString.getValue();
    }
    public void setIdString(String in) {
        idString.setValue(in);
    }
}

And here is the class IDString (in theory):

public class IDString {
    // I really want to be a POJO
    private final String the_string;
    public IdString(String input) {
        if (isValid(input)) {
            the_string = input;
        } else {
            throw new SomeCoolException("Invalid format of the ID String");
        }
    public boolean isValid(String input) {
        // bunch of code to validate the input string
    }
    public String getValue() {
        return the_string;
    }
    public void setValue(String input) {
        if (isValid(input)) the_string = s;
        else throw new SomeCoolException("Invalid format of the ID String");
}

I know that I could place the validation if the IDString inside the Entity, but the IDString will be used elsewhere (it's a general custom class), so I don't want to do that. Is there a simple way?

CodePudding user response:

@Converter(autoApply=true) // autoApply is reasonable, if not use @Converter on field
public class IDStringConverter implements AttributeConverter<IDString,String> {
  @Override
  public String convertToDatabaseColumn(IDString attribute) {
    return attribute != null ? attribute.getValue() : null;
  }

  @Override
  public IDString convertToEntityAttribute(String dbData) {
    return dbData != null ? new IDString(dbData) : null;
  }
}

With this you should not need any other modifications in your code. One limitation of the AttributeConverter is that it maps from exactly 1 Java field to exactly 1 DB column. If you wanted to map to more columns (not the case here), you would need embeddables.

CodePudding user response:

You could also put a @Column annotation on the getter:

@Entity
public class Person {

    private final IdString idString = new IdString();

    @Column(name = "ID_STRiNG")      
    public IdString getIdString() {
        return idString.getValue();
    }

    public void setIdString(String input) {
        idString.setValue(input);
    }

Another solution could be to convert to/from IdString using @PostLoad and @PrePersit event handlers:

@Entity
public class Person {

    @Column(name = "ID_STRiNG")
    private String the_string; // no getters & setters

    @Transient
    private final IdString idString = new IdString();

    @PostLoad
    public void postLoad() {
        idString.setValue(the_string);
    }

    @PrePersist
    public void prePersist() {
        the_string = idString.getValue();
    }

    // getters & setters for idString 
  • Related