Hibernate学习笔记3

Hibernate–3

学习大纲:

  • 数据库中表的关系

    • 一对一

    • 一对多

    • 多对多

      注意:不管是哪种关系,在逻辑上都有主表和从表之分

  • 外键 : 从表中的一列来源于主表的主键,或者为null,默认情况下外键是可以重复的

  • 如何确立和实现表之间的关系

    • 确立:看表的外键90%的情况下能确立表之间的关系

    • 实现:

      • 一对一 (A表和B表):

        • B表中的外键是A表的主键,且此外键设置唯一约束,非空约束
        • B表的外键是A表的主键,同时又是主键
      • 一对多(A表和B表):

        其中A表示主表,B表示从表,主表一般指的是一,从表指的是多

        B表的外键是A表的主键,也可以为null

      • 多对多(A表和B表)

        维护一个第三表,该表只有两个字段,且都为主键,引用自A,B两个表的主键

        任何一个多表和第三表之间的关系都是一对多的关系

  • 使用Hibernate多表映射配置的步骤

    • 第一步:确立两张表之间的关系
    • 第二步:在数据库中创建出这两张表,并实现两张表之间的关系
    • 第三步:在实体类中描述出两个实体之间的关系
    • 第四步:在映射配置文件中配置两张表之间的关系

