当前位置: 首页 > article >正文

【苍穹外卖】总结

1 pom 依赖

1.1 MyBatis Spring

        用于简化 MyBatis 与 Spring Boot 的集成,提供了对 MyBatis 框架的自动配置支持,简化了数据访问层的开发

       

1.2 Lombok

        Lombok 是一个 Java 库,能够通过注解自动生成常见的代码(如 gettersettertoString 等),减少了代码冗余,提升了开发效率

1. @Data

  • 作用: 这是一个综合性注解,包含了 @Getter@Setter@ToString@EqualsAndHashCode@RequiredArgsConstructor,也就是说,@Data 会为所有字段自动生成 gettersetter,同时生成 toString()equals()hashCode() 方法,并且为 final 字段生成构造函数。

2. @Builder

  • 作用: 生成建造者模式(Builder Pattern)代码,可以通过流式调用来构建对象。它允许更灵活、清晰地创建对象,尤其适合包含多个属性的复杂对象创建。

3. @NoArgsConstructor

  • 作用: 生成无参数的构造函数。适用于需要创建空对象的情况,比如从数据库反序列化时,或者某些框架(如 JPA)需要一个无参构造函数。

4. @AllArgsConstructor

  • 作用: 生成包含所有字段的全参数构造函数。适用于在创建对象时直接通过构造函数初始化所有字段。

1.3 FastJSON

        FastJSON 是阿里巴巴开源的一个高性能 JSON 序列化/反序列化库,主要用于将 Java 对象转换为 JSON 字符串或将 JSON 字符串转换为 Java 对象

1.4 Commons Lang

        提供了一些常用的 Java 工具类,扩展了 Java 核心库中的 java.lang 包,提供了字符串操作、数字处理等实用功能

1.5 Druid

        Druid 是阿里巴巴开源的高效数据库连接池,集成了数据库监控和性能优化功能。druid-spring-boot-starter 提供了与 Spring Boot 的自动集成

1.6 PageHelper

        PageHelper 是一个 MyBatis 分页插件,简化了分页操作,支持多种数据库并能自动分页

1.7 Knife4j

        Knife4j 是基于 Swagger 的增强 UI,提供了更丰富的文档功能,便于 API 文档的生成与查看

1.8 AspectJ

        AspectJ 是一个面向切面编程(AOP)框架,aspectjrt 是其运行时库,用于在运行时处理切面相关的逻辑

        AspectJjweaver 是用于支持 AspectJ 切面的织入器(weaver),允许在编译期、类加载期或运行期插入切面代码

1.9 JSON Web Token (JJWT)

        JJWT 是用于生成和验证 JSON Web Tokens (JWT) 的库,主要用于处理身份验证和安全

1.10 Aliyun OSS

        阿里云对象存储(OSS)SDK,用于与阿里云 OSS 进行集成,实现文件的上传、下载和管理

1.11 JAXB API

        JAXB(Java Architecture for XML Binding)是用于将 Java 对象转换为 XML 以及将 XML 转换为 Java 对象的 API

2 项目结构

common:相当于 util,放置自定义的方法

pojo:Plain Old Java Object,放置对象

server:服务端代码

2.1 common

2.1.1 constant

常量类

public class JwtClaimsConstant {

    public static final String EMP_ID = "empId";
    public static final String USER_ID = "userId";
    public static final String PHONE = "phone";
    public static final String USERNAME = "username";
    public static final String NAME = "name";

}

2.1.2 context

上下文

public class BaseContext {

    public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();

    public static void setCurrentId(Long id) {
        threadLocal.set(id);
    }

    public static Long getCurrentId() {
        return threadLocal.get();
    }

    public static void removeCurrentId() {
        threadLocal.remove();
    }

}

        管理当前线程中的上下文信息,特别是与线程相关的 Long 类型的 id。通过使用 ThreadLocal,它允许每个线程独立存储和访问自己的 id,从而确保在多线程环境中不会出现线程间数据冲突的问题

        这样可以 在任何时候获取用户 ID

