0

I want to persist into DB 2 entities :

  • Attribute

    @Entity
    public class Attribute<T> {
    
    @Id @GeneratedValue(strategy = AUTO)
    Long id;
    
    @ManyToOne @JoinColumn(name = "item_id")
    Item item;
    
    String name;
    
    T value;
    
    boolean isTemplate;
    // skip setter and getter
    }
    
  • Item

    public class Item {
    
    @Id
    @GeneratedValue(strategy = AUTO)
    Long id;
    
    @OneToMany(cascade = ALL)
    @JoinColumn(name= "item_id")
    List<Attribute> attributes;
    
    private boolean isTemplate;
        // skip setter and getter
    }
    
    in short Item 1-->* Attribute
    
  • Error message that i get because hibernate can't map T value;

    Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.hibernate.SessionFactory]: Factory method 'sessionFactory' threw exception; nested exception is org.hibernate.AnnotationException: Property domain.item.Attribute.value has an unbound type and no explicit target entity. Resolve this Generic usage issue or set an explicit target attribute (eg @OneToMany(target=) or use an explicit @Type

  • I only need this simple table

    • Item
      | id:int | isTemplate:boolean |
    • Attribute
      | id:int | Name:String | type:String (i.e:String,Integer - > based value type) | value:String | fk_item_id |

Thanks in advance for any help or suggestion to solve this problem.

2 Answers 2

2

You can't persist generic T due to java type erasure Type Erasure. Type T exist only in source and at runtime it's type Object. hibernate doent's know how to store / present Object in database it might be any type - entity , collection ,embeded object , some simple object - string , integer .....

Also in relational databases you can't persist java object without appropriate type (you can try to serialize object and save it as blob and in java side de-serialize it :) :) )

General :

  1. if T it's entity : need to provide an interface/superclass instead of T, be it only an empty marker one if the differences between subclasses are large.

    @ManyToOne(targetEntity=T_EntityClass.class) @JoinColumn(name = "") private T value;

  2. if it's not entity - looks like your case : create abstract entity with all fields except value field , and extends from this entity child - implementation , like for String , Integer....

    @Entity
    @Inheritance(strategy = InheritanceType.JOINED)
    public abstract class Attribute<T> {
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Long id;
    
        @ManyToOne
        @JoinColumn(name = "item_id")
        private Item item;
    
        @Column
        private String name;
    
        @Column
        boolean isTemplate;
    
        public abstract T getValue();
    
        public abstract void setValue(T value);
        ........
       }  
    

String implementation :

@Entity
public class AttributeStringValue extends Attribute<String>{

    @Column
    String value;

    @Override
    public String getValue() {
        return value;
    }

    @Override
    public void setValue(String value) {
        this.value = value;
    }
}

Integer implementation :

@Entity
public class AttributeIntegerValue extends Attribute<Integer>{

    @Column
    Integer value;

    @Override
    public Integer getValue() {
        return value;
    }

    @Override
    public void setValue(Integer value) {
        this.value = value;
    }
}

as result you have 3 tables :

create table attribute (
    id bigint generated by default as identity,
    is_template boolean,
    name varchar(255),
    item_id bigint,
    primary key (id)
)

create table attribute_integer_value (
    value integer,
    id bigint not null,
    primary key (id)
)

create table attribute_string_value (
    value varchar(255),
    id bigint not null,
    primary key (id)
)
Sign up to request clarification or add additional context in comments.

Comments

0

sbjavateam, thank you for your thoroughly explanation. I have arrived in almost the same conclusion after I try to implement AttributeConverter. I stuck to convert T value to to String. It ends up to use instance of to get Object type only but I cant map its value. It is well explained in your explained Also in relational databases you can't persist java object without appropriate type.

I ends up almost the same approach like you to create additional class wrapper but Attribute<T> really a good idea.... It brings me arsenal to prove design your entity first clean and nice and do mapping latter.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.