门户网站建设注意问题上海网站制作全包

张小明 2025/12/30 15:08:17
门户网站建设注意问题,上海网站制作全包,请问如何做网站,手机怎么制作微信公众号前情回顾#xff1a; 在 《MyBatis基础入门《十二》批量操作优化》 中#xff0c;我们解决了海量数据写入的性能瓶颈。 但随着项目规模扩大#xff0c;代码冗余、类型转换混乱、DTO/Entity 膨胀等问题日益突出#xff1a;手动编写 getter/setter/toString 占据 50% 代码量在 《MyBatis基础入门《十二》批量操作优化》 中我们解决了海量数据写入的性能瓶颈。但随着项目规模扩大代码冗余、类型转换混乱、DTO/Entity 膨胀等问题日益突出手动编写getter/setter/toString占据 50% 代码量Service 层充斥userDto.setUsername(user.getUsername())数据库实体Entity与接口模型VO/DTO强耦合难以演进。如何让代码回归简洁、安全、可读答案采用Lombok MapStruct MyBatis黄金组合本文将从零构建一个完整工程覆盖✅ Lombok 自动化生成样板代码✅ MapStruct 零反射高性能对象映射✅ MyBatis 与 DTO/Entity 分离的最佳实践✅ 分层架构设计Controller → Service → Mapper✅ 异常处理、日志、校验一体化✅ 单元测试与集成测试策略目标写出像 Spring Data JPA 一样简洁却保留 MyBatis 全部灵活性的代码一、为什么需要 Lombok MapStruct1.1 Java 的“样板代码”之痛传统 Java Beanpublic class User { private Long id; private String username; private String email; private LocalDateTime createTime; // 4个字段 → 20行 getter/setter public Long getId() { return id; } public void setId(Long id) { this.id id; } public String getUsername() { return username; } public void setUsername(String username) { this.username username; } // ... 还有 equals, hashCode, toString, 构造函数... }❌ 代码冗长阅读成本高❌ 修改字段需同步更新多个方法❌ IDE 自动生成仍占用物理行数干扰 Git diff。1.2 对象转换的“手写地狱”Service 层常见代码public UserDetailVO getUserDetail(Long userId) { User user userMapper.selectById(userId); if (user null) throw new NotFoundException(); UserDetailVO vo new UserDetailVO(); vo.setId(user.getId()); vo.setUsername(user.getUsername()); vo.setEmail(user.getEmail()); vo.setCreateTime(user.getCreateTime()); vo.setOrderCount(orderService.countByUserId(userId)); // 额外字段 return vo; }❌ 字段多时赋值代码爆炸❌ 字段名不一致时如create_time→createTime易出错❌ 反射工具如 BeanUtils性能差、类型不安全。1.3 解决方案Lombok MapStruct工具作用优势Lombok编译期自动生成 getter/setter/构造函数等减少 70% 样板代码提升可读性MapStruct编译期生成类型安全的对象映射器性能≈手写零反射支持复杂转换✅ 二者均在编译期处理无运行时依赖无性能损耗二、工程搭建Spring Boot MyBatis Lombok MapStruct2.1 Maven 依赖关键部分dependencies !-- Spring Boot Web -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- MyBatis -- dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version3.0.3/version /dependency !-- MySQL -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency !-- Lombok -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency !-- MapStruct -- dependency groupIdorg.mapstruct/groupId artifactIdmapstruct/artifactId version1.5.5.Final/version /dependency !-- MapStruct Processor (编译期注解处理器) -- dependency groupIdorg.mapstruct/groupId artifactIdmapstruct-processor/artifactId version1.5.5.Final/version scopeprovided/scope /dependency /dependencies 注意mapstruct-processor必须声明否则无法生成实现类Lombok 需在 IDE 中安装插件IntelliJ IDEA 默认支持。2.2 项目结构设计推荐src/main/java └── com.charles.mybatissimple ├── controller │ └── UserController.java # 接收请求返回 VO ├── service │ ├── UserService.java # 业务逻辑 │ └── impl/UserServiceImpl.java ├── mapper │ └── UserMapper.java # MyBatis Mapper操作 Entity ├── entity │ └── User.java # 数据库实体Table 注解可选 ├── dto │ └── UserCreateDTO.java # 接收创建请求 ├── vo │ ├── UserVO.java # 返回给前端的视图对象 │ └── UserDetailVO.java ├── converter │ └── UserConverter.java # MapStruct 映射器 ├── exception │ ├── GlobalExceptionHandler.java # 全局异常处理 │ └── NotFoundException.java └── MyBatisSimpleApplication.java✅ 分层清晰职责单一便于团队协作。三、Lombok 实战告别 getter/setter3.1 Entity 使用 Lombok// entity/User.java package com.charles.mybatissimple.entity; import lombok.Data; import lombok.experimental.Accessors; import java.time.LocalDateTime; Data // 自动生成 getter, setter, toString, equals, hashCode Accessors(chain true) // 支持链式调用new User().setId(1).setUsername(张三) public class User { private Long id; private String username; private String email; private LocalDateTime createTime; } 常用 Lombok 注解Data全能型适合 POJOGetter/Setter按需生成NoArgsConstructor,AllArgsConstructor构造函数Builder建造者模式适合复杂对象创建。3.2 DTO/VO 同样受益// dto/UserCreateDTO.java package com.charles.mybatissimple.dto; import lombok.Data; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Email; Data public class UserCreateDTO { NotBlank(message 用户名不能为空) private String username; Email(message 邮箱格式不正确) private String email; }// vo/UserVO.java package com.charles.mybatissimple.vo; import lombok.Data; import java.time.LocalDateTime; Data public class UserVO { private Long id; private String username; private String email; private LocalDateTime createTime; }✅ 代码量减少 60%专注业务字段定义四、MapStruct 实战安全高效的对象映射4.1 定义映射接口// converter/UserConverter.java package com.charles.mybatissimple.converter; import com.charles.mybatissimple.entity.User; import com.charles.mybatissimple.vo.UserVO; import com.charles.mybatissimple.dto.UserCreateDTO; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; Mapper // 告诉 MapStruct 这是一个映射器 public interface UserConverter { // 单例模式获取实例也可交由 Spring 管理 UserConverter INSTANCE Mappers.getMapper(UserConverter.class); // Entity → VO UserVO toUserVO(User user); // DTO → Entity创建时 Mapping(target createTime, ignore true) // 忽略 createTime由数据库生成 User fromUserCreateDTO(UserCreateDTO dto); } 关键点Mapper标记为 MapStruct 接口方法签名决定映射规则同名字段自动映射Mapping处理字段名不一致或特殊逻辑。4.2 编译后生成的实现类自动生成无需手写// target/generated-sources/annotations/.../UserConverterImpl.java public class UserConverterImpl implements UserConverter { Override public UserVO toUserVO(User user) { if (user null) return null; UserVO vo new UserVO(); vo.setId(user.getId()); vo.setUsername(user.getUsername()); vo.setEmail(user.getEmail()); vo.setCreateTime(user.getCreateTime()); return vo; } Override public User fromUserCreateDTO(UserCreateDTO dto) { if (dto null) return null; User user new User(); user.setUsername(dto.getUsername()); user.setEmail(dto.getEmail()); // createTime 被 ignore未设置 return user; } }✅ 性能 ≈ 手写代码无反射类型安全4.3 复杂场景嵌套对象、集合、自定义方法场景User 包含 ProfileJSON 字段// entity/User.java Data public class User { private Long id; private String username; private UserProfile profile; // TypeHandler 已处理 JSON ↔ Object } // vo/UserDetailVO.java Data public class UserDetailVO { private Long id; private String username; private String avatar; // 来自 profile.avatar private String city; // 来自 profile.city }MapStruct 映射Mapper public interface UserConverter { UserConverter INSTANCE Mappers.getMapper(UserConverter.class); default UserDetailVO toUserDetailVO(User user) { if (user null) return null; UserDetailVO vo new UserDetailVO(); vo.setId(user.getId()); vo.setUsername(user.getUsername()); UserProfile profile user.getProfile(); if (profile ! null) { vo.setAvatar(profile.getAvatar()); vo.setCity(profile.getCity()); } return vo; } } 对于复杂逻辑可使用default方法手动实现MapStruct 不限制五、MyBatis 整合Entity 与 Mapper 设计5.1 Mapper 接口仅操作 Entity// mapper/UserMapper.java Mapper public interface UserMapper { User selectById(Long id); void insert(User user); void update(User user); ListUser selectAll(); }✅原则Mapper 层只与Entity打交道不暴露 DTO/VO5.2 XML 映射配合 TypeHandler!-- mapper/UserMapper.xml -- mapper namespacecom.charles.mybatissimple.mapper.UserMapper resultMap idUserResultMap typeUser id propertyid columnid/ result propertyusername columnusername/ result propertyemail columnemail/ result propertycreateTime columncreate_time/ !-- 若有 JSON 字段此处指定 typeHandler -- !-- result propertyprofile columnprofile typeHandlerJsonTypeHandler/ -- /resultMap select idselectById resultMapUserResultMap SELECT * FROM tbl_user WHERE id #{id} /select insert idinsert useGeneratedKeystrue keyPropertyid INSERT INTO tbl_user (username, email, create_time) VALUES (#{username}, #{email}, NOW()) /insert /mapper六、Service 层业务逻辑 对象转换// service/UserService.java public interface UserService { UserVO createUser(UserCreateDTO dto); UserDetailVO getUserDetail(Long id); } // service/impl/UserServiceImpl.java Service RequiredArgsConstructor // Lombok 自动生成 final 字段构造函数 public class UserServiceImpl implements UserService { private final UserMapper userMapper; private final OrderService orderService; // 假设有其他服务 Override Transactional public UserVO createUser(UserCreateDTO dto) { // 1. DTO → Entity User user UserConverter.INSTANCE.fromUserCreateDTO(dto); // 2. 保存到数据库 userMapper.insert(user); // 3. Entity → VO return UserConverter.INSTANCE.toUserVO(user); } Override public UserDetailVO getUserDetail(Long id) { User user userMapper.selectById(id); if (user null) { throw new NotFoundException(用户不存在); } return UserConverter.INSTANCE.toUserDetailVO(user); } }✅ 代码干净、逻辑清晰无任何手写赋值七、Controller 层参数校验 统一返回// controller/UserController.java RestController RequestMapping(/users) RequiredArgsConstructor public class UserController { private final UserService userService; PostMapping public ResponseEntityUserVO createUser(Valid RequestBody UserCreateDTO dto) { UserVO vo userService.createUser(dto); return ResponseEntity.ok(vo); } GetMapping(/{id}) public ResponseEntityUserDetailVO getUser(PathVariable Long id) { UserDetailVO vo userService.getUserDetail(id); return ResponseEntity.ok(vo); } }✅ 结合Valid实现参数校验异常由全局处理器捕获。八、全局异常处理 统一响应8.1 自定义异常// exception/NotFoundException.java public class NotFoundException extends RuntimeException { public NotFoundException(String message) { super(message); } }8.2 全局异常处理器// exception/GlobalExceptionHandler.java RestControllerAdvice public class GlobalExceptionHandler { ExceptionHandler(NotFoundException.class) public ResponseEntityErrorResponse handleNotFound(NotFoundException e) { ErrorResponse error new ErrorResponse(NOT_FOUND, e.getMessage()); return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error); } ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntityErrorResponse handleValidation(MethodArgumentNotValidException e) { String msg e.getBindingResult().getFieldError().getDefaultMessage(); ErrorResponse error new ErrorResponse(VALIDATION_ERROR, msg); return ResponseEntity.badRequest().body(error); } } // ErrorResponse.java Data AllArgsConstructor public class ErrorResponse { private String code; private String message; }✅ 前端收到统一格式错误信息体验一致。九、单元测试与集成测试9.1 Service 层单元测试Mock MapperExtendWith(MockitoExtension.class) class UserServiceImplTest { Mock private UserMapper userMapper; InjectMocks private UserServiceImpl userService; Test void shouldCreateUser() { // Given UserCreateDTO dto new UserCreateDTO(); dto.setUsername(张三); dto.setEmail(zhangsanexample.com); User savedUser new User(); savedUser.setId(1L); savedUser.setUsername(张三); savedUser.setEmail(zhangsanexample.com); when(userMapper.insert(any(User.class))).thenAnswer(invocation - { User u invocation.getArgument(0); u.setId(1L); // 模拟数据库设 ID return null; }); // When UserVO result userService.createUser(dto); // Then assertThat(result.getId()).isEqualTo(1L); assertThat(result.getUsername()).isEqualTo(张三); verify(userMapper).insert(any(User.class)); } }9.2 集成测试真实数据库SpringBootTest Testcontainers class UserControllerIntegrationTest { Container static MySQLContainer? mysql new MySQLContainer(mysql:8.0) .withDatabaseName(testdb) .withUsername(test) .withPassword(test); DynamicPropertySource static void configureProperties(DynamicPropertyRegistry registry) { registry.add(spring.datasource.url, mysql::getJdbcUrl); registry.add(spring.datasource.username, mysql::getUsername); registry.add(spring.datasource.password, mysql::getPassword); } Autowired private TestRestTemplate restTemplate; Test void shouldCreateUserSuccessfully() { UserCreateDTO dto new UserCreateDTO(); dto.setUsername(李四); dto.setEmail(lisiexample.com); ResponseEntityUserVO response restTemplate.postForEntity( /users, dto, UserVO.class); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(response.getBody().getUsername()).isEqualTo(李四); } }✅ 覆盖单元与集成保障代码质量。十、高级技巧与避坑指南10.1 MapStruct 与 Spring 集成推荐默认Mappers.getMapper()是单例但若需注入其他 Bean如 Converter 中调用 Service可交由 Spring 管理Mapper(componentModel spring) // 生成的实现类带 Component public interface UserConverter { // ... }然后在 Service 中直接注入Service public class UserServiceImpl implements UserService { private final UserConverter userConverter; // Spring 自动注入 }✅ 支持依赖注入更灵活10.2 Lombok 与 Jackson 冲突若使用DataJsonIgnore可能因生成equals导致序列化问题。解决方案使用ToString.Exclude,EqualsAndHashCode.ExcludeData public class User { private String password; ToString.Exclude EqualsAndHashCode.Exclude private String secretKey; }10.3 MyBatis 返回 Map谨慎避免在 Mapper 中返回MapString, Object破坏类型安全。替代方案定义专用 VO 或使用Results映射到对象。10.4 性能对比MapStruct vs BeanUtils工具100 万次转换耗时是否类型安全是否支持复杂逻辑手写代码~80 ms✅✅MapStruct~85 ms✅✅Apache BeanUtils~2,500 ms❌⚠️ 有限Spring BeanUtils~1,800 ms❌⚠️ 有限✅ MapStruct 是性能与安全的最佳平衡十一、总结现代化 MyBatis 开发范式层级技术栈职责EntityLombok MyBatis数据库表映射DTO/VOLombok接收/返回数据模型ConverterMapStructEntity ↔ DTO/VO 安全转换MapperMyBatis数据库 CRUD仅操作 EntityServiceSpring Converter业务逻辑 对象转换ControllerSpring MVC Validation请求处理 参数校验✨核心价值代码极简Lombok 消除样板代码类型安全MapStruct 编译期检查分层清晰Entity 与 VO 解耦演进无忧性能卓越无反射接近手写速度易于测试各层可独立 Mock。本文通过完整工程示例展示了如何用Lombok MapStruct MyBatis构建高可维护、高性能、现代化的 Java 应用。下一篇我们将探索MyBatis 动态表名、多租户 SaaS 架构支持解锁企业级复杂场景 如果你觉得有帮助欢迎点赞、收藏、转发 你在项目中是如何简化对象转换的欢迎评论区交流
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

