springboot +mybatis实现多表一对一查询

这篇文章并没有新加什么依赖。若你是新项目可以参考一下这篇文章:springboot整合mybatis实现单表增删改查
项目效果:

一对一查询

首先展示页面效果和项目结构吧。这里是联合了数据库中的person表和idcard表。
person表:
在这里插入图片描述
idcard表:
在这里插入图片描述
最终效果:
在这里插入图片描述
项目结构:
在这里插入图片描述
先来解释一些名词含义:
一对一查询,就是指的是表关系,比如我这里的是学生表和学生卡表就是一对一的,一个学生只有一张学生卡,一张学生卡必然也对应着一个学生;
一对多:可以理解为一个母亲有多个孩子,或者一个部门有多个员工。一就是母亲或者部门,多就是孩子和员工;
多对多:可以理解为一个学生可以选择很多门课程,一门课程可以有很多个学生去选择。此时两者的关系就需要一个中间表来建立,比如说选课表。
字段名:数据库表中定义的每一列的名字。
属性名:定义在springboot中类中s定义的名字。如下图:
在这里插入图片描述

今天这里就介绍一下一对一查询,介绍两种方法。

第一种方法

pojo对象类

Person类

package com.example.csdn_release.pojo;

import lombok.Data;

import java.util.List;

//可以省略getset方法和toString方法
@Data
public class Person {
    private int id;
    private int age;
    private String name;
    private Idcard idcardid;
}

Idcard类

package com.example.csdn_release.pojo;

import lombok.Data;

@Data
public class Idcard {
    private int id;
    private String stuid;
    private String classname;
}

dao层

还是从底层dao开始写。在PersonMapper中新增一个方法

//查询所有person信息和他们的学生卡信息
public List<Person> findAllPersonIdcardid();

mappers

继续写mappers文件夹中的PersonMapper.xml文件,这也是待会比价难懂的地方。这里先讲普通的方法,也是我觉得巨简单的方法。先贴代码在解释

<!-- 查询所有的idcardid,一对一查询   -->
    <select id="findAllPersonIdcardid"  resultMap="personIdcard">
        select person.* ,idcard.stuid,idcard.classname
        from person,idcard
        where person.idcardid=idcard.id
    </select>

    <resultMap id="personIdcard" type="com.example.csdn_release.pojo.Person">
        <id column="id" property="id"></id>
        <result property="name" column="name"></result>
        <result property="age" column="age"></result>
<!--person需要IDcard实体类子段映射到数据库 的多个字段-->
<!--    idcardid是与上面查询语句查询出来的列明对应,也就是对应于person表中发idcardid-->

        <association property="idcardid" javaType="com.example.csdn_release.pojo.Idcard">
            <id property="id" column="idcardid"></id>
            <result column="stuid" property="stuid"></result>
            <result property="classname" column="classname"></result>
        </association>
    </resultMap>

select标签

这里select标签中的id就是对应于我们dao层中的新加的那个方法名,然后resultMap=“”就是对应于select标签下的那个resultMap标签的id名。这样就是得select查询标签与resultMap关联起来了。继续讲解这个SQL语句,因为是使用两表联查,所以我们这里就涉及了两个表中查询。person表中的idcardid就对应于idcard表中 的id。但是这可不是随便对应 的,必须得是idcard表中的主键才行。

resultMap标签

你可能会有疑问,为什么要有resultMap标签呢?不要可不可以呢?
如果sql查询到的字段与pojo中定义的类中的属性名不一致,则需要使用resultMap将字段名和属性名对应起来,进行手动配置封装,将结果映射到pojo中。这里我们就是person表中的属性不一致,因为它多了一个idcard属性。涉及了两个或者多个就需要resultMap了。所以resultMap中就需要包含person表中的所有字段和属性,还要加上idcard表中的字段和属性。person表中的就是常规的就OK。idcard表中的字段就需要使用association ,理解为集合的意思,在这个标签中写idcard表中的所有字段和属性就OK啦。然后这句话中的property就是对应于我在Person类中定义的属性名:private Idcard idcardid;

