Comment annoter des entités Hibernate pour honorer à la fois Java et les modèles de données?


J'essaie d'obtenir Hibernate ( v 4.2.3) pour valider (hbm2ddl.auto = valider) mes 4 tables de base de données existantes lorsque l'application démarre. Voici ma table créer des scripts SQL (c'est une base de données H2):

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

Par conséquent, 4 tableaux: words, synonyms & antonyms (qui contiennent des relations plusieurs à plusieurs entre différents words) et une table de recherche/référence word_types (comme un ADVERBE, un NOM, etc.). Pour clarifier, s'il existe un enregistrement words avec une valeur word_text de "quick", et un autre words / word_text enregistrement / valeur de "fast", alors il peut y avoir une entrée dans la table synonyms où le base_word_id est l'ID de"quick", et has_synonym_id peut être l'ID de"fast"; parce que quick a un synonyme appelé fast. Voici le modèle Java que je veux utiliser pour ces tables:

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

Donc BaseModel fournit à chaque modèle un ID. BaseLookup fournit trois champs/colonnes que toutes les tables de recherche auront, au minimum. Word est assez simple, et WordType est un wrapper de recherche qui n'en ajoute aucun champs supplémentaires sur son parent. Cependant, il peut être très concevable d'avoir un jour une sous-classe BaseLookup qui ajoute des champs au-delà des champs label/description/tag que BaseLookup fournit.

J'essaie donc de comprendre quelles annotations je dois ajouter à chacune de mes classes afin que Hibernate soit configuré correctement pour utiliser à la fois mes modèles Java et de données, et je rencontre des murs de briques. Voici le meilleur que j'ai pu trouver:

// 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);
    }
}

Peut certains le vétéran Hibernate fatigué de la bataille m'aide à annoter correctement mes classes/champs POJO afin que Hibernate s'adapte à la fois à mes modèles Java et de données? Plus précisément, j'ai besoin de solutions pour:

  1. Comment faire BaseModel#id l'ID de tous les autres entités, mais d'apparaître comme une colonne unique avec un unique nom de la colonne pour chaque entité (word_id, word_type_id, color_id", etc.).
  2. Comment annoter le champ Word#type pour que Hibernate sache qu'il s'agit de la clé étrangère word_type_id. De plus, j'ai besoin de cascade pour travailler de telle sorte que lorsque j'obtiens une instance Word POJO à partir de la base de données, elle est déjà remplie avec son type WordType.
  3. Comment annoter Word#synonyms et Word#antonyms afin que Hibernate stocke leurs relations dans les tables de croisement (des mêmes noms).
  4. Comment annoter WordType et BaseLookup tels que Hibernate sait chercher une table appelée word_types, avec les champs suivants: word_type_label, word_type_description et word_type_tag. , Mais, l'annoter, de telle sorte que je puisse aussi avoir d'autres BaseLookup sous-classes, comme Color qui pourraient se rapporter à une table colors avec color_label, color_description et color_tag.

Merci d'avance!

Author: Paul Bellora, 2013-10-27

1 answers

Je pense que vous devez simplement utiliser @Entity à partir du point de départ (dans votre hiérarchie d'héritage) lorsque vous avez des tables dans DB et @MappedSuperClass si vous voulez juste stocker des annotations JPA pour la hiérarchie d'héritage, sans avoir de tables DB (dans votre cas pour BaseModel et BaseLookup).

L'annotation @AttributeOverride est également utile dans votre cas d'utilisation afin de remplacer les informations de mappage.

En outre, afin de définir des informations de mappage qui font partie d'une relation, vous utilisez @JoinColumn dans combinaison avec l'un des @ManyToMany, @ManyToOne, @OneToMany ou @OneToOneannotations.

Pour les réponses à chacune de vos 4 questions, voir la partie inférieure de ma réponse.

// 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);
    }
}

Et maintenant, pour répondre à vos questions:

1.Comment faire de BaseModel # id l'ID de toutes les autres entités, mais pour apparaître en tant que colonne unique avec un nom de colonne unique pour chaque entité (word_id, mot_type_id, color_id`, etc.).

Utiliser @AttributeOverrides sur les classes qui étendent les classes annoté avec @MappedSuperClass (ce ne sont pas des entités, donc pas mappées aux tables DB).

2.Comment annoter le champ Word # type pour que Hibernate sache que c'est le mot_type_id clé étrangère. De plus, j'ai besoin de cascade pour travailler de cette manière que lorsque j'obtiens une instance Word POJO de la base de données, c'est déjà rempli avec son type WordType.

Utilisez des annotations de type @ManyToMany. Le chargement de WordType est effectué automatiquement. Vous pouvez considérer le fetch=FetchType.Paramètres paresseux dans les annotations de type @ManyToMany pour l'effet inverse.

3.Comment annoter Word # synonymes et Word # antonymes pour Hiberner stocke leurs relations dans les tables de croisement (du même nom).

Utiliser @ManyToMany, en combinaison avec @JoinTable (si nécessaire)

4.Comment annoter WordType et BaseLookup tels que Hibernate sait recherchez une table appelée word_types avec les champs suivants: word_type_label, word_type_description et mot_type_tag. Mais, annotez - les de telle sorte que je puisse également avoir d'autres BaseLookup sous-classes, comme la couleur qui pourrait se rapporter à une table de couleurs avec color_label, color_description et color_tag.

Identique à 1.

PS: Dans JPA, vous DEVEZ avoir le constructeur par défaut dans chaque entité, dans le cas où il n'y en a pas (dans votre entité WordType). En outre vous pouvez considérer les conseils des commentaires liés à la création abstraite de certaines classes et à l'utilisation le singulier dans vos noms de table. Bien que vous n'ayez pas explicitement abordé la question avec l'unicité de certaines colonnes: voir cette réponse pour plus de détails sur la façon de le faire.

 1
Author: Andrei I, 2017-05-23 12:10:59