1. 程式人生 > >hibernate關聯映射

hibernate關聯映射

課程 裏的 rac nco dsm 實現 rem 也有 urn

本文可作為北京尚學堂馬士兵hibernate課程的學習筆記。


hibernate的映射,主要分為一對一,一對多,多對一,多對多,同一時候還要單向與雙向的差別。


OK,不要糾結於名字,我們開始看樣例。


一對一單向

老公是一個實體,老婆也是一個實體。
一個老公僅僅有一個老婆,同一時候一個老婆也僅僅有一個老公。
上面的關系就叫做一對一。


什麽叫單向呢。
看代碼:

package com.bjsxt.hibernate;

@Entity
public class Husband {
    private int id;
    private String name;
    private Wife wife;
    
    @Id
    @GeneratedValue
    public int getId() {
        return id;
    }
    
    @OneToOne
    @JoinColumn(name="wifeId")
    public Wife getWife() {
        return wife;
    }
    //省略get set
}


package com.bjsxt.hibernate;

@Entity
public class Wife {
    private int id;
    private String name;
    
    @Id
    @GeneratedValue
    public int getId() {
        return id;
    }
    //省略get set
}


看上面的代碼,老公裏面有老婆的引用,而老婆裏面並沒有老婆的引用。
換句話說在代碼層次上,依據老公我們能夠獲得他的老婆,可是依據老婆無法獲得老公。(這就是單向)

我們看看它執行的代碼
package com.bjsxt.hibernate;

import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.tool.hbm2ddl.SchemaExport;

public class Test {
    public static void main(String[] args) {
        new SchemaExport(new AnnotationConfiguration().configure()).create(false, true);    
    }
}
在hibernate的配置文件中
加上
<property name="hbm2ddl.auto">update</property>
結果
19:41:04,229 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
    create table Husband (
        id integer not null auto_increment,
        name varchar(255),
        wifeId integer,
        primary key (id)
    )
19:41:04,549 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
    create table Wife (
        id integer not null auto_increment,
        name varchar(255),
        primary key (id)
    )
19:41:04,880 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
    alter table Husband
        add index FKAEEA401B109E78ED (wifeId),
        add constraint FKAEEA401B109E78ED
        foreign key (wifeId)
        references Wife (id)

先給husband加了一個字段,wifeId,然後讓他作為外鍵引用wife表的id字段。
大家能夠看到,@JoinColumn(name="wifeId")這個元標簽指示了husband表內部那那個關聯字段的名字。
假設沒有這個元標簽呢?
那麽關聯字段就是是:wife_id。

即tableName_primarykey。
假設把 @onetoone寫到Wife裏(husband裏沒有onotoone的標簽),那麽我們就能從wife獲得husband了(在代碼層次)。



一對一雙向

假設我既想從husband裏取得wife,也想從wife裏取得husband。那咋辦?


把husband裏的標簽復制一遍就OK了嘛。


package com.bjsxt.hibernate;

@Entity
public class Wife {
    private int id;
    private String name;
    private Husband husband;
    
    @Id
    @GeneratedValue
    public int getId() {
        return id;
    }
    
    @OneToOne
    @JoinColumn(name="husbandId")
    public Husband getHusband() {
        return husband;
    }

}

OK,上面的代碼裏,wife也能夠取到husband了。
我們看看hibernate生成的sql
create table Husband (
        id integer not null auto_increment,
        name varchar(255),
        wifeId integer,
        primary key (id)
    )
19:53:20,487 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
    create table Wife (
        id integer not null auto_increment,
        name varchar(255),
        husbandId integer,
        primary key (id)
    )
19:53:20,824 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
    alter table Husband
        add index FKAEEA401B109E78ED (wifeId),
        add constraint FKAEEA401B109E78ED
        foreign key (wifeId)
        references Wife (id)
19:53:21,421 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
    alter table Wife
        add index FK292331CE01A6E1 (husbandId),
        add constraint FK292331CE01A6E1
        foreign key (husbandId)
        references Husband (id)
