SpringDataJpa一对多、多对一关系关联以及一对多多对一双向关联

导读:本篇文章讲解 SpringDataJpa一对多、多对一关系关联以及一对多多对一双向关联,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

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

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!