2.1.3 exception

异常类

/**
 * 登录失败
 */
public class LoginFailedException extends BaseException {
    public LoginFailedException(String msg) {
        super(msg);
    }
}

2.1.4 json

将 Java 对象与 JSON 之间进行序列化和反序列化

通过这个类,可以将 Java 对象转换为 JSON,或者将 JSON 转换为 Java 对象

/**
 * 对象映射器:基于 jackson 将 Java 对象转为 json,或者将 json 转为 Java 对象
 * 将 JSON 解析为 Java 对象的过程称为 [从 JSON 反序列化 Java 对象]
 * 从 Java 对象生成 JSON 的过程称为 [序列化 Java 对象到 JSON]
 */
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_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm";
    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(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);
    }
}

2.1.5 properties

属性类

@Component
@ConfigurationProperties(prefix = "sky.jwt") // 配置属性类
@Data
public class JwtProperties {

    /**
     * 管理端员工生成jwt令牌相关配置
     */
    private String adminSecretKey;
    private long adminTtl;
    private String adminTokenName;

    /**
     * 用户端微信用户生成jwt令牌相关配置
     */
    private String userSecretKey;
    private long userTtl;
    private String userTokenName;

}

        这里有个 配置属性类 的注解 @ConfigurationProperties(prefix = "sky.jwt"),将配置文件 application.yml 中的配置属性 映射 到 Java 类,前缀 prefix 指定了配置属性:

# application.yml

sky:
  jwt:
    # 设置jwt签名加密时使用的秘钥
    admin-secret-key: itcast
    # 设置jwt过期时间
    admin-ttl: 72000000000
    # 设置前端传递过来的令牌名称
    admin-token-name: token

    # 设置jwt签名加密时使用的秘钥
    user-secret-key: userKey
    # 设置jwt过期时间
    user-ttl: 72000000000
    # 设置前端传递过来的令牌名称 与 前端 一致,不可更改
    user-token-name: authentication

2.1.6 result

结果类

/**
 * 封装分页查询结果
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PageResult implements Serializable {

    private long total; //总记录数

    private List records; //当前页数据集合

}
/**
 * 后端统一返回结果
 *
 * @param <T>
 */
@Data
public class Result<T> implements Serializable {

    private Integer code; //编码:1成功,0和其它数字为失败
    private String msg; //错误信息
    private T data; //数据

    public static <T> Result<T> success() {
        Result<T> result = new Result<T>();
        result.code = 1;
        return result;
    }

    public static <T> Result<T> success(T object) {
        Result<T> result = new Result<T>();
        result.data = object;
        result.code = 1;
        return result;
    }

    public static <T> Result<T> error(String msg) {
        Result result = new Result();
        result.msg = msg;
        result.code = 0;
        return result;
    }

}

2.1.7 utils

实用类,包括AliOss对象文件上传,jwt令牌

@Data
@AllArgsConstructor
@Slf4j
public class AliOssUtil {

    private String endpoint;
    private String accessKeyId;
    private String accessKeySecret;
    private String bucketName;

    /**
     * 文件上传
     *
     * @param bytes
     * @param objectName
     * @return
     */
    public String upload(byte[] bytes, String objectName) {

        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        try {
            // 创建PutObject请求。
            ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(bytes));
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }

        //文件访问路径规则 https://BucketName.Endpoint/ObjectName
        StringBuilder stringBuilder = new StringBuilder("https://");
        stringBuilder
                .append(bucketName)
                .append(".")
                .append(endpoint)
                .append("/")
                .append(objectName);

        log.info("文件上传到:{}", stringBuilder.toString());

