Oleg Mikhailov
Oleg Mikhailov

Reputation: 6081

Hibernate: loading a class by its field without a table

I have an Ink class with a fixed set of constants. They never change and I don't want to store this class as a table.

public final class Ink {
    public static final Ink Y = new Ink(91001, 'y', 90.70,  -5.23,  94.37), "Yellow");
    public static final Ink M = new Ink(92001, 'm', 48.19,  72.01,  -1.78), "Magenta");
    public static final Ink C = new Ink(93001, 'c', 56.46, -41.00, -43.50), "Cyan");
    public static final Ink K = new Ink(94001, 'k', 18.64,   1.80,   5.21), "Black");      

    public int code;

    public static Ink forCode(int code) {
        //...
    }

    //...
}

Other classes can have Ink ink or Collection<Ink> inks fields, but every ink instance must be mapped as a simple integer column with its code value only. Code is not a foreign key or something – it is a simple integer value from which I can always obtain Ink instance.

@Entity
public class Order {
     //...
     @OneToMany    
     public Set<Ink> inks;
     //...
}

I can use converter to restore Ink class by its code and back

@Converter(autoApply = true)
public class InkConverter implements AttributeConverter<Ink, Integer> {

     @Overrcodee
     public Integer convertToDatabaseColumn(Ink ink) {
         return ink.code;
     }

     @Overrcodee
     public Ink convertToEntityAttribute(Integer code) {
         return Ink.forCode(code);
     }
}

but the question is:

how to prevent Hibernate from mapping the class as a table and still have a class field loaded from its code column?

Upvotes: 0

Views: 61

Answers (2)

Alan Hay
Alan Hay

Reputation: 23226

Firstly, you can create an Enum to represent Ink instances. If you can change the column to store a char reference (Y,M,C,K) rather than code this would the easiest solution. If not then you can use a converter as you have suggested to convert from the code to the Enum instance.

public Enum Ink {
    Y = new Ink(91001, 'y', 90.70,  -5.23,  94.37), "Yellow");
    M = new Ink(92001, 'm', 48.19,  72.01,  -1.78), "Magenta");
    C = new Ink(93001, 'c', 56.46, -41.00, -43.50), "Cyan");
    K = new Ink(94001, 'k', 18.64,   1.80,   5.21), "Black"); 

    .....     

    private static Ink (...) {
        ...
    }
}

Mappings would be like the below if you save Y,M,C,K. If you must save the code then add the necessary converters (http://www.nurkiewicz.com/2013/06/mapping-enums-done-right-with-convert.html)

@Entity
public class Order {

     @ElementCollection   
     @CollectionTable(...)
     @Enumerated(EnumType.STRING)
     public Set<Ink> inks;

     @Enumerated(EnumType.STRING)
     private Ink ink;
}

Upvotes: 1

xierui
xierui

Reputation: 1055

As you store the ink in your Java code. So you should implement the load action all by your self. First, you can prevent Hibernate from mapping the class as a table by annotation @Transient. And then you can handle the load action after order loaded:

public interface Inked {
    getCode();
    setInk(Ink ink);
}

public abstract class AbstractEntity {

    @PostLoad
    public void loadInk() {
        if(this instanceof Inked){
            Inked inked = (Inked) this;
            inked.setInk(Ink.forCode(getCode()));
        }
    }
}

@Entity
public class Order extends AbstractEntity implement Inked {
    //...
    public Integer code;

    @Transient
    public Ink ink;
    //...

}

Upvotes: 1

Related Questions