行业资讯
MyBatis核心原理与Spring Boot集成实战
1. MyBatis核心价值与基础配置第一次接触MyBatis是在2015年的一个电商后台项目当时团队从纯JDBC切换过来最大的感受就是终于不用再写那些重复的样板代码了。MyBatis最吸引我的地方在于它既保留了SQL的控制权又解决了JDBC的繁琐问题。1.1 为什么选择MyBatis在传统的JDBC编程中我们需要处理大量重复代码Connection conn dataSource.getConnection(); PreparedStatement ps conn.prepareStatement(SELECT * FROM users WHERE id?); ps.setLong(1, userId); ResultSet rs ps.executeQuery(); User user null; if (rs.next()) { user new User(); user.setId(rs.getLong(id)); // 更多字段设置... } // 必须手动关闭所有资源 rs.close(); ps.close(); conn.close();而使用MyBatis后同样的操作简化为User user userMapper.selectById(userId);MyBatis的核心优势在于SQL控制权开发者完全掌控SQL语句自动映射结果集到Java对象的转换自动完成动态SQL灵活的条件拼接能力与Spring无缝集成通过starter快速配置1.2 Spring Boot集成配置现代Java项目通常使用Spring Boot集成MyBatis。以下是关键配置步骤Maven依赖dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version3.0.5/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependencyapplication.yml配置spring: datasource: url: jdbc:mysql://localhost:3306/demo?useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.demo.entity configuration: map-underscore-to-camel-case: true default-statement-timeout: 30 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl启动类配置SpringBootApplication MapperScan(com.example.demo.mapper) public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }提示开发环境建议开启SQL日志(mybatis.configuration.log-impl)生产环境应关闭2. XML映射文件深度解析2.1 Mapper XML基础结构每个Mapper接口对应一个XML文件标准结构如下?xml version1.0 encodingUTF-8? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN https://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.example.demo.mapper.UserMapper !-- SQL定义 -- /mapper关键点namespace必须与Mapper接口全限定名一致SQL语句的id要与接口方法名对应建议XML文件放在resources/mapper目录下2.2 结果映射策略MyBatis提供两种结果映射方式resultType简单映射select idselectById resultTypeUser SELECT id, username, email FROM users WHERE id #{id} /selectresultMap精细映射resultMap idUserResultMap typeUser id propertyid columnid/ result propertyusername columnusername/ result propertyemail columnemail/ !-- 更多字段映射 -- /resultMap select idselectById resultMapUserResultMap SELECT id, username, email FROM users WHERE id #{id} /select实际项目中我建议简单查询使用resultType复杂查询多表关联、字段名不一致等使用resultMap对于日期、枚举等特殊类型可以配合TypeHandler使用2.3 CRUD操作示例插入数据insert idinsert useGeneratedKeystrue keyPropertyid INSERT INTO users (username, email, age) VALUES (#{username}, #{email}, #{age}) /insertuseGeneratedKeys和keyProperty配合使用可以将生成的主键回填到实体对象。更新数据update idupdate UPDATE users SET username#{username}, email#{email}, age#{age} WHERE id#{id} /update删除数据delete iddeleteById DELETE FROM users WHERE id#{id} /delete在实际业务中我更推荐使用逻辑删除update idlogicDelete UPDATE users SET deleted1 WHERE id#{id} /update3. 动态SQL实战技巧3.1 条件判断标签if标签select idsearch resultTypeUser SELECT * FROM users where if testusername ! null and username ! AND username LIKE CONCAT(%, #{username}, %) /if if testemail ! null AND email #{email} /if if testminAge ! null AND age #{minAge} /if /where /selectwhere标签会自动处理条件前的AND/OR如果没有条件则不会生成WHERE关键字。choose/when/otherwiseselect idsearch resultTypeUser SELECT * FROM users where choose when testusername ! null username #{username} /when when testemail ! null email #{email} /when otherwise status ACTIVE /otherwise /choose /where /select3.2 循环与批量操作foreach批量查询select idselectByIds resultTypeUser SELECT * FROM users WHERE id IN foreach collectionids itemid open( separator, close) #{id} /foreach /selectforeach批量插入insert idbatchInsert INSERT INTO users (username, email) VALUES foreach collectionusers itemuser separator, (#{user.username}, #{user.email}) /foreach /insert3.3 SQL片段复用对于重复使用的SQL片段可以使用sql定义include引用sql idBase_Column_List id, username, email, age, status /sql select idselectById resultTypeUser SELECT include refidBase_Column_List/ FROM users WHERE id#{id} /select4. 高级特性与最佳实践4.1 关联关系映射一对一关联resultMap idOrderWithUserMap typeOrder id propertyid columnorder_id/ !-- 其他订单字段 -- association propertyuser javaTypeUser id propertyid columnuser_id/ result propertyusername columnusername/ !-- 其他用户字段 -- /association /resultMap一对多关联resultMap idUserWithOrdersMap typeUser id propertyid columnuser_id/ !-- 其他用户字段 -- collection propertyorders ofTypeOrder id propertyid columnorder_id/ !-- 其他订单字段 -- /collection /resultMap4.2 分页实现方案原生分页select idselectPage resultTypeUser SELECT * FROM users ORDER BY id DESC LIMIT #{size} OFFSET #{offset} /selectPageHelper插件// 在Service层调用 PageHelper.startPage(pageNum, pageSize); ListUser users userMapper.selectAll(); return new PageInfo(users);4.3 缓存机制MyBatis提供两级缓存一级缓存SqlSession级别默认开启二级缓存Mapper命名空间级别需要配置开启!-- 在Mapper XML中开启二级缓存 -- cache/注意生产环境中使用二级缓存要谨慎需要考虑数据一致性问题。我建议在Service层使用Redis等分布式缓存替代。4.4 事务管理MyBatis本身不管理事务通常与Spring事务配合使用Service public class UserService { Transactional public void createUser(User user) { userMapper.insert(user); // 其他操作... } }5. 常见问题与性能优化5.1 参数绑定问题多参数处理// Mapper接口 User selectByUsernameAndEmail(Param(username) String username, Param(email) String email);select idselectByUsernameAndEmail resultTypeUser SELECT * FROM users WHERE username#{username} AND email#{email} /select5.2 SQL注入防范永远不要这样写select idselectByOrder resultTypeUser SELECT * FROM users ORDER BY ${orderBy} /select应该使用白名单校验public String safeOrderBy(String input) { if (username.equals(input)) return username; if (email.equals(input)) return email; return id; }5.3 性能优化建议批量操作使用foreach实现批量插入/更新延迟加载对于大对象关联配置lazyLoadingEnabledtrue合理使用缓存根据业务场景配置缓存策略SQL优化通过EXPLAIN分析慢查询连接池配置使用HikariCP等高性能连接池6. 项目实战用户管理系统6.1 数据库设计CREATE TABLE users ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL, email VARCHAR(100) NOT NULL, age INT, status VARCHAR(20) DEFAULT ACTIVE, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME ); CREATE TABLE orders ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, amount DECIMAL(10,2) NOT NULL, status VARCHAR(20) NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) );6.2 分层架构实现Entity层Data public class User { private Long id; private String username; private String email; private Integer age; private String status; private LocalDateTime createdAt; private LocalDateTime updatedAt; private ListOrder orders; }Mapper层public interface UserMapper { int insert(User user); int update(User user); int deleteById(Long id); User selectById(Long id); ListUser selectAll(); ListUser search(Param(username) String username, Param(email) String email); ListUser selectByIds(Param(ids) ListLong ids); }Service层Service RequiredArgsConstructor public class UserService { private final UserMapper userMapper; Transactional public Long createUser(User user) { user.setCreatedAt(LocalDateTime.now()); userMapper.insert(user); return user.getId(); } public PageInfoUser getUsers(int pageNum, int pageSize) { PageHelper.startPage(pageNum, pageSize); return new PageInfo(userMapper.selectAll()); } }Controller层RestController RequestMapping(/api/users) RequiredArgsConstructor public class UserController { private final UserService userService; PostMapping public ResponseEntityLong createUser(RequestBody User user) { return ResponseEntity.ok(userService.createUser(user)); } GetMapping public ResponseEntityPageInfoUser getUsers( RequestParam(defaultValue 1) int page, RequestParam(defaultValue 10) int size) { return ResponseEntity.ok(userService.getUsers(page, size)); } }7. 扩展与进阶7.1 插件开发自定义插件可以拦截MyBatis的核心操作Intercepts({ Signature(type Executor.class, methodupdate, args{MappedStatement.class, Object.class}) }) public class MyPlugin implements Interceptor { Override public Object intercept(Invocation invocation) throws Throwable { // 前置处理 Object result invocation.proceed(); // 后置处理 return result; } }常见应用场景SQL执行时间监控分页统一处理数据权限过滤敏感数据加解密7.2 多数据源配置Configuration MapperScan(basePackages com.example.mapper.db1, sqlSessionFactoryRef db1SqlSessionFactory) public class Db1Config { Bean ConfigurationProperties(spring.datasource.db1) public DataSource db1DataSource() { return DataSourceBuilder.create().build(); } Bean public SqlSessionFactory db1SqlSessionFactory() throws Exception { SqlSessionFactoryBean factory new SqlSessionFactoryBean(); factory.setDataSource(db1DataSource()); return factory.getObject(); } }7.3 MyBatis与其它技术对比特性JDBCMyBatisJPA/HibernateSQL控制完全控制完全控制有限控制开发效率低中高高学习曲线低中高性能调优灵活灵活受限适合场景简单查询复杂业务系统标准CRUD系统在实际项目中我通常会根据业务特点选择报表类复杂查询MyBatis标准CRUD操作JPA特殊场景混合使用8. 个人经验分享经过多年MyBatis使用我总结了以下实战经验XML管理将SQL集中管理在XML中不要分散在注解里便于DBA审核和优化分页策略对于大数据量分页推荐使用游标分页而非LIMIT OFFSETSELECT * FROM users WHERE id #{lastId} ORDER BY id LIMIT #{size}动态SQL复杂条件查询使用script标签包裹原生SQL提高可读性script SELECT * FROM users where /* 动态条件 */ /where /script枚举处理为枚举类型实现自定义TypeHandler避免在业务代码中转换SQL注入防护建立字段白名单机制所有${}参数必须经过校验批量操作大数据量操作使用ExecutorType.BATCH模式结果映射复杂结果集优先使用resultMap避免自动映射的不确定性缓存使用生产环境慎用二级缓存建议在Service层实现业务缓存SQL优化所有SQL上线前必须经过EXPLAIN分析版本控制将Mapper XML文件纳入代码审查流程一个特别容易踩的坑是当使用if标签判断数字0时MyBatis会认为0是空值。解决方案是if teststatus ! null or status 0 AND status #{status} /if
郑州网站建设
网页设计
企业官网