        return stringBuilder.toString();
    }
}
public class JwtUtil {
    /**
     * 生成jwt
     * 使用Hs256算法, 私匙使用固定秘钥
     *
     * @param secretKey jwt秘钥
     * @param ttlMillis jwt过期时间(毫秒)
     * @param claims    设置的信息
     * @return
     */
    public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {
        // 指定签名的时候使用的签名算法,也就是header那部分
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        // 生成JWT的时间
        long expMillis = System.currentTimeMillis() + ttlMillis;
        Date exp = new Date(expMillis);

        // 设置jwt的body
        JwtBuilder builder = Jwts.builder()
                // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
                .setClaims(claims)
                // 设置签名使用的签名算法和签名使用的秘钥
                .signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))
                // 设置过期时间
                .setExpiration(exp);

        return builder.compact();
    }

    /**
     * Token解密
     *
     * @param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
     * @param token     加密后的token
     * @return
     */
    public static Claims parseJWT(String secretKey, String token) {
        // 得到DefaultJwtParser
        Claims claims = Jwts.parser()
                // 设置签名的秘钥
                .setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
                // 设置需要解析的jwt
                .parseClaimsJws(token).getBody();
        return claims;
    }

}

2.2 pojo

分为

entity(实体对象)

vo(View Object,视图对象)

dto(Data Transfer Object,数据传输对象)

2.2.1 entity

  • 以 Entity 结尾
  • 数据对象名 与 数据库表名 一致
  • 字段 与 数据库字段 一致

2.2.2 vo

用于 展示层,作用是把 某个指定页面(或组件)的所有数据封装

  • 不可继承自 Entity
  • vo 可以继承、组合其他 DTO、VO、BO 等对象
  • vo 只能用于返回前端

2.2.3 dto

用于从数据库中检索数据

2.3 server

除了 controller、service、mapper 三个层,还有其他一些类

2.3.1 config

配置类

@Configuration
@Slf4j
public class RedisConfiguration {
    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        log.info("开始创建redis模板类");
//        RedisTemplate redisTemplate = new RedisTemplate();
//        redisTemplate.setConnectionFactory(redisConnectionFactory);
//
        redisTemplate.setKeySerializer(new StringRedisSerializer());
//
//        return redisTemplate;


        RedisTemplate redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        // 设置Key的序列化器为String
        redisTemplate.setKeySerializer(new StringRedisSerializer());

        // 设置Value的序列化器为String
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
//        // 设置Hash Key的序列化器为String
//        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//
//        // 设置Hash Value的序列化器为String
//        redisTemplate.setHashValueSerializer(new StringRedisSerializer());
        return redisTemplate;
    }
}

 redis 数据库

web 层配置

/**
 * 配置类,注册web层相关组件
 */
@Configuration
@Slf4j
public class WebMvcConfiguration extends WebMvcConfigurationSupport {

    @Autowired
    private JwtTokenAdminInterceptor jwtTokenAdminInterceptor;

    @Autowired
    private JwtTokenUserInterceptor jwtTokenUserInterceptor;

    /**
     * 注册自定义拦截器
     *
     * @param registry
     */
    protected void addInterceptors(InterceptorRegistry registry) {
        log.info("开始注册自定义拦截器...");
        registry.addInterceptor(jwtTokenAdminInterceptor)
                .addPathPatterns("/admin/**") // 添加拦截路径
                .excludePathPatterns("/admin/employee/login"); // 排除拦截路径(登录不需要拦截)

        registry.addInterceptor(jwtTokenUserInterceptor)
                .addPathPatterns("/user/**")
                .excludePathPatterns("/user/user/login")
                .excludePathPatterns("/user/shop/status");
    }

    /**
     * 通过 knife4j 生成接口文档
     *
     * @return
     */
    @Bean
    public Docket docket1() {
        ApiInfo apiInfo = new ApiInfoBuilder()
                .title("苍穹外卖项目接口文档")
                .version("2.0")
                .description("苍穹外卖项目接口文档")
                .build();
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .groupName("管理端接口")
                .apiInfo(apiInfo)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.sky.controller.admin"))
                .paths(PathSelectors.any())
                .build();
        return docket;
    }

