SpringBoot+MyBatis CRUD 进阶:局部更新、批量删除、条件查询与分页查询(1.30)

一、Spring Boot Controller 请求参数获取

1.如何从Controller中取出以前Servlet中的对象?

在方法的参数列表中定义HttpServiceRequest request,HttpServletResponse response,对象打点调用即可。

@Controller
public class PageController {
    @RequestMapping("/page2")
    public String page2(Map<String,Object> map,HttpServiceRequest request,HttpServletResponse response){
        //例如:
      	request.getParameter("username");
        return "page2";
    }
}

2.如何获取前端传过来的Header?

image-20260130170740033

在方法的参数列表中定义@RequestHeader("token") String token,@RequestHeader括号中的名字与前端一致。

@Controller
public class PageController {
    @RequestMapping("/page2")
    public String page2(Map<String,Object> map,@RequestHeader("token") String token){
        //输出token
        System.out.println(token);        
        return "page2";
    }
}

二、MyBatis项目搭建

1.数据库的配置:

在pom.xml中,我们把MyBatis的配置放开后,再启动会报错,如下:

image-20260130172138225

原因是:我们在启动时,启动器会帮我们创建一个DataSource数据源,DataSource会建立数据库的连接,然后把这个连接对象放到IOC中。

接下来,我们就要在application.properties下配置数据库:

spring.application.name=BootTest
#数据库连接
spring.datasource.url=jdbc:mysql://localhost:3306/javafk?serverTimezone=GMT%2B8&characterEncoding=utf8&useUnicode=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=1234
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

2.domain包下新建Employee类:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Employee {
    private Long id;
    private String name;
    private Double sal;
    private Boolean sex;
}

3.mapper包下新建EmployeeMapper接口:

public interface EmployeeMapper {
}

4.(可省略此步骤)resources下创建配置文件,选择创建目录,要求同包同名

image-20260130185831738

image-20260130185913091

选择一个Mapper.xml文件加入----->其实不用建xml文件也可以,后期可替换。

小结:

1.mapper接口的实现类已经不用手动创建了,但需要在mapper接口上写@Mapper注解,它会帮我们自动生成mapper的实现类。

2.mapper.xml文件可省略,但要在mapper的方法中加上注解。

3.MyBais阶段中service层和controller层还用不到。主要是在test类中写方法,再到mapper接口通过注解实现。

4.本次测试只选择用注解方式实现sql语句的使用,若是复杂的操作,如多表查询,则建议用Mapper.xml文件书写sql。

5.项目结构:

image-20260130191210611

三、SpringBoot+MyBatis实战

1.查询所有:

test类:注意要先从IOC容器中取出EmployeeMapper对象

@SpringBootTest
class SpringBootCrudTset1ApplicationTests {
    @Autowired
    EmployeeMapper employeeMapper;//注意
    //查询所有
    @Test
    void selectAll() {
        List<Employee> empList = employeeMapper.selectAll();
        empList.forEach(System.out::println);
    }   
}

mapper接口:注意要先写@Mapper注解

@Mapper//注意
public interface EmployeeMapper {
    //查询所有
    @Select("select * from employee")
    //注解式映射配置
    @Results({
            @Result(property = "id",column = "id",id = true),
            @Result(property = "name",column = "name"),
            @Result(property = "sal",column = "sal"),
            @Result(property = "sex",column = "sex")
    })
    List<Employee> selectAll();

2.添加:

test类:我们没有设置id,是因为我们使用了@Options(useGeneratedKeys = true,keyProperty ="id")注解,即让数据库主键自增长和从数据库中获取到的id值赋值给实体对象中的id属性。即使手动设置了 id,也会被这个注解覆盖掉