情景一:建立房东(landlord)数据表和租户(tenant)数据表

  • 第一步:确立两张表之间的关系

    一个房东可以有多个租户

    一个租户只能有一个房东

    所以房东和租户之间是一对多的关系

  • 第二步:在数据库中创建出这两张表,并实现两张表之间的关系

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    /*房东信息表*/
    CREATE TABLE hb_landlord(
    l_id INT PRIMARY KEY AUTO_INCREMENT,
    l_name VARCHAR(10),
    l_sex CHAR(1),
    l_age INT(3),
    l_phone VARCHAR(15)
    )

    /*租户信息表*/
    CREATE TABLE hb_tenant(
    t_id INT PRIMARY KEY AUTO_INCREMENT,
    t_name VARCHAR(10),
    t_sex CHAR(1),
    t_age INT(3),
    t_phone VARCHAR(15),
    t_job VARCHAR(10)
    )

    只要在Hibernate中配置好表的映射配置文件以后,获取Session的时候,会自动加载配置文件,Hibernate检查表是否已经创建,如果没有创建就替我们创建表

  • 第三步:在实体类中描述出两个实体之间的关系

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    // 房东 表
    public class Landlord {
    private Integer lid;
    private String lname;
    private String lsex;
    private Integer lage;
    private String lphone;

    // 一位房东可以包含多位租客,建立一对多关系
    // 注意这里的处理方式:防止后面调用添加租客的时候报空指针异常(set还没有创建),提前将其创建出来
    // 如果数据众多的时候,会有多个tenants创建,会很占用内存,所以初始化的时候现将其初始容量设置成0
    private Set<Tenant> tenants = new HashSet<>(0);

    public Landlord() {
    }

    public Set<Tenant> getTenants() {
    return tenants;
    }

    public void setTenants(Set<Tenant> tenants) {
    this.tenants = tenants;
    }

    public Integer getLid() {
    return lid;
    }

    public void setLid(Integer lid) {
    this.lid = lid;
    }

    public String getLname() {
    return lname;
    }

    public void setLname(String lname) {
    this.lname = lname;
    }

    public String getLsex() {
    return lsex;
    }

    public void setLsex(String lsex) {
    this.lsex = lsex;
    }

    public Integer getLage() {
    return lage;
    }

    public void setLage(Integer lage) {
    this.lage = lage;
    }

    public String getLphone() {
    return lphone;
    }

    public void setLphone(String lphone) {
    this.lphone = lphone;
    }

    @Override
    public String toString() {
    return "Landlord{" +
    "lid=" + lid +
    ", lname='" + lname + '\'' +
    ", lsex='" + lsex + '\'' +
    ", lage=" + lage +
    ", lphone='" + lphone + '\'' +
    '}';
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    //租户 表
    public class Tenant {
    private Integer tid;
    private String tname;
    private String tsex;
    private Integer tage;
    private String tphone;
    private String tjob; // 从事的工作

    // 一位租户只能有一个房东,建立关系
    private Landlord landlord;

    public Tenant() {
    }

    public Integer getTid() {
    return tid;
    }

    public void setTid(Integer tid) {
    this.tid = tid;
    }

    public String getTname() {
    return tname;
    }

    public void setTname(String tname) {
    this.tname = tname;
    }

    public String getTsex() {
    return tsex;
    }

    public void setTsex(String tsex) {
    this.tsex = tsex;
    }

    public Integer getTage() {
    return tage;
    }

    public void setTage(Integer tage) {
    this.tage = tage;
    }

    public String getTphone() {
    return tphone;
    }

    public void setTphone(String tphone) {
    this.tphone = tphone;
    }

    public String getTjob() {
    return tjob;
    }

    public void setTjob(String tjob) {
    this.tjob = tjob;
    }

    @Override
    public String toString() {
    return "Tenant{" +
    "tid=" + tid +
    ", tname='" + tname + '\'' +
    ", tsex='" + tsex + '\'' +
    ", tage=" + tage +
    ", tphone='" + tphone + '\'' +
    ", tjob='" + tjob + '\'' +
    '}';
    }
    }

  • 第四步:在映射配置文件中配置两张表之间的关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?xml version="1.0" encoding="UTF-8" ?>
<!--指定命名空间,从hibernate-core-5.2.16.Final包中找-->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.lee.one2many">
<class name="Landlord" table="hb_landlord">
<id name="lid" column="l_id">
<!--主键自增长,使用数据库本地的自增长能力-->
<generator class="native"></generator>
</id>

<property name="lname" column="l_name"></property>
<property name="lsex" column="l_sex"></property>
<property name="lage" column="l_age"></property>
<property name="lphone" column="l_phone"></property>

<!--
set:配置set集合属性
name:一对多关系中,维护的"多"对应的集合set的成员属性名:private Set<Tenant> tenants = new HashSet<>(0);
table:对应的从表的表名(在一对多的配置中可以不写)

key:用于映射从表的外键字段
column:指定从表中外键的字段名称

one-to-many:从表对应的实体类

其他字段:
lazy:是否懒加载,true:懒加载(延迟加载)-指的是是否延迟加载关联的从表记录

inverse:true,代表放弃了外键的维护权,表示它不关心外键是否正确或null(默认为false)

cascade : save-update:级联保存或者更新
delete:级联删除(慎用)
cascade="save-update,delete"
-->
<set name="tenants" table="hb_tenant" cascade="save-update" lazy="false">
<key column="ten_land_fk_id"></key>
<one-to-many class="Tenant"></one-to-many>
</set>

<!--
理解 : 因为维护了多的一方的集合,肯定要知道多的一方对应的实体类(!!必须配置实体类名)
: 多的一方怎么跟我建立起关系的呢?因为它引用了我的主键作为它的外键(!!必须要知道外键对应的字段)
: 因为已经指定了实体类,而实体类已经通过映射配置文件为其配置了对应的表名,因此表名可有可无
-->
</class>
</hibernate-mapping>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?xml version="1.0" encoding="UTF-8" ?>
<!--指定命名空间,从hibernate-core-5.2.16.Final包中找-->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.lee.one2many">
<class name="Tenant" table="hb_tenant" lazy="false">
<id name="tid" column="t_id">
<!--主键自增长,使用数据库本地的自增长能力-->
<generator class="native"></generator>
</id>
<property name="tname" column="t_name"></property>
<property name="tsex" column="t_sex"></property>
<property name="tage" column="t_age"></property>
<property name="tphone" column="t_phone"></property>
<property name="tjob" column="t_job"></property>


<!--
name : 多对一中,"一"对应的属性字段
class : "一"对应的实体类
column : 映射的外键
lazy: proxy:代理,依赖主表实体类的加载模式(在主表映射配置文件的<class>标签中进行配置)
false:立即加载
no-proxy:
默认为proxy
-->
<many-to-one name="landlord" class="Landlord" column="ten_land_fk_id" lazy="proxy"/>
</class>
</hibernate-mapping>

情景二:学生和老师的信息

  • 第一步:确立两张表之间的关系

    一位学生可以拥有多个老师

    一位老师可以拥有多个学生

    所以学生表和老师表是多对多的关系

  • 第二步:在数据库中创建出这两张表,并实现两张表之间的关系

    只要配置文件中没有出错,Hibernate就会创建出正确的表来

  • 第三步:在实体类中描述出两个实体之间的关系

    互相持有对方的一个Set集合引用

  • 第四步:在映射配置文件中配置两张表之间的关系

    Student实体对应的数据库表

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <?xml version="1.0" encoding="UTF-8" ?>
    <!--指定命名空间,从hibernate-core-5.2.16.Final包中找-->
    <!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping package="com.lee.many2many">
    <class name="Student" table="hb_student">
    <id name="sid" column="s_id">
    <!--主键自增长,使用数据库本地的自增长能力-->
    <generator class="native"></generator>
    </id>

    <property name="sname" column="s_name"></property>
    <property name="sage" column="s_age"></property>
        <!--
        set:用于配置映射的集合属性
        name:指定当前映射实体类中对应集合属性的属性名称
        table:指定生成中间表的表名
        column:当前映射文件对应实体在中间表中的联合主键字段
        class:指定集合属性所装的实体类型
        column:指定对方在表中对应的字段名称
        -->
    
        <!--注意:多对多情况下慎用级联删除,可能会出现删除不需要删除的对象-->
        <set name="teachers" table="tea_stu_ref"  cascade="save-update,delete">
            <key column="stu_id"></key>
            <many-to-many class="Teacher" column="tea_id"></many-to-many>
        </set>
    
        <!--
        理解 : 因为维护了多的一方的集合,肯定要知道多的一方对应的实体类(!!必须配置实体类名)
            : 多的一方怎么跟我建立起关系的呢?因为它引用了我的主键作为它的外键(!!必须要知道外键对应的字段)
           : 因为已经指定了实体类,而实体类已经通过映射配置文件为其配置了对应的表名,因此表名可有可无
         -->
    </class>
    

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31

    Teacher实体对应的数据库表

    ```xml
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping package="com.lee.many2many">
    <class name="Teacher" table="hb_teacher">
    <id name="tid" column="t_id">
    <generator class="native"></generator>
    </id>

    <property name="tname" column="t_name"></property>
    <property name="tage" column="t_sex"></property>


    <!--注意:多对多情况下慎用级联删除,可能会出现删除不需要删除的对象-->
    <set name="students" table="tea_stu_ref">
    <key column="tea_id"></key>
    <many-to-many class="Student" column="stu_id"></many-to-many>
    </set>

    <!--
    理解 : 因为维护了多的一方的集合,肯定要知道多的一方对应的实体类(!!必须配置实体类名)
    : 多的一方怎么跟我建立起关系的呢?因为它引用了我的主键作为它的外键(!!必须要知道外键对应的字段)
    : 因为已经指定了实体类,而实体类已经通过映射配置文件为其配置了对应的表名,因此表名可有可无
    -->
    </class>
    </hibernate-mapping>

  • 延迟加载思想

  • 导航查询思想

  • load方法和get方法的区别,get方法立即查询.get方法取决于当前实体类映射配置文件中<calss>标签中的lazy属性,true表示延迟加载.