    @Bean
    public Docket docket2() {
        ApiInfo apiInfo = new ApiInfoBuilder()
                .title("苍穹外卖项目接口文档")
                .version("2.0")
                .description("苍穹外卖项目接口文档")
                .build();
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .groupName("用户端接口")
                .apiInfo(apiInfo)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.sky.controller.user"))
                .paths(PathSelectors.any())
                .build();
        return docket;
    }

    /**
     * 设置静态资源映射
     *
     * @param registry
     */
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

    /**
     * 扩展 Spring MVC 的消息转换器
     *
     * @param converters
     */
    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        // 创建一个 消息转换器 对象
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        // 为 消息转换器 设置一个 对象转换器,对象转换器可以将 Java 对象序列化为 Json 数据
        converter.setObjectMapper(new JacksonObjectMapper());
        // 将自己的 消息转换器 加入到 容器 中,放置在第一位
        converters.add(0, converter);
    }
}

2.3.2 handler

        @RestControllerAdvice 可以捕获并处理控制器层抛出的异常,从而统一管理异常处理逻辑,发生异常时,sping自动调用 handler 方法

/**
 * 全局异常处理器,处理项目中抛出的业务异常
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 捕获业务异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler
    public Result exceptionHandler(BaseException ex) {
        log.error("异常信息:{}", ex.getMessage());
        return Result.error(ex.getMessage());
    }

    /**
     * 处理 SQL 异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler
    public Result exceptionHandler(SQLIntegrityConstraintViolationException ex) {
        // Duplicate entry 'ada' for key 'employee.idx_username'
        if (ex.getMessage().contains("Duplicate entry")) {
            String[] s = ex.getMessage().split(" ");
            String username = s[2]; // 拿到用户名
            String msg = username + MessageConstant.ALREADY_EXIST;
            return Result.error(msg);
        } else {
            return Result.error(MessageConstant.UNKNOWN_ERROR);
        }
    }
}

2.3.3 interceptor

拦截器

/**
 * jwt令牌校验的拦截器
 */
@Component
@Slf4j
public class JwtTokenAdminInterceptor implements HandlerInterceptor {

    @Autowired
    private JwtProperties jwtProperties;