 //添加
    @Test
    void Add() {
        Employee employee = new Employee(null,"翠花",6000.0,true);
        int m =employeeMapper.Add(employee);
        System.out.println(m>0?"添加成功":"添加失败";
    }

mapper接口:

 //添加
    @Insert("insert into employee (name,sal,sex) values (#{name},#{sal},#{sex})")
    @Options(useGeneratedKeys = true,keyProperty ="id")
    int Add(Employee employee);

小结:

如果不用@Options(useGeneratedKeys = true, keyProperty = "id")这个注解,执行save添加方法后,传入的 Employee 对象的 id 属性会是 null,数据库里虽然能正常插入数据(依赖数据库主键自增),但主键 id 不会回写到实体对象中。

3.全量更新:

test类:注意全量更新要传递的是对象,不是id,MyBatis会根据你传的id进行查找,再用对象的其他属性更新值。

 //全量更新 -->根据id
 @Test
 void update() {
     Employee employee = new Employee(3L,"蹦蹦",3000.0,true);
     int m = employeeMapper.update(employee);
     System.out.println(employee);
     System.out.println(m>0?"更新成功":"更新失败");
 }

mapper接口:

 //全量更新
    @Update("update employee set name=#{name},sal=#{sal},sex=#{sex} where id = #{id}")
    int update(Employee employee);

4.局部更新(动态):

test类:

设为null的字段不更新,需要使用Mapper接口的SQL 构建器内部类自定义方法实现动态拼接sql。

@UpdateProvidermethod属性值,需要和SQLProvider内部类中实际拼接 SQL 的方法名完全一致与测试类方法名无关

 //局部更新
    @Test
    void patchUpdate() {
        Employee employee = new Employee(10L,"团子",null,null);
        int m = employeeMapper.patchUpdate(employee);
        System.out.println(employee);
        System.out.println(m>0?"更新成功":"更新失败");
    }

mapper接口:

//局部更新
    @UpdateProvider(type = EmployeeMapper.SQLProvider.class,method = "patchUpdate")
    int patchUpdate(Employee employee);

    class SQLProvider{
        public String patchUpdate(Employee employee) {
            StringBuffer sql = new StringBuffer("update employee set ");
            if(!StringUtils.isEmpty(employee.getName())){
                sql.append("name = #{name},");
            }
            if(!StringUtils.isEmpty(employee.getSal())){
                sql.append("sal = #{sal},");
            }
            if(!StringUtils.isEmpty(employee.getSex())){
                sql.append("sex = #{sex},");
            }
            String subString = sql.substring(0,sql.length()-1);//要取出sql.substring的返回值,不然,还是没去掉
            subString += " where id = #{id}";//注意where前有空格
            return subString.toString();
        }
    }

5.局部更新2:

test类:

//局部更新2
    @Test
    void patchUpdate2() {
        //传入的更新对象
        Employee employee = new Employee(11L,"冰点",8000.0,true);
        //根据id查找原始对象
        Employee emp = employeeMapper.findById(employee.getId());
        //替换,为空 --> 不替换,不为空 -->替换
        if(!StringUtils.isEmpty(emp.getName())){
            emp.setName(employee.getName());
        }
        if(!StringUtils.isEmpty(emp.getSal())){
            emp.setSal(employee.getSal());
        }
        if(!StringUtils.isEmpty(emp.getSex())){
            emp.setSex(employee.getSex());
        }
        //全量更新
        int m = employeeMapper.update(emp);
        System.out.println(m>0?"更新成功":"更新失败");
    }

mapper接口:

 //局部更新2
    @Select("select * from employee where id = #{id}")
    @Results({
            @Result(property = "id",column = "id",id = true),
            @Result(property = "name",column = "name"),
            @Result(property = "sal",column = "sal"),
            @Result(property = "sex",column = "sex")
    })
    Employee findById(Long id);
注意:
  • @Results注解的作用范围默认只作用在它当前修饰的查询方法上,并非全局生效。
  • 不要忘了if语句中的!

6.删除:

test类:

//删除
    @Test
    void deleteById() {
        Long id = 11L;
        int m = employeeMapper.deleteById(id);
        System.out.println(m>0?"删除成功":"删除失败");
    }

mapper接口:

 //删除
    @Delete("delete from employee where id = #{id}")
    int deleteById(Long id);

7.批量删除:

test类:

//批量删除
    @Test
    void deleteByIds() {
        Long[] ids = {12L,13L};
        int m = employeeMapper.deleteByIds(ids);
        System.out.println(m>0?"删除成功":"删除失败");
    }

mapper接口:

//批量删除
    @DeleteProvider(value = EmployeeMapper.SQLProvider.class,method = "deleteByIds")
    int deleteByIds(Long[] ids);
    
     class SQLProvider{
        public String deleteByIds(Long[] ids){
            StringBuffer sql = new StringBuffer("delete from employee where id in (");
            for(Long id:ids){
                sql.append(id).append(",");
            }
            String subString = sql.substring(0,sql.length()-1);
            subString += ")";
            return subString;
        }
    }

8.条件查询:

①定义qo包下EmployeeQO类:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class EmployeeQO {
    private String name;
    private int salStart;
    private int salEnd;

}

②test类:

//条件查询
    @Test
    void queryByCondition() {
        EmployeeQO qo = new EmployeeQO();
        qo.setName("吉吉");
        qo.setSalStart(new BigDecimal(1000));
        qo.setSalEnd(new BigDecimal(10000));
        List<Employee> employeeList = employeeMapper.queryByCondition(qo);
        employeeList.forEach(System.out::println);
    }

③mapper接口:

//条件查询
    @SelectProvider(value = EmployeeMapper.SQLProvider.class,method = "queryByCondition")
    List<Employee> queryByCondition(EmployeeQO qo);

class SQLProvider{
        public String queryByCondition(EmployeeQO qo) {
            StringBuffer sql = new StringBuffer("select * from employee where 1=1");
            if(!StringUtils.isEmpty(qo.getName())){
                sql.append(" and name like concat('%',#{name},'%')");
            }
            if(!StringUtils.isEmpty(qo.getSalStart())) {
                sql.append(" and sal >= #{salStart}");
            }
            if(!StringUtils.isEmpty(qo.getSalEnd())) {
                sql.append(" and sal <= #{salEnd}");
            }
            return sql.toString();
        }

    }

9.分页查询:

①引入依赖:

<dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper-spring-boot-starter</artifactId>
      <version>1.4.6</version>
</dependency>

②test类:

//分页
    @Test
    void Page() {
        EmployeeQO qo = new EmployeeQO();
        //设定分页信息
        PageHelper.startPage(1,3);
        //无条件查询 -->查询所有数据,此查询会被PageHelper自动分页,返回的是分页后的结果集
        List<Employee> employeeList = employeeMapper.queryByCondition(qo);
        //计算参数+封装
        PageInfo<Employee> pageInfo = new PageInfo<>(employeeList);
        //获取分页后的小集合
        List<Employee> list = pageInfo.getList();
        employeeList.forEach(System.out::println);
    }

流程概述:

  • 实例化一个qo对象,为后续条件查询传入字段全为默认值null的对象

  • 设定分页信息,指定第?页,每页显示?条

  • 无条件查询,即查询所有数据;而PageHelper的拦截器会拦截startPage后的第一条查询语句,自动为原 sql 拼接分页语法;

    返回的List是(页码 1,页大小 3)的结果集

  • PageHelper会通过上面的分页参数和查询结果,自动计算出总条数、总页数、上一页 / 下一页等信息,封装到 PageInfo 中

  • 获取当前页数据,即employeeList

思考:为什么 PageHelper.startPage();筛选出了要分页的数据,还要用new PageInfo<>()计算参数信息并进行封装?

1.实际开发中,网页还需要显示总记录数、总页数以及当前页数等数据,不仅仅只是显示出你要的分页后的数据,例如,第一页的三条数据。

2.new PageInfo<>()简化操作,无需手写大量重复代码。

posted on 2026-01-31 10:26  冬冬咚  阅读(4)  评论(0)    收藏  举报