看見了吧,wife裏面有外鍵,husband裏面也有外鍵!
這不是冗余了嘛。
把wife類改成以下的樣子
    @OneToOne(mappedBy="wife")
    public Husband getHusband() {
        return husband;
    }
這個mappedBy的意思是說,我(Wife這個類)和husband這個類是一對一關聯的,可是關聯的外鍵由husband類的getWife方法控制。
執行一下。
20:03:18,611 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
    create table Husband (
        id integer not null auto_increment,
        name varchar(255),
        wifeId integer,
        primary key (id)
    )
20:03:18,618 ERROR org.hibernate.tool.hbm2ddl.SchemaExport:348 - Unsuccessful: create table Husband (id integer not null auto_increment, name varchar(255), wifeId integer, primary key (id))
20:03:18,618 ERROR org.hibernate.tool.hbm2ddl.SchemaExport:349 - Table ‘husband‘ already exists
20:03:18,619 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
    create table Wife (
        id integer not null auto_increment,
        name varchar(255),
        primary key (id)
    )
20:03:18,933 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
    alter table Husband
        add index FKAEEA401B109E78ED (wifeId),
        add constraint FKAEEA401B109E78ED
        foreign key (wifeId)
        references Wife (id)
數據庫裏,wife表裏沒有外鍵了。
可是,在代碼層次我們依舊能夠從Wife類裏獲得Husband。



註意上面的 @OneToOne(mappedBy="wife")
僅僅有是雙向關聯,最好就都寫上mappedBy,兩張表有一個外鍵就夠了。




多對一單向關聯

每個人都會有非常多個夢想,可是每個夢想僅僅屬於某一個詳細的人。
我們先無論java代碼上怎樣實現,在數據庫裏。
上面的兩張表,一個是person,裏面就是id,name
再就是dream表,一個id,一個description。
那麽它們的外鍵放在哪張表裏呢?


放在dream裏,換言之,dream表裏有一個字段叫personId。


這個的原因,不用解釋。


全部一對多,多對一的關系,在數據庫裏,外鍵都是放在多的一方裏的。
為什麽。自己想。




看代碼

package com.bjsxt.hibernate;

@Entity
public class Dream {
    private int id;
    private String description;    
    private Person person;
    
    @Id
    @GeneratedValue
    public int getId() {
        return id;
    }
    @ManyToOne
    @JoinColumn(name="personId")
    public Person getPerson() {
        return person;
    }
    //省略get set
}


package com.bjsxt.hibernate;

@Entity
public class Person {
    private int id;
    private String name;
    
    @Id
    @GeneratedValue
    public int getId() {
        return id;
    }    
    //省略get set
}
從代碼層次上看"多對一單向"
就是我能夠從多的一方(dream)裏獲得一的那一方(person)。


由於是單向,所以不能從person獲得他全部的dream。
OK,生成的表裏,dream裏有personid。

20:20:21,970 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
    create table Dream (
        id integer not null auto_increment,
        description varchar(255),
        personId integer,
        primary key (id)
    )
20:20:22,264 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
    create table Person (
        id integer not null auto_increment,
        name varchar(255),
        primary key (id)
    )
20:20:22,765 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
    alter table Dream
        add index FK3F397E3C1409475 (personId),
        add constraint FK3F397E3C1409475
        foreign key (personId)
        references Person (id)



一對多單向關聯

還是人與夢想的樣例。
我想在代碼層次,通過人獲得他的全部的夢想,怎麽辦?
首先在dream類裏刪除person的引用
package com.bjsxt.hibernate;

@Entity
public class Person {
    private int id;
    private String name;
    private Set<Dream> dreams=new HashSet<>();
    
    @Id
    @GeneratedValue
    public int getId() {
        return id;
    }

    @OneToMany
    @JoinColumn(name="psrsonId")
    public Set<Dream> getDreams() {
        return dreams;
    }    
}


OK,大功告成。


建表語句和上面的一樣。
裝dream的集合類為什麽是set?

list行不,map行不?
都行。
可是記著,數據庫裏面的記錄是無序且不相等的。
你說用哪個?