    /**
     * 校验jwt
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //判断当前拦截到的是 Controller 的方法还是其他资源
        if (!(handler instanceof HandlerMethod)) {
            //当前拦截到的不是动态方法,直接放行
            return true;
        }

        //1、从请求头中获取令牌
        String token = request.getHeader(jwtProperties.getAdminTokenName());

        //2、校验令牌
        try {
            log.info("jwt 校验:{}", token);
            Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);
            Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());
            log.info("当前员工id:", empId);

            BaseContext.setCurrentId(empId);

            //3、通过,放行
            return true;
        } catch (Exception ex) {
            //4、不通过,响应 401 状态码
            response.setStatus(401);
            return false;
        }
    }
}

2.3.3 启动类

@SpringBootApplication
@EnableTransactionManagement //开启注解方式的事务管理
@Slf4j
@EnableCaching // 开启缓存注解
@EnableScheduling // 开启任务调度
public class SkyApplication {
    public static void main(String[] args) {
        SpringApplication.run(SkyApplication.class, args);
        log.info("server started");
    }
}

2.3.4 配置

服务器端口号

server:
  port: 8080

dev:开发环境

spring:
  profiles:
    active: dev
  main:
    allow-circular-references: true
  datasource:
    druid:
      driver-class-name: ${sky.datasource.driver-class-name}
      url: jdbc:mysql://${sky.datasource.host}:${sky.datasource.port}/${sky.datasource.database}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
      username: ${sky.datasource.username}
      password: ${sky.datasource.password}
  redis:
    host: localhost
    port: 6379
    password: 123456

mybatis 配置

        type-aliases-package: MyBatis 的类型别名包路径。MyBatis 提供了类型别名功能,可以为 Java 类型指定简短的别名,便于在 Mapper XML 文件中使用

开发环境下

数据库连接配置 和 阿里云oss

2.3.5 Controller & Service & Mapper

        Controller 层是应用程序的最上层,负责处理来自客户端的请求(如 HTTP 请求),调用服务层的业务逻辑,并将响应返回给客户端。这个层是 MVC 模式中的控制器部分

常用注解

@RestController

        类注解,是 @Controller 和 @ResponseBody 的组合注解

@RequestMapping

        类注解,Spring MVC 中用于映射 HTTP 请求到处理方法(控制器方法)或类上的重要注解。它定义了一个 URL 路径和 HTTP 方法的映射,使得应用程序可以响应用户发起的不同请求

        指定了默认的路径

@Autowired

        类字段注解,用于 自动注入依赖

@PostMapping / @GetMapping / @PutMapping / @DeleteMapping

        方法注解,是 HTTP 请求方法的映射注解,它们简化了对特定 HTTP 请求(POST、GET、PUT、DELETE)类型的映射,后面加上路径 url

@RequestBody

        将 HTTP 请求体中的数据解析为 Java 对象

@PathVariable

        用于获取 URL 路径中的占位符,并将该占位符的值绑定到控制器方法的参数上

三种不同方式的请求:

  • 请求体:@RequestBody
  • 路径参数:/path/{data} @PathVariable("data") Data data
  • Query 请求:不需要加注解,由Spring自动解析到对象

以两个例子:员工登录 和 新增员工 为例:

员工登录

        在方法前面加上统一的 service 层注入

@Autowired
private EmployeeService employeeService;

        这里是 spring 两大特性(依赖注入(Dependency Injection, DI)和面向切面编程(Aspect-Oriented Programming, AOP))之一的 依赖注入

        依赖注入是 Spring 的核心功能之一。它是一种设计模式,旨在减少类之间的耦合,使得对象不需要手动创建和管理依赖项,而是由 Spring 容器自动注入它们所需要的依赖

       

如果不这样做,

手动创建对象意味着必须在代码中自己决定什么时候以及如何创建对象,这会导致:

  • 耦合性增强:如果 EmployeeService 的实现发生变化(如需要添加依赖),你必须手动修改控制器代码。Spring 的依赖注入允许你解耦对象的创建和使用,使得代码更具灵活性
  • 代码重复:你可能需要在多个地方重复创建 Service 对象。使用依赖注入,可以通过 Spring 容器在整个应用中管理和复用相同的 Service 对象
/**
     * 登录
     *
     * @param employeeLoginDTO
     * @return
     */
    @PostMapping("/login")
    @ApiOperation(value = "员工登录")
    public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO employeeLoginDTO) {
        log.info("员工登录:{}", employeeLoginDTO);

        Employee employee = employeeService.login(employeeLoginDTO);

        //登录成功后,生成 jwt 令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put(JwtClaimsConstant.EMP_ID, employee.getId());
        String token = JwtUtil.createJWT(
                jwtProperties.getAdminSecretKey(),
                jwtProperties.getAdminTtl(),
                claims);

        // 前端页面使用的 视图对象
        // 通过 builder 构建器构造对象,而不是 new
        EmployeeLoginVO employeeLoginVO = EmployeeLoginVO.builder()
                .id(employee.getId())
                .userName(employee.getUsername())
                .name(employee.getName())
                .token(token)
                .build();

        return Result.success(employeeLoginVO);
    }

登录接口的描述:

路径 Path:/login

方法 Method:POST

请求参数格式:JSON

请求参数:封装在一个数据传输对象 DTO 中

新增员工

/**
     * 新增员工
     *
     * @param employeeDTO
     * @return
     */
    @ApiOperation("新增员工")
    @PostMapping("/admin/employee")
    public Result addEmployee(@RequestBody EmployeeDTO employeeDTO) {
        employeeService.addEmployee(employeeDTO);
        return Result.success();
    }

Service 层:

注解 @Service

