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());
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/140099.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vue3 ref函数和active函数

一、ref函数 我们在setup函数中导出的属性和方法虽然能够在模板上展示出来&#xff0c;但是并没有给属性添加响应式&#xff0c;因此&#xff0c;我们需要使用ref函数来为我们的数据提供响应式。 &#xff08;一&#xff09;引入ref函数 import { ref } from "vue"…

Sprint Boot 学习路线 6

测试 Spring提供了一组测试工具&#xff0c;可以轻松地测试Spring应用程序的各个组件&#xff0c;包括控制器、服务、存储库和其他组件。它具有丰富的测试注释、实用程序类和其他功能&#xff0c;以帮助进行单元测试、集成测试等。 JPA测试 Spring JPA&#xff08;Java Pers…

pytorch框架学习(tensorboard的使用)

什么是tensorboard&#xff1f; tensorboard是一个可视化工具&#xff0c;它可以把训练过程中的数据变化以图像的形式绘制出来&#xff0c;或者记录训练过程中使用的图片 tensorboard的安装&#xff1a; 在pycharm的终端中输出安装命令后自动安装—— pip install tensorbo…

加速mvn下载seatunnel相关jar包

seatunnel安装的时候&#xff0c;居然要使用mvnw来下载jar包&#xff0c;而且是从https://repo.maven.apache.org 下载&#xff0c;速度及其缓慢&#xff0c;改用自己本地的mvn下载。 修改其安装插件相关脚本&#xff0c;复制install-plugin.sh重命名为install-plugin-mvn.sh …

玩转硬件之Micro:bit的玩法(五)——垃圾分类

垃圾分类&#xff0c;为了美好的明天 垃圾是我们生活中不可避免的产物&#xff0c;每天都有大量的垃圾被丢弃&#xff0c;如果不加以处理&#xff0c;就会给环境和人类带来严重的危害。 垃圾分类是一种有效的垃圾管理方式&#xff0c;它是指按照一定的标准或规则&#xff0c;将…

[量子计算与量子信息] 2.1 线性代数

2.1 线性代数 符号对照表 量子力学中&#xff0c;向量使用 ∣ ψ ⟩ \ket \psi ∣ψ⟩ (ket)来表示&#xff0c;可以理解为一个列向量。其对偶向量为 ⟨ ψ ∣ \bra \psi ⟨ψ∣ &#xff0c;可以理解为行向量。 向量空间中零向量直接用 0 0 0 表示&#xff0c; ∣ 0 ⟩ \…

网络安全黑客技术自学

前言 一、什么是网络安全 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域&#xff0c;都有攻与防…

如何利用黑群晖虚拟机和内网穿透实现公网远程访问

文章目录 前言本教程解决的问题是&#xff1a;按照本教程方法操作后&#xff0c;达到的效果是前排提醒&#xff1a; 1. 搭建群晖虚拟机1.1 下载黑群晖文件vmvare虚拟机安装包1.2 安装VMware虚拟机&#xff1a;1.3 解压黑群晖虚拟机文件1.4 虚拟机初始化1.5 没有搜索到黑群晖的解…

基于51单片机DS18B20温度检测报警系统串口设置阀值-仿真及源程序

一、系统方案 1、本设计采用51单片机作为主控器。 2、DS18B20采集温度值送到数码管显示。 3、按键报警阀值或串口设置阀值。 4、测量温度小于下限或大于上限&#xff0c;蜂鸣器报警。 二、硬件设计 原理图如下&#xff1a; 三、单片机软件设计 1、首先是系统初始化 uint z; …

八皇后问题

1.八皇后BOSS 2.战术分析 第一个皇后先放第一行第一列第二个皇后放在第二行第一列、然后判断是否OK,如果不OK,继续放在第二列、第三列、依次把所有列都放完,找到一个合适继续第三个皇后,还是第一列、第二列.…直到第8个皇后也能放在一个不冲突的位置,算是找到了一个正确解当得到…

