SpringDataJpa一对多、多对一关系关联以及一对多多对一双向关联
前言
案例Github地址(可以用git clone 到本地) https://github.com/chenxiban/SpringBootJpa-One-To-Many.git
今天为大家分享:SpringDataJpa一对多、多对一关系关联以及一对多多对一双向关联。
前面讲了SpringDataJpa自定义查询语句(JPQL),请查看博主的SpringDataJpa系列文章。欢迎关注!
一对多实体关联关系
一对多是以一的一方为主,当我们对班级进行操作时会相应的级联到学生表,比如查询班级,自动会得到该班级下
的所有学生。
1.搭建项目,配置项目环境
2.配置pom
<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>com.ysd</groupId>
<artifactId>spring-boot-jap-one-to-many</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-boot-jap-one-to-many</name>
<url>http://maven.apache.org</url>
<!-- Spring Boot 启动父依赖 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.1.RELEASE</version>
</parent>
<!-- 项目全局属性 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<mybatis-spring-boot>1.2.0</mybatis-spring-boot>
<mysql-connector>5.1.39</mysql-connector>
</properties>
<dependencies>
<!-- Spring Boot Web 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Test 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring Boot devtools 热部署 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<!-- Spring Boot JPA 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MySQL 连接驱动依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector}</version>
</dependency>
<!-- Junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
<!-- SpringBoot 项目发布 打jar包 依赖 -->
<build>
<plugins>
<!-- SpringBoot 项目发布 打jar包 依赖 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- Maven test junit 报告中文UTF-8编码 插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.6</version>
<configuration>
<forkMode>once</forkMode>
<argLine>-Dfile.encoding=UTF-8</argLine>
</configuration>
</plugin>
</plugins>
</build>
</project>
3.配置yml属性文件(sql文件在static文件夹下)
spring:
datasource:
url: jdbc:mysql://localhost:3306/springbootjpaonetomany?useUnicode=true&characterEncoding=utf8
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
jpa:
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect #不加这句则默认为myisam引擎
##运行时输出jpa执行的sql语句
show-sql: true
## spring-boot-starter-data-jpa自动映射创建表动作 配置: 有表更新,无表创建
hibernate:
ddl-auto: update
#集中解决各种编码问题
banner:
charset: UTF-8
http:
encoding:
charset: UTF-8
enabled: true
force: true
messages:
encoding: UTF-8
# spring mvc 视图解析器
mvc:
view:
prefix: /
suffix: .html
# 时间格式化
jackson:
date-format: yyyy-MM-dd HH:mm:ss
# 时区设置
time-zone: GMT+8
4.编写实体类
》 在com.cyj.springboot.entity下,编写class(班级)、student(学生)、代码如下:
package com.cyj.springboot.entity;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.persistence.Transient;
import com.fasterxml.jackson.annotation.JsonIgnore;
@Entity
@Table(name = "clazztb")
public class Clazz implements Serializable {
@Id // 实体类的主键
@GeneratedValue // 自动增长列
@Column(columnDefinition = "int unsigned NOT NULL comment '备注:班级自动增长主键' ")
private Integer clazzId;
@Column(length = 10, unique = true)
private String clazzName;
@OrderBy
@Column(columnDefinition = "int unsigned DEFAULT 0 comment '备注:班级总人数' ")
private Integer clazzNumber;
// @JsonIgnore
@OneToMany(mappedBy = "clazz", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<Student> list = new ArrayList<>();
// ----------------------------- 以下是构造方法 ------------------------
// ----------------------------- 以下是Getter和setter方法 -----------------
public Integer getClazzId() {
return clazzId;
}
public void setClazzId(Integer clazzId) {
this.clazzId = clazzId;
}
public String getClazzName() {
return clazzName;
}
public void setClazzName(String clazzName) {
this.clazzName = clazzName;
}
public Integer getClazzNumber() {
return clazzNumber;
}
public void setClazzNumber(Integer clazzNumber) {
this.clazzNumber = clazzNumber;
}
public List<Student> getList() {
return list;
}
public void setList(List<Student> list) {
this.list = list;
}
// ----------------------------- 以下是重写的toString方法 ------------------------
/*
* @Override public String toString() { return "Clazz [clazzId=" + clazzId +
* ", clazzName=" + clazzName + ", clazzNumber=" + clazzNumber + "]"; }
*
*
* public String showClazz() { return "Clazz [clazzId=" + clazzId +
* ", clazzName=" + clazzName + ", clazzNumber=" + clazzNumber + "]"; }
*
* public String showClazzAndStudent() { return "Clazz [clazzId=" + clazzId +
* ", clazzName=" + clazzName + ", clazzNumber=" + clazzNumber + ", list=" +
* list + "]"; }
*/
}
学生实体
package com.cyj.springboot.entity;
import java.io.Serializable;
import java.sql.Timestamp;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.persistence.Transient;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
@Entity
@Table(name = "studenttb")
public class Student implements Serializable {
@Id // 实体类的主键
@GeneratedValue // 自动增长列
@OrderBy // 数据加载顺序
@Column(columnDefinition = "int unsigned NOT NULL comment '备注:学生自动增长主键' ")
private Integer studentId;
@Column(length = 20) // 字符长度20
private String studentName;
@Column(columnDefinition = "char(1) comment '备注:学生姓名' ")
private String studentSex;
@Column(columnDefinition = "int unsigned DEFAULT 0 comment '备注:学生年龄' ")
private Integer studentAge;
private Date studentBirthday;
// @CreationTimestamp@UpdateTimestamp //插入,修改时自动维护时间戳
@Column(columnDefinition = "TIMESTAMP", nullable = false, updatable = false, insertable = false)
private Timestamp updateTime;
@Transient // 临时参数,不映射到数据库表字段
private String studentSpare;
@JsonIgnore
// @JsonUnwrapped
@ManyToOne(targetEntity = Clazz.class)
@JoinColumn(name = "student_clazz_id") // 副表中的外键字段名称
private Clazz clazz;
// ----------------------------- 以下是构造方法 ------------------------
// ----------------------------- 以下是Getter和setter方法 ------------------------
public Integer getStudentId() {
return studentId;
}
public void setStudentId(Integer studentId) {
this.studentId = studentId;
}
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
public String getStudentSex() {
return studentSex;
}
public void setStudentSex(String studentSex) {
this.studentSex = studentSex;
}
public Integer getStudentAge() {
return studentAge;
}
public void setStudentAge(Integer studentAge) {
this.studentAge = studentAge;
}
public Date getStudentBirthday() {
return studentBirthday;
}
public void setStudentBirthday(Date studentBirthday) {
this.studentBirthday = studentBirthday;
}
public Timestamp getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Timestamp updateTime) {
this.updateTime = updateTime;
}
public String getStudentSpare() {
return clazz.getClazzName();
}
public void setStudentSpare(String studentSpare) {
this.studentSpare = studentSpare;
}
public Clazz getClazz() {
return clazz;
}
public void setClazz(Clazz clazz) {
this.clazz = clazz;
}
/*
* @Override public String toString() { return "Student [studentId=" + studentId
* + ", studentName=" + studentName + ", studentSex=" + studentSex +
* ", studentAge=" + studentAge + ", studentBirthday=" + studentBirthday +
* ", updateTime=" + updateTime + ", studentSpare=" + studentSpare + ", clazz="
* + clazz + "]"; }
*/
// ----------------------------- 以下是重写的toString方法 ------------------------
/*
* @Override public String toString() { return "Student [studentId=" + studentId
* + ", studentName=" + studentName + ", studentSex=" + studentSex +
* ", studentAge=" + studentAge + ", studentBirthday=" + studentBirthday +
* ", updateTime=" + updateTime + ", studentSpare=" + studentSpare + "]"; }
*
* public String showStudent() { return "Student [studentId=" + studentId +
* ", studentName=" + studentName + ", studentSex=" + studentSex +
* ", studentAge=" + studentAge + ", studentBirthday=" + studentBirthday +
* ", updateTime=" + updateTime + ", studentSpare=" + studentSpare + "]"; }
*
* public String showStudentAndClazz() { return "Student [studentId=" +
* studentId + ", studentName=" + studentName + ", studentSex=" + studentSex +
* ", studentAge=" + studentAge + ", studentBirthday=" + studentBirthday +
* ", updateTime=" + updateTime + ", studentSpare=" + studentSpare + ", clazz="
* + clazz + "]"; }
*/
}
此时多的一方学生类无影响,对应studenttb表正常写出所有列,包括外键列,需注意外键列对应属性名必须叫做
clazzId。
@OneToMany(fetch=FetchType.EAGER,cascade=CascadeType.ALL):
@OneToMany表示该列为一对多关系列;
- fetch表示该实体的加载方式,有两种:LAZY和EAGER,懒加载和立即加载;
- cascade表示与此实体一对一关联的实体的联级样式类型。联级样式上当对实体进行操作时的策略。
说明:在定义关系时经常会涉及是否定义Cascade(级联处理)属性,担心造成负面影响。- 不定义,则对关系表不会产生任何影响
- CascadeType.PERSIST (级联新建)
- CascadeType.REMOVE (级联删除)
- CascadeType.REFRESH (级联刷新)
- CascadeType.MERGE (级联更新)
- CascadeType.ALL ,表示选择全部四项
- targetEntity 表示默认关联的实体类型,默认为当前标注的实体类;
mappedBy属性用于双向关联实体时使用,在一的一方进行声明,表示自己不是一对多的关系维护端,由对
方来维护。取值应该为多的一方的外键列对应的属性名。
5.编写dao接口
在com.cyj.springboot.dao,编写ClazzRepository、StudentRepository接口,代码如下:
package com.cyj.springboot.dao;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import com.cyj.springboot.entity.Clazz;
public interface ClazzRepository extends JpaRepository<Clazz, Integer> {
// Like --- 等价于 SQL 中的 "like",比如 findByNameLike(String name);
public List<Clazz> findByClazzNameLike(String name);
}
学生
package com.cyj.springboot.dao;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import com.cyj.springboot.entity.Student;
public interface StudentRepository extends JpaRepository<Student, Integer> {
// Like --- 等价于 SQL 中的 "like",比如 findByNameLike(String name);
public List<Student> findByStudentNameLike(String name);
}
6.编写业务逻辑层
在com.cyj.springboot.service,代码如下:
package com.cyj.springboot.service;
import java.util.List;
import com.cyj.springboot.entity.Clazz;
public interface ClazzService {
public Clazz save(Clazz clazz);
public Clazz queryById(Integer id);
public List<Clazz> queryByNameLike(String name);
}
学生
package com.cyj.springboot.service;
import java.util.List;
import com.cyj.springboot.entity.Student;
public interface StudentService {
public Student queryById(Integer id);
public List<Student> queryByNameLike(String name);
}
7.编写业务逻辑层
在com.cyj.springboot.ServiceImpl下编写业务逻辑实现层,代码如下:
package com.cyj.springboot.ServiceImpl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.cyj.springboot.dao.ClazzRepository;
import com.cyj.springboot.entity.Clazz;
import com.cyj.springboot.entity.Student;
import com.cyj.springboot.service.ClazzService;
@Service
public class ClazzServiceImpl implements ClazzService {
@Autowired
private ClazzRepository repository;
@Override
public Clazz save(Clazz clazz) {
return repository.save(clazz);
}
@Override
public Clazz queryById(Integer id) {
return repository.findOne(id);
}
@Override
public List<Clazz> queryByNameLike(String name) {
return repository.findByClazzNameLike("%" + name + "%");
}
}
学生
package com.cyj.springboot.ServiceImpl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.cyj.springboot.dao.StudentRepository;
import com.cyj.springboot.entity.Student;
import com.cyj.springboot.service.StudentService;
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
private StudentRepository repository;
@Override
public Student queryById(Integer id) {
return repository.findOne(id);
}
@Override
public List<Student> queryByNameLike(String name) {
return repository.findByStudentNameLike("%" + name + "%");
}
}
8.编写controller层
在com.cyj.springboot.controller下,编写班级控制层、学生控制层,代码如下:
package com.cyj.springboot.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.cyj.springboot.entity.Clazz;
import com.cyj.springboot.entity.Student;
import com.cyj.springboot.service.ClazzService;
/**
* SpringMVC控制器
*
* @Description: 子模块
* @ClassName: CityRestController.java
* @author ChenYongJia
* @Date 2017-10-4 下午8:04:34
* @Email 867647213@qq.com
*/
@RestController
@RequestMapping("/clazz")
public class ClazzController {
@Autowired
private ClazzService service;
/**
* http://localhost:8080/clazz/queryById?id=1
*
* @param id
* @return Student
*/
@RequestMapping("/queryById")
public Clazz queryById(Integer id) {
Clazz clazz = service.queryById(id);
System.out.println("queryById clazz=>" + clazz);// .showClazzAndStudent());
return clazz;
}
/**
* http://localhost:8080/clazz/queryId?id=1
*
* @param id
* @return Student
*/
@RequestMapping("/queryId")
public Object queryId(Integer id) {
Clazz clazz = service.queryById(id);
System.out.println("queryById clazz=>" + clazz);// .showClazzAndStudent());
return clazz;
}
}
学生
package com.cyj.springboot.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.cyj.springboot.entity.Student;
import com.cyj.springboot.service.StudentService;
/**
* SpringMVC控制器
*
* @Description: 子模块
* @ClassName: CityRestController.java
* @author ChenYongJia
* @Date 2017-10-4 下午8:04:34
* @Email 867647213@qq.com
*/
@RestController
@RequestMapping("/student")
public class StudentController {
@Autowired
private StudentService service;
/**
* http://localhost:8080/student/queryById?id=1
*
* @param id
* @return Student
*/
@RequestMapping("/queryById")
public Student queryById(Integer id) {
Student student = service.queryById(id);
System.out.println("queryById student=>" + student);// .showStudentAndClazz());
return student;
}
/**
* http://localhost:8080/student/queryId?id=1
*
* @param id
* @return Student
*/
@RequestMapping("/queryId")
public String queryId(Integer id) {
Student student = service.queryById(id);
System.out.println("queryById student=>" + student);// .showStudentAndClazz());
return "查询成功";
}
/**
* http://localhost:8080/student/queryByNameLike?name=张三
*
* @param id
* @return Student
*/
@RequestMapping("/queryByNameLike")
public Object queryByNameLike(String name) {
List<Student> list = service.queryByNameLike(name);
System.out.println("queryByNameLike list=>" + list);
for (Student s : list) {
System.out.println("Student =>" + s);// .showStudentAndClazz());
}
return list;
}
}
9.编写项目主类
在com.cyj.springboot下编写项目主类,代码如下:
package com.cyj.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
/**
* Spring Boot 应用启动类
*
* @Description: 主模块
* @ClassName: Application.java
* @author ChenYongJia
* @Date 2017-10-4 下午8:03:41
* @Email 867647213@qq.com
*/
@EnableJpaRepositories(basePackages = "com.cyj.springboot.dao") // Spring Jpa 启用注解
@EntityScan(basePackages = "com.cyj.springboot.entity") // 扫描Jpa实体对象
@SpringBootApplication // Spring Boot 应用的标识
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);// 程序启动入口 启动嵌入式的 Tomcat 并初始化 Spring 环境及其各 Spring 组件
}
}
启动项目,查看运行结果,测试路径可根据controller层的提示进行测试
多对一关系
多对一是以多的一方为主,当我们对学生进行操作时会相应的级联到班级表,比如查询学生,自动会得到该学生所
在的班级。
@Entity @Table(name="studenttb")
public class Student implements Serializable{
//多的一方
//...其他属性省略
@ManyToOne(targetEntity = Clazz.class)//通过实体反射说明该外键列来自于哪张表
private Clazz clazz;//因为不只保存所属班级主键,而是所属班级所有信息
}
此时一的一方班级类无影响,对应clazztb表正常写出所有列。
一对多多对一双向关联:
很显然,当一对多的时候,操作学生并不能级联到班级;同样的,当多对一的时候,操作班级也不能级联到学生,
那么想要双向关联要怎么做呢?很简单,把上面两者结合即可,即:
\
多的一方学生因为要显示班级信息,多个学生属于同一个班级,所以学生实体类当中要有一个属性是班级对象,当
查询学生时显示其所属班级,因为该类当中已经有了班级主键作为外键列,所以就有了2.2多对一中的 private Clazz clazz 的写法来代替并包含了外键列;同样的,一个班级对象中也要显示该班级下所有的学生,而班级表并
不需要学生表主键来做外键,为了满足保存所有学生的情况,我们在班级实体类加入泛型为学生的集合来做属性。
此时两者互相关联,那么外键如何处理呢?当我们添加一个学生,如果该班级不存在,按照业务设计要么失败要么
自动级联添加,反之添加班级时也会添加…,此时两方都在维护外键,就会导致冗余和冲突,所以我们需要指定一
方来维护即可。所以我们按照谁使用谁处理的原则,交给多的一方来处理。具体写法为,在一的一方也就是
@OneToMany
中添加 mappedBy="clazz"
来进行指定。
所以实现双向关联即为一对多+多对一组合,且在
@OneToMany
中添加mappedBy="clazz"
即可。
好了到这里也该结束了,各位要自己多动手才能学到真正的东西。加油各位
最后
-
更多参考精彩博文请看这里:《陈永佳的博客》
-
喜欢博主的小伙伴可以加个关注、点个赞哦,持续更新嘿嘿!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/97630.html