day38-springboot-crud开发

springboot实现CRUD

一、需求

实现对一张表数据的增删改查,使用springboot+ssm实现后端接口代码,实现接口工具测试

二、搭建环境

2.1 数据库环境

创建数据库: springboot_crud

注意: 确定是否有该库

创建表:

create table stu (
    id int primary key auto_increment,
    sname varchar(255),
    age int,
    sex char(1),
    score double(10,2),
    birthday date
);

2.2 项目环境

  • 创建springboot注意jdk和springboot版本
  • 找之前项目复制pom依赖和yml文件
  • 创建项目包结构
    • controller,service,mapper,model,util
    • 根据表创建实体类

三、 实现功能(单表)

编码思路

  1. 正写 controller -->service --> mapper
  2. 反写 mapper -> service --> controller

后续前后端分离开发,一般会有接口文档,文档中定义了请求类型,路径,参数已经响应的结果实例

day38-springboot-crud开发_第1张图片

3.1 查询一个数据

mapper

// 接口
public interface StudentMapper {
    // 查询一个数据
    Student findById(int id);
}


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.qf.mapper.StudentMapper">

    
    <select id="findById" resultType="Student">
        select * from stu where id = #{id}
    select>
mapper>

service

// 接口文件
public interface StudentService {
    // 查询一个数据
    Student findById(int id);
}
// 实现类文件
@Service // 创建业务层对象
public class StudentServiceImpl implements StudentService {

    @Autowired // 注入mapper对象
    private StudentMapper mapper;

    @Override
    public Student findById(int id) {
        return mapper.findById(id);
    }
}

controller

// 接收请求
@RestController // 全部返回JSON
@RequestMapping("/stu")
public class StudentController {

    @Autowired // 注入Service对象
    private StudentService service;

    @GetMapping("/find")
    public R findById(int id){
        Student student = service.findById(id);
        return R.ok(student);
    }
}

主类扫描mapper

@SpringBootApplication
@MapperScan("com.qf.mapper") // 扫描mapper接口产生代理对象
public class Day39SpringbootCrudApplication {

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

}

启动项目,接口工具测试

day38-springboot-crud开发_第2张图片

3.2 查询全部数据

mapper

	// 接口
  	// 无条件查询全部
    List<Student> findAll();


 <select id="findAll" resultType="Student">
        select * from stu
    select>

service

// 接口文件
 // 无条件查询全部
    List<Student> findAll();
// 实现类文件
 @Override
    public List<Student> findAll() {
        return mapper.findAll();
    }

controller

// 接收请求
@GetMapping("/list")
public R findAll(){
    List<Student> list = service.findAll( );
    return R.ok(list);
}

测试

3.3 条件查询

需求: 模拟搜索框根据年龄或者姓名查询学生

mapper

// 接口

  // [重点]模拟搜索框根据年龄或者模糊搜索姓名查询学生
   List<Student> findByKeyword(HashMap<String,Object> map);

