【问题标题】:How to annotate Hibernate entities to honor both Java and data models?如何注释 Hibernate 实体以支持 Java 和数据模型?
【发布时间】:2013-11-06 04:56:25
【问题描述】:

我正在尝试让 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 个表:wordssynonymsantonyms(在不同的 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_descriptionword_type_tag但是,请以这样的方式对它们进行注释,以便我也可以拥有其他 BaseLookup 子类,例如 Color 可能与带有 color_labelcolor_description 和 @987654363 的 colors 表相关@。

提前致谢!

【问题讨论】:

  • 关系数据库表名不应该是复数,除非单行代表多个事物,如果这样做将是一个糟糕的关系设计。
  • 我不同意;没有比我说“Jarrod”应该拼写“Jared”更合乎逻辑的了。而在 Java 模型中,将 POJO 称为“Word”而不是“Words”是有意义的(因为,当您处理 POJO 的一个实例时,您正在处理一个单词)。但是表格代表了一个记录的集合,这就是我将它复数的原因。

标签: java sql hibernate jpa data-modeling


【解决方案1】:

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

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

此外,为了设置一些属于关系的映射信息,您可以将@JoinColumn@ManyToMany@ManyToOne@OneToMany@OneToOneannotations 之一结合使用。

有关 4 个问题的每一个问题的答案,请参阅我的回复的底部。

// 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`等)。

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

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

使用类似@ManyToMany 的注解。 WordType 的加载是自动进行的。您可能会考虑使用类似 @ManyToMany 的注解中的 fetch=FetchType.LAZY 参数以获得相反的效果。

3.如何标注Word#synonyms和Word#antonyms以便Hibernate 将它们的关系存储在人行横道表中(相同的 名字)。

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

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

同1。

PS:在 JPA 中,您必须在每个实体中都有默认构造函数,以防万一(在您的 WordType 实体中)。此外,您可能会考虑 cmets 的建议,这些建议与抽象某些类和在表名中使用单数有关。尽管您没有通过某些列的唯一性明确解决该问题:请参阅this response 了解如何做到这一点的详细信息。

【讨论】:

  • 另外我会亲自将 BaseModel 抽象化。它不会转换为表格,您应该无法创建实例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-12
  • 1970-01-01
  • 2010-09-21
  • 2011-05-15
  • 1970-01-01
相关资源
最近更新 更多