service层

PersonService接口类

新加查询方法。

//  级联查询查询所有人和校园卡信息
     public List<Person> findAllPersonAndIdcard2();

PersonServiceImpl接口实现类

就是在这里调用dao层中personMapper中的findAllPersonIdcardid就行啦

  /*person表
  person表和idcard表一对一查询出来所有人的信息和校园卡情况*/
 @Override
  public List<Person> findAllPersonAndIdcard() {
    return personMapper.findAllPersonIdcardid();
  }

controller层

  @RequestMapping("personlist")
//    model可以为html页面传递参数
    public String personList(Model model){
//        传递参数,在这里调用service接口
//        使用一对一查询借口
//普通多表一对一查询
      model.addAttribute("personlist",personService.findAllPersonAndIdcard()); 
        //model.addAttribute("personlist",personService.findAllPersonAndIdcard2());第二种方式级联查询接口
//        拦截到请求后将personlist.html的页面映射出来
        return "personlist.html";
    }

templates

新建一个personlist.html文件,与上面return返回的页面对应。

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>用户列表</title>
</head>
<body>
<!--一个基本的 html表格-->
<table border="1px" cellspacing="0px">
  <tr>
    <th th:width="40px">id</th>
    <th th:width="40px">年龄</th>
    <th th:width="40px">姓名</th>
    <th th:width="40px">学号</th>
    <th th:width="150px">班级</th>
    <th >删除</th>
    <th>更新</th>
  </tr>

  <tr th:each="person:${personlist}">
    <td th:text="${person.id}"></td>
    <td th:text="${person.age}"></td>
    <td th:text="${person.name}"></td>
    <td th:text="${person.idcardid.stuid}"></td>
    <td th:text="${person.idcardid.classname}"></td>
    <td><a th:href="@{deletepersonbyid(id=${person.id})}">删除用户</a> </td>
    <td><a th:href="@{updatepersonbyid(id=${person.id},name=${person.name},age=${person.age})}">更新用户</a> </td>
  </tr>
</table>
</body>
</html>

第一种方式就讲完了,第二种方式除了mapper映射的xml文件有些不同。

第二种方式

pojo中新建一个Idcard类,最上面我已经给了粗来了。往上面翻一翻,

dao层

新建一个IDcardMapper接口类

代码:
这里就是传入一个id来查询idcard的信息。有些人可能就会问,我们不是要查询表中所有的信息吗,往上面要传入id呀,那不是只能查询一个人的 信息吗?这也是我上课的时候有点不理解的地方,导致最后跟着老师的节奏把我的上课那个项目给写崩了,一直没有解决是哪里出错,问了老师也没有解决。但是还好我一直坚持在写博客,我写博客这个项目是我重写上课老师的那个项目的,所以我直接就换了这个项目了。我仔细回想一遍好像和我现在写的步骤完全一致呀。思路也很清晰为什么就崩了呢。不理解。继续写这个项目把。放心!!!!!这个项目是跑得起来的,我不会把错误的代码放到我的博客上面滴!!!!!好吧,待会来个视频演示算了~~~~~~~~

package com.example.csdn_release.dao;
import com.example.csdn_release.pojo.Idcard;
import org.apache.ibatis.annotations.Mapper;

    @Mapper
public interface IDcardMapper {
    Idcard findIdcardById(Integer id);//级联查询第一步
}

mappers中新建IdcardMapper.xml文件

就是为了对应上面那个IdcardMapper接口类。执行SQL语句

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.csdn_release.dao.IDcardMapper">
    <select id="findIdcardById"
            parameterType="Integer"
            resultType="com.example.csdn_release.pojo.Idcard">
        select * from idcard where id=#{id}
    </select>
</mapper>

PersonMapper.xml

在PersonMapper.xml中新加一个级联查询的select标签,并为这个标签设置一个resultMap.xml 直接放在第一种方法的resultMap下就可以了。方便对比