LeetCode(9)跳跃游戏【数组/字符串】【中等】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; 55. 跳跃游戏 1.题目 给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回…

青少年编程学习 等级考试 蓝桥杯/NOC/GESP等比赛资料合集

一、博主愚见 在当今信息技术高速发展的时代&#xff0c;编程已经成为了一种必备的技能。随着社会对于科技人才的需求不断增加&#xff0c;青少年编程学习正逐渐成为一种趋势。为了更好地帮助青少年学习编程&#xff0c;提升他们的技能和素质&#xff0c;博主结合自身多年从事青…

五款常见的自动化测试框架

在自动化的软件测试系统实现过程中使用框架设计可以使得测试脚本的维护量减至最少。然而&#xff0c;大量的自动化测试工具均采用传统的“录制一回放”模 型&#xff0c;导致了较高的脚本维护量&#xff0c;因为测试数据在测试脚本程序中是以硬编码方式实现的。此外&#xff0…

向量数据库的分类概况

保存和检索矢量数据的五种方法&#xff1a; 像 Pinecone 这样的纯矢量数据库 全文搜索数据库&#xff0c;例如 ElasticSearch 矢量库&#xff0c;如 Faiss、Annoy 和 Hnswlib 支持矢量的NoSQL 数据库&#xff0c;例如 MongoDB、Cosmos DB 和 Cassandra 支持矢量的SQL 数据库&am…

Web视频会议:搭建CS for WebRTC

1. 下载Centos 7, WebRTC 需要Centos7版本 本文福利&#xff0c; 免费领取C音视频学习资料包学习路线大纲、技术视频/代码&#xff0c;内容包括&#xff08;音视频开发&#xff0c;面试题&#xff0c;FFmpeg &#xff0c;webRTC &#xff0c;rtmp &#xff0c;hls &#xff0c;…

垂直领域对话系统架构

垂直领域对话系统是指针对特定领域或行业的需求而构建的对话系统。这种系统通常需要具备高度的专业知识和对特定领域的知识库进行深入的学习和训练&#xff0c;以便能够提供准确、高效、实用的服务。 垂直领域对话系统的构建通常包括以下步骤&#xff1a; 确定目标领域或行业…

详解JDBC

JDBC简介 概念: jdbc就是使用java语言操作关系型数据库的一套API 全称 : (Java DataBase Connectivity) Java数据库连接 本质: 官方(sun公司)定义的一套操作所有关系型数据库的规则&#xff0c;即接口&#xff1b; 各个数据库厂商实现这套接口&#xff0c;提供数据库驱动j…

如何快速入门笔记软件『Obsidian』

前言 Obsidian 是基于 Markdown 语法的笔记软件&#xff0c;界面简洁&#xff0c;使用简单&#xff0c;功能实用&#xff0c;支持跨平台数据同步&#xff0c;实现基于双向链接的知识图谱&#xff0c;同时提供各种各样的扩展主题和插件 本文将会详细讲解笔记软件 Obsidian 的安…

threejs (三) 几何体

定义&#xff1a;用来表示物体的形状&#xff0c;可以定义物体的大小&#xff0c;可以被缩放、旋转和平移 内置几何体&#xff1a; 二维几何体&#xff1a;PlaneGeometry矩形平面、CircleGeometry圆形平面、RingGeometry环形平面、ShapeGeometry二维图形三维几何体&#xff1a…

阿里云通用算力型u1服务器和e实例有什么区别?选择攻略

阿里云服务器ECS经济型e实例和通用算力型u1实例有什么区别&#xff1f;如何选择&#xff1f;ECS经济型e实例是共享型云服务器&#xff0c;通用算力型u实例是企业级独享型云服务器&#xff0c;e实例性价比高&#xff0c;现在2核2G3M带宽一年99元&#xff0c;云服务器u1价格相对要…