0
点赞
收藏
分享

微信扫一扫

spirngboot使用hibernate,完成映射关系及其使用场景探究

悄然丝语 2022-07-12 阅读 9


   一直对数据库映射这块缺乏练习,导致在实践中总是卡在一个某个具体的问题上。因此这里花上一小段时间将映射这块练习一下,方便日后回顾。

   环境准备:

    

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.automannn</groupId>
<artifactId>hibernateRelationShipPractice</artifactId>
<version>1.0-SNAPSHOT</version>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
</parent>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>


</project>

数据源,目录,以及启动类:

spirngboot使用hibernate,完成映射关系及其使用场景探究_ide

spirngboot使用hibernate,完成映射关系及其使用场景探究_java_02

package com.automannn;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
*
* @time 2018/10/20 16:51
*/
@SpringBootApplication
public class App {

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

    一对一单向映射的练习:

设  一个学校有且仅有一个校长,一个校长仅能任职于一个学校。 因此他们就构成了一对一的关系。  

package com.automannn.entity;

import javax.persistence.*;

/**
*
* @time 2018/10/20 17:08
*/
@Entity
public class HeadMaster {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

private String headMasterName;

@OneToOne
private School school;
}

package com.automannn.entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

/**
*
* @time 2018/10/20 17:07
*/
@Entity
public class School {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

private String schoolName;
}

  这个时候,校长是主控方,持有学校的信息。运行之后查表:

spirngboot使用hibernate,完成映射关系及其使用场景探究_ide_03

spirngboot使用hibernate,完成映射关系及其使用场景探究_java_04

  在这种场合下,我们可以通过主控方查看另一方的信息。即通过校长查看到相应的学校信息。 反之,若学校为主控方,那么相应的也要反转,这是单向的。  并且他们的联系是通过外键关联的,但是我看到很多的博客都说不要使用外键,关系的对应通过程序控制。

  一对一双向映射练习:

package com.automannn.entity;

import javax.persistence.*;

/**
* @author
* @time 2018/10/20 17:08
*/
@Entity
public class HeadMaster {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

private String headMasterName;

@OneToOne(mappedBy = "headMaster")
private School school;
}

package com.automannn.entity;

import javax.persistence.*;

/**
* @time 2018/10/20 17:07
*/
@Entity
public class School {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

private String schoolName;

@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "headMaster_id")
private HeadMaster headMaster;
}

它们的表结构如下:

spirngboot使用hibernate,完成映射关系及其使用场景探究_ide_05

spirngboot使用hibernate,完成映射关系及其使用场景探究_java_06

   要注意,将hibernate在update状态下,貌似对于删除表结构的操作可能有缺陷,所以导致我测试浪费了一些时间。

   

spirngboot使用hibernate,完成映射关系及其使用场景探究_ide_07

   最简形式:


spirngboot使用hibernate,完成映射关系及其使用场景探究_spring_08

spirngboot使用hibernate,完成映射关系及其使用场景探究_java_09

spirngboot使用hibernate,完成映射关系及其使用场景探究_spring_10

同时,注解的属性与类中的属性通过程序上下文联系起来。  不具有固定性!!!   后面思考了下,这种说法不准确,虽然是上下文,但是属性变量实际上是起到了一个管道的作用,因此要写的对应关系还是很好理解的了。

多与一的常见关系映射练习:

  设:  某个班具有多个学生,每个学生只能属于一个班。  因此,构成了这样的一个多与一的映射关系。

   现有一个需求如下:

     需要知道每个班有哪些学生?  那么可以通过单向一对多,单向多对一考虑。

    单向一对多:

package com.automannn.entity;

import javax.persistence.*;
import java.util.List;

/**
* @author
* @time 2018/10/20 16:53
*/
@Entity
public class Clazz {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

private String clazzName;

@OneToMany
private List<Student> studentList;
}

package com.automannn.entity;