<!--    级联查询-->

    <select id="findAllPersonIdcardid2"  resultMap="personIdcard2">
        select * from person
    </select>

    <resultMap id="personIdcard2" type="com.example.csdn_release.pojo.Person">
        <id column="id" property="id"></id>
        <result property="name" column="name"></result>
        <result property="age" column="age"></result>
        <!--person需要IDcard实体类子段映射到数据库 的多个字段-->
        <!--    idcardid是与上面查询语句查询出来的列明对应,
        也就是对应于person表中发idcardid,
        select查询语句将查询出来的结果映射到javaType中去
        column中的idcardid是通过使用这个resultMap的select中查询得到的idcardid。
        然后将查询到的IDcardid作为参数传到这个select语句中去查询作为参数传到select语句中-->
        <association property="idcardid"
                     javaType="com.example.csdn_release.pojo.Idcard"
                     column="idcardid"
                    select="com.example.csdn_release.dao.IDcardMapper.findIdcardById">

        </association>
    </resultMap>

在这里首先可以发现select标签中的SQL语句变了。因为级联查询是分为好几步的,我们这里是先查询出所有的person表信息。然后我们这里为它加了一个id为personIdcard2的resultMap。idcardid是与上面查询语句查询出来的列明对应, 也就是对应于person表中发idcardid,select查询语句将查询出来的结果映射到javaType中去column中的idcardid是通过使用这个resultMap的select中查询得到的idcardid。然后将查询到的IDcardid作为参数传到这个select语句中去查询作为参数传到select语句中
在这里插入图片描述

service层

personService接口类新加一个方法

这里我为了让你们更加清楚我就多写一个接口,就不用第一个方法的接口了,免得你们混淆

//  级联查询查询所有人
     public List<Person> findAllPersonAndIdcard2();

实现类

  //  级联查询查询所有的person表与IDcard表的关联
  @Override
  public List<Person> findAllPersonAndIdcard2() {
    return personMapper.findAllPersonIdcardid2();
  }

Controll层

在PersonControl中直接修改里面的一行代码就行。就是改了一个调用方法。

不要新加,就是在原有的里面修改

   @RequestMapping("personlist")
//    model可以为html页面传递参数
    public String personList(Model model){
//        传递参数,在这里调用service接口
//        使用一对一查询借口
//普通多表一对一查询
      //model.addAttribute("personlist",personService.findAllPersonAndIdcard()); 
      //第二种方式级联查询接口
        model.addAttribute("personlist",personService.findAllPersonAndIdcard2());
//        拦截到请求后将personlist.html的页面映射出来
        return "personlist.html";
    }

增加

IDcardMapper接口类

新加一个方法

int addIdcard(Idcard idcard);

对应的xml问件