<select id="findByKeyword" resultType="Student">
    
    select * from stu
    <where>
        
        <if test="age != null">
            and age = #{age}
        if>
        <if test="sname != null and sname != ''">
            
            
            
            and sname like concat('%',#{sname},'%')
        if>
    where>
select>

service

// 接口文件
    List<Student> findByKeyword(HashMap<String,Object> map);

// 实现类文件

    @Override
    public List<Student> findByKeyword(HashMap<String, Object> map) {
        return mapper.findByKeyword(map);
    }

controller

// 接收请求
    /**
     * 如果不熟悉: 看SpringBootV12.md笔记第五章5.3.5 封装Map
     * @param map
     * @return
     */
    @GetMapping("/search")
    public R findAll(@RequestParam HashMap<String,Object> map){
        List<Student> list = service.findByKeyword(map);
        return R.ok(list);
    }

测试

day38-springboot-crud开发_第3张图片

3.4 增加

mapper

// 接口
 // 增加(参数列表一定是对象)
    void add(Student student);


<insert id="add">
        
        insert into stu (sname,age,sex,score,birthday)
        values (#{sname},#{age},#{sex},#{score},#{birthday})

    insert>

service

// 接口文件
    // 增加
    void add(Student student);
// 实现类文件
  @Override
    public void add(Student student) {
        mapper.add(student);
    }

controller

// 接收请求
  /**
     * 添加(向数据库发送的,请求方式一般是post)
     * 能封装数据的前提是前端参数和对象属性名一致
     */
    @PostMapping("/add")
    public R add(Student student){
        service.add(student);
        return R.ok();
    }

测试

3.5 修改

方案1: 全表更新(推荐)

方案2: 部分更新

mapper

// 接口
// 更新
    void edit(Student student);

  <update id="edit">
        update stu set sname=#{sname},age=#{age},sex=#{sex},
        score=#{score},birthday=#{birthday} where id = #{id}
    update>

service

// 接口文件
 // 更新
    void edit(Student student);
// 实现类文件
 	@Override
    public void edit(Student student) {
        mapper.edit(student);
    }

controller

// 接收请求
	/**
     * 更新(向数据库发送的,请求方式一般是post)
     */
    @PostMapping("/edit")
    public R edit(Student student){
        service.edit(student);
        return R.ok();
    }

测试

3.6 删除

mapper

// 接口
    // 删除
    void deleteById(int id);

 	<delete id="deleteById">
        delete from stu where id = #{id}
    delete>

service

// 接口文件
 	// 删除
    void deleteById(int id);
// 实现类文件
 	@Override
    public void deleteById(int id) {
        mapper.deleteById(id);
    }

controller

// 接收请求
 	@GetMapping("/del")
    public R del(int id){
        service.deleteById(id);
        return R.ok();
    }

测试

四、多表联查

(场景:高中学校)

现有学生表 stu,再设计教室表classroom,与学生表一对一关系,即一个学生固定在一个教室

学科表subject, 与学生是多对多,即一个学生学习多种学科,一个学科多个学生学习

老师表teacher,与学生表是多对多,即一个学生对应多个老师,一个老师对应多个学生,

​ 与学科表是一对一,即一个老师教授一个学科

day38-springboot-crud开发_第4张图片

4.1 一对一(两表)

需求: 查询学生信息以及关联的教室信息

day38-springboot-crud开发_第5张图片

4.1.1 数据库环境

教室表

create table classroom(
  cid int primary key comment 'id',
  cnum varchar(255) comment '教室编号',
  seatNum int comment '座位数'   
);

day38-springboot-crud开发_第6张图片

学生与教室是一对一关系,但是教室与学生是一对多关系,所以两表的关联列应该设置在学生表里面。修改学生表,添加cid列,当然Student实体类也需要添加cid属性

day38-springboot-crud开发_第7张图片

4.1.2 实体类

教室类

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Classroom {

    private int cid;
    private String cnum;
    private int seatNum;
}

封装一对一扩展类StudentVO

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class StudentVO extends Student{
    private Classroom classroom;
}

4.1.3 一对一查询

StudentMapper

// 查询所有学生信息以及关联的班级信息
List<StudentVO> findAllStudentAndClassroom();
    <resultMap id="studentAndClassroomResultMap" type="studentVO">
        <id column="id" property="id"/>
        <result column="sname" property="sname"/>
        <result column="age" property="age"/>
        <result column="sex" property="sex"/>
        <result column="score" property="score"/>
        <result column="birthday" property="birthday"/>
        
        <association property="classroom" javaType="Classroom">
            <id column="cid" property="cid"/>
            <result column="cnum" property="cnum"/>
            <result column="seatNum" property="seatNum"/>
        association>

    resultMap>

    <select id="findAllStudentAndClassroom" resultMap="studentAndClassroomResultMap">
        select * from stu s left join classroom c
        on s.cid = c.cid
    select>

service

    // 查询所有学生信息以及关联的班级信息
    List<StudentVO> findAllStudentAndClassroom();
    @Override
    public List<StudentVO> findAllStudentAndClassroom() {
        return mapper.findAllStudentAndClassroom();
    }

controller

    @GetMapping("/class")
    public R findAllStudentAndClassroom(){
        List<StudentVO> list = service.findAllStudentAndClassroom( );
        return R.ok(list);
    }

测试结果

{
  "code": 20000,
  "msg": "成功",
  "data": [
    {  // 学生对象
      "id": 1,
      "sname": "老王",
      "age": 18,
      "sex": "男",
      "score": 100.0,
      "birthday": "2023-08-30T16:00:00.000+00:00",
      "cid": 0,
      "classroom": { // 教室对象
        "cid": 1,
        "cnum": "101",
        "seatNum": 50
      }
    },
    {
      "id": 2,
      "sname": "老李",
      "age": 19,
      "sex": "男",
      "score": 200.0,
      "birthday": "2023-08-29T16:00:00.000+00:00",
      "cid": 0,
      "classroom": {
        "cid": 1,
        "cnum": "101",
        "seatNum": 50
      }
    },
    {
      "id": 3,
      "sname": "老杭",
      "age": 20,
      "sex": "女",
      "score": 50.0,
      "birthday": "2023-07-31T16:00:00.000+00:00",
      "cid": 0,
      "classroom": {
        "cid": 2,
        "cnum": "102",
        "seatNum": 70
      }
    }
  ]
}

4.2 一对多(两表)

需求: 查询一个学生信息以及所学习的所有学科信息

注意: 虽然学生表和学科表是多对多,但是从单方向看,查询一个学生对应的多个学科的话就是1对多

day38-springboot-crud开发_第8张图片

4.2.1 数据库环境

学科表

create table subject(
  sub_id int primary key comment '学科id',
  sub_name varchar(255) comment '学科名'
);

day38-springboot-crud开发_第9张图片

因为学生表和学科表是多对多,所以需要设计中间关联表

CREATE TABLE `stu_sub` (
  `sid` int(11) NOT NULL COMMENT '学生id',
  `sub_id` int(11) NOT NULL COMMENT '科目id',
  PRIMARY KEY (`sid`,`sub_id`) -- 联合主键
);

day38-springboot-crud开发_第10张图片

再次强调需求: 查询一个学生信息以及所学习的所有学科信息

select stu.*,sub.* from stu
left join `stu_sub` ss on stu.id = ss.sid
left join `subject` sub on ss.sub_id = sub.sub_id
where stu.id = 1

day38-springboot-crud开发_第11张图片

4.2.2 实体类

学科类

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Subject {
    private int subId;
    private String subName;
}

封装一对多扩展类StudentVO(就是之前那个)

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class StudentVO extends Student{
    private Classroom classroom;
    // 封装多个学科信息
    private List<Subject> subjectList;
}

4.2.3 一对多查询

StudentMapper

  // 根据id查询1个学生信息以及关联的所有学科信息
    StudentVO findStudentAndAllSubjectByStuid(int sid);
    
    <resultMap id="studentAndAllSubjectResultMap" type="studentVO">
        <id column="id" property="id"/>
        <result column="sname" property="sname"/>
        <result column="age" property="age"/>
        <result column="sex" property="sex"/>
        <result column="score" property="score"/>
        <result column="birthday" property="birthday"/>
        
        <collection property="subjectList" ofType="Subject">
            <id column="sub_id" property="subId"/>
            <result column="sub_name" property="subName"/>
        collection>

    resultMap>
    <select id="findStudentAndAllSubjectByStuid" resultMap="studentAndAllSubjectResultMap">
        select stu.*,sub.* from stu
        left join `stu_sub` ss on stu.id = ss.sid
        left join `subject` sub on ss.sub_id = sub.sub_id
        where stu.id = #{sid}

    select>

service

  // 根据id查询1个学生信息以及关联的所有学科信息
    StudentVO findStudentAndAllSubjectByStuid(int sid);
    @Override
    public StudentVO findStudentAndAllSubjectByStuid(int sid) {
        return mapper.findStudentAndAllSubjectByStuid(sid );
    }

controller

    @GetMapping("/subjects")
    public R findStudentAndAllSubjectByStuid(int sid){
        StudentVO studentVO = service.findStudentAndAllSubjectByStuid(sid);
        return R.ok(studentVO);
    }

测试结果

{
  "code": 20000,
  "msg": "成功",
  "data": {  // 1个学生对象
    "id": 1,
    "sname": "老王",
    "age": 18,
    "sex": "男",
    "score": 100.0,
    "birthday": "2023-08-30T16:00:00.000+00:00",
    "cid": 0,
    "classroom": null,
    "subjectList": [ // 多个学科对象,是数组
      {
        "subId": 1,
        "subName": "语文"
      },
      {
        "subId": 2,
        "subName": "数学"
      },
      {
        "subId": 3,
        "subName": "英语"
      },
      {
        "subId": 4,
        "subName": "物理"
      }
    ]
  }
}

4.3 一对多对一(三表)

需求: 查询一个学生信息以及所学习的所有学科信息,以及每个学科关联的老师信息

day38-springboot-crud开发_第12张图片

4.3.1 数据库环境

学生表,学科表已有,不再赘述

老师表

create table teacher(
  tid int primary key comment '老师id',
  tname varchar(255) comment '老师名字',
  tage int comment '老师年龄',
  education varchar(255) comment '学历'
  sub_id int comment '关联的学科id'
);

day38-springboot-crud开发_第13张图片

学生与学科一对多,学科与老师一对一

再次强调需求: 查询一个学生信息以及所学习的所有学科信息,以及每个学科关联的老师信息

select stu.*,sub.*,t.* from stu
left join `stu_sub` ss on stu.id = ss.sid
left join `subject` sub on ss.sub_id = sub.sub_id
left join teacher t on t.sub_id = sub.sub_id
where stu.id = 1

day38-springboot-crud开发_第14张图片

4.3.2 实体类

老师类

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {

    private int tid;
    private String tname;
    private int tage;
    private String education;
    private int subId;

}

设置封装学科和老师的扩展类SubjectVO

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class SubjectVO extends Subject{
    private Teacher teacher;
}

封装一对多扩展类StudentVO,用于封装学生多个学科,对没错,还是上一章的一对多

但是!!! 有变化,因为这次不但要封装Subject学科信息,还需要封装学科对应的老师信息,所以此处要修改为SubjectVO,那么之前涉及到这一块的mapper中的代码要修改!!!

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class StudentVO extends Student{

    private Classroom classroom;
    // 封装多个学科信息
    private List<SubjectVO> subjectList;
}

4.3.3 一对多对一查询

mapper

  // 根据id查询1个学生信息以及关联的所有学科信息以及每个学科关联的老师信息
    StudentVO findStudentAndAllSubjectAndTeacherByStuid(int sid);

    
    <resultMap id="studentAndAllSubjectAndTeacherResultMap" type="StudentVO">
        <id column="id" property="id"/>
        <result column="sname" property="sname"/>
        <result column="age" property="age"/>
        <result column="sex" property="sex"/>
        <result column="score" property="score"/>
        <result column="birthday" property="birthday"/>
        
        <collection property="subjectList" ofType="SubjectVO">
            <id column="sub_id" property="subId"/>
            <result column="sub_name" property="subName"/>
            
            
            <association property="teacher" javaType="Teacher">
                <id column="tid" property="tid"/>
                <result column="tname" property="tname"/>
                <result column="tage" property="tage"/>
                <result column="education" property="education"/>
                <result column="sub_id" property="subId"/>
            association>
        collection>

    resultMap>
    <select id="findStudentAndAllSubjectAndTeacherByStuid" resultMap="studentAndAllSubjectAndTeacherResultMap">
        select stu.*,sub.*,t.* from stu
        left join `stu_sub` ss on stu.id = ss.sid
        left join `subject` sub on ss.sub_id = sub.sub_id
        left join teacher t on t.sub_id = sub.sub_id
        where stu.id = 1
    select>

day38-springboot-crud开发_第15张图片

service

    // 根据id查询1个学生信息以及关联的所有学科信息以及每个学科关联的老师信息
    StudentVO findStudentAndAllSubjectAndTeacherByStuid(int sid);

    @Override
    public StudentVO findStudentAndAllSubjectAndTeacherByStuid(int sid) {
        return mapper.findStudentAndAllSubjectAndTeacherByStuid(sid );
    }

controller

    @GetMapping("/subjects/teacher")
    public R findStudentAndAllSubjectAndTeacherByStuid(int sid){
        StudentVO studentVO = service.findStudentAndAllSubjectAndTeacherByStuid(sid);
        return R.ok(studentVO);
    }

测试

{
  "code": 20000,
  "msg": "成功",
  "data": { // 1个学生信息
    "id": 1,
    "sname": "老王",
    "age": 18,
    "sex": "男",
    "score": 100.0,
    "birthday": "2023-08-30T16:00:00.000+00:00",
    "cid": 0,
    "classroom": null,
    "subjectList": [ // 多个学科信息
      {
        "subId": 1,
        "subName": "语文",
        "teacher": { // 每个学科信息都关联对应一个老师信息
          "tid": 1,
          "tname": "老邱",
          "tage": 18,
          "education": "本科",
          "subId": 1
        }
      },
      {
        "subId": 2,
        "subName": "数学",
        "teacher": {
          "tid": 2,
          "tname": "老邢",
          "tage": 38,
          "education": "本科",
          "subId": 2
        }
      },
      {
        "subId": 3,
        "subName": "英语",
        "teacher": {
          "tid": 3,
          "tname": "老王",
          "tage": 35,
          "education": "本科",
          "subId": 3
        }
      },
      {
        "subId": 4,
        "subName": "物理",
        "teacher": {
          "tid": 4,
          "tname": "老万",
          "tage": 30,
          "education": "本科",
          "subId": 4
        }
      }
    ]
  }
}

五、业务层拆解多表联查

5.1 一对一

还是这个需求: 查询学生信息以及关联的教室信息

但是这次不进行多表联查,而是进行单表操作, 思路是什么?如何实现?底层逻辑是什么?


  1. 首先这个需求的目的是:得到学生信息以及关联的教室信息 ,
  2. 那么我只要想办法获得学生信息和教室信息就行了,
  3. 我可以先根据学生id查询出学生信息,
  4. 再通过学生信息中的教室id查出教室信息
  5. 然后再把学生信息和教室信息封装到一起就行
  6. 这样就变成了单表查询,一次查询学生表,一次查询教室表

控制层代码

    /**
     * 学生 --> 教室一对一查询,V2
     * @return
     */
    @GetMapping("/class/v2")
    public R findAllStudentAndClassroomV2(){
        List<StudentVO> list = service.findAllStudentAndClassroomV2( );
        return R.ok(list);
    }

业务层代码

@Autowired
private StudentMapper stuMapper;

@Autowired
private ClassroomMapper classroomMapper;

@Override
public List<StudentVO> findAllStudentAndClassroomV2() {

    // 创建最终的结果集合,存储学生信息及教室信息
    List<StudentVO> list = new ArrayList<>( );

    // 查询所有学生
    List<Student> studentList = stuMapper.findAll( );
    // 遍历得到所有学生
    studentList.forEach(student -> {
        // 再根据学生对象中的教室id查询对应的教室
        Classroom classroom = classroomMapper.findClassroomById(student.getCid( ));
        // 创建最终封装数据的对象
        StudentVO studentVO = new StudentVO( );
        // BeanUtils是spring框架提供的工具类,copyProperties方法用与拷贝对象属性
        // 此处是将student对象中的属性赋值到studentVO中
        BeanUtils.copyProperties(student, studentVO);
        // studentVO中存储教室信息
        studentVO.setClassroom(classroom);
        // 将每一个StudentVO添加到List集合
        list.add(studentVO);
    });
    return list;
}

持久层代码

StudentMapper.xml (之前就有该方法,直接使用的)

 <select id="findAll" resultType="Student">
        select * from stu
 select>

新创建ClassroomMapper.java和ClassroomMapper.xml映射文件

public interface ClassroomMapper {
    Classroom findClassroomById(int cid);
}

DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.mapper.ClassroomMapper">

    <select id="findClassroomById" resultType="Classroom">
        select * from classroom where cid = #{cid}
    select>
mapper>

5.2 一对多

需求: 查询一个学生信息以及所学习的所有学科信息

思路

  1. 先根据学生id查出一个学生信息
  2. 再根据学生id去中间关联表中查出该学生对应的所有学科id
  3. 再根据每一个学科id查出每一个学科对象
  4. 然后组装到一起

根据id查询学生 – StudentMapper.xml(之前就有的,直接调用)

   <select id="findById" resultType="Student">
        select * from stu where id = #{id}
    select>

根据学生id查询关联表的中对应的所有学科id,这个方法之前没有,需要在StudentMappe中添加接口方法和Mapper中映射语句

    List<Integer> findAllSubjectId(int sid);
   <select id="findAllSubjectId" resultType="java.lang.Integer">
        select sub_id from stu_sub where sid = #{sid}
    select>

业务层

@Autowired
private StudentMapper mapper;

@Autowired
private SubjectMapper subjectMapper;

@Override
public StudentVO findStudentAndAllSubjectByStuidV2(int sid) {

    // 先根据学生id查出1个学生
    Student student = mapper.findById(sid);

    // 创建集合准备存储每个学科对象
    List<Subject> subjectList = new ArrayList<>(  );
    // 再根据学生id查出关联表中所关联的所有学科id
    List<Integer> subIdList = mapper.findAllSubjectId(sid);
    // 遍历所有id
    subIdList.forEach(subId -> {
        // 再根据学科id获得每一个学科对象
        Subject subject = subjectMapper.findSubjectById(subId);
        // 存储学科对象
        subjectList.add(subject);
    });

    // 封装数据到VO
    StudentVO studentVO = new StudentVO( );
    BeanUtils.copyProperties(student,studentVO);
    studentVO.setSubjectListV2(subjectList);
    return studentVO;
}

控制层

    /**
     * 一对多演示,1个学生对应多个学科信息V2
     * @param sid
     * @return
     */
    @GetMapping("/subjects/v2")
    public R findStudentAndAllSubjectByStuidV2(int sid){
        StudentVO studentVO = service.findStudentAndAllSubjectByStuidV2(sid);
        return R.ok(studentVO);
    }

BUG

1 数据库不存在

2 数据表不存在

3 数据长度不匹配

4 创建项目没有选择jdk版本和springboot版本导致版本过高

5 application.yml或者properties文件图标应该是绿色树叶如果不是就是不正常

6 application.yml文件中不能出现黄色阴影,出现说明不识别

7 修改yml中的内容(数据源信息) , 以及yml语法的换行,缩进,空格

8 执行mapper中的sql 报错提示某表不存在, 某某列未找到 , sql语法错误

9 忘了加注解@MapperScan @Service就不会创建对象,那么@Autowired时就会报错找不到该对象

10 代码都对,检查无误,但是不执行 --> 找target看代码是否编译,删除target重新编译代码运行

11 报错的状态码 404路径未找到 500后端代码有报错(空指针,sql语法) 400(数据解析出错,基本都是日期格式)

12 添加/更新时日期 后台需要加@DateTimeFormat

13 SpringBoot主类要放在最外层,扫描到所有包内的类

你可能感兴趣的:(#,Java2313,spring,boot,后端,java)