無論是oneToMany還是manyToMany,加的 @JoinColumn(name="abc")
都是在多的一方增加指向少的一方的名叫abc的外鍵。




一對多雙向關聯(多對一雙向關聯)

以下的樣例,大家猜也猜到了,我既想從dream獲得它所屬的person,還想從person獲得他全部的dream。
我們看代碼:
package com.bjsxt.hibernate;

@Entity
public class Dream {
    private int id;
    private String description;
    
    private Person person;
    
    @Id
    @GeneratedValue
    public int getId() {
        return id;
    }

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

    @ManyToOne
    @JoinColumn(name="personId")
    public Person getPerson() {
        return person;
    }
}

package com.bjsxt.hibernate;

@Entity
public class Person {
    private int id;
    private String name;
    private Set<Dream> dreams=new HashSet<>();

    @Id
    @GeneratedValue
    public int getId() {
        return id;
    }

    @OneToMany(mappedBy="person")
    public Set<Dream> getDreams() {
        return dreams;
    }
}
ok,我們能夠在代碼裏,通過dream獲得person也能夠通過person獲得dream



多對多單向關聯

樣例非常easy,一個老師能夠教多個學生,一個學生能夠被多個老師教。
單向是說,要麽老師能"知道"自己的學生,要麽學生能知道自己的老師。
我們先看老師能"知道"自己學生的代碼。

package com.bjsxt.hibernate;

@Entity
public class Student {
    private int id;
    private String name;
    
    @Id
    @GeneratedValue
    public int getId() {
        return id;
    }
}
OK,學生裏面沒有老師的引用。
package com.bjsxt.hibernate;

@Entity
public class Teacher {
    private int id;
    private String name;
    private Set<Student> students = new HashSet<Student>();
    @Id
    @GeneratedValue
    public int getId() {
        return id;
    }
    
    @ManyToMany
    @JoinTable(name="t_s",
        [email protected](name="teacher_id")},
        [email protected](name="student_id")}
        )
    public Set<Student> getStudents() {
        return students;
    }
}
看結果:
21:10:35,854 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
    create table Student (
        id integer not null auto_increment,
        name varchar(255),
        primary key (id)
    )
21:10:36,192 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
    create table Teacher (
        id integer not null auto_increment,
        name varchar(255),
        primary key (id)
    )
21:10:36,643 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
    create table t_s (
        teacher_id integer not null,
        student_id integer not null,
        primary key (teacher_id, student_id)
    )
21:10:36,947 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
    alter table t_s
        add index FK1BF68BF77BA8A (teacher_id),
        add constraint FK1BF68BF77BA8A
        foreign key (teacher_id)
        references Teacher (id)
21:10:37,588 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
    alter table t_s
        add index FK1BF68AEDC6FEA (student_id),
        add constraint FK1BF68AEDC6FEA
        foreign key (student_id)
        references Student (id)
21:10:38,263  INFO org.hibernate.tool.hbm2ddl.SchemaExport:268 - schema export complete

我們看看生成的t_s表和裏面的字段名稱應該就能明確 @JoinTable的作用
它就是在manytomany時,指定第三張表的表名和字段。


joinColumns 指的是指向自己這個類的字段名
inverseJoinColumns inverse是反向的意思 所以這個標簽就是運行另外那個類的字段
假設沒有 @JoinTable這個標簽,會是什麽樣子呢?大家自己試一下。
另外,上面的代碼是從老師到學生,大家再試試從學生到老師。


多對多雙向關聯

在上面的樣例裏
改變student,給它加上teacher的引用。

package com.bjsxt.hibernate;


@Entity
public class Student {
    private int id;
    private String name;
    private Set<Teacher> teachers=new HashSet<>();
    
    @Id
    @GeneratedValue
    public int getId() {
        return id;
    }
    
    //記著 雙向的時候 就加上mappedBy
    @ManyToMany(mappedBy="students")
    public Set<Teacher> getTeachers() {
        return teachers;
    }

}


glt likes dlf

dlf likes glt

oneToOne



hibernate關聯映射