<!--    useGeneratedKeys:增加的数据主键返回,又叫主键回填,因为我们这里并没有新加id值
    它是由数据库设置的id自增长的,所以我们增加一条数据后可以拿到这个id
     keyProperty:返回的主键复制给idcard的属性-->
    <insert id="addIdcard"
            parameterType="com.example.csdn_release.pojo.Idcard"
            useGeneratedKeys="true" keyProperty="id">
        insert into idcard(stuid,classname) values (#{stuid},#{classname})
    </insert>

接口实现类

  public int addPerson(Person person) {
//    第一步:先增加IDcard表数据
      int result=iDcardMapper.addIdcard(person.getIdcardid());
//     第二部:再增加Person表数据
      int result2=personMapper.addPerson(person);
      if(result2>0&&result>0){
          return result2;
      }
      //添加失败
      return 0;
  }

controll层

 @RequestMapping("addperson")
    public String addperson(){
        return "addperson.html";
    }
    @RequestMapping("addpersoncommit")
    public String addpersoncommit(Person person){
        personService.addPerson(person);
        return "redirect:/personlist";
    }

删除

删除主要是需要同时删除两个表中的数据。这里就快一点了。

personList.html

在这个页面中主要是在删除的 时候同时向服务器传递两个主键就OK了。两种拼接法:

//第一种
  <td><a th:href="@{'/deletepersonbyid?id='+${person.id}+'&cardid='+${person.idcardid.id}}">删除用户</a> </td>
  //第二种
   <td><a th:href="@{'deletepersonbyid(id=${person.id},cardid=${person.idcardid.id})}">删除用户</a> </td>

dao层两个删除方法

删除学生卡信息:

 public Integer deleteIdcardById(Integer id);

对应的xml文件:

  <delete id="deleteIdcardById"  parameterType="Integer">
        delete from idcard where id=#{id}
    </delete>

删除学生信息:

 public Integer deletePersonById(Integer id);

对应的xml文件:

    <delete id="deletePersonById"  parameterType="Integer">
        delete from person where id=#{id}
    </delete>

service 层

@Override
  public int deletePersonById(Integer id,Integer cardid) {
    int result=personMapper.deletePersonById(id);
    int result2=iDcardMapper.deleteIdcardById(cardid);
      if(result2>0&&result>0){
          return result2;
      }
      return 0;
  }

controller层

   @RequestMapping("deletepersonbyid")
    public String deletepersonbyid(int id,int cardid){
        personService.deletePersonById(id,cardid);
        return "redirect:/personlist";
    }

然后其他的就不用变,直接运行就OK了。如果有什么问题欢迎评论区留言或者私信哟

假设有两张表,一张是学生表(student),一张是课程表(course),一个学生可以选多门课程,一门课程也可以被多个学生选中。这种情况下,我们可以使用一对多的关系来实现。下面是一个简单的实现代码: 1. 定义学生表(student)和课程表(course)的实体类 ```java public class Student { private Long id; private String name; private List<Course> courses; //省略getter和setter方法 } public class Course { private Long id; private String name; //省略getter和setter方法 } ``` 2. 定义学生表(student)和课程表(course)的mapper接口 ```java public interface StudentMapper { Student selectStudentById(Long id); } public interface CourseMapper { List<Course> selectCoursesByStudentId(Long studentId); } ``` 3. 在StudentMapper.xml中定义一对多的关系 ```xml <mapper namespace="com.example.mapper.StudentMapper"> <resultMap id="studentMap" type="Student"> <id column="id" property="id" /> <result column="name" property="name" /> <collection property="courses" ofType="Course" resultMap="courseMap" /> </resultMap> <resultMap id="courseMap" type="Course"> <id column="id" property="id" /> <result column="name" property="name" /> </resultMap> <select id="selectStudentById" resultMap="studentMap"> SELECT s.id, s.name, c.id AS course_id, c.name AS course_name FROM student s LEFT JOIN student_course sc ON s.id = sc.student_id LEFT JOIN course c ON sc.course_id = c.id WHERE s.id = #{id} </select> </mapper> ``` 4. 在CourseMapper.xml中定义查询课程信息的SQL语句 ```xml <mapper namespace="com.example.mapper.CourseMapper"> <select id="selectCoursesByStudentId" resultType="Course"> SELECT c.id, c.name FROM course c LEFT JOIN student_course sc ON c.id = sc.course_id WHERE sc.student_id = #{studentId} </select> </mapper> ``` 5. 编写Controller和Service层代码 ```java @RestController public class StudentController { @Autowired private StudentService studentService; @GetMapping("/students/{id}") public Student getStudentById(@PathVariable Long id) { return studentService.getStudentById(id); } } @Service public class StudentServiceImpl implements StudentService { @Autowired private StudentMapper studentMapper; @Autowired private CourseMapper courseMapper; @Override public Student getStudentById(Long id) { Student student = studentMapper.selectStudentById(id); List<Course> courses = courseMapper.selectCoursesByStudentId(id); student.setCourses(courses); return student; } } ``` 以上就是一对多的实现代码。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

H-rosy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值