【Spring Boot】关系映射开发(一):一对一映射

news2024/7/6 18:40:01

关系映射开发(一):一对一映射

  • 1.认识实体间关系映射
    • 1.1 映射方向
    • 1.2 ORM 映射类型
  • 2.实现 “一对一” 映射
    • 2.1 编写实体
      • 2.1.1 新建 Student 实体
      • 2.1.2 新建 Card 实体
    • 2.2 编写 Repository 层
      • 2.2.1 编写 Student 实体的 Repository
      • 2.2.2 编写 Card 实体的 Repository
    • 2.3 编写 Service 层
      • 2.3.1 编写 Student 的 Service 层
      • 2.3.2 编写 Card 的 Service 层
    • 2.4 编写 Service 实现
      • 2.4.1 编写 Student 实体的 Service 实现
      • 2.4.2 编写 Card 实体的 Service 实现
    • 2.5 编写测试
  • 3.单向 / 双向 OneToOne
    • 3.1 单向一对一关系的拥有端
    • 3.2 单向一对一关系的接收端
    • 3.3 双向一对一关系的接收端

1.认识实体间关系映射

对象关系映射object relational mapping)是指通过将对象状态映射到数据库列,来开发和维护对象和关系数据库之间的关系。它能够轻松处理(执行)各种数据库操作,如插入、更新、 删除等。

1.1 映射方向

ORM 的映射方向是表与表的关联(join),可分为两种。

  • 单向关系:代表一个实体可以将属性引用到另一个实体。即只能从 A 表向 B 表进行联表查询。
  • 双向关系:代表每个实体都有一个关系字段(属性)引用了其他实体。

1.2 ORM 映射类型

  • 一对一@OneToOne):实体的每个实例与另一个实体的单个实例相关联。
  • 一对多@OneToMany):一个实体的实例可以与另一个实体的多个实例相关联。
  • 多对一@ManyToOne): —个实体的多个实例可以与另一个实体的单个实例相关联。
  • 多对多@ManyToMany):—个实体的多个实例可能与另一个实体的多个实例有关。在这个映射中,任何一方都可以成为所有者方。

2.实现 “一对一” 映射

一对一 映射首先要确定实体间的关系,并考虑表结构,还要考虑实体关系的方向性。

若为 双向关联,则在保存实体关系的实体中要配合注解 @JoinColumn;在没有保存实体关系的实体中,要用 mappedBy 属性明确所关联的实体。

下面通过实例来介绍如何构建一对一的关系映射。

2.1 编写实体

2.1.1 新建 Student 实体

package com.example.demo.entity;

import lombok.Data;
import javax.persistence.*;

@Entity
@Data
@Table(name = "stdu")
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    private String name;
    @Column(columnDefinition = "enum('male','female')")
    private String sex;
    /**
     * Description:
     * 建立集合,指定关系是一对一,并且申明它在cart类中的名称
     * 关联的表为card表,其主键是id
     * 指定外键名为card_id
     */
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "card_id")
    private Card card;
}

在这里插入图片描述

2.1.2 新建 Card 实体

package com.example.demo.entity;

import lombok.Data;
import javax.persistence.*;

@Entity
@Table(name = "card")
@Data
public class Card {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    private Integer num;
}

在这里插入图片描述

2.2 编写 Repository 层

2.2.1 编写 Student 实体的 Repository

package com.example.demo.repository;

import com.example.demo.entity.Student;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import javax.transaction.Transactional;

public interface StudentRepository extends JpaRepository<Student, Long> {
    Student findById(long id);
    Student deleteById(long id);
}

2.2.2 编写 Card 实体的 Repository

package com.example.demo.repository;

import com.example.demo.entity.Card;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

public interface CardRepository extends JpaRepository<Card, Long>, JpaSpecificationExecutor<Card> {
    Card findById(long id);
}

2.3 编写 Service 层

2.3.1 编写 Student 的 Service 层

package com.example.demo.service;

import com.example.demo.entity.Student;
import java.util.List;

public interface StudentService {
    public List<Student> getStudentlist();
    public Student findStudentById(long id);
}

2.3.2 编写 Card 的 Service 层

package com.example.demo.service;

import com.example.demo.entity.Card;
import java.util.List;

public interface CardService {
    public List<Card> getCardList();
    public Card findCardById(long id);
}

2.4 编写 Service 实现

2.4.1 编写 Student 实体的 Service 实现

