02-瑞吉外卖员工表的增删改查

添加员工信息

执行流程

第一步: 用户点击添加员工按钮跳转到add.html页面,然后在页面中输入要添加的员工的信息

第二步: 用户点击保存按钮发送Ajax请求将用户输入的员工信息以json的格式提交到服务端

第三步: 服务端Controller接收页面提交的json格式的数据并转化为java对象, 通过Service调用Mapper将员工信息插入到employee表中,最后向前端返回通用结果类

  • id: 由雪花算法/自动递增机制自动生成
  • username: 设置唯一性约束, 员工的的登录账户必须是唯一的
  • password: 默认指定一个密码(身份证后六位,123456等),但是这个密码不能直接在数据库中设为默认值,因为数据库设置的默认值无法加密
  • status: 设定员工的状态,默认值1表示启用(正常),0表示禁用,直接在数据库中设置默认值不需要加密
  • createTime/updateTime: 创建/更新员工信息的时间,这个就指定当前时间就好了
  • createUser/updateUser: 创建/更新员工信息的管理员ID,依靠这个可以溯源避免出现莫名的员工账号

在这里插入图片描述

前端页面

add.html为公共页面,新增员工和编辑员工都是在此页面操作,只不过添加员工时还会显示保存并继续添加按钮

<script>
    // add.html页面提交的数据模型 
    ruleForm : {
        'name': '',
        'phone': '',
        'sex': '男',
        'idNumber': '',
        username: ''
    }
</script>
        
<el-form
         ref="ruleForm"
         :model="ruleForm"
         :rules="rules"
         :inline="false"
         label-width="180px"
         class="demo-ruleForm"
         >
    <el-form-item label="账号:" prop="username">
        <el-input v-model="ruleForm.username" placeholder="请输入账号" maxlength="20"/>
    </el-form-item>
    <el-form-item
                  label="员工姓名:"
                  prop="name"
                  >
        <el-input
                  v-model="ruleForm.name"
                  placeholder="请输入员工姓名"
                  maxlength="20"
                  />
    </el-form-item>

    <el-form-item
                  label="手机号:"
                  prop="phone"
                  >
        <el-input
                  v-model="ruleForm.phone"
                  placeholder="请输入手机号"
                  maxlength="20"
                  />
    </el-form-item>
    <el-form-item
                  label="性别:"
                  prop="sex"
                  >
        <el-radio-group v-model="ruleForm.sex">
            <el-radio label=""></el-radio>
            <el-radio label=""></el-radio>
        </el-radio-group>
    </el-form-item>
    <el-form-item
                  label="身份证号:"
                  prop="idNumber"
                  >
        <el-input
                  v-model="ruleForm.idNumber"
                  placeholder="请输入身份证号"
                  maxlength="20"
                  />
    </el-form-item>
    <div class="subBox address">
        <el-form-item>
            <el-button  @click="goBack()">
                取消
            </el-button>
            <el-button
                       type="primary"
                       <!--发送ajax请求将用户输入的员工信息以json的格式提交到服务端-->
                       @click="submitForm('ruleForm', false)"
                       >
                保存
            </el-button>
            <el-button
                       <!--如果是添加员工显示该按钮,如果是修改员工则不显示该按钮-->
                       v-if="actionType == 'add'"
                       type="primary"
                       class="continue"
                       @click="submitForm('ruleForm', true)"
                       >
                保存并继续添加
            </el-button>
        </el-form-item>
    </div>
</el-form>

submitForm函数调用addEmployee函数发送Ajax请求将用户输入的员工信息以json的格式提交到服务端,最后接收服务器响应的通用结果类执行Ajax的回调函数

