本文共计8693字,预计阅读时间15分钟内
本文为笔者学习阶段笔记以及经验转化而来,适用于新手,有一定项目经验的可直接跳转“经验随笔”
前言:什么是crud
- C:create(增)
R:read (查)
U:update(改)
D:delete (删)
增加员工
- 设计
用原员工类来封装也没毛病,但是一般要求使用DTO来封装数据
-
post请求
-
接口: /admin/employee/addEmployee
-
三层
controller
记得打个log
/* * 新增员工 */ @PostMapping("/addEmployee") public Result save(@RequestBody EmployeeDTO employeeDTO){ log.info("新增员工:{}",emmployeeDTO); employeeService.save(employeeDTO); return Result.success(); }service
使用对象属性拷贝来复制对象
工具类:BeanUtils(org.springframeword.beans)
设置状态、默认密码、创建时间、修改时间、修改人id(从jwt令牌中解析得到[之前创建令牌的时候就放了一个id的键值对])
定义员工状态,最好使用StatusConstant里面定义的常量
获取当前时间使用的是“LocalDateTime.now()”
设置密码的写法:
employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));public void save(EmployeeDTO employeeDTO) { Employee employee = new Employee(); //把前面的拷贝到后面 BeanUtils.copyProperties(employeeDTO,employee); employee.setStatus(StatusConstant.ENABLE); employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes())); employee.setCreateTime(LocalDateTime.now()); employee.setUpdateTime(LocalDateTime.now()); Long currentId = BaseContext.getCurrentId(); employee.setCreateUser(currentId); employee.setUpdateUser(currentId); employeeMapper.insert(employee); }xml(Mapper层略)
<insert id="insert"> insert into employee (name,username,password,phone,sex,id_number,status,create_time,update_time,create_user,update_user) values (#{name},#{username},#{password},#{phone},#{sex},#{idNumber},#{status},#{createTime},#{updateTime},#{createUser},#{updateUser}) </insert>
分页查询
-
get请求
-
接口: /admin/employee/selectPage
PageHelper插件
底层基于mybatis拦截器
会将limit关键字动态地拼接进sql语句
pom.xml中引入插件
<!--分页插件的pagehelper-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.6</version>
<exclusions>
<exclusion>
<artifactId>mybatis</artifactId>
<groupId>org.mybatis</groupId>
</exclusion>
</exclusions>
</dependency>
时间格式的转换,详见“项目的基础(起步代码)”(暂未转换为博客文章)
注意是
<if test="name != null and name != ''">name like concat('%',#{name},'%')</if>不是
<if test="name != null">name like concat('%',#{name},'%')</if>
-
不使用DTO和PageResult的写法
-
三层
controller
/* * 分页查询 * pageNum:当前页码 * pageSize:每页显示的记录数 */ @GetMapping( "selectPage")//路径传参 public Result selectPage(Employee employee,@RequestParam(defaultValue = "1") Integer pageNum,@RequestParam(defaultValue = "10") Integer pageSize ){ //返回分页数据 PageInfo<Employee> pageInfo = employeeService.selectPage(employee,pageNum,pageSize); return Result.success(pageInfo); }service
使用对象属性拷贝来复制对象
工具类:BeanUtils(导入org.springframeword.beans包)
设置状态、修改时间
public PageInfo<Employee> selectPage(Employee employee,Integer pageNum, Integer pageSize) { //pageinfo类由pagehelper提供(import com.github.pagehelper.PageInfo;) //三行实现分页查询 PageHelper.startPage(pageNum,pageSize); List<Employee> list = aobjectMapper.selectAll(aobject); return PageInfo.of(list); }xml
<select id="selectAll" resultType="com.bzh.entity.Employee"> select * from Employee <where> <if test="name != null and name != ''">name like concat('%',#{name},'%')</if> </where> order by id desc </select>
-
-
使用EmployeePageQueryDTO和PageResult的写法
(数据格式不是json时,不需要使用@RequestBody)
-
三层
controller
/** * 分页查询 * @param page * @param pageSize * @param name * @return */ @GetMapping("/page") public Result<PageResult> page(EmployeePageQueryDTO employeePageQueryDTO){ log.info("员工分页查询,参数为:{}",employeePageQueryDTO); PageResult pageResult = employeeService.pageQuery(employeePageQueryDTO); return Result.success(pageResult); }service
使用对象属性拷贝来复制对象
工具类:BeanUtils(org.springframeword.beans)
设置状态、修改时间、
/** * 分页查询 * @param employeePageQueryDTO * @return */ public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) { PageHelper.startPage(employeePageQueryDTO.getPage(),employeePageQueryDTO.getPageSize()); //得到(获取)page对象 Page<Employee> page = employeeMapper.pageQuery(employeePageQueryDTO); long total = page.getTotal(); //从page里面获取list集合 List<Employee> records = page.getResult(); return new PageResult(total, records); }
xml
PageHelper底层基于mysql关键字limit来查询(select * from employee limit 0,10;) (如果不用pageHelper,就要依据pageNum和pageSize进行计算,然后动态地拼到sql语句中)<select id="pageQuery" resultType="com.sky.entity.Employee"> select * from employee <where> <if test="name != null and name != ''">name like concat('%',#{name},'%')</if> </where> order by create_time desc </select>
-
修改员工信息
账号的启用和禁用
- post请求
-
接口: /admin/employee/status/{status}
-
传参方式: status采用路径传参 id采用地址栏传参
-
(这里1为启用,0为禁用)
controller层记得打log
-
三层
controller
这个接口采用 POST 而不是 PUT 可能是因为:一、状态更新操作具有一定的业务意图,不完全依赖幂等性,POST 更符合这种“提交意图”的语义;二、开发者的习惯或所用框架对 POST 的支持更友好,使得 POST 更易于实现;三、接口的路径设计更倾向于 POST 的语义。总之,虽然 POST 和 PUT 都能完成状态更新的功能,但在这里选用 POST 有其合理性。
@PostMapping("/status/{status}") public Result changeStatus(@PathViriable Integer status,Long id){ log.info("启用禁用员工账号:{}{}",status,id); employeeService.changeStatus(status,id); return Result.success(); }service
使用对象属性拷贝来复制对象
public void changeStatus(Integer status ,Long id){ // Employee employee = new Employee(); employee.setStatus (status); employee.setId(id); emPloyee.update(employee); employeeMapper.update(emp); }mapper
/** * 根据主键动态更新信息 * @param employee */ void update(Employee employee);
xml
为了方法的复用性,将update语句写成动态的sql,根据 传入的参数修改不同的字段-
<update id="update" parameterType="com.sky.entity.Employee">
update employee
<set>
<if test="name != null">name = #{name},</if>
<if test="username != null">username = #{username},</if>
<if test="password != null">password = #{password},</if>
<if test="phone != null">phone = #{phone},</if>
<if test="sex != null">sex = #{sex},</if>
<if test="idNumber != null">id_number = #{idNumber},</if>
<if test="updateUser != null">update_user = #{updateUser},</if>
<if test="status != null">status = #{status},</if>
update_time = #{updateTime}
</set>
where id = #{id}
</update>
编辑员工信息
查询回显接口
-
根据id查询员工信息(回显[也可以前端实现])
-
编辑员工信息
-
get请求
-
接口: /admin/employee/{id}
-
三层
controller
@GetMapping("/{id}") public Result<Employee> getById(@PathVariable Long id){ Employee employee = employeeService.getById(id); return Result.success(employee); }service
public Employee getById(Long id){ Employee employee = employeeMapper.getById(id); employee.setPassword("****"); return employee; }xml
select * from employee where id = #{id}
更新信息接口
-
put请求
-
接口: /admin/employee
更新对象的时候不要忘了在service层设置updateTime还有updateUser
-
三层
controller
@PutMapping public Result update(@RequestBody EmployeeDTO employeeDTO){ log.info("编辑员工信息:{}",employeeDTO); employeeService.update(employeeDTO); return Result.success(); }service
使用对象属性拷贝来复制对象
工具类:BeanUtils(org.springframeword.beans)
public void update(EmployeeDTO employeeDTO) { Employee employee = new Employee(); BeanUtils.copyProperties(employeeDTO,employee); //update要传入的是employee对象,所以属性拷贝一下 employee.setUpdateTime(LocalDateTime.now()); employee.setUpdateUser(BaseContext.getCurrentId()); employeeMapper.update(employee); }xml
<update id="update"> update employee <set> <if test="name != null">name = #{name},</if> <if test="username != null">username = #{username},</if> <if test="password != null">password = #{password},</if> <if test="phone != null">phone = #{phone},</if> <if test="sex != null">sex = #{sex},</if> <if test="idNumber != null">id_number = #{idNumber},</if> <if test="updateUser != null">update_user = #{updateUser},</if> <if test="status != null">status = #{status},</if> update_time = #{updateTime} </set> where id = #{id} </update>
构建实体对象的builder写法
(前提:该实体类需要加上@Builder注解)
Employee employee = new Employee(); employee.setStatus (status); employee.setId(id); emPloyee.update(employee);
-
builder写法
Employee employee = Employee.builder() .status(status) .id(id) .build();
经验随笔
xxServiceImpl记得implements xxService接口
controller层使用的是xxDTO类去接收前端的json数据
id是long类型,用Long id接收
controller层的几个常见接口
@RestController @RequestMapping("/xxx") @Slf4j (@Api(tags = "员工管理相关接口"))
mapper层是interface类型,不是class类型 (public interface EmployeeMapper{})
分页查询细节
分页查询的sql有时候需要联表
错误版本![]()
正确版本
分页查询页也不要忘了加上查询条件(用where包裹动态sql)
新增-insert
新增的时候注意对照数据表格,看下是否有些字段需要我们在业务层手动填充
注意看一下是否要设置status(最好设置1/0,前端不一定会设置)
注意看一下是否要设置userId
setUserId(BaseContext.getCurrentId())- 注意是否要填充createTime和updateTime
注意给update和insert 的mapper方法上增加@AutoFill注解(自定义,用于自动填充createTime、updateTime)
更新-update
前端传来id、修改一两个字段的更新,不需要查询出来构建完整的对象再更新(因为动态sql里面有判断,不等于null的字段才会更新)
update语句中<if test="" >里面写的是属性名、而不是数据库字段,也就是说里面的属性名是驼峰命名格式的
非字符串类型的属性不用(不能)写 " and xx != '' ",(updateTime和createTime是LocalDateTime类型,不是String类型,不能写 <if test="createTime != null and createTime != "" "> 这样的判断)
批量更新 (使用foreach标签包裹update语句)
查询select
查询语句末尾记得加上 order by xxx desc
注意是否需要加上status的判断(是否只返回启用中的条目)
查询类接口的返回值注意是否是返回VO类
查询特定数据可能出现查不到的情况,需要分情况处理逻辑
删除
注意检查数据是否能够删除(如status = 1的数据可能不能删除)
检查status的样例:
ids.forEach(id -> { Setmeal setmeal = setmealMapper.getById(id); if(StatusConstant.ENABLE == setmeal.getStatus()){ //起售中的套餐不能删除 throw new DeletionNotAllowedException(MessageConstant.SETMEAL_ON_SALE); } });可以一次删除一个,也可以批量删除
sql语句细节
foreach语句条目不要 遗漏,这次遗漏了item="id"没看出来导致我调试了半天
sql语句中的大于号小于号需要替换
“<”替换为“<”,“>”替换为“>”
附属信息(XDTO有,而X实体类没有的属性) 往往需要采用先删除后插入的方式实现更新(比如员工的工作经验、DishDTO的List<DishFlavor>数据)
对于有附属信息的类的新增操作时 前端传过来的DishDTO里面没有dish的主键id值,需要再xml文件中执行新增操作时采取主键回填的方式获取到
主键Id
![]()
其中,keyProperty="x",x不能随便写,必须确认这个类中有x属性并且是主键
![]()
例如:此处的更新操作中,在更新附属信息之前,给List<DishFlavor>集合里面的DishFlavor补充好属性DishId(因为口味数据可能是新增出来的,dishId没放进去)
![]()
对于比较复杂的查询sql,最好在数据库里面调试好、确保当前sql能够查询到想要的结果,再拷贝到xml文件中
对sql进行优化,提升复用性
案例
优化后
![]()
2.
list方法的复用
![]()









970

被折叠的 条评论
为什么被折叠?