package com.example.demo.service.impl;

import com.example.demo.entity.Student;
import com.example.demo.repository.StudentRepository;
import com.example.demo.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class StudentServiceImpl implements StudentService {
    @Autowired
    private StudentRepository studentRepository;

    @Override
    public List<Student> getStudentlist() {
        return studentRepository.findAll();
    }

    @Override
    public Student findStudentById(long id) {
        return studentRepository.findById(id);
    }
}

2.4.2 编写 Card 实体的 Service 实现

package com.example.demo.service.impl;

import com.example.demo.entity.Card;
import com.example.demo.repository.CardRepository;
import com.example.demo.service.CardService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class CardServiceImpl implements CardService {
    @Autowired
    private CardRepository cardRepository;

    @Override
    public List<Card> getCardList() {
        return cardRepository.findAll();
    }

    @Override
    public Card findCardById(long id) {
        return cardRepository.findById(id);
    }
}

2.5 编写测试

完成了上面的工作后,就可以测试它们的关联关系了。

package com.example.demo.entity;

import com.example.demo.repository.CardRepository;
import com.example.demo.repository.StudentRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import static org.junit.Assert.*;

@SpringBootTest
@RunWith(SpringRunner.class)
public class oneToOneTest {
    @Autowired
    private StudentRepository studentRepository;
    @Autowired
    private CardRepository cardRepository;

    @Test
    public void testOneToOne() {
        Student student1 = new Student();
        student1.setName("赵大伟");
        student1.setSex("male");
        Student student2 = new Student();
        student2.setName("赵大宝");
        student2.setSex("male");

        Card card1 = new Card();
        card1.setNum(422802);
        student1.setCard(card1);
        studentRepository.save(student1);
        studentRepository.save(student2);
        Card card2 = new Card();
        card2.setNum(422803);
        cardRepository.save(card2);
        /**
         * Description: 获取添加之后的id
         */
        Long id = student1.getId();
        /**
         * Description: 删除刚刚添加的student1
         */
        studentRepository.deleteById(id);
    }
}

首先,注释掉 studentRepository.deleteById(id);,看一下数据库。因为级联的原因,在运行 studentRepository.save(student1); 时,就保存了 card1,所以不需要像 cardRepository.save(card2); 一样运行 cardRepository.save(card1);

在这里插入图片描述
在这里插入图片描述
运行代码,在控制台输出如下测试结果:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

可以看到,同时在两个表 stducard 中添加了内容,然后删除了刚添加的有关联的 stducard 表中的值。如果没有关联,则不删除。

🚀 在双向关系中,有一方为关系的 发出端,另一方是关系的 反端,即 Inverse 端(接收端)。对于双向的一对一关系映射,发出端和接收端都要使用注解 @OneToOne,同时定义一个接收端类型的字段属性和 @OneToOne 注解中的 mappedBy 属性。这个在双向关系的接收端是必需的。

3.单向 / 双向 OneToOne

单向一对一 是关联关系映射中最简单的一种,简单地说就是可以从关联的一方去查询另一方,却不能反向查询。我们用下面的例子来举例说明,清单 1 中的 Person 实体类和清单 2 中的 Address 类就是这种单向的一对一关系,我们可以查询一个 Person 的对应的 Address 的内容,但是我们却不能由一个 Address 的值去查询这个值对应的 Person。

3.1 单向一对一关系的拥有端

@Entity 
public class Person implements Serializable { 
	private static final long serialVersionUID = 1L; 
	@Id 
	@GeneratedValue(strategy = GenerationType.AUTO) 
	private Long id; 
	private String name; 
	private int age; 
	@OneToOne 
	private Address address; 
 
	// Getters & Setters 
}

3.2 单向一对一关系的接收端

@Entity 
public class Address implements Serializable { 
	private static final long serialVersionUID = 1L; 
	@Id 
	@GeneratedValue(strategy = GenerationType.AUTO) 
	private Long id; 
	private String street; 
	private String city; 
	private String country;
	
	// Gettes & Setters 
}

在这里插入图片描述
从上面的 ER 图可以看出,这种单向的一对一关系在数据库中是以外键的形式被映射的。其中关系的 发出端 存储一个指向关系的接收端的一个外键。在这个例子中 person 表中的 ADDRESS_ID 就是指向 address 表的一个外键,缺省情况下这个外键的字段名称,是以它指向的表的名称加下划线 _ID 组成的。当然我们也可以根据我们的喜好来修改这个字段,修改的办法就是使用 @JoinColumn 这个注解。在这个例子中我们可以将这个注解注释在 Person 类中的 Address 属性上去。