submitForm (formName, st) {
    this.$refs[formName].validate((valid) => {// 对表单的数据进行校验
        if (valid) {
        // 判断是添加还是修改
        if (this.actionType === 'add') {
            const params = {
            ...this.ruleForm,
            sex: this.ruleForm.sex === '女' ? '0' : '1'// 将表单中的男女转换为数据库中对应的0或1
            }
            addEmployee(params).then(res => {// 接收服务器Controller响应的结果
            if (res.code === 1) {
                this.$message.success('员工添加成功!')
                if (!st) {
                this.goBack()
                } else {// 员工添加失败
                this.ruleForm = {
                    username: '',
                    'name': '',
                    'phone': '',
                    // 'password': '',
                    // 'rePassword': '',/
                    'sex': '男',
                    'idNumber': ''
                }
                }
            } else {
                this.$message.error(res.msg || '操作失败')
            }
            }).catch(err => {
            this.$message.error('请求出错了:' + err)
            })
        } else {
          // 修改员工信息......
        }
        } else {
        console.log('error submit!!')
        return false
        }
    })
}

// 发起ajax请求新增添加员工
function addEmployee (params) {
  return $axios({
    url: '/employee',
    method: 'post',
    data: { ...params }
  })
} 

后端处理请求

处理请求路径为/employee的POST请求将添加的员工信息保存到数据库

@RestController
@RequestMapping("/employee")
public class EmployeeController {
    @Autowired
    private EmployeeService employeeService;
    
    @PostMapping
    public Result<String> save(HttpServletRequest request, @RequestBody Employee employee) {
        // 接收的employee对象的id,password,status,createTime属性都还是null
        log.info("新增的员工信息:{}", employee.toString());
        // 设置默认密码为123456,并采用MD5加密
        employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));
        // 设置createTime和updateTime
        employee.setCreateTime(LocalDateTime.now());
        employee.setUpdateTime(LocalDateTime.now());
        // 在session中获取创建人的id设置CreateUser和UpdateUser
        Long empId = (Long) request.getSession().getAttribute("employee");
        employee.setCreateUser(empId);
        employee.setUpdateUser(empId);
        // 将employee对象存入数据库
        employeeService.save(employee);
        // 响应通用的结果封装类
        return Result.success("添加员工成功");
    }
}	

全局异常处理器

添加员工时存入相同的username(唯一)控制台会报java.sql.SQLIntegrityConstraintViolationException:Duplicate entry...,有两种方式捕获

  • 在Controller方法中对employeeService.save(employee)使用try...catch进行异常捕获,这种方式只能捕获当前代码发生的异常
  • 使用全局异常处理器可以捕获整个项目中指定类型的异常

创建一个全局异常处理类common/GlobalExceptionHandler,添加exceptionHandler方法用来捕获异常,并返回通用错误信息如提示用户名xxx已存在

@Slf4j
// 只有加了@Controller注解的类发生的异常才会捕获
@ControllerAdvice(annotations = {RestController.class,Controller.class})
@ResponseBody	
public class GlobalExceptionHandler {
    // 拦截SQLIntegrityConstraintViolationException异常
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public Result<String> exceptionHandler(SQLIntegrityConstraintViolationException exception) {
		// 控制台日志输出的异常信息为Duplicate entry 'zhangsan' for key 'employee.idx_username'
        log.error(exception.getMessage());
        // 如果异常信息包含Duplicate entry,则说明有条目重复
        if (exception.getMessage().contains("Duplicate entry")) {
            // 将字符串按照空格分割得到username(字符串格式是固定的)
            String[] split = exception.getMessage().split(" ");
            String username = split[2];
            // 拼串作为错误信息返回
            return Result.error("用户名" + username + "已存在");
        }
        // 其他错误信息
        return Result.error("未知错误");
    }
}

员工信息分页查询

执行流程

第一步: 在加载index.html页面时自动调用list.html中的creatued()函数发送ajax请求,将分页查询需要的参数page,pageSize,name提交到服务端

  • 如果当前就在index.html页面中可以通过指定查询条件发起Ajax请求查询分页数据, 请求参数除了携带page,pageSize外还携带name表示查询条件
  • 如果用户点击页数上下翻页的时候也会发起Ajax请求提交page,pageSize请求参数查询员工分页数据

