苍穹外卖-员工分页查询、启用禁用、编辑员工、公共字段填充(二)
Controller
@GetMapping(@~"/page")
@Api0peration("员工分页查询")
public Result<PageResult> page(EmployeeQueryDTO employeeDTO){
log.info("{}分页査询"employeeQueryDTO);
PageResult pageResult = employeeService.pageQuery(employeeQueryDTO);
return Result.success(pageResult);
}
pagehelper插件
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pagehelper}</version>
</dependency>
@Override
public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {
//传进去页码,和每页的记录数
PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize());
Page<Employee> page = employeeMapper.pageQuery(employeePageQueryDTO);
long total = page.getTotal();
List<Employee> records = page.getResult();
return new PageResult(total,records);
}
<select id="pageQuery" resultType="com.sky.entity.Employee">
select * from employee
<where>
<if test="name != '' and name != null">
and name like concat('%',#{name},'%')
</if>
</where>
order by create_time desc
</select>
第一钟方式注解,日期格式,不建议
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
第二种扩展springmvc框架的消息转换器
日期格式转换
在server-config下
extendMessageConverters
对后端返回给前端的数据统一进行转换处理
new一个 MappingJackson2HttpMessageConverter()
converter.setObjectMapper(); //字面意思,对象转换器/new一个传进去
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
log.info("扩展消息转换器");
//创建一个消息转换器
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
//需要为消息转换器设置一个对象转换器,对象转换器可以将Java对象序列化为json数据
converter.setObjectMapper(new JacksonObjectMapper());
//将自己的消息转换器加入容器中
converters.add(0,converter); //加到首位,优先使用
}
}
启用禁用员工
关于Result返回值的<>泛型,因为查询类操作要返回一个数据data所以泛型加上
非查询类的,泛型就不需要了,就返回code表状态就好了
关于更新修改员工字段status为使得动态sql可以传入不同的参数来修改员工信息
所以需要实现mapper动态语句update的通用性 直接调用mapper的方法并且传参为一个实体类employee
关于@Builder筑建器可以不用new直接取类调用.status()并且属性设置status
所以上述方法和属性是一致的
Employee employee = Employee.builder().status(status).id(id).build();
Page
【1】悬念1
在new了新对象employee对象设置了status和id其他属性值为null然而sql语句要根据id查找并且修改其他属性,其他属性不为null的情况下赋值为#{}所以会不会被覆盖
悬念1解答在sql赋值语句中,null并不能给属性赋值,故不会被null所覆盖属性值,只修改其他需要修改的属性值
编辑员工
编辑员工功能涉及到两个接口:
1.根据id查询员工信息
2.编辑员工信息
导入分类管理功能模块代码
category分类表 分类相关
接口文档存放在apikit分为用户端和管理端
利用swagger框架查看写好的接口信息
这里在移动写好的mvc倒着移文件,先mapper再xml再service最后是controller层,中间还有service的impl不能忽略,是对业务逻辑接口的详细实现方法
当然category的分类相关的六个方法对应的六个接口
公共字段自动填充
关于字段创建时间更新时间的修改,持久层(大概是mybatis数据库操作吧)的操作并不是都需要处理,也就是update和insert
使用AOP面向切面统一对公共字段进行处理
-自定义注解 AutoFill,用于标识需要进行公共字段自动填充的方法
-自定义切面类 AutoFillAspect,统一拦截加入了 AutoFill 注解的方法,通过反射为公共字段赋值
-在 Mapper 的方法上加入 AutoFill 注解
技术点:枚举、注解、AOP、反射
枚举不同的操作类型(insert、update)
自定义注解,自定义切面,反射:为公共字段进行赋值
总结三步走 (总结面向切面定义公共字段填充的注解分三步)
一、自定义注解 AutoFill,用于标识需要进行公共字段自动填充的方法
创建一个annotation包自定义一个注解取名AutoFill用于字段的填充处理(其实就跟创建一个class一样)
标记为 固定写法
@Target(ElementType.METHOD) //指定这个注解只能加在方法上面
@Retention(RetentionPolicy.RUNTIME)
在sky-common中的enum已经枚举好数据库操作类型
总体的注解代码
@Target(ElementType.METHOD) //指定这个注解只能加在方法上面
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
//枚举指定数据库操作类型
OperationType value();
}
二、自定义切面类 AutoFillAspect,统一拦截加入了 AutoFill 注解的方法,通过反射为公共字段赋值
创建一个Aspect的切面类就要加Aspect注解,component放入容器里管理也是个bean
@Pointcut() 里面写切入表达式,哪些类的哪些方法进行拦截 execution指定拦截的是什么,返回值是所有的* 拦截的是com.sky.mapper..(..)" @注解为(com.sky.annotation.AutoFill)
两个条件:mapper包下的方法, 必须是上面带autofill注解的方法
在持久层对数据库进行update或者insert操作的时候进行拦截,做一个切点,
而公共字段自动填充的操作写进通知,亦为前置通知,并且指定了切入点方法名,
意味着切点表达式即将执行,也就是匹配到切点表达式的时候,就进行执行通知方法
mapper下所有的类所有的方法
@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
public void autoFillPoinCut(){
}
@Before("autoFillPoinCut()")
public void autoFill(JoinPoint joinPoint){ //定义参数为joinpoint
//传进来的参数进行一定的方法操作
log.info("开始进行公共字段的填充");
}
要想对公共字段进行自动填充, 就需要对mapper文件下的方法进行注解标记为@AutoFill(value = OperationType.INSERT)
这里使用到了slf4j的日志打印,loginfo日志用法如同控制台的consolelog和sysout来判断执行
构写自动填充字段方法,首先1、获取操作类型,2、齐次获取被拦截的方法的参数--实体对象。update(Employee) 3、准备好需要赋值的参数的数据
create和update的time和user。time就是当前localtime,而user使用到common下的Context的BaseContext调用getCurrentId获得
4、根据当前不同操作的类型,对相应属性进行反射赋值
比如insert 就需要给createTime、createUser updateTime updateUser进行赋值,而update 就只有update - -
第二步,这里注意因为要获得参数的实体类,对类的属性进行填充,所以约定,自动获取第一个参数args[0]
这里通过获取参数实体类,指定方法名和类型来获取方法
为了防止实体类调用方法的时候,写方法名写错。可以写一个类来存放常量写在了common里的autoFillConstant中,直接将方法名
Method setUpdateTime = entity.getClass().getDeclaredMethod("setUpdateTime", LocalDateTime.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod("setUpdateUser", Long.class);
修改成
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
以后都不用修改create和update,直接在方法上加一个注解就可以了
总体的切面代码 定义在aspect
@Aspect
@Component
@Slf4j
public class AutoFillAspect {
//切入点
@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
//里面写切入表达式,哪些类的哪些方法进行拦截
//execution指定拦截的是什么,返回值是所有的* 拦截的是com.sky.mapper.*.*(..)"
public void autoFillPoinCut(){
}
//定义一个前置通知 因为在执行update之前就要对公共字段进行填充
//before注解表示前置通知,并且指定为切入点,切点表达式即将执行,也就是匹配到切点表达式的时候,就进行执行方法
//指定为切点表达式的方法名称 就OK了
@Before("autoFillPoinCut()")
//定义参数为joinpoint 参数用于哪个方法被拦截到了,以及被拦截是怎么样的
public void autoFill(JoinPoint joinPoint){
log.info("开始进行公共字段的填充");
//方法签名对象 这里类型转换调用第二个包
MethodSignature signature = (MethodSignature)joinPoint.getSignature();//获得签名 类型强制转换成子接口Method类型
AutoFill autofill = signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解对象
OperationType operationType = autofill.value(); //获得数据操作类型
//1、获取方法操作数据类型
Object[] args =joinPoint.getArgs();
if(args == null|| args.length == 0) {
return;
}
Object entity = args[0];
//2、获取参数
LocalDateTime now=LocalDateTime.now();
Long currentId = BaseContext.getCurrentId();
//3、准备赋值的参数的数据
if (operationType == OperationType.INSERT){
//四个公共字段都需要赋值
try {
Method setCreateTime =entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME,LocalDateTime.class);
Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
//通过反射给对象属性赋值
setCreateTime.invoke(entity,now); //刚才已经把这个数据now准备好了
setCreateUser.invoke(entity,currentId); //
setUpdateTime.invoke(entity,now);
setUpdateUser.invoke(entity,currentId);
} catch (Exception e) {
e.printStackTrace();
}
} else if (operationType == OperationType.UPDATE) {
//就两个赋值
//通过这实体类获得class然后获得指定的方法(方法名,需要的参数的类型,)
try {
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
setUpdateTime.invoke(entity,now);
setUpdateUser.invoke(entity,currentId);
} catch (Exception e) {
e.printStackTrace();
}
}
//4、进行反射
}
}
三、在 Mapper 文件中的update和insert方法上加入 AutoFill 注解
这时可以把CategoryService和EmployeeService中的create和update的赋值都注释掉
@AutoFill(value = OperationType.UPDATE)
void update(Category category);
浙公网安备 33010602011771号