如何注释Hibernate实体以同时支持Java和数据模型?

时间:2013-10-27 11:27:01

标签: java sql hibernate jpa data-modeling

我正在尝试让Hibernate( v 4.2.3 )在应用程序启动时验证(hbm2ddl.auto = validate)我的4个现有数据库表。这是我的表创建SQL脚本(这是一个H2 DB):

-- Lookup/reference table, example records might be for ADVERB, NOUN,
-- VERB, etc.
CREATE TABLE word_types (
    word_type_id BIGINT AUTO_INCREMENT,
    word_type_label VARCHAR(100) NOT NULL,
    word_type_description VARCHAR(100) NOT NULL,
    word_type_tag VARCHAR(100) NOT NULL,
    CONSTRAINT uc_tag UNIQUE (word_type_tag)
);

-- A word in the English language. length is the number of chars in the
-- word, type ID is the word_types#word_type_id above (foreign key),
-- text is the actual word itself "quick", "fast", etc.
CREATE TABLE words (
    word_id BIGINT AUTO_INCREMENT,
    word_length INTEGER NOT NULL,
    word_type_id INTEGER NOT NULL,
    word_text VARCHAR(100) NOT NULL,
    word_definition VARCHAR(1000) NOT NULL,
    CONSTRAINT fk_word_types FOREIGN KEY (word_type_id) REFERENCES word_types(word_type_id),
    CONSTRAINT uc_text_type UNIQUE (word_text, word_type_id)
);

-- Crosswalk/junction table holding a many-to-many relationships between
-- pairs of words. Example: fast is a synonym of quick. So there would be
-- a words record for fast, and a words record for quick, and a record in
-- this table linking the 2 together.
CREATE TABLE synonyms (
    synonym_id BIGINT AUTO_INCREMENT,
    base_word_id INTEGER NOT NULL,
    has_synonym_id INTEGER NOT NULL,
    CONSTRAINT fk_word_1_base_id FOREIGN KEY (base_word_id) REFERENCES words(word_id),
    CONSTRAINT fk_word_synonym_id FOREIGN KEY (has_synonym_id) REFERENCES words(word_id),
    CONSTRAINT uc_syn_id_sets UNIQUE (base_word_id, has_synonym_id)
);

-- Same as above except this table relates words that are antonyms of
-- each other.
CREATE TABLE antonyms (
    antonym_id BIGINT AUTO_INCREMENT,
    base_word_id INTEGER NOT NULL,
    has_antonym_id INTEGER NOT NULL,
    CONSTRAINT fk_word_2_base_id FOREIGN KEY (base_word_id) REFERENCES words(word_id),
    CONSTRAINT fk_word_antonym_id FOREIGN KEY (has_antonym_id) REFERENCES words(word_id),
    CONSTRAINT uc_ant_id_sets UNIQUE (base_word_id, has_antonym_id)
);

因此,有4个表格:wordssynonyms& antonyms(包含不同words之间的多对多关系)和查找/引用表word_types(例如ADVERB,NOUN等)。为了澄清,如果words记录的word_text值为“快速”,另一个words / word_text记录/值为“快速”,则可能是synonyms表格中base_word_id为“快速”ID的条目,has_synonym_id可能是“快”的ID;因为 quick 有一个名为 fast 的同义词。这是我想用于这些表的Java模型:

public class BaseModel {
    protected Long id;

    public Long getId() {
        return id;
    }

    public void setId(final Long id) {
        this.id = id;
    }
}

public class Word extends BaseModel {
    private String text;
    private Integer length;
    private WordType type;
    private String definition;
    private List<Word> synonyms;
    private List<Word> antonyms;

    // Getters, setters, ctors omitted for brevity...
}

public class BaseLookup extends BaseModel {
    private String label;
    private String description;
    private String tag;

    // Getters, setters, ctors omitted for brevity...
}

public class WordType extends BaseLookup {
    public WordType(String label, String description, String tag) {
        super(label, description, tag);
    }
}

所以BaseModel为每个模型提供一个ID。 BaseLookup提供了三个字段/列,所有查找表都至少具有这些字段/列。 Word非常简单,WordType是一个查找包装器,不会在其父级上添加任何其他字段。但是,有一天可能会有一个BaseLookup子类, BaseLookup提供的标签/描述/标记字段之外添加字段。