import javax.persistence.*;

/**
* @author automannn@163.com
* @time 2018/10/20 16:52
*/

@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

private String name;

}

  这种情况下,它生成的表结构:

spirngboot使用hibernate,完成映射关系及其使用场景探究_spring_11

spirngboot使用hibernate,完成映射关系及其使用场景探究_spring_12

   会生成一张只具有外键的中间表。 并不是我们想要的效果。

  这是在具有最简注解:

spirngboot使用hibernate,完成映射关系及其使用场景探究_java_13

的情况。

当我们在Clazz.class修改如下:

spirngboot使用hibernate,完成映射关系及其使用场景探究_spring_14

  可以发现它的表结构:

spirngboot使用hibernate,完成映射关系及其使用场景探究_java_15

spirngboot使用hibernate,完成映射关系及其使用场景探究_ide_16

spirngboot使用hibernate,完成映射关系及其使用场景探究_spring_17

   可以发现一个现象,就是我们的那个joinColumn是在 Clazz.class实体写的,但是加入的属性列在Student表中。  因为,只要是一与多的关系,hibernate都是在多的一方记录信息。 

一致性维护由哪一方完成,因为这里涉及到性能的问题。但是由于这里是单向的关系,jpa规范应该是默认由多的一方完成吧,因为在注解中没找到inverse属性,同时看网上的解释也有:

spirngboot使用hibernate,完成映射关系及其使用场景探究_ide_18

  因此就不再去看了。  另外要注意,mappedBy  与 joinColumn不能同时出现,会报错。mappedBy只适用于双向关系的那种可能。

  最简配置:

spirngboot使用hibernate,完成映射关系及其使用场景探究_spring_19

   结构:

spirngboot使用hibernate,完成映射关系及其使用场景探究_spring_20

以上可以满足通过班级查询学生,而且是比较直观的理解。

那么,多对一实际也可以满足这种需求。

  单向一对多的练习:

package com.automannn.entity;

import javax.persistence.*;

/**
* @author
* @time 2018/10/20 16:52
*/

@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

private String name;

@ManyToOne
private Clazz clazz;

}

package com.automannn.entity;

import javax.persistence.*;
import java.util.List;

/**
* @author
* @time 2018/10/20 16:53
*/
@Entity
public class Clazz {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

private String clazzName;

}

   它生成的表结构:

spirngboot使用hibernate,完成映射关系及其使用场景探究_java_21

spirngboot使用hibernate,完成映射关系及其使用场景探究_java_22

spirngboot使用hibernate,完成映射关系及其使用场景探究_java_23

   这种情况下,我们实际上是去查询学生表,将班级以属性的方式进行查询的

查询班级表,以列表方式直观的将本班级学生给查询出来

都能够对两个表的信息进行增删该查。 并且表的结构也一样,只是查询的逻辑不同,还有就是性能方面可能有一些不同。

    由于我并没有很多的实战经验,但是我的感觉是,当查询的请求为主时,完全可以使用单向一对多的方式将所有的信息给查出来。否则,当以数据更新操作为主的时候,应该使用单向多对一的方式。

Ok,如果有另一个需求,需要知道每个学生所在的班级信息。  也就是需要从多的一端获取一的数据。这个时候可以这样干,那就是在根本上修改这种一与多的关系,导致它变长多与多的关系,形成一个回环。  但是这样很明显是不明智的。正确的做法应该是使用双向关系。因为这里是双向的,所以也就不存在方向性了,因为它的表结构都是一样的。

  双向多与一映射关系练习:

package com.automannn.entity;

import javax.persistence.*;
import java.util.List;

/**
* @author
* @time 2018/10/20 16:53
*/
@Entity
public class Clazz {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

private String clazzName;

@OneToMany(mappedBy = "clazz")
private List<Student> studentList;

}

package com.automannn.entity;

import javax.persistence.*;

/**
* @aut
* @time 2018/10/20 16:52
*/

