Reputation: 441
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
Thanks in advance for any help or suggestion to solve this problem.
Upvotes: 0
Views: 1524
Reputation: 441
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
.
Upvotes: 0
Reputation: 5407
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 :
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;
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)
)
Upvotes: 2