所以我试图弄清楚我需要为每个类添加哪些注释,以便正确配置Hibernate以同时使用我的Java和数据模型,并且我遇到了一些问题。这是我能够提出的最好的:

// This class doesn't translate into a table; it's just a base class that provides
// an ID for all other entities, and perhaps (down the road) other common fields as
// well.
public class BaseModel {
    @Id @GeneratedValue(strategy=GenerationType.AUTO)
    protected Long id;

    public Long getId() {
        return id;
    }

    public void setId(final Long id) {
        this.id = id;
    }
}

@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@Table(name="words")
public class Word extends BaseModel {
    // How do I force Word.getId() to be "words_id"?

    @Column(name="word_text")
    private String text;

    @Column(name="word_length")
    private Integer length;

    // But how do I make this the ID of a word_types record?
    @Column(name="word_type_id")
    private WordType type;

    @Column(name="word_definition")
    private String definition;

    // The words table doesn't have any synonyms or antonyms.
    // Rather there is a many-to-many relationship between
    // a word and its synonyms and its antonyms...
    @Column(name="???")
    private List<Word> synonyms;

    @Column(name="???")
    private List<Word> antonyms;

    // Getters, setters, ctors omitted for brevity...
}

// Not sure what to annotate this table with, because there is not
// base_lookup table or anything like that...
public class BaseLookup extends BaseModel {
    private String label;
    private String description;
    private String tag;

    // Getters, setters, ctors omitted for brevity...
}

// Furthermore, here, in the case of WordType, I'd like to force the parent
// fields to be "word_type_label", "word_type_description", and "word_type_tag";
// however, other BaseLookup subclasses should be able to force those same fields
// to map/bind to other tables with other field names.
//
// For example, I might some day want a Color POJO relating to a colors table with
// the following fields: color_label, color_description and color_tag, etc.
public class WordType extends BaseLookup {
    // How do I force WordType.getId() to be word_type_id?

    public WordType(String label, String description, String tag) {
        super(label, description, tag);
    }
}

一些厌倦战斗的Hibernate老手可以帮我正确地注释我的POJO类/字段,以便Hibernate能够容纳我的Java和数据模型吗?具体来说,我需要解决方案:

  1. 如何为所有其他实体设置BaseModel#id ID,但显示为每个实体(word_idword_type_id,color_id`等具有唯一列名称的唯一列)。
  2. 如何注释Word#type字段,以便Hibernate知道它是word_type_id外键。此外,我需要级联才能以这样的方式工作:当我从数据库中获取Word POJO实例时,它已经填充了WordType类型。
  3. 如何注释Word#synonymsWord#antonyms,以便Hibernate将他们的关系存储在人行横道表(同名)中。
  4. 如何注释WordTypeBaseLookup,以便Hibernate知道要查找名为word_types的表,其中包含以下字段:word_type_labelword_type_description和{ {1}}。 但是,以这样的方式对它们进行注释,我也可以使用其他word_type_tag子类,例如BaseLookup,可能与Color表格colors相关联},color_labelcolor_description
  5. 提前致谢!

1 个答案:

答案 0 :(得分:1)

我认为你只需要在起始点(在你的继承层次结构中)使用@Entity,当你在DB中有表格时@MappedSuperClass如果你只想为继承层次结构存储JPA注释,没有数据库表(在您的情况下为BaseModelBaseLookup)。

在您的用例中也有用@AttributeOverride注释,以覆盖映射信息。

此外,为了设置作为关系一部分的一些映射信息,您可以将@JoinColumn@ManyToMany@ManyToOne@OneToMany之一结合使用,或者@OneToOne的注释。

要获得每个问题的答案,请参阅我的回复的底部。

// This class doesn't translate into a table; it's just a base class that provides
// an ID for all other entities, and perhaps (down the road) other common fields as
// well.
@MappedSuperClass
public class BaseModel {
    @Id @GeneratedValue(strategy=GenerationType.AUTO)
    protected Long id;

    public Long getId() {
        return id;
    }

    public void setId(final Long id) {
        this.id = id;
    }
}

