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?

Answers


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;
    //...

}

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;
}

Need Your Help

How to make RabbitMQ worker listening continuously ?

php cron rabbitmq daemon

I am implementing rabbitMQ with more than 3 workers processing. To test it, I need to execute worker file each time, but I don't want that.