在这里插入图片描述

第二步: 服务端Controller接收页面提交的参数,通过Service调用Mapper操作数据库查询分页数据,然后将查询到的分页数据响应给页面

第三步: 页面接收到分页数据并通过ElementUI的Table组件展示到页面上

在这里插入图片描述

前端页面

list.html页面携带分页查询需要的参数page,pageSize,name发起Ajax请求,然后将服务端的Controller响应的所有员工数据展示到页面

created() {
    this.init()
        this.user = JSON.parse(localStorage.getItem('userInfo')).username
},

async init () {
    // json格式的请求参数
    const params = {
        page: this.page,
        pageSize: this.pageSize,
        name: this.input ? this.input : undefined
    }
    await getMemberList(params).then(res => {// 执行回调函数接收服务端Controller响应的所有员工数据
        if (String(res.code) === '1') {
        this.tableData = res.data.records || []
        this.counts = res.data.total
        }
    }).catch(err => {
        this.$message.error('请求出错了:' + err)
    })

        
function getMemberList (params) {
  return $axios({
    url: '/employee/page',
    method: 'get',
    params
  })
}

request全局拦截器拦截get请求将json格式的请求参数转化为name=value的格式拼接到URL上

service.interceptors.request.use(config => {
// 是否需要在响应头中添加token
// const isToken = (config.headers || {}).isToken === false
// if (getToken() && !isToken) {
//   config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
// }
// get请求映射params参数
if (config.method === 'get' && config.params) {
    let url = config.url + '?';
    for (const propName of Object.keys(config.params)) {
    const value = config.params[propName];
    var part = encodeURIComponent(propName) + "=";
    if (value !== null && typeof(value) !== "undefined") {
        if (typeof value === 'object') {
        for (const key of Object.keys(value)) {
            let params = propName + '[' + key + ']';
            var subPart = encodeURIComponent(params) + "=";
            url += subPart + encodeURIComponent(value[key]) + "&";
        }
        } else {
        url += part + encodeURIComponent(value) + "&";
        }
    }
    }
    url = url.slice(0, -1);
    config.params = {};
    config.url = url;
}
return config
}, error => {
    console.log(error)
    Promise.reject(error)
})

list.html页面中展示数据的时候,将后端响应的Integer类型的status转化为已禁用或者正常的字符串

<el-table-column label="账号状态">
    <template slot-scope="scope">
    {{ String(scope.row.status) === '0' ? '已禁用' : '正常' }}
    </template>
</el-table-column>

后端处理请求

第一步: 在config/MybatisPlusConfig中添加MyBatisPlus的分页插件对象并交给IoC容器管理

@Configuration
//可以将主类中的扫描Mapper接口的注解移到此处
@MapperScan("com.atguigu.mybatisplus.mapper") 
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        // 配置MyBatis Plus中插件的对象
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 在插件对象中添加内部插件并设置数据库类型
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

第二步: 查询所有的员工信息并进行分页,Page对象中封装了查询到的所有员工信息(records属性)和其他分页信息如total,size属性

@GetMapping("/page")
public Result<Page> page(int page, int pageSize, String name) {
    log.info("page={},pageSize={},name={}", page, pageSize, name);
    // 在分页对象中设置分页参数,当前页码数和每页显示的记录数
    Page<Employee> pageInfo = new Page<>(page, pageSize);
    // 构造条件构造器
    LambdaQueryWrapper<Employee> wrapper = new LambdaQueryWrapper<>();
    // 添加过滤条件,当我们没有输入name查询条件时表示查询所有
    wrapper.like(!(name == null || "".equals(name)), Employee::getName, name);
    // wrapper.like(StringUtils.isNotEmpty(name),Employee::getName,name);
    // 添加排序条件,根据更新时间对查询的结果进行降序排序
    wrapper.orderByDesc(Employee::getUpdateTime);
    // 添加分页对象和条件构造器查询所有,最终数据都会被封装到分页对象中
    employeeService.page(pageInfo, wrapper);
    // 将Page对象封装到Result对象的data属性中,Page对象的records属性中封装了查询到的员工信息
    return Result.success(pageInfo);
}

启用/禁用员工账号

执行流程

第一步: 用户点击启用/禁用按钮时页面发送Ajax的PUT请求将参数元素所在行的id和status提交到服务端

第二步: 服务端Controller接收页面提交的数据通过Service调用Mapper操作数据库更新对应id员工的status的值

在这里插入图片描述

前端页面

只有管理员(admin)可以对其他所有员工账号进行启用、禁用操作, 普通用户登录系统后启用、禁用按钮不显示

  • 如果某个员工账号状态为正常则按钮显示为禁用,如果员工账号状态为已禁用则按钮显示为启用

当加载完list.html页面时获取一下当前登录账号的username并判断是不是admin,如果是就显示启用/禁用按钮否则不显示

<script>
    created() {
        this.init()
        this.user = JSON.parse(localStorage.getItem('userInfo')).username
    }
</script>

<el-button
    type="text"
    size="small"
    class="delBut non"
    @click="statusHandle(scope.row)"
    v-if="user === 'admin'"
>
    {{ scope.row.status == '1' ? '禁用' : '启用' }}
</el-button>

用户点完启用禁用按钮后调用statusHandle函数弹出提示窗口,点击确定之后调用enableOrDisableEmployee函数携带当前行的id值与!status发起PUT请求

statusHandle (row) {
    // row表示每行数据对应的json对象
    this.id = row.id
    this.status = row.status
    this.$confirm('确认调整该账号的状态?', '提示', {
        'confirmButtonText': '确定',
        'cancelButtonText': '取消',
        'type': 'warning'
        }).then(() => {
        // 对当前状态进行取反操作实现切换禁用/启用状态,如果this.status为1则status为0,如果this.status为0则status为1
        enableOrDisableEmployee({ 'id': this.id, 'status': !this.status ? 1 : 0 }).then(res => {// 根据服务器响应的结果执行回调函数
        console.log('enableOrDisableEmployee',res)
        if (String(res.code) === '1') {
            this.$message.success('账号状态更改成功!')
            this.handleQuery()
        }
        }).catch(err => {
        this.$message.error('请求出错了:' + err)
        })
    })
}

// 修改启用/禁用接口
function enableOrDisableEmployee (params) {
  return $axios({
    url: '/employee',
    method: 'put',
    data: { ...params }
  })
}

配置状态转换器

执行完分页查询请求服务器响应给页面的Employee对象的id是正确的,但JS将id渲染到页面上时会丢失精度,这样获取的页面id就和数据库中id有差异

JS对Long型数据进行处理时会丢失精度导致提交的id和数据库中的id不一致, 需要服务端在给页面响应json数据时将Long型数据统一转为String字符串

在这里插入图片描述

对象转换器:基于jackson将Java对象转为json[序列化Java对象到JSON],或者将json转为Java对象[从JSON反序列化Java对象]

第一步: 创建commom/JacksonObjectMapper继承ObjectMapper,不需要向工程中导入依赖

public class JacksonObjectMapper extends ObjectMapper {
    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
    public JacksonObjectMapper() {
        super();
        // 收到未知属性时不报异常
        this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 反序列化时,属性不存在的兼容处理
        this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
  				SimpleModule simpleModule = new SimpleModule()
   .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
                .addSerializer(BigInteger.class, ToStringSerializer.instance)
                 // 将Long类型转化为String类型
                .addSerializer(Long.class, ToStringSerializer.instance)
                 // 将日期类型转化为日期格式的字符串
    .addSerializer(LocalDateTime.class,new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
        //注册功能模块 例如可以添加自定义序列化器和反序列化器
        this.registerModule(simpleModule);
    }
}

第二步: 扩展Mvc框架的消息转换器,在此消息转换器中使用自己的对象转换器进行java数据到json对象的转换

@Configuration
@Slf4j
public class WebMvcConfig extends WebMvcConfigurationSupport {
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");
        registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");
    }

    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
        // 设置自己的对象转化器,底层使用jackson将java对象转为json
        messageConverter.setObjectMapper(new JacksonObjectMapper());
        // 将自己的消息转换器对象追加到mvc框架的转换器集合当中(index设置为0,表示设置在第一个位置,避免被其它转换器接收,从而达不到想要的功能)
        converters.add(0, messageConverter);
    }
}