@Entity
@AttributeOverrides({
        @AttributeOverride(name="id", column=@Column(name="word_id"))
})
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@Table(name="words")
public class Word extends BaseModel {
    // How do I force Word.getId() to be "words_id"?

    @Column(name="word_text")
    private String text;

    @Column(name="word_length")
    private Integer length;

    // But how do I make this the ID of a word_types record?
    //@Column(name="")
    @ManyToOne
    @JoinColumn(name="word_type_id", referencedColumnName="word_type_id")
    private WordType type;

    @Column(name="word_definition")
    private String definition;

    // The words table doesn't have any synonyms or antonyms.
    // Rather there is a many-to-many relationship between
    // a word and its synonyms and its antonyms...
    @ManyToMany()
    //use the below annotation if you want to set the names of the columns
    //    @JoinTable(joinColumns = @JoinColumn(name="word_id")},//column in this entity
    //          inverseJoinColumns = {@JoinColumn(name="synonym_id")})//column in the table of the set.
    private List<Word> synonyms;

    //@Column(name="???")
    @ManyToMany()
    //use the below annotation if you want to set the names of the columns
    //    @JoinTable(joinColumns = @JoinColumn(name="word_id")},//column in this entity
    //          inverseJoinColumns = {@JoinColumn(name="antonym_id")})//column in the table of the set.
    private List<Word> antonyms;

    // Getters, setters, ctors omitted for brevity...
}

// Not sure what to annotate this table with, because there is not
// base_lookup table or anything like that...
@MappedSuperClass
public class BaseLookup extends BaseModel {
    private String label;
    private String description;
    private String tag;

    // Getters, setters, ctors omitted for brevity...
}

// Furthermore, here, in the case of WordType, I'd like to force the parent
// fields to be "word_type_label", "word_type_description", and "word_type_tag";
// however, other BaseLookup subclasses should be able to force those same fields
// to map/bind to other tables with other field names.
//
// For example, I might some day want a Color POJO relating to a colors table with
// the following fields: color_label, color_description and color_tag, etc.
@Entity
    // How do I force WordType.getId() to be word_type_id?
    // this is how:
@AttributeOverrides({
    @AttributeOverride(name="id", column=@Column(name="word_type_id")),
    @AttributeOverride(name="label", column=@Column(name="word_type_label")),
    @AttributeOverride(name="description", column=@Column(name="word_type_description")),
    @AttributeOverride(name="tag", column=@Column(name="word_type_tag"))
})
public class WordType extends BaseLookup {


    public WordType(String label, String description, String tag) {
        super(label, description, tag);
    }
}

现在回答你的问题:

  

1.如何使BaseModel #id成为所有其他实体的ID,但要显示   作为每个实体具有唯一列名称的唯一列(word_id,   word_type_id,color_id`等。

对扩展使用@AttributeOverrides注释的类的类使用@MappedSuperClass(它们不是实体,因此不映射到数据库表)。

  

2.如何注释Word#type字段,以便Hibernate知道它是   word_type_id外键。另外,我需要级联才能以这种方式工作   当我从数据库中获取Word POJO实例时,它已经存在了   填充了WordType类型。

使用@ManyToMany - 就像注释一样。自动加载WordType。您可能会考虑@ManyToMany中的fetch = FetchType.LAZY参数 - 就像注释一样具有相反的效果。

  

3.如何注释Word#的同义词和Word#反义词以便Hibernate   将他们的关系存储在人行横道表中(同样的   名)。

@ManyToMany@JoinTable结合使用(如果需要)

  

4.如何注释Hibernate知道的WordType和BaseLookup   查找名为word_types的表,其中包含以下字段:   word_type_label,word_type_description和word_type_tag。但,   以这样的方式注释它们我也可以有其他BaseLookup   子类,如Color可能与颜色表有关   color_label,color_description和color_tag。

与1相同。

PS:在JPA中,必须在每个实体中都有默认构造函数,如果没有人(在WordType实体中)。除此之外,您可以考虑与制作抽象某些类和在表名中使用单数形式相关的注释建议。虽然您没有明确地解决某些列的唯一性问题:请参阅this response了解如何制作该列的详细信息。