3.3 双向一对一关系的接收端

@Entity 
public class Address implements Serializable { 
	private static final long serialVersionUID = 1L; 
	@Id 
	@GeneratedValue(strategy = GenerationType.AUTO) 
	private Long id; 
	private String street; 
	private String city; 
	private String country; 
	@OneToOne(mappedBy = "address") 
	private Person person; 
	
	// Gettes & Setters 
}

双向关系有一方为关系的拥有端,另一方是关系的接收端(反端),也就是 Inverse 端。在这个例子中 Person 拥有这个关系,而 Address 就是关系的 Inverse 端。Address 中我们定义了一个 person 属性,在这个属性上我们使用了 @OneToOne 注解并且定义了它的 mappedBy 属性,这个在双向关系的 Inverse 端是必需的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1891049.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

轮换IP是什么?——深入了解轮换IP的特点

大家在日常上网时&#xff0c;可能听说过“轮换IP”这个词。那么&#xff0c;轮换IP到底是什么&#xff1f;它有哪些特点&#xff1f;今天&#xff0c;我们就来揭开轮换IP的神秘面纱。 什么是轮换IP&#xff1f; 简单来说&#xff0c;轮换IP是指定期更换上网时使用的IP地址。…

C++字体库开发二

Font: 字体大小&#xff0c;方向&#xff0c;变换 FontContext: 多语言&#xff0c;更新&#xff0c;基础字体&#xff0c;表情 FontDescription: 字重&#xff0c;子样式&#xff0c;名称&#xff0c;底色 FontDir: 字体目标 FontFace: Regular,Bold特殊字重 FontFamily: 字体…

嵌入式c语言3——自定义数据类型

结构体struct&#xff0c;共用体union 结构体中定义变量&#xff0c;首尾地址相连 对于union&#xff0c;其包含变量对起始地址相同 由于其起始地址相同&#xff0c;则改变其中某一变量值时有可能使得另一个变量值发生改变 enum 枚举&#xff0c;可以用来定义一堆整形常量构成…

itk::ShapedNeighborhoodIterator类C2516问题

错误问题&#xff1a; 1>C:\itk\src-5.3.0\Modules\Core\Common\include\itkShapedNeighborhoodIterator.h(183,1): error C2516: itk::ShapedNeighborhoodIterator<TImage,TBoundaryCondition>::ConstIterator: is not a legal base class 1>C:\itk\src-5.3.0\Mo…

解决Visual Studio 一直弹出管理员身份运行问题(win10/11解决办法)

不知道大家是否有遇到这个问题 解决办法也很简单 找到启动文件 如果是快捷方式就继续打开文件位置 找到这个程序启动项 右键 选择 兼容性疑难解答&#xff08;win11 则需要 按住 shift 右键&#xff09; win10 解决办法 这样操作完后就可以了 win11解决办法按以下选择就行

Android触摸事件分发关键点【笔记摘要】

触摸事件分发&#xff1a;就是一个为了解决触摸事件冲突而设置的机制 1.事件类型 ACTION_DOWN -> ACTION_UP / ACTION_CANCEL ACTION_DOWN -> ACTION_MOVE -> ACTION_MOVE -> ACTION_MOVE -> ACTION_UP / ACTION_CANCEL 这个取消事件ACTION_CANCEL它是一种特殊…

Go语言工程管理

本文内容为Go工程创建和配置开发及简单程序示例。 目录 工程管理 GOPATH 配置GOPATH GOROOT 新建系统变量 配置go工程 添加go path 简单的程序实现 程序代码 开始运行 运行结果 内容解析 总结 工程管理 GOPATH go语言的项目&#xff0c;需要有特定的目录结构进行…

Osg中的智能指针和观察指针

目录 1 设计 内容 1 设计 osg中能够使用智能指针的对象都继承自引用计数类Referenced&#xff0c;观察指针(observer_ptr)与智能指针之间通过ObserverSet相互关联&#xff0c;其中obserserver_ptr直接依赖ObeserverSet。 Referenced不直接依赖ObserverSet类&#xff0c;但可…

springboot java.lang.ClassNotFoundException: dm.jdbc.driver.DmDriver 应该如何解决

