JPA基础(4)—— 关联表映射

2019-05-30  本文已影响0人  黄宝玲_1003

JPA 基础(1)—— 数据库持久化代码实战
JPA 基础(2)—— 分页操作
JPA 基础(3)—— Auditing
JPA 基础(4)—— 关联表映射

从JPA2.0开始,不仅支持实体集合的映射,还支持基本类型(如String,Integer等)集合以及值对象(Embeded)集合的映射。

代码实战

学生表——student

实体包含:
学号no(主键),姓名name,成绩关系映射,家庭关系映射,班级关系映射。

package com.sunseaiot.stu.entity;

import lombok.Data;

import javax.persistence.*;
import java.util.Map;
import java.util.Set;

/**
 * @author huangbaoling
 * @date 2019/5/29 9:39 AM
 */
@Data
@Entity
@Table(name = "student")
public class StudentEntity {

    @Id
    @Column(length = 36)
    private String no;

    @Column(length = 32, nullable = false)
    private String name;

    @ElementCollection(fetch = FetchType.EAGER) 
    @CollectionTable(name = "student_to_grade", joinColumns = @JoinColumn(name = "stu_no"))
    @Column(length = 36, nullable = false)
    private Map<String, Grade> grade;

    @ElementCollection(fetch = FetchType.LAZY)
    @CollectionTable(name = "student_to_family", joinColumns = @JoinColumn(name = "stu_no"))
    @Column(name = "family_name")
    private Set<String> familyName;

    @ManyToOne(fetch = FetchType.LAZY) 
    @JoinTable(name = "student_to_class",
            joinColumns = @JoinColumn(name = "stu_no"),
            inverseJoinColumns = @JoinColumn(name = "class_no"))
    private ClassEntity classNo;
}

下面解析student表涉及知识点:

属性 是否必须 说明
name 指定该连接表的表名
JoinColumns 该属性值可接受多个 @JoinColumn,用于配置连接表中外键列的信息,这些外键列参照当前实体对应表的主键列
inverseJoinColumns 该属性值可接受多个@JoinColumn,用于配置连接表中外键列的信息,这些外键列参照当前实体的关联实体对应表的主键列
targetEntity 该属性指定关联实体的类名。在默认情况下,Hibernate将通过反射来判断关联实体的类名
catalog 设置将该连接表放入指定的catalog中。如果没有指定该属性,连接表将放入默认的catalog
schema 设置将该连接表放入指定的schema中。如果没有指定该属性,连接表将放入默认的schema
uniqueConstraints 该属性用于为连接表增加唯一约束
indexes 该属性值为@Index注解数组,用于为该连接表定义多个索引

三张关联表的设计:
1、学生-成绩关联表
学生有成绩,但成绩无法独立于学生而存在,所以不应该将成绩映射为一个实体entity,这时需要映射为组件(表述可能不正确,只是为了实现对非实体类的关联,如果场景需要也是可以将成绩独立成一个pojo类的)。成绩可以分为各个科目,所以我用Map<String, Grade>存储,key对应科目, value对应期中、期末之类分时间段的成绩。
Grade结构:

package com.sunseaiot.stu.entity;

import lombok.Data;

import javax.persistence.Embeddable;

/**
 * @author huangbaoling
 * @date 2019/5/29 2:53 PM
 */
@Data
@Embeddable
public class Grade {

    private String time;

    private String score;
}

2、学生-家人关联表
同样对该连接表使用非实体的映射@ElementCollection,与上表实现相似。

3、学生-班级关联表
班级是一个独立的实体类:

package com.sunseaiot.stu.entity;

import lombok.Data;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * @author huangbaoling
 * @date 2019/5/29 10:37 AM
 */
@Data
@Entity
@Table(name = "class")
public class ClassEntity {

    @Id
    private String no;

    private String name;
}

由于学生和班级是多对一的关系,所以使用注解 @ManyToOne ,@JoinTable(name = "student_to_class", joinColumns = @JoinColumn(name = "stu_no"), inverseJoinColumns = @JoinColumn(name = "class_no")) 指明了关联表名student_to_class,设置学生student表的主键为外键stu_no,设置班级class表的主键为外键class_no。

自动建表配置:

spring:
  jpa:
    hibernate:
      ddl-auto: update
  datasource:
    driver-class-name: "com.mysql.cj.jdbc.Driver"
    url: "jdbc:mysql://192.168.5.17:3306/hbl_test?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false"
    username: "root"
    password: "root"

目前Mysql只有InooDB类型的引擎支持外键约束。

启动类:

package com.sunseaiot.stu;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

/**
 * @author huangbaoling
 * @date 2019/5/27 5:19 PM
 */
@SpringBootApplication
@ComponentScan({"com.sunseaiot.stu.entity"})
public class StudentInstallApplication {

    public static void main(String[] args) {
        SpringApplication.run(StudentInstallApplication.class, args);
    }

}

当数据库没有表时,就会自动生成。

疑问:
1、发现生成的student_to_family没有主键,后来将family属性的@Column注解加上不为空后@Column(name = "family_name", nullable = false),得到的表结构如下:

解答:后者的写法应该才是正确的,因为该关联表是一对多的关系,stu_no和family_name 必须是联合主键。(看到此处请去纠正前面的代码)

2、同样的我们可以看到student_to_class表的class_no也不是主键,但是在classEntity中no为主键,所以应该也是注解出了问题,查看建表ddl语句:

-- auto-generated definition
create table student_to_class
(
  class_no varchar(255) null,
  stu_no   varchar(36)  not null
    primary key,
  constraint FK84yvwttrfub9kbqhs1np9x488
    foreign key (class_no) references class (no),
  constraint FKj5smmktp1b5suue91k2i68apy
    foreign key (stu_no) references student (no)
);

果然class_no不是主键,这显然不是我们想要的。

解答:未解决,后续补充。

3、表 student_to_grade 映射的map值为Grade,生成的关联表会默认为Grade结构体生成一个grade_key,它和指定的stu_no组成联合主键。

解答:未解决,后续补充。

Tips

上一篇 下一篇

猜你喜欢

热点阅读