@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

private String name;

@ManyToOne
private Clazz clazz;

}

    它生成的表结构:

spirngboot使用hibernate,完成映射关系及其使用场景探究_ide_24

spirngboot使用hibernate,完成映射关系及其使用场景探究_ide_25

spirngboot使用hibernate,完成映射关系及其使用场景探究_ide_26

   注意一下,@ManyToOne是没有mappedBy属性的。

    fetch属性一般默认即可,因为默认的都是推荐的。

     可以说,双向的这种一与多的关系简直就是我们的理想状态了。  并且将维护关系设置为多的一方,那我们就可以很方便,愉快的进行开发哈哈哈。  都说对性能有影响,但是我觉得性能是一个虚的概念,当业务需求根本不可能到达瓶颈的时候,性能问题根本不是问题了。

   很开心愉快的进行了一对一单双向,一对多单向,多对一单向,一与多双向的练习。  接下来就进入最后一个的练习。 多对多映射。

多对多映射的练习:

 设:一个老师有多个学生,一个学生可以被多个老师教。  因此他们就构成了一种多对多的关系。

  在这种多对多的关系中,我们经常要通过一方查询另一方的需求,因此也就不考虑单双向的问题了。

   但是发现多对多关系中是存在单双向问题的:

spirngboot使用hibernate,完成映射关系及其使用场景探究_ide_27

  

package com.automannn.entity;

import javax.persistence.*;
import java.util.List;

/**
* @author
* @time 2018/10/20 19:29
*/
@Entity
public class Teacher {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

private String teacherName;

@ManyToMany
private List<Student> student;
}

package com.automannn.entity;

import com.automannn.before.Clazz;

import javax.persistence.*;

/**
* @author automannn@163.com
* @time 2018/10/20 16:52
*/

@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

private String name;


}

生成的表结构如下:

spirngboot使用hibernate,完成映射关系及其使用场景探究_spring_28

spirngboot使用hibernate,完成映射关系及其使用场景探究_java_29

spirngboot使用hibernate,完成映射关系及其使用场景探究_spring_30

spirngboot使用hibernate,完成映射关系及其使用场景探究_ide_31

   这是单向的情况。  接着修改代码,使之变为双向,以满足业务需求。

package com.automannn.entity;

import javax.persistence.*;
import java.util.List;

/**
* @author
* @time 2018/10/20 19:29
*/
@Entity
public class Teacher {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

private String teacherName;

@ManyToMany(mappedBy ="teacherList" )
private List<Student> studentList;
}

package com.automannn.entity;

import com.automannn.before.Clazz;

import javax.persistence.*;
import java.util.List;

/**
* @time 2018/10/20 16:52
*/

@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

private String name;

@ManyToMany
private List<Teacher> teacherList;
}

它的表结构:

spirngboot使用hibernate,完成映射关系及其使用场景探究_java_32

spirngboot使用hibernate,完成映射关系及其使用场景探究_ide_33

spirngboot使用hibernate,完成映射关系及其使用场景探究_ide_34

实践过程的一些反思与总结:

    1.OnetoOne,ManyToOne,OneToMany,ManyToMany这几种关系中,是这样来表示逻辑关系的:  to之前的表示本端,或者说本实体对象,to之后的表示当前的属性对象,即被注解的实体对象。

     2.哪些情况下会在不设置其它的附加信息的情况下会生成中间表?  当被映射的实体对象为“一”的逻辑地位(不论是To之前还是之后),同时具有“多”外键的情况下的时候,会生成中间表!!!  即当“一”的一方需要维护多的一方关系的时候,会自动生成中间表。如:单向OneToMany,且不加@JoinColumn的情况。

     3.当逻辑地位为“多”的属性时候,必需设置为集合,否则会报错!  如: @ManyToOne private Student student;正确,而  @OneToMany private Student student; 错误!

举报

相关推荐

0 条评论