遇到的问题&#xff1a;项目中引用了外部的达梦jar包 在idea中正常使用 也能找到dm.jdbc.driver.DmDriver 驱动 但是当通过jenkins 构建部署到服务器上 总是报 ClassNotFoundException: dm.jdbc.driver.DmDriver 找不到驱动 应用到的驱动代码如下格式 排查步骤 1.首先看你的项…

hibernate与jpa学习

jpa是一个规范&#xff0c;hibernate是jpa的实现&#xff0c;是一种框架。 hibernate&#xff1a; ORM框架/持久层框架&#xff08;Object Relational Mapping&#xff09; 它是是一个轻量级开放源代码的对象关系映射框架&#xff0c;hibernate可以自动生成SQL语句&#xff0…

什么是yum源?如何对其进行配置?

哈喽&#xff0c;大家好呀&#xff01;这里是码农后端。今天来聊一聊Linux下的yum源及其配置相关的内容。简单来说&#xff0c;yum源就相当于一个管理软件的工具&#xff0c;可以想象成一个很大的仓库&#xff0c;里面存放着各种我们所需要的软件包及其依赖。 一、Linux下软件包…

MySQL:保护数据库

保护数据库 1. 用户1.1 创建用户1.2 查看用户1.3 删除用户1.4 修改密码 2. 权限2.1 授予权限2.2 查看权限2.3 撤销权限 之前都是介绍本地数据库而你自己就是数据库的唯一用户&#xff0c;所以不必考虑安全问题。但实际业务中数据库大多放在服务器里&#xff0c;你必须妥善处理好…

git提交实战

以新项目为例&#xff0c;如何在新项目新分支提交代码。 1.查看文件所在位置 git init 2.克隆项目到本地并完成身份配置 3.将需要新增的文件放到指定目录路径下 4.进入新克隆的文件 cd XXX 5.切换分支 git checkout XXX 6.标红者即为新提交的文件 git status 7.加入 git …

研发驱动 再谱新篇丨美格智能南通研发中心正式成立

近日&#xff0c;美格智能全资设立的众格智能技术&#xff08;南通&#xff09;有限公司&#xff0c;正式在江苏省南通市紫琅科技城揭牌成立&#xff0c;此举也标志着继上海、西安、深圳之后&#xff0c;美格智能研发力量布局再谱新篇&#xff1a;美格智能南通研发中心正式成立…

液压传动知识

绪论 工作原理 依靠运动者的液体的压力能传递动力液体在受调节、控制状态下工作&#xff0c;液压传动和控制同等重要液压传动以液体为工作介质 液压系统组成 动力元件&#xff1a;机械能转换为液体压力能。液压泵控制元件&#xff1a;对液体流动方向、压力、流量进行控制或…

Flask框架下图像的base64编码接收和解码流程

一、Flask简介 Flask是一个轻量级的可定制框架&#xff0c;使用Python语言编写&#xff0c;较其他同类型框架更为灵活、轻便、安全且容易上手。它可以很好地结合MVC模式进行开发&#xff0c;开发人员分工合作&#xff0c;小型团队在短时间内就可以完成功能丰富的中小型网站或We…

39. 自由空间光通信中光学天线的设计

设计要求如下&#xff1a; 设计一接收光学天线&#xff0c;满足以下技术要求&#xff1a; 焦距&#xff1a;f60mm&#xff0c;D/f1/1.2&#xff0c;视场角2w0.1&#xff0c;激光波长&#xff1a;λ0.850μm&#xff0c;激光漂移波长&#xff1a;0.848~0.852μm。 光学特性特…

基于物品的协同过滤算法

预估用过户对候选物品的兴趣 计算物品相似度 召回通道 快速做召回条件

Empire Lupin One

信息收集&#xff1a; nmap存活&#xff1a; nmap端口&#xff1a; nmap服务&#xff1a; nmap漏扫&#xff1a; 访问 robots.txt 文件发现目录&#xff1a; 源码&#xff1a; web&#xff1a; 源码并没有藏东西&#xff0c; 用wfuzz发现新页面&#xff1a; icex64 在创建ss…

redis主从复制哨兵模式集群管理

主从复制&#xff1a; 主从复制是高可用Redis的基础&#xff0c;哨兵和集群都是在主从复制基础上实现高可用的。主从复制主要实现了数据的多机备份&#xff0c;以及对于读操作的负载均衡和简单的故障恢复。缺陷&#xff1a;故障恢复无法自动化&#xff1b;写操作无法负载均衡&…