尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

MyBatis从入门到精通:SQL可控性与动态SQL实战指南

MyBatis从入门到精通:SQL可控性与动态SQL实战指南 1. 从“为什么是MyBatis”开始一个Java开发者的真实选择如果你刚开始接触Java后端开发或者刚从JDBC、Spring JDBC Template这类“原始”的数据访问方式走过来第一次听到MyBatis这个名字可能会有点懵。它不像Hibernate那样自带“全自动ORM”的光环也不像JPA那样是标准规范。那为什么在Spring Data JPA如此流行的今天还有大量项目尤其是国内互联网公司坚定地选择MyBatis或它的增强版MyBatis-Plus呢作为一个从ServletJDBC时代一路走过来的开发者我的体会是MyBatis在SQL可控性、开发效率和性能之间找到了一个非常舒服的平衡点。它不像纯手写JDBC那样繁琐易错也不像全自动ORM那样在复杂查询时让你有种“隔靴搔痒”、想优化却无从下手的无力感。MyBatis的核心思想是“SQL即代码”它把SQL语句从Java代码中彻底解放出来放到XML或注解里但同时又通过动态SQL、结果集映射等特性让你能像操作对象一样方便地处理数据。这篇从0到1的入门指南我不会只给你一堆配置文件和代码片段而是会带你理解每一个配置项背后的意图以及在实际项目中我们是如何一步步搭建、使用并最终信赖这套框架的。2. 环境准备不仅仅是引入一个Jar包很多人以为MyBatis入门就是加个依赖、写个配置但真正的“准备”远不止于此。你需要理解你的技术栈并做出合适的选择。2.1 依赖管理Maven还是Gradle选型背后的考量对于现代Java项目依赖管理工具几乎是标配。这里以最广泛的Maven为例。在pom.xml中你需要引入MyBatis的核心库。dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version3.5.13/version !-- 请使用当时最新稳定版 -- /dependency为什么是这个版本选择版本时我通常会去官方仓库查看最新稳定版非SNAPSHOT并留意其发布日期和更新日志。较新的版本通常修复了更多Bug并可能包含性能优化。但如果是老项目升级则需要仔细阅读版本迁移指南因为MyBatis在主要版本间可能会有不兼容的变更。仅仅有核心库还不够。99%的情况下你的项目都会连接数据库因此你需要数据库驱动。以MySQL 8.x为例dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version scoperuntime/scope !-- 设置为runtime因为编译期不需要 -- /dependency关键细节scope设为runtime。这是因为你的代码在编译时并不直接调用MySQL驱动的类你调用的是MyBatis或JDBC的接口只有在运行时才需要。这样设置能使你的编译类路径更干净。如果你的项目是Spring Boot那么引入mybatis-spring-boot-starter是更省心的选择它会自动处理好MyBatis与Spring的集成、会话管理等一系列繁琐配置。dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.3.1/version /dependency个人经验对于新手入门我反而建议先从非Spring Boot的纯MyBatis开始。这能让你更清晰地看到MyBatis本身是如何工作的理解SqlSessionFactory、SqlSession这些核心生命周期的对象。当你理解了这些再使用Spring Boot Starter你就会明白它帮你自动化了什么出了问题也知道从哪里排查。2.2 核心配置文件mybatis-config.xml的逐行解读在src/main/resources目录下创建mybatis-config.xml这是MyBatis的全局配置文件。很多教程只给一个模板但我想带你读懂每一行的意义。?xml version1.0 encodingUTF-8? !DOCTYPE configuration PUBLIC -//mybatis.org//DTD Config 3.0//EN http://mybatis.org/dtd/mybatis-3-config.dtd configuration !-- 1. 属性配置解耦数据库连接信息 -- properties resourcedb.properties/ !-- 2. 设置影响MyBatis运行时的行为 -- settings !-- 将下划线列名自动映射为驼峰属性名 -- setting namemapUnderscoreToCamelCase valuetrue/ !-- 开启二级缓存默认是关闭的 -- setting namecacheEnabled valuetrue/ !-- 打印SQL语句到控制台调试神器 -- setting namelogImpl valueSTDOUT_LOGGING/ /settings !-- 3. 类型别名为Java类起短名字方便Mapper中引用 -- typeAliases package namecom.example.demo.entity/ /typeAliases !-- 4. 环境配置核心可以配置多个环境开发、测试、生产 -- environments defaultdevelopment environment iddevelopment !-- 事务管理器JDBC表示使用JDBC的事务管理 -- transactionManager typeJDBC/ !-- 数据源POOLED是MyBatis自带的连接池 -- dataSource typePOOLED property namedriver value${db.driver}/ property nameurl value${db.url}/ property nameusername value${db.username}/ property namepassword value${db.password}/ /dataSource /environment /environments !-- 5. 映射器告诉MyBatis你的SQL映射文件在哪里 -- mappers !-- 推荐使用类路径资源引用 -- mapper resourcemapper/UserMapper.xml/ !-- 或者使用包扫描需要Mapper接口和XML文件同名且在同一个包下 -- !-- package namecom.example.demo.mapper/ -- /mappers /configuration逐段解析与避坑指南properties强烈建议将数据库连接信息抽取到单独的db.properties文件中。这样当部署到测试、生产环境时你无需修改代码和XML只需替换属性文件即可。这是配置与代码分离的基本实践。settings这是MyBatis的“调优面板”。mapUnderscoreToCamelCase数据库字段user_name自动映射到Java属性userName。这是我最先开启的设置之一能省去大量resultMap的繁琐配置。但要注意如果数据库设计就是驼峰或者字段名与属性名差异巨大则需要关闭它并使用显式映射。logImpl开发阶段设为STDOUT_LOGGING非常直观。上线前务必改为SLF4J等并与项目的日志框架集成。typeAliases通过包扫描com.example.demo.entity.User类在Mapper XML中可以直接用User引用。小技巧对于常用类如String、IntegerMyBatis内置了别名无需配置。environmentsdefault属性指定默认使用的环境ID。你可以配置多个environment通过代码SqlSessionFactoryBuilder().build(inputStream, “test”)来指定使用哪个。生产环境注意自带的POOLED数据源适用于简单场景对于高并发生产环境通常会用更强大的连接池如HikariCP或Druid来替换它。mappers这是新手最容易出错的地方。mapper resource是基于类路径的。确保你的XML文件在resources目录下对应的包路径里如resources/mapper/UserMapper.xml。如果使用package name扫描则必须保证Mapper接口如UserMapper.java和对应的XML文件UserMapper.xml同名并且在同一个包目录下通常java下的接口和resources下的XML需要建立相同的包结构。对应的db.properties文件db.drivercom.mysql.cj.jdbc.Driver db.urljdbc:mysql://localhost:3306/mybatis_demo?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai db.usernameroot db.passwordyourpassword注意MySQL 8.0的驱动类名和时区参数这是新手常踩的坑。3. 从实体到Mapper建立对象与关系的桥梁配置好了环境我们开始写代码。MyBatis操作的核心是“对象-关系映射”第一步就是定义这个“对象”。3.1 实体类Entity/POJO不只是Getter和Setter创建一个简单的用户实体类User.javapackage com.example.demo.entity; import java.time.LocalDateTime; public class User { private Long id; private String username; private String email; // 数据库字段可能是 user_email靠 mapUnderscoreToCamelCase 映射 private Integer age; private LocalDateTime createTime; // 无参构造器是必须的MyBatis通过反射创建对象 public User() { } // 全参构造器方便测试 public User(Long id, String username, String email, Integer age, LocalDateTime createTime) { this.id id; this.username username; this.email email; this.age age; this.createTime createTime; } // Getter和Setter省略... // toString() 方法对于调试非常有用建议重写 }关键点无参构造器MyBatis通过反射创建对象实例必须有无参构造器。即使不写Java也会默认提供一个但如果你写了有参构造器就必须显式地把无参构造器也写上。属性类型尽量使用Java 8的日期时间APILocalDateTime,LocalDate。在与数据库的datetime、timestamp字段映射时需要确保数据库驱动支持MySQL 8驱动是支持的。早期版本可能需要类型处理器TypeHandler。命名属性名遵循驼峰与开启的mapUnderscoreToCamelCase设置配合。3.2 Mapper接口定义契约不写实现MyBatis的妙处在于你只需要定义一个接口告诉它方法名、参数和返回类型具体的SQL实现写在XML里。创建UserMapper.javapackage com.example.demo.mapper; import com.example.demo.entity.User; import org.apache.ibatis.annotations.Param; import java.util.List; public interface UserMapper { // 根据ID查询用户 User selectById(Param(id) Long id); // 查询所有用户 ListUser selectAll(); // 插入用户返回影响的行数 int insert(User user); // 更新用户信息 int update(User user); // 根据ID删除用户 int deleteById(Param(id) Long id); // 根据用户名模糊查询 ListUser selectByUsernameLike(Param(keyword) String keyword); // 根据多个ID查询用户用于讲解动态SQL ListUser selectByIds(Param(ids) ListLong ids); }接口设计心得方法名清晰表达意图如selectById、insert。MyBatis不强制命名规则但良好的命名是团队协作的基础。参数注解Param当方法有多个参数或者参数是基本类型/String时必须使用Param注解给参数起一个名字。这个名字会用在XML映射文件中作为SQL语句里#{xxx}的占位符。如果只有一个参数且是JavaBean如User则可以省略MyBatis会直接使用其属性名。返回值select操作通常返回实体对象或集合。insert、update、delete返回int类型表示受影响的行数。你也可以配置useGeneratedKeys和keyProperty来让插入操作后数据库生成的主键自动回填到实体对象中。3.3 Mapper XMLSQL的舞台映射的艺术在resources/mapper目录下创建UserMapper.xml这是魔法发生的地方。?xml version1.0 encodingUTF-8? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd !-- namespace 必须对应Mapper接口的全限定名 -- mapper namespacecom.example.demo.mapper.UserMapper !-- 结果映射ResultMap当字段名和属性名无法自动映射时显式定义 -- resultMap idBaseResultMap typeUser id propertyid columnid/ result propertyusername columnusername/ result propertyemail columnemail/ result propertyage columnage/ result propertycreateTime columncreate_time/ /resultMap !-- 可重用的SQL片段 -- sql idBase_Column_List id, username, email, age, create_time /sql !-- 1. 根据ID查询使用 #{id} 防止SQL注入 -- select idselectById resultMapBaseResultMap SELECT include refidBase_Column_List/ FROM user WHERE id #{id} /select !-- 2. 查询所有 -- select idselectAll resultMapBaseResultMap SELECT include refidBase_Column_List/ FROM user /select !-- 3. 插入useGeneratedKeys 和 keyProperty 用于获取自增主键 -- insert idinsert parameterTypeUser useGeneratedKeystrue keyPropertyid INSERT INTO user (username, email, age, create_time) VALUES (#{username}, #{email}, #{age}, #{createTime}) /insert !-- 4. 更新动态SET语句避免更新全部字段 -- update idupdate parameterTypeUser UPDATE user set if testusername ! nullusername #{username},/if if testemail ! nullemail #{email},/if if testage ! nullage #{age},/if !-- create_time 通常不更新 -- /set WHERE id #{id} /update !-- 5. 根据ID删除 -- delete iddeleteById DELETE FROM user WHERE id #{id} /delete !-- 6. 模糊查询使用 CONCAT 函数拼接注意SQL注入风险已由MyBatis处理 -- select idselectByUsernameLike resultMapBaseResultMap SELECT include refidBase_Column_List/ FROM user WHERE username LIKE CONCAT(%, #{keyword}, %) /select !-- 7. 动态SQL根据ID集合查询 -- select idselectByIds resultMapBaseResultMap SELECT include refidBase_Column_List/ FROM user WHERE id IN foreach collectionids itemid open( separator, close) #{id} /foreach /select /mapperXML文件深度解析与避坑namespace这是连接XML与Mapper接口的桥梁必须完全一致。如果写错MyBatis会抛出BindingException告诉你找不到对应的Mapper。resultMap尽管我们开启了自动驼峰映射但显式定义ResultMap是一个好习惯。它更清晰尤其在处理复杂关联查询一对一、一对多时是必须的。id标签用于指定主键有助于提高性能。sql片段将常用的列列表定义成片段可以极大减少重复代码并在修改表结构时只需改动一处。#{ }与${ }的区别重中之重#{id}这是预编译参数PreparedStatement的参数占位符?。MyBatis会对其进行类型处理并防止SQL注入。绝大多数情况下都应该使用#{}。${columnName}这是字符串替换。MyBatis会直接将${}中的内容替换到SQL语句中。存在SQL注入风险仅在动态指定表名、列名等非用户输入的场景下谨慎使用例如ORDER BY ${orderByColumn}。动态SQL这是MyBatis的精华之一。if用于条件判断。test属性里是OGNL表达式可以直接引用参数对象的属性。set用在UPDATE语句中会自动去除末尾多余的逗号比写一堆if再手动处理逗号优雅得多。foreach遍历集合常用于IN查询或批量插入。collection属性对应Mapper接口中Param注解的名字或参数名item是遍历时的每个元素别名。useGeneratedKeys和keyProperty在插入后将数据库自动生成的主键值回填到参数对象User的id属性中。这样执行完insert方法后传入的user对象就拥有了id值。4. 核心API与生命周期理解MyBatis如何工作有了实体、接口和XML我们来看看如何让这一切运转起来。你需要理解几个核心对象。4.1 构建SqlSessionFactory一切的开端SqlSessionFactory是MyBatis的“工厂”负责创建SqlSession。每个数据库对应一个SqlSessionFactory实例且应用生命周期内通常只需一个单例。它通过读取我们之前编写的mybatis-config.xml来构建。package com.example.demo; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; public class MyBatisDemo { public static void main(String[] args) { String resource mybatis-config.xml; InputStream inputStream null; SqlSessionFactory sqlSessionFactory null; try { // 1. 加载配置文件 inputStream Resources.getResourceAsStream(resource); // 2. 构建SqlSessionFactory sqlSessionFactory new SqlSessionFactoryBuilder().build(inputStream); System.out.println(SqlSessionFactory 创建成功); } catch (IOException e) { e.printStackTrace(); } finally { if (inputStream ! null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } // 后续使用 sqlSessionFactory... } }关键点SqlSessionFactoryBuilder用完后就可以丢弃了它的唯一作用就是创建SqlSessionFactory。而SqlSessionFactory一旦创建就应该在应用运行期间一直存在通常将其放入一个静态工具类或通过依赖注入框架如Spring管理。4.2 获取SqlSession与数据库的一次会话SqlSession代表一次数据库会话。它包含了执行SQL、提交/回滚事务、获取Mapper代理对象的所有方法。它不是线程安全的因此每次需要时从SqlSessionFactory获取用完后必须关闭。// 接上面的代码 try (SqlSession session sqlSessionFactory.openSession()) { // 3. 获取Mapper接口的代理对象 UserMapper userMapper session.getMapper(UserMapper.class); // 4. 执行查询 User user userMapper.selectById(1L); System.out.println(查询到的用户 user); // 5. 执行插入 User newUser new User(null, testUser, testexample.com, 25, LocalDateTime.now()); int rows userMapper.insert(newUser); System.out.println(插入影响行数 rows “ 新用户ID” newUser.getId()); // ID已回填 // 6. 提交事务默认是手动提交openSession() 或 openSession(true) 是自动提交 session.commit(); } catch (Exception e) { e.printStackTrace(); // 发生异常回滚事务 // session.rollback(); // 如果try-with-resources自动关闭前发生异常需要在这里回滚 } // try-with-resources 语法会自动调用 session.close()事务管理详解openSession()默认获取一个需要手动提交的会话。你必须显式调用session.commit()否则所有更改不会持久化到数据库。这是最常用的方式因为它允许你将多个操作放在一个事务里。openSession(true)获取一个自动提交的会话。每条语句执行后立即提交。这在简单测试中方便但在生产环境中慎用因为它破坏了事务的原子性。最佳实践始终在try-catch-finally或try-with-resources块中操作SqlSession并在finally中确保session.close()被调用以释放连接资源。在catch块中进行rollback。4.3 Mapper代理接口与XML绑定的魔法session.getMapper(UserMapper.class)这行代码是MyBatis的核心魔法。它返回了一个实现了UserMapper接口的动态代理对象。当你调用userMapper.selectById(1L)时这个代理对象会根据接口全限定名namespace和方法名id找到对应的Mapper XML中的SQL语句。将方法参数转换为SQL语句所需的参数。委托给SqlSession去执行SQL。将结果集按照resultMap或resultType映射成Java对象并返回。所以你永远不需要写UserMapper接口的实现类。这种设计实现了接口与实现的完全解耦让SQL集中在XML中管理Java代码变得非常干净。5. 动态SQL实战应对复杂查询场景基础CRUD只是开始MyBatis真正的威力在于其强大的动态SQL能力它能让你优雅地构建复杂的、条件多变的查询。5.1where,if,choose组合拳假设有一个用户高级搜索功能可以根据用户名模糊、邮箱精确、年龄范围进行筛选且这些条件都是可选的。首先在UserMapper接口中添加方法ListUser searchUsers(Param(username) String username, Param(email) String email, Param(minAge) Integer minAge, Param(maxAge) Integer maxAge);然后在XML中编写动态SQLselect idsearchUsers resultMapBaseResultMap SELECT include refidBase_Column_List/ FROM user where !-- 用户名模糊查询 -- if testusername ! null and username ! AND username LIKE CONCAT(%, #{username}, %) /if !-- 邮箱精确匹配 -- if testemail ! null and email ! AND email #{email} /if !-- 年龄范围查询 -- if testminAge ! null AND age #{minAge} /if if testmaxAge ! null AND age #{maxAge} /if /where ORDER BY id DESC /selectwhere标签的妙用它会自动处理WHERE关键字和开头的AND/OR。如果where标签内的所有if条件都不成立则WHERE关键字不会被添加。如果条件成立它会智能地去掉第一个条件前的AND。这比手动拼接WHERE 11要优雅和安全得多。choose、when、otherwise实现类似switch-case的逻辑。例如根据传入的sortBy参数决定排序方式select idselectAllSorted resultMapBaseResultMap SELECT * FROM user ORDER BY choose when testsortBy name username /when when testsortBy age age /when otherwise id /otherwise /choose /select5.2foreach批量操作批量操作是常见需求foreach标签可以轻松应对。批量插入insert idbatchInsert parameterTypejava.util.List INSERT INTO user (username, email, age, create_time) VALUES foreach collectionlist itemuser separator, (#{user.username}, #{user.email}, #{user.age}, #{user.createTime}) /foreach /insert注意MySQL对单条SQL语句的长度有限制当列表非常大时例如超过1000条直接拼接可能会超出限制。在实际生产中需要对大列表进行分批次插入。批量更新逐条更新MyBatis没有直接的“批量更新”语法但可以通过foreach遍历在Java代码中循环调用单条更新或者使用foreach配合CASE WHEN语句编写一条复杂的SQL数据库支持的话。更常见的做法是使用MyBatis的BatchExecutor。5.3script与注解中的动态SQL除了XML你还可以在Mapper接口的方法上使用Select、Insert等注解直接编写SQL。对于简单的SQL这很便捷但对于复杂的动态SQL注解会显得混乱。此时可以使用script标签包裹在注解字符串中。Update({ script, UPDATE user, set, if testusername ! nullusername#{username},/if, if testemail ! nullemail#{email},/if, /set, WHERE id#{id}, /script }) int updateUserSelective(User user);个人建议简单的、固定的SQL用注解复杂的、动态的SQL用XML。XML在可视化、语法高亮、可维护性上优势明显。6. 高级映射与关联查询处理复杂对象关系现实中的对象很少是孤立的。一个用户有多篇文章一篇文章属于一个用户。MyBatis提供了association一对一和collection一对多来处理这种关联映射。6.1 一对一关联association假设每个用户都有一个关联的UserDetail用户详情对象。创建UserDetail实体和对应的Mapper。在User实体中添加UserDetail detail属性。在UserMapper.xml中扩展ResultMapresultMap idUserWithDetailResultMap typeUser extendsBaseResultMap !-- 继承BaseResultMap的映射 -- !-- 一对一关联 -- association propertydetail javaTypeUserDetail id propertyid columndetail_id/ result propertyuserId columnid/ !-- 与user表关联的id -- result propertyrealName columnreal_name/ result propertyaddress columnaddress/ /association /resultMap select idselectUserWithDetailById resultMapUserWithDetailResultMap SELECT u.*, ud.id as detail_id, ud.real_name, ud.address FROM user u LEFT JOIN user_detail ud ON u.id ud.user_id WHERE u.id #{id} /select关键点extendsBaseResultMap复用基础映射避免重复。association映射单个关联对象。property是User实体中的属性名javaType是该属性的Java类型。列别名在SQL中必须为关联表的列起别名如ud.id as detail_id以避免与主表的列名冲突确保ResultMap能正确映射。6.2 一对多关联collection假设一个用户有多篇Article文章。创建Article实体。在User实体中添加ListArticle articles属性。在XML中配置resultMap idUserWithArticlesResultMap typeUser extendsBaseResultMap !-- 一对多关联 -- collection propertyarticles ofTypeArticle id propertyid columnarticle_id/ result propertytitle columnarticle_title/ result propertycontent columnarticle_content/ result propertyauthorId columnid/ !-- 关联回User的id -- /collection /resultMap select idselectUserWithArticlesById resultMapUserWithArticlesResultMap SELECT u.*, a.id as article_id, a.title as article_title, a.content as article_content FROM user u LEFT JOIN article a ON u.id a.author_id WHERE u.id #{id} /select关键点collection映射集合属性。property是User中的集合属性名ofType是集合中元素的Java类型。N1查询问题上面的SQL使用单条JOIN查询一次性获取所有数据。另一种方式是先在select中查询用户列表然后在collection中通过select属性嵌套另一个查询分步查询。分步查询会导致“N1”问题查1个用户再查N篇文章。MyBatis提供了fetchTypelazy懒加载来缓解但最佳实践是在能接受单次查询数据量不大的情况下优先使用JOIN一次性查询。6.3 分步查询与懒加载对于上面的一对多例子可以用分步查询实现resultMap idUserWithArticlesLazyResultMap typeUser extendsBaseResultMap collection propertyarticles ofTypeArticle selectcom.example.demo.mapper.ArticleMapper.selectByAuthorId columnid fetchTypelazy/ !-- 设置为懒加载 -- /resultMap select idselectUserByIdLazy resultMapUserWithArticlesLazyResultMap SELECT * FROM user WHERE id #{id} /select在ArticleMapper.xml中select idselectByAuthorId resultTypeArticle SELECT * FROM article WHERE author_id #{authorId} /select工作原理当调用selectUserByIdLazy时只执行查询用户的SQL。只有当代码中真正访问user.getArticles()时MyBatis才会触发selectByAuthorId查询去获取文章列表。优缺点优点延迟加载可能减少不必要的数据库查询。缺点著名的“N1”问题。如果先查询10个用户然后循环访问每个用户的文章就会产生1查用户 10查文章 11条查询性能极差。因此在需要获取关联数据的列表查询中应避免使用分步查询而使用JOIN。分步查询更适合于单对象详情页这种明确知道只会触发一次额外查询的场景。7. 整合Spring Boot现代开发的标准姿势如今大部分项目都基于Spring Boot。MyBatis与Spring Boot的整合已经变得极其简单。7.1 快速启动使用Spring Initializr创建项目依赖选择Spring Web,MyBatis Framework,MySQL Driver。在application.yml或application.properties中配置数据库和MyBatis# application.yml spring: datasource: url: jdbc:mysql://localhost:3306/mybatis_demo?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: yourpassword driver-class-name: com.mysql.cj.jdbc.Driver # 推荐使用HikariCPSpring Boot 2.x默认就是它 hikari: maximum-pool-size: 10 minimum-idle: 5 mybatis: # 指定Mapper XML文件的位置 mapper-locations: classpath:mapper/*.xml # 指定实体类别名所在的包 type-aliases-package: com.example.demo.entity configuration: # 开启驼峰命名映射 map-underscore-to-camel-case: true # 配置日志实现使用Spring Boot的日志配置 log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl在你的Mapper接口上添加Mapper注解或者在主启动类上添加MapperScan(“com.example.demo.mapper”)注解让Spring能扫描到这些接口并创建代理对象。package com.example.demo.mapper; import com.example.demo.entity.User; import org.apache.ibatis.annotations.Mapper; import java.util.List; Mapper // 关键注解 public interface UserMapper { ListUser selectAll(); // ... 其他方法 }package com.example.demo; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; SpringBootApplication MapperScan(com.example.demo.mapper) // 扫描指定包下的所有接口 public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }在Service中直接Autowired注入Mapper使用即可。Service public class UserService { Autowired private UserMapper userMapper; public ListUser getAllUsers() { return userMapper.selectAll(); } }7.2 事务管理在Spring Boot中事务管理变得异常简单。只需在Service层的方法上添加Transactional注解。Service public class UserService { Autowired private UserMapper userMapper; Autowired private LogMapper logMapper; Transactional // 添加此注解该方法内的所有数据库操作在一个事务中 public void createUserWithLog(User user, String operation) { userMapper.insert(user); // 模拟一个日志记录 Log log new Log(user.getId(), operation, LocalDateTime.now()); logMapper.insert(log); // 如果这里抛出异常上面的user插入和log插入都会回滚 } }Transactional的工作原理Spring通过AOP代理在方法开始前开启事务方法执行成功后提交事务如果抛出运行时异常RuntimeException或Error则回滚事务。注意默认只对运行时异常回滚检查型异常Exception不会触发回滚。可以通过Transactional(rollbackFor Exception.class)来修改。7.3 常见配置与优化mybatis.configuration在application.yml中mybatis.configuration下的配置项对应于原生mybatis-config.xml中settings里的内容。你可以在这里配置缓存、延迟加载、默认执行器类型等。分页插件MyBatis本身不提供物理分页但有一个非常流行的插件PageHelper。引入依赖后只需在查询前调用PageHelper.startPage(pageNum, pageSize)接下来的第一个select语句就会被自动分页。性能监控整合像Druid这样的连接池它不仅提供高性能的连接管理还自带强大的SQL监控和防火墙功能可以帮你分析慢SQL。从纯MyBatis到Spring Boot整合你会发现大部分核心概念是相通的。Spring Boot帮你自动化了SqlSessionFactory、SqlSession的生命周期管理和依赖注入让你能更专注于业务逻辑和SQL本身。这种从底层到集成的学习路径能让你在遇到问题时更有底气去深入排查。
返回列表