实现 Service 接口

注入 Mapper

处理业务逻辑,封装的dto少了一些属性,添加属性变成数据库对应的entity才传入到Mapper

/**
     * 新增员工
     *
     * @param employeeDTO
     */
    @Override
    public void addEmployee(EmployeeDTO employeeDTO) {
        // 往下传递到 Mapper 层前,不要传 DTO,传的是实体对象 employee
        // 而且 DTO 相比 employee 少了几个属性,分别是:
        // password status createTime updateTime createUser updateUser
        Employee employee = new Employee();

        // 对象属性拷贝
        BeanUtils.copyProperties(employeeDTO, employee);

        // 设置 密码,默认是 123456,并且使用 MD5 加密,往数据库存的是加密后的密文,这里使用常量类,便于维护
        employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));

        // 设置 状态,默认为 1,这里使用常量类,便于维护
        employee.setStatus(StatusConstant.ENABLE);

        // 设置 创建时间 和 更新时间
        employee.setCreateTime(LocalDateTime.now());
        employee.setUpdateTime(LocalDateTime.now());

        // 设置 创建人 和 修改人
        // 就是当前登录的用户 id
        Long id = BaseContext.getCurrentId();
        employee.setCreateUser(id);
        employee.setUpdateUser(id);

        employeeMapper.addEmployee(employee);
    }

Mapper 层

使用占位符传参(当传参只有一个时,不用指定名字,直接输入字段,否则要#{employee.name})

/**
     * 新增员工
     *
     * @param employee
     */
    @Insert("insert into employee (name, username, password, phone, sex, " +
            "id_number, create_time, update_time, create_user, update_user) " +
            "values (#{name},#{username},#{password},#{phone},#{sex},#{idNumber}," +
            "#{createTime},#{updateTime},#{createUser},#{updateUser})")
    void addEmployee(Employee employee);


http://www.kler.cn/news/309132.html

相关文章:

  • 排序算法-选择排序
  • 深度学习自编码器 - 正则自编码器篇
  • 3.python 爬虫基础HTTP原理2和网页基础
  • Qt/C++ 了解NTFS文件系统,获取首张MFT表数据,解析文件记录头内容找到第一个属性偏移地址
  • 工具类中使用@Value注解引入静态地址
  • uniapp uview扩展u-picker支持日历期间 年期间 月期间 时分期间组件
  • GC垃圾回收机制-Serial GC
  • Uniapp + Vue3 + Vite +Uview + Pinia 分商家实现购物车功能(最新附源码保姆级)
  • Linux下root用户共享conda环境给其他用户
  • 力扣121-买卖股票的最佳时机(Java详细题解)
  • Encountered 31 files that should have been pointers, but weren‘t:(已解决,无废话)
  • System.out源码解读——err 和 out 一起用导致的顺序异常Bug
  • 论文翻译:USENIX-2021 Extracting Training Data from Large Language Models
  • 网络设备登录——《路由与交换技术》实验报告
  • 养宠浮毛严重怎么清理?希喂、范罗士、IAM宠物空气净化器真实测评
  • C++:初始化列表
  • 在线包装盒型生成工具,各种异型包装盒型,PDF导出方便
  • 【蜡笔小新专享】安装虚拟机、PHP、DVWA
  • Linux容器化管理——Docker常见命令总结
  • Apache Pulsar 与 Kafka Streams
  • React实现类似Vue的路由监听Hook
  • 在新电脑上将文件推送到已有的 Git 仓库
  • 【编程基础知识】Java命名规范及最佳实践
  • 孙怡带你深度学习(2)--PyTorch框架认识
  • Unity实战案例全解析:PVZ 植物卡片状态分析
  • 【乐企】基础版接口代码实现
  • Spring Boot校园管理系统:技术选型与架构设计
  • Java | Leetcode Java题解之第405题数字转换为十六进制数
  • TS React 项目中使用TypeScript
  • 【Java】synchronized 基础线程安全