该认真学习了,已经摆烂很久了
目录
完成任务(接口):
- 新增员工业务
- 员工分页查询接口
- 启用禁用员工账号
- 编辑员工(通过id查询员工+编辑员工信息)
- 导入分类管理功能模块
- 公共字段自动填充
思路:
员工分页查询
本质仍然为employee单表的查询,每页展示10条数据,可以根据员工姓名查询
分析:GET请求
请求参数Query:需页码、每页记录数、员工姓名,
@Data
public class EmployeePageQueryDTO implements Serializable {
//员工姓名
private String name;
//页码
private int page;
//每页显示记录数
private int pageSize;
}
后端响应:总记录数、每页的数据集合
public class PageResult implements Serializable {
private long total; //总记录数
private List records; //当前页数据集合
}
查询过程通过controller->service->mapper,在server中使用pagehelper来实现分页
public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {
//select * from employee limit 1,10
//开始分页查询
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);
}
在Mvc配置文件中增加自己的消息转换器,将日期格式转为"yyyy-MM-dd HH:mm"
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
log.info("扩展消息转换器.....");
//创建一个雄安锡转换器对象
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
//需要为雄安锡转换器设置一个对象转换器,对象转换器可以将java对象序列转换为Jaso数据
converter.setObjectMapper(new JacksonObjectMapper());
//将自己的消息转换器加入容器中
converters.add(0,converter);
}
启用禁用:
相当与通过动态sql将获取的Id查询出来员工信息,然后将status的值改变
编辑员工:
接口:根据id查询员工(通过url参数)
编辑员工
编辑员工可以使用update的sql
公共字段自动填充
由于在开发过程中,涉及到很多重复的代码,例如:插入、删除,在新增员工/编辑员工接口中都会用到,所以对其优化,使用APO切面,也就是将将这些方法通过自定义注解提前拦截,然后在通知中对其进行统一赋值
- 自定义注解 AutoFill,用于标识需要进行公共字段自动填充的方法(mapper中涉及insert、update的方法,通过AutoFill注解标识这些需要AOP的方法)
/**
* 自定义注解,用于标识某个方法需要进行功能字段自动填充处理
*/
@Target(ElementType.METHOD)//指定这个注解只能加载到方法上 10个方法
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
//数据库操作类型,UPDATE,INSERT
OperationType value();
}
- 自定义切面类 AutoFillAspect,统一拦截加入了 AutoFill 注解的方法,通过反射为公共字段赋值(定义切入点【拦截什么请求】和通知【对拦截的请求做什么额外的代码补充与增强】)
public class AutoFillAspect {
/**
* 切入点
* 对哪些类的哪些方法进行拦截
* 选择mapper包下带有AUtoFilL的注解
*/
@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
public void autoFillPointCut(){}
/**
* 前置通知,在通知中进行公共字段的赋值
*/
@Before("autoFillPointCut()")
public void autoFill(JoinPoint joinPoint){
log.info("开始进行公共字段自动填充...");
//获取当前被拦截的方法上的数据库操作类型
MethodSignature signature =(MethodSignature) joinPoint.getSignature();//方法签名对象
AutoFill antoFill = signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解对象
OperationType operationType = antoFill.value();//获得数据库操作类型
//获取当前被拦截方法的参数--实体对象
Object[] args = joinPoint.getArgs();//获得方法里的参数
if (args==null || args.length==0)
return;
Object entity = args[0];//获得实体对象
//准备赋值的数据
LocalDateTime now = LocalDateTime.now();
Long currentId = BaseContext.getCurrentId();
//根据当前不同的操作类型,为对应的属性通过反射来赋值
if (operationType==OperationType.INSERT){
//为4个公共字段赋值
try {
Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
Method setCreaterUser = 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);
setCreaterUser.invoke(entity,currentId);
setUpdateTime.invoke(entity,now);
setUpdateUser.invoke(entity,currentId);
}catch (Exception e){
e.printStackTrace();
}
}else if (operationType==OperationType.UPDATE){
//为2个公共字段赋值
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();
}
}
}
- 在 Mapper 的方法上加入 AutoFill 注解
@Insert("insert into employee(name, username, password, phone, sex, id_number, create_time, update_time, create_user, update_user,status)" +
"values " +
"(#{name},#{username},#{password},#{phone},#{sex},#{idNumber},#{createTime},#{updateTime},#{createUser},#{updateUser},#{status})")
@AutoFill(value = OperationType.INSERT)
void insert(Employee employee);
收获:
DTO和实体类
entity:实体类,是用于说明某实体中所包含的所有属性,与数据库中的字段是保持一致的
DTO:数据传输对象,顾名思义,DTO是用于数据传输的,是根据实际的业务需求来决定这个对象具体包含哪些属性。是根据业务需要。里面包含了部分实体里面的属性用来完成业务,从而保证数据不暴露
在完成编辑员工接口时,在service实现类中,从控制层传入的时一个dto对象,这时可以通过BeanUtils.copyProperties来将dto的数据拷贝给实体对象,但里面需要修改的数据这时就要通过set来修改
public void update(EmployeeDTO employeeDTO) {
Employee employee=new Employee();
BeanUtils.copyProperties(employeeDTO,employee);
employee.setUpdateTime(LocalDateTime.now());
employee.setUpdateUser(BaseContext.getCurrentId());
employeeMapper.update(employee);
}
相关注解
getDeclaredMethod() 获取的是类自身声明的所有方法,包含public、protected和private方法。
getMethod () 获取的是类的所有共有方法,这就包括自身的所有public方法,和从基类继承的、从接口实现的所有public方法
逻辑外键
逻辑外键是在关系数据库中的一个概念,它是通过程序或规则实现的关联关系,而不是通过数据库引擎的外键约束来实现的。
问题及解决:
启用禁用:
测试的时候一直有500的错误,传入的id是null,以为是sql语句有问题,改了好几遍,后来一行一行的debug,发现是在接口实现层中构建实体对象没有给他创建接受的employee,修改代码如下
public void startOrStop(Integer status, Long id) {
//update employee set status=? where id=?
//创建实体对象并设置值
Employee employee=Employee.builder()
.status(status)
.id(id)
.build();
employeeMapper.update(employee);
}