后端处理请求

更新员工记录的status状态字段的值,该方法也可以修改员工信息的其他字段

@PutMapping
public Result<String> update(@RequestBody Employee employee, HttpServletRequest request) {
    log.info(employee.toString());
    Long id = (Long) request.getSession().getAttribute("employee");
    employee.setUpdateUser(id);
    employee.setUpdateTime(LocalDateTime.now());
    employeeService.updateById(employee);
    return Result.success("员工信息修改成功");
}

编辑员工信息

执行流程

第一步用户点击编辑按钮跳转到add.html公共页面中并在URL中携带编辑员工的id作为请求参数,如果URL中没有携带id表示添加操作

在这里插入图片描述

<el-button
    type="text"
    size="small"
    class="blueBug"
    @click="addMemberHandle(scope.row.id)"
    :class="{notAdmin:user !== 'admin'}"
>
    编辑
</el-button>
<script>
	addMemberHandle (st) {
    if (st === 'add'){
        window.parent.menuHandle({
        id: '2',
        url: '/backend/page/member/add.html',
        name: '添加员工'
        },true)
    } else {
        window.parent.menuHandle({
        id: '2',
        url: '/backend/page/member/add.html?id='+st,
        name: '修改员工'
        },true)
    }
}
</script>

第二步: 在add.html页面中获取URL中携带的员工id,如果获取得到id发起修改员工的Ajax请求,如果获取不到发起添加员工的Ajax请求