个人网站备案需要哪些资料论坛网站建设需要多少钱

一、传统服务模式下日用品行业的痛点日用品行业超 60% 中小商家依赖纯人工客服,服务短板制约经营:日用品品类繁杂(清洁用品、家居百货、个人护理等),需求分散且高频,人工客服日均处理上限仅 200 条&#xf…

张小明 2025/12/30 15:46:26 网站建设

南阳专业网站建设用jsp做的网站框架

一、变量的核心作用(结合实例理解)1. 存储数据,复用数据变量最基础的作用是保存数据,让你可以在代码的不同位置重复使用这个数据,而不用反复写相同的值。javascript运行// 没有变量:每次计算都要写固定值&a…

张小明 2025/12/30 12:19:14 网站建设

开发网站商城新乡专业做淘宝网站

第一章:Open-AutoGLM景点门票预约系统概述Open-AutoGLM 是一个基于自动化大语言模型(AutoGLM)技术构建的智能景点门票预约系统,旨在提升游客预约体验与景区管理效率。该系统融合自然语言理解、动态调度算法与实时数据同步机制&…

张小明 2025/12/30 16:44:19 网站建设

幕墙设计培训乡网站建设上海十大策划公司排名

Python金融数据分析革命:Mootdx让通达信数据获取变得如此简单 【免费下载链接】mootdx 通达信数据读取的一个简便使用封装 项目地址: https://gitcode.com/GitHub_Trending/mo/mootdx 你是否曾经为了获取股票数据而四处奔波?是否在量化分析的道路…

张小明 2025/12/30 20:43:25 网站建设

天津网站建设工具泸州市建设工程质量监督站网站

突破传统:3大实战方法让GLM语言模型成为你的AI生产力工具 【免费下载链接】GLM GLM (General Language Model) 项目地址: https://gitcode.com/gh_mirrors/glm2/GLM 你是否曾经遇到过这样的困境:面对海量文本数据却无从下手?想要构建智…

张小明 2025/12/30 14:22:31 网站建设

什么网站开发外贸客户网站建设公司与维护

FileBrowser API终极指南:解锁文件管理自动化的10个高级技巧 【免费下载链接】filebrowser 📂 Web File Browser 项目地址: https://gitcode.com/gh_mirrors/fi/filebrowser 在当今数据驱动的时代,高效的文件管理已成为企业和个人提升…

张小明 2025/12/29 8:41:01 网站建设