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

news2024/11/21 2:34:36

添加员工信息

执行流程

第一步: 用户点击添加员工按钮跳转到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());
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

酷开系统 | 酷开科技:打造精彩纷呈的电影盛宴

对于许多人来说&#xff0c;观看电影是一种享受、一种放松、一种逃避现实的方式。而现在&#xff0c;酷开科技作为行业内领军企业&#xff0c;为我们带来了一种全新的居家观影体验&#xff0c;让电影不仅是一种娱乐方式&#xff0c;更是科技的展现。 酷开科技致力于为观众带来…

【MySQL日志与备份篇】数据库备份与恢复

数据库备份与恢复 文章目录 数据库备份与恢复1. 物理备份与逻辑备份2. mysqldump实现逻辑备份2.1 备份一个数据库2.2 备份全部数据库2.3 备份部分数据库2.4 备份部分表2.5 备份单表的部分数据2.6 排除某些表的备份2.7 只备份结构或只备份数据2.8 备份中包含存储过程、函数、事件…

金蝶云星空设置单据体行高

文章目录 金蝶云星空设置单据体行高表单插件Python脚本 金蝶云星空设置单据体行高 表单插件 新建类继承AbstractBillPlugIn&#xff0c;重写OnInitialize方法进行设置 public override void OnInitialize(InitializeEventArgs e){base.OnInitialize(e);this.View.GetControl&…

图片高清重建

图像超分辨率重建&#xff08;super resolution,SR&#xff09;是指利用计算机将一幅低分辨率图像&#xff08;low resolution,LR&#xff09;或图像序列进行处理&#xff0c;恢复出高分辨率图像&#xff08;high resolution&#xff0c;HR&#xff09;的一种图像处理技术。简单…

红海云签约湘湖实验室,助力科研机构人力资源数字化全面升级

湘湖实验室&#xff08;农业浙江省实验室&#xff09;是由浙江省农业科学院和萧山区人民政府共同举办的新型研发机构&#xff0c;定位为农业核心种质资源生物制造与生物互作科学问题和核心技术研究&#xff0c;瞄准世界农业科技发展前沿&#xff0c;面向国家重大战略&#xff0…

Qt执行带参sql

//准备执行的sql语句&#xff0c;此为带参的sql语句query.prepare("update employee set Name:Name, Gender:Gender,Height:Height,"" Birthday:Birthday, Mobile:Mobile, Province:Province,"" City:City, Department:Department, Education:Educati…

DeepFool: a simple and accurate method to fool deep neural networks

DeepFool: a simple and accurate method to fool deep neural networks----《DeepFool&#xff1a;一种简单而准确的欺骗深度神经网络的方法》 摘要 最先进的深度神经网络已经在许多图像分类任务上取得了令人印象深刻的结果。然而&#xff0c;这些相同的架构已被证明对于图像…

使用cmd运行控制面板工具

如何通过键入命令运行“控制面板”工具 - Microsoft 支持 windows自带管理工具&#xff08;exe/cpl/msc&#xff09;-CSDN博客 CMD下打开系统各面板_cmd打开轻松使用面板-CSDN博客 示例&#xff1a; rundll32.exe shell32.dll,Control_RunDLL powercfg.cpl 替换powercfg.…

EXCEL——计算数据分散程度的相关函数

一、PERCENTIL函数 1.函数介绍 通常用来返回数据集给定百分点上的值。 2.函数解读 函数公式&#xff1a; PERCENTILE(数据, 百分点) 参数释义&#xff1a; 数据&#xff08;必填&#xff09;&#xff1a;待处理的数组或数据区域。 百分点&#xff08;必填&#xff09;&…

uview的u-calendar日历组件,当设置了 minDate配置项后,会导致第一次打开日历弹窗,不会精准的滚动到选中的日期(设置了默认日期都没用)

发现需要给month.vue文件里的getMonth方法加一个延时器&#xff0c;猜测是因为设置最小日期后&#xff0c;日历没渲染完毕的时候就已经开始获取节点信息了

基于SSM的超市积分管理系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

从C语言到C++_40(多线程相关)C++线程接口+线程安全问题加锁(shared_ptr+STL+单例)

目录 1. C多线程 1.1 thread库 1.2 mutex库 1.3 RAII锁 1.4 atomicCAS 1.5 condition_variable 1.6 分别打印奇数和偶数 2. shared_ptr线程安全 2.1 库里面的shared_ptr使用 2.2 shared_ptr加锁代码 3. 单例模式线程安全 3.1 懒汉模式线程安全问题 3.2 懒汉模式最…

openGauss学习笔记-122 openGauss 数据库管理-设置密态等值查询-密态支持函数/存储过程

文章目录 openGauss学习笔记-122 openGauss 数据库管理-设置密态等值查询-密态支持函数/存储过程122.1 创建并执行涉及加密列的函数/存储过程 openGauss学习笔记-122 openGauss 数据库管理-设置密态等值查询-密态支持函数/存储过程 密态支持函数/存储过程当前版本只支持sql和P…

初始Linux(五)(定时任务)

crontab 进行 定时任务的设置 概述: 任务调度:是指系统在某个时间执行的特定的命令或程序 任务调度分类: 1.系统工作:有些重要的工作必须周而复始的执行,如病毒扫描 2.个别用户工作: 个别用户可能希望执行某些程序,比如mysql数据库的备份 基本语法: crontab [选项] -e 表示…

CCF CSP认证历年题目自练Day45

这几天搞泰迪杯数据分析技能赛去了。等拿国奖了就出一期关于泰迪杯的。 题目 试题编号&#xff1a; 201703-3 试题名称&#xff1a; Markdown 时间限制&#xff1a; 1.0s 内存限制&#xff1a; 256.0MB 问题描述&#xff1a; 问题描述   Markdown 是一种很流行的轻量级标记…

11-13 周一 同济子豪兄CNN卷积神经网络学习记录

11-13 周一 同济子豪兄CNN卷积神经网络学习记录 时间版本修改人描述2023年11月13日14:02:14V0.1宋全恒新建文档2023年11月13日19:05:29V0.2宋全恒完成 大白话讲解卷积神经网络的学习 简介 为了深入理解CNN&#xff0c;进行B站 同济子豪兄深度学习之卷积神经网络的学习. 主要内…

加速mvn下载seatunnel相关jar包

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

【数据分享】我国独角兽企业数据(excel格式\shp格式)

企业是经济活动的参与主体。一个城市的企业数量决定了这个城市的经济发展水平&#xff01;比如一个城市的金融企业较多&#xff0c;那这个城市的金融产业肯定比较发达&#xff1b;一个城市的制造业企业较多&#xff0c;那这个城市的制造业肯定比较发达。 本次我们为大家带来的…

论文导读 | 融合大规模语言模型与知识图谱的推理方法

前 言 大规模语言模型在多种自然语言处理相关任务上展现了惊人的能力&#xff0c;如智能问答等&#xff0c;但是其推理能力尚未充分展现。本文首先介绍大模型进行推理的经典方法&#xff0c;然后进一步介绍知识图谱与大模型融合共同进行推理的工作。 文章一&#xff1a;使用思维…

Python大神用的贼溜的九个技巧,超级实用~

文章目录 一、整理字符串输入二、迭代器&#xff08;切片&#xff09;三、跳过可对对象的开头四、只包含关键字参数的函数 (kwargs)五、创建支持「with」语句的对象六、用「slots」节省内存七、限制「CPU」和内存使用量八、控制可以/不可以导入什么九、实现比较运算符的简单方法…