created() {
    this.id = requestUrlParam('id')
    // id有值表示修改,没有值表示添加
    this.actionType = this.id ? 'edit' : 'add'
    if (this.id) {
    this.init()
    }
}

// 获取url地址上面的参数
function requestUrlParam(argname){
  var url = location.href
  var arrStr = url.substring(url.indexOf("?")+1).split("&")
  for(var i =0;i<arrStr.length;i++){
      // 查找"id="出现的索引
      var loc = arrStr[i].indexOf(argname+"=")
      if(loc!=-1){
          return arrStr[i].replace(argname+"=","").replace("?","")
      }
  }
  return ""
}

员工信息回显

第一步: 根据编辑员工的id查询员工信息然后以json形式响应给页面

@GetMapping("/{id}")// 使用占位符接收员工的id
public Result<Employee> getById(@PathVariable Long id){
    log.info("根据id查询员工信息..");
    Employee employee = employeeService.getById(id);
    return Result.success(employee);
}

第二步: 在回调函数中接收服务端响应的json数据,并通过Vue的双向绑定功能进行员工信息回显

  • 前端接收到服务端响应的json数据之后先判断状态码是否为1,如果是1则说明操作成功,然后将获取到的数据渲染到表单中从而达到回显数据的效果
// created钩子函数中调用的init函数
async init () {
    queryEmployeeById(this.id).then(res => {
        console.log(res)
        if (String(res.code) === '1') {
        console.log(res.data)
        this.ruleForm = res.data
        this.ruleForm.sex = res.data.sex === '0' ? '女' : '男'
        // this.ruleForm.password = ''
        } else {
        this.$message.error(res.msg || '操作失败')
        }
    })
}

更新员工信息

第一步: 用户点击保存按钮发送Ajax请求将页面中修改后的员工信息以json形式提交给服务端

<el-button
    type="primary"
    @click="submitForm('ruleForm', false)"
>
    保存
