将Java Generic映射到String值

时间:2017-06-22 16:40:25

标签: hibernate generics java-8

我想坚持使用DB 2实体:

  • 属性

    @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
    }
    
  • 项目

    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
    
  • 我得到的错误消息是因为hibernate无法映射T值;

      

    引起:org.springframework.beans.BeanInstantiationException:无法实例化[org.hibernate.SessionFactory]:工厂方法'sessionFactory'引发异常;嵌套异常是org.hibernate.AnnotationException:属性domain.item.Attribute.value具有未绑定类型且没有显式目标实体。解决此通用用法问题或设置显式目标属性(例如@OneToMany(target =)或使用显式@Type

  • 我只需要这个简单的表

    • 物品
      | id:int | isTemplate:boolean |
    • 属性
      | id:int |名称:字符串| type:String(即:String,Integer - &gt; based value type)| value:String | fk_item_id |

提前感谢您提供解决此问题的任何帮助或建议。

2 个答案:

答案 0 :(得分:2)

由于java类型擦除Type Erasure,您无法保留泛型T.类型T仅存在于源中,在运行时它的类型为Object。 hibernate doent知道如何在数据库中存储/呈现Object它可能是任何类型 - 实体,集合,嵌入对象,一些简单的对象 - 字符串,整数......

同样在关系数据库中,你不能在没有适当类型的情况下持久化java对象(你可以尝试序列化对象并将其保存为blob,并在java端反序列化它:) :)

将军:

  1. 如果它是实体:需要提供一个接口/超类而不是T,如果子类之间的差异很大,那么它只是一个空标记。

    @ManyToOne(targetEntity = T_EntityClass.class) @JoinColumn(name =“”) 私人T值;

  2. 如果它不是实体 - 看起来像你的情况: 使用除value字段之外的所有字段创建抽象实体,并从此实体扩展子类 - 实现,如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);
        ........
       }  
    
  3. 字符串实现:

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

    整数实施:

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

    结果你有3个表:

    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)
    )
    

答案 1 :(得分:0)

sbjavateam,谢谢你的彻底解释。在我尝试实施AttributeConverter之后,我得出了几乎相同的结论。我坚持将T value转换为String。最终只使用instance of来获取Object类型,但我无法映射其值。在您解释的Also in relational databases you can't persist java object without appropriate type

中对此进行了详细解释

我最终会像你一样创建额外的类包装器,但Attribute<T>真的是个好主意......它带来了我的武器来证明design your entity first clean and nice and do mapping latter