</el-button>
<script>
	submitForm (formName, st) {
    this.$refs[formName].validate((valid) => {
        if (valid) {
        // 判断是添加还是修改 
        if (this.actionType === 'add') {
            // 添加员工信息的接口
            addEmployee(params).then(res => {// 接收服务器响应的结果执行回调函数
        } else {                        
            const params = {
            ...this.ruleForm,
            sex: this.ruleForm.sex === '女' ? '0' : '1'
            }
            // 修改员工信息
            editEmployee(params).then(res => {
            if (res.code === 1) {
                this.$message.success('员工信息修改成功!')
                this.goBack()
            } else {
                this.$message.error(res.msg || '操作失败')
            }
            }).catch(err => {
            this.$message.error('请求出错了:' + err)
            })
        }
        } else {
        console.log('error submit!!')
        return false
        }
    })
}
// 修改员工的接口
function editEmployee (params) {
  return $axios({
    url: '/employee',
    method: 'put',
    data: { ...params }
  })
}
</script>

第二步: 服务端将修改后的员工信息保存到数据库,然后响应成功的结果状态信息

@PutMapping
public Result<String> update(@RequestBody Employee employee, HttpServletRequest request) {
    log.info(employee.toString());
    Long id = (Long) request.getSession().getAttribute("employee");
    employee.setUpdateUser(id);
    employee.setUpdateTime(LocalDateTime.now());
    employeeService.updateById(employee);
    return Result.success("员工信息修改成功");
}

第三步: 页面Ajax请求的回调函数接收到服务端响应的信息后进行相应处理,调用goBack函数跳转至员工管理页面

goBack(){
    window.parent.menuHandle({
        id: '2',
        url: '/backend/page/member/list.html',
        name: '员工管理'
    },false)
}

填充公共字段

添加/修改员工数据的时候,都需要指定一下创建人,创建时间,修改人,修改时间等公共字段,在员工表,菜品表,分类表等其他表中都拥有的字段

使用MybatisPlus提供的自动填充功能将这些公共字段在一个地方统一管理,在插入或者更新的时候为指定字段赋予指定的值

实现步骤

第一步: 在实体类的需要自动填充的属性上方加入@TableFiled注解并指定自动填充的策略

@Data
public class Employee implements Serializable {
    private static final long serialVersionUID = 1L;
    private Long id;
    private String username;
    private String name;
    private String password;
    private String phone;
    private String sex;
    private String idNumber;//身份证号码
    private Integer status;
    // 插入时填充字段
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    // 插入和更新时填充字段
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    @TableField(fill = FieldFill.INSERT)
    private Long createUser;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;
}

第二步: 按照框架要求编写元数据对象处理器common/MyMetaObjectHandle在此类中实现插入和更新的方法并统一对公共字段赋值

  • 在MyMetaObjectHandler类中不能获得HttpSession对象,所以我们需要使用ThreadLocal存储当前登录用户的Id
方法名功能
metaObject(元数据)封装了需要执行更新和插入的实体类对象,它提供了setValue方法用来完成字段的填充
insertFill(MetaObject metaObject)在执行插入语句时对指定的公共字段进行填充
updateFill(MetaObject metaObject)在执行更新语句时对指定的公共字段进行填充
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("公共字段自动填充(insert)...");
        log.info(metaObject.toString());
        metaObject.setValue("createTime", LocalDateTime.now());
        metaObject.setValue("updateTime", LocalDateTime.now());
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("公共字段自动填充(update)...");
        log.info(metaObject.toString());
        metaObject.setValue("updateTime", LocalDateTime.now());
    }
}

ThreadLocal

对于客户端每次发送的Http请求, 服务端都会创建一个对应的线程来处理

  • 在一次请求过程中LocalCheekFilter中的doFilter-->EmployeeController中的update-->MyMetaObjectHandler中的updateFill处于同一个线程当中

ThreadLocal并不是一个Thread(线程)而是Thread的局部变量,主要为每个使用该变量的线程提供独立的变量副本

  • 使用ThreadLocal维护变量时每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本
  • ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问
方法名功能
public void set(T value)设置当前线程对应的线程局部变量的值
public T get()返回当前线程所对应的线程局部变量的值

第一步: 新建一个基于ThreadLocal的工具类common/BaseContext ,用于存储和获取当前登录用户的Id

public class BaseContext {
    private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();
    public static void setCurrentId(Long id) {
        threadLocal.set(id);
    }
    public static Long getCurrentId() {
        return threadLocal.get();
    }
}

第二步: 在LoginCheckFilter类中使用request.getSession获取当前登录用户的Id,然后使用BaseContext工具类将Id值存到ThreadLocal当中,key是当前线程

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    // 判断用户的登录状态,如果已登录,则直接放行
    if (request.getSession().getAttribute("employee") != null) {
        log.info("用户已登录,id为{}", request.getSession().getAttribute("employee"));
        // 在这里获取一下当前线程的id
        long id = Thread.currentThread().getId();
        log.info("doFilter的线程id为:{}", id);
        // 从session中获取之前我们存的用户Id
        Long empId = (Long) request.getSession().getAttribute("employee");
        // 使用BaseContext工具类将用户的Id存到ThreadLocal当中,key是当前线程
        BaseContext.setCurrentId(empId);
        filterChain.doFilter(request, response);
        return;
    }
}

第三步: 在MyMetaObjectHandler类中使用BaseContext工具类从当前线程中获取对应登录用户的Id,然后填充到对应表中的字段

@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
    // 插入操作自动填充
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("公共字段填充(create)...");
        metaObject.setValue("createTime", LocalDateTime.now());
        metaObject.setValue("updateTime", LocalDateTime.now());
        // 获取当前线程对应的用户Id,然后设置创建人Id
        metaObject.setValue("createUser", BaseContext.getCurrentId());
        metaObject.setValue("updateUser", BaseContext.getCurrentId());
    }
	
    // 更新操作自动填充
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("公共字段填充(insert)...");
        metaObject.setValue("updateTime", LocalDateTime.now());
        // 设置更新人id
        metaObject.setValue("updateUser", BaseContext.getCurrentId());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。在编写C程序时,需要注意变量的声明和定义、指针的使用、内存的分配与释放等问题。C语言中常用的数据结构包括: 1. 数组:一种存储同类型数据的结构,可以进行索引访问和修。 2. 链:一种存储不同类型数据的结构,每个节点包含数据和指向下一个节点的指针。 3. 栈:一种后进先出(LIFO)的数据结构,可以通过压入(push)和弹出(pop)操作进行数据的存储和取出。 4. 队列:一种先进先出(FIFO)的数据结构,可以通过入队(enqueue)和出队(dequeue)操作进行数据的存储和取出。 5. 树:一种存储具有父子关系的数据结构,可以通过中序遍历、前序遍历和后序遍历等方式进行数据的访问和修。 6. 图:一种存储具有节点和边关系的数据结构,可以通过广度优先搜索、深度优先搜索等方式进行数据的访问和修。 这些数据结构在C语言中都有相应的实现方式,可以应用于各种不同的场景。C语言中的各种数据结构都有其优缺点,下面列举一些常见的数据结构的优缺点: 数组: 优点:访问和修元素的速度非常快,适用于需要频繁读取和修数据的场合。 缺点:数组的长度是固定的,不适合存储大小不固定的动态数据,另外数组在内存中是连续分配的,当数组较大时可能会导致内存碎片化。 链: 优点:可以方便地插入和删除元素,适用于需要频繁插入和删除数据的场合。 缺点:访问和修元素的速度相对较慢,因为需要遍历链找到指定的节点。 栈: 优点:后进先出(LIFO)的特性使得栈在处理递归和括号匹配等问题时非常方便。 缺点:栈的空间有限,当数据量较大时可能会导致栈溢出。 队列: 优点:先进先出(FIFO)的特性使得

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值