事务事务特性1、数据库事务ACID 特性是保证多步数据库操作「原子性、一致性、隔离性、持久性」的核心机制原子性一组操作要么全成要么全败 一致性操作前后数据符合业务规则如转账后双方余额总和不变 持久性提交后数据永久保存到数据库 隔离性多事务并发时互不干扰。Mybatis事务默认是「自动提交关闭」状态1、MyBatis 的 SqlSession 底层封装了 JDBC 的 Connection事务默认是「自动提交关闭」状态autoCommitfalse所有通过 SqlSession 执行的增删改操作默认都在一个未提交的事务中必须手动提交才能生效。自动提交状态行为逻辑开启ON每执行一条 SQL 语句数据库会自动将该语句封装为一个独立事务并立即提交 —— 这就是 “语句级生效” 的本质关闭OFF所有后续 SQL 语句都归入同一个未提交的事务直到显式执行COMMIT才提交或ROLLBACK回滚》2、MyBatis 执行 insert/update/delete 后若不提交事务 操作仅停留在数据库的「事务缓冲区」未写入磁盘 数据库连接关闭后事务会自动回滚所有操作全部失效 即使查询当前 SqlSession 能看到未提交的数据隔离级别问题但其他会话 / 连接完全不可见等同于操作白做。// 获取SqlSession默认autoCommitfalse SqlSession session sqlSessionFactory.openSession(); UserMapper mapper session.getMapper(UserMapper.class); ​ // 执行更新操作 mapper.updateUserStatus(1, ACTIVE); ​ // 未调用session.commit()直接关闭会话 session.close(); ​ // 结果数据库中user_id1的状态未更新操作全部回滚3、实际开发中 非 Spring 环境手动控制 SqlSession 的 commit()/rollback() Spring 环境优先使用 Transactional 声明式事务让框架自动管理提交 / 回滚减少手动操作的失误。Service public class UserService { Autowired private UserMapper userMapper; ​ // Spring 事务注解自动管理提交/回滚 Transactional(rollbackFor Exception.class) public void batchUpdate(User user1, User user2) { // 无需手动commit/rollbackSpring自动处理 userMapper.insertUser(user1); userMapper.updateUser(user2); } }数据库客户端默认开启自动提交相比之下 数据库客户端默认开启自动提交是为了「简化交互式操作」—— 用户在客户端执行 SQL 时大多是单条查询 / 修改需要 “写一条生效一条”提升易用性而 上面介绍了JDBC/MyBatis 作为 “程序级操作接口”首要目标是「支持业务事务的原子性」因此默认关闭自动提交优先保证数据一致性。场景自动提交状态事务行为数据库客户端Navicat / 命令行ON默认每条 SQL 自动封装为独立事务执行后立即 COMMIT看似 “语句级生效”JDBC/MyBatis默认OFF默认多条 SQL 归入同一个事务需显式 COMMIT 才提交支持原子性JDBC/MyBatis手动开启ON与客户端行为一致单条 SQL 自动提交失去多语句原子性mapper层1、对于mapper层的理解在springboot中如果使用mybatis框架与数据库打交道的层我们叫mapper层。也就是我们没使用mybatis框架时叫的dao层既然是框架就有框架他的设计规范只要我们遵循mybatis的规范去完成一些工作在某些实现上去自定义我们的逻辑就能满足上层servicecontroller)对于数据库数据的使用。2、mapper层和dao层的叫法传统 JDBC/原生开发我们通常称为 DAO (Data Access Object) 层。这是一个设计模式的概念强调对象化地访问数据。MyBatis 语境虽然本质依然是做数据访问所以叫它 DAO 层在逻辑上没错但 MyBatis 官方和社区更倾向于称之为 Mapper。原因MyBatis 的核心机制是“映射”Mapping。它将 Java 接口方法映射到 SQL 语句将数据库结果集映射到 Java 对象。因此UserMapper比UserDao更能体现其“SQL 映射器”的本质。结论在 MyBatis 项目中叫Mapper 层是行业标准叫 DAO 层大家也能听懂但 Mapper 更专业。SqlSession及Springboot对它的封装SqlSession是mybatis提供的操作数据库的会话对象从而我们在测试某个mapper接口的方法的一般步骤都是1、加载核心配置文件2、获取SqlSessionFactoryBuilder3、获取SqlSessionFactory4、获取SqlSession 从而用SqlSession.getMapper(User.class)来获取UserMapper对象 从而再来进行增删改查//读取MyBatis的核心配置文件 InputStream is Resources.getResourceAsStream(mybatis-config.xml); //创建SqlSessionFactoryBuilder对象 SqlSessionFactoryBuilder sqlSessionFactoryBuilder new SqlSessionFactoryBuilder(); //通过核心配置文件所对应的字节输入流创建工厂类SqlSessionFactory生产SqlSession对象 SqlSessionFactory sqlSessionFactory sqlSessionFactoryBuilder.build(is); //创建SqlSession对象此时通过SqlSession对象所操作的sql都必须手动提交或回滚事务 //SqlSession sqlSession sqlSessionFactory.openSession(); //创建SqlSession对象此时通过SqlSession对象所操作的sql都会自动提交 SqlSession sqlSession sqlSessionFactory.openSession(true); //通过代理模式创建UserMapper接口的代理实现类对象 UserMapper userMapper sqlSession.getMapper(UserMapper.class); //调用UserMapper接口中的方法就可以根据UserMapper的全类名匹配元素文件通过调用的方法名匹配映射文件中的SQL标签并执行标签中的SQL语句 int result userMapper.insertUser(); //sqlSession.commit();可是在上述springbootmybatis中怎么没看到SqlSession啊 在 Spring Boot MyBatis 的整合中SqlSession 依然存在但被 Spring 框架「自动管理」了—— 它不再需要你手动创建 / 关闭而是通过 Spring 的依赖注入和自动配置隐藏在底层逻辑中。Spring Boot 整合 MyBatis 的核心魔法“约定优于配置” “依赖注入” “动态代理”。第一步启动时的“扫描与注册”动作Spring Boot 启动读取MapperScan(com.example.mapper)。内部机制MyBatis-Spring 的MapperScannerConfigurer会扫描指定包下的接口。关键点它不会尝试实例化这些接口因为接口不能实例化而是将这些接口定义注册为 Spring 容器中的BeanDefinition。此时Bean 的具体类是MapperFactoryBean。第二步运行时的“动态代理” (最关键的一步)动作当你的 Service 层需要UserMapper时向 Spring 容器请求这个 Bean。内部机制Spring 调用MapperFactoryBean.getObject()。MyBatis 使用 JDK 动态代理或 CGLIB为UserMapper接口生成一个代理对象MapperProxy。这个代理对象被放入 Spring 容器等待注入。结果你的Autowired UserMapper userMapper拿到的就是这个代理对象而不是真正的实现类因为本来就没有实现类。第三步调用时的“借还会话”动作你调用userMapper.selectById(1)。内部机制请求被拦截器MapperProxy.invoke()捕获。获取 SqlSession代理对象不会 new 一个 Session而是向 Spring 的 SqlSessionTemplate 请求。如果有事务(Transactional)SqlSessionTemplate会从当前线程绑定的事务上下文中获取已经存在的SqlSession保证同一个事务内多次数据库操作共用一个连接。如果没有事务它会临时打开一个新的SqlSession。执行 SQL通过SqlSession找到对应的 XML 或注解 SQL执行查询映射结果。归还/关闭如果有事务什么都不做等事务提交/回滚时统一由事务管理器关闭。如果无事务立即关闭SqlSession释放数据库连接。Spring Boot MyBatis案例接下来我们会以对一张user表进行操作的例子为例介绍SpringBootMybatis的步骤。一、先创建 user 表以 MySQL 为例CREATE TABLE user ( id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 主键ID, username VARCHAR(50) NOT NULL COMMENT 用户名, age INT(11) DEFAULT NULL COMMENT 年龄, email VARCHAR(100) DEFAULT NULL COMMENT 邮箱, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; ​ -- 插入测试数据 INSERT INTO user (username, age, email) VALUES (zhangsan, 20, zhangsantest.com), (lisi, 25, lisitest.com);二、Spring Boot 集成 MyBatis 完整步骤步骤 1创建 Spring Boot 项目并引入依赖略步骤 2yml配置配置数据库连接替代原生 MyBatis 的 mybatis-config.xml 环境配置Spring Boot 中无需编写 mybatis-config.xml 直接在 application.yml或 application.properties中配置数据库和 MyBatis 核心参数# 服务器配置可选默认8080 server: port: 8080 ​ # 数据库连接配置Spring Boot 4.0 兼容MySQL 8.x spring: datasource: url: jdbc:mysql://localhost:3306/test_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue username: root # 替换为你的MySQL用户名 password: 123456 # 替换为你的MySQL密码 driver-class-name: com.mysql.cj.jdbc.Driver # Spring Boot 4.0 默认使用HikariCP连接池无需额外配置 ​ # MyBatis 核心配置适配4.0 mybatis: # 映射文件位置XML方式SQL存放路径 mapper-locations: classpath:mapper/*.xml # 实体类别名包简化XML中全类名 type-aliases-package: org.example.mybatisdemo.entity # MyBatis 配置项4.0 兼容 configuration: map-underscore-to-camel-case: true # 驼峰命名转换 cache-enabled: false # 关闭二级缓存默认 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印SQL日志调试用步骤 3创建实体类1、对应 user 表创建 User 实体类可以使用Alt Insert来自动生成所有属性的 GettergetXxx()和 SettersetXxx()构造方法生成 toString () 方法亦可以使用 Lombok 一键替代所有方法生成推荐 手动生成 Getter/Setter 等方法会让实体类代码臃肿Spring Boot 项目中更推荐用 Lombok 插件通过注解一键生成所有方法无需手动操作。1引入lombok依赖2安装lombok插件2、注意MyBatis 兼容性必须保证实体类有无参构造NoArgsConstructor或手动生成否则 MyBatis 反射创建对象时会报错Lombok 注解在编译期生效运行时与手动生成的方法完全一致不影响 MyBatis 映射。注解作用替代手动生成的方法Data综合注解包含 Getter/Setter/toString/equals/hashCodeGetter Setter toString 等NoArgsConstructor生成无参构造无参构造方法AllArgsConstructor生成全参构造全参构造方法Getter仅生成 Getter按需Getter 方法Setter仅生成 Setter按需Setter 方法ToString仅生成 toString按需toString 方法下面给出使用了注解之后的实体类的形态// 核心注解自动生成Getter/Setter/toString/equals/hashCode Data // 生成无参构造MyBatis 必须 NoArgsConstructor // 生成全参构造 AllArgsConstructor public class User { private Long id; private String username; private Integer age; private String email; }步骤 4创建 Mapper 接口Spring Boot 中 Mapper 接口只需添加 Mapper 注解或全局扫描无需手动实现类package org.example.mybatisdemo.mapper; ​ import org.example.mybatisdemo.entity.User; import org.apache.ibatis.annotations.*; ​ import java.util.List; ​ /** * UserMapper接口MyBatis 核心 * 无需加Mapper注解启动类已用MapperScan扫描 */ public interface UserMapper { ​ // 注解式SQL根据ID查询 Select(SELECT * FROM user WHERE id #{id}) User getUserById(Long id); ​ // 注解式SQL查询所有 Select(SELECT * FROM user) ListUser listAllUsers(); ​ Insert(INSERT INTO user (username, age, email) VALUES (#{username}, #{age}, #{email})) Options(useGeneratedKeys true, keyProperty id) // 回填自增ID int addUser(User user); ​ // 注解式SQL修改 Update(UPDATE user SET username #{username}, age #{age}, email #{email} WHERE id #{id}) int updateUser(User user); ​ // 注解式SQL删除 Delete(DELETE FROM user WHERE id #{id}) int deleteUser(Long id); ​ // XML式SQL根据年龄查询复杂SQL推荐XML ListUser listUsersByAge(Integer age); }注解式Sql和XML映射式Sql早期 MyBatis(iBatis 时代)只有 XML 模式。这是为了彻底将 SQL 从 Java 代码中剥离让 DBA数据库管理员可以独立优化 SQL 而不触碰 Java 代码。MyBatis 3 引入注解随着敏捷开发和简单微服务的流行很多场景只需要简单的单表查询。每次都去写一个 XML 文件太麻烦了。于是引入了注解让简单场景变得极度轻量。现状小型项目/简单模块倾向于全注解甚至配合MyBatis-Plus它连注解都不用写直接调用方法。大型项目/核心业务倾向于XML 为主注解为辅。因为核心业务往往涉及复杂的联表查询和动态条件XML 的可维护性和 SQL 调优便利性是注解无法比拟的。步骤 5可选创建 XML 映射文件复杂 SQL 场景若需编写复杂 SQL联表、分页、动态 SQL创建 XML 映射文件 UserMapper.xml放在 resources/mapper/ 目录下与 application.yml 中 mapper-locations 配置一致在 src/main/resources/mapper 下创建?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 namespaceorg.example.mybatisdemo.mapper.UserMapper !-- id 接口方法名resultType 实体类已配置别名包直接写User -- select idlistUsersByAge parameterTypeInteger resultTypeUser SELECT * FROM user WHERE age #{age} /select ​ !-- 示例动态SQLSpring Boot 4.0 兼容 -- select idlistUsersByCondition parameterTypeUser resultTypeUser SELECT * FROM user where if testusername ! null and username ! AND username LIKE CONCAT(%, #{username}, %) /if if testage ! null AND age #{age} /if /where /select /mapper步骤 6创建 Service 层需手动管理 SqlSession 的创建 / 提交 / 回滚Transactional 注解自动处理事务。package org.example.mybatisdemo.service; ​ import org.example.mybatisdemo.entity.User; ​ import java.util.List; ​ public interface UserService { User getUserById(Long id); ListUser listAllUsers(); int addUser(User user); int updateUser(User user); int deleteUser(Long id); ListUser listUsersByAge(Integer age); }实现类UserServiceImpl.java/** * Service实现类Spring Boot 4.0 事务注解兼容 */ Service public class UserServiceImpl implements UserService { ​ Autowired private UserMapper userMapper; ​ Override public User getUserById(Long id) { return userMapper.getUserById(id); } ​ Override public ListUser listAllUsers() { return userMapper.listAllUsers(); } ​ // 事务注解Spring Boot 4.0 支持rollbackFor Exception.class Override Transactional(rollbackFor Exception.class) public int addUser(User user) { return userMapper.addUser(user); } ​ Override Transactional(rollbackFor Exception.class) public int updateUser(User user) { return userMapper.updateUser(user); } ​ Override Transactional(rollbackFor Exception.class) public int deleteUser(Long id) { return userMapper.deleteUser(id); } ​ Override public ListUser listUsersByAge(Integer age) { return userMapper.listUsersByAge(age); } }步骤 7创建 Controller 层接口测试可选步骤 8启动类核心入口package org.example.mybatisdemo; ​ import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; ​ /** * Spring Boot 4.0 启动类 * 注意MapperScan 扫描Mapper接口包替代每个Mapper的Mapper注解 */ SpringBootApplication MapperScan(org.example.mybatisdemo.mapper) // 扫描Mapper接口包 public class MybatisDemoApplication { public static void main(String[] args) { SpringApplication.run(MybatisDemoApplication.class, args); } }总结1、本文介绍了数据库事务ACID 特性原子性一致性持久性隔离性的定义并指出MyBatis 的 SqlSession 底层封装了 JDBC 的 Connection事务默认是「自动提交关闭」状态。建议在springboot项目中使用 Transactional 声明式事务让框架自动管理提交 / 回滚减少手动操作的失误。又对比指出了数据库客户端事务默认是「自动提交开启」状态。2、分享了对mapper层的理解以及mapper层和dao层两种叫法的原因。3、介绍了SqlSession这一mybatis提供的操作数据库的会话对象指出我们在操作某个mapper接口的一般操作时 读取MyBatis的核心配置文件获取SqlSessionFactoryBuilder 获取SqlSessionFactory 获取SqlSession 从而用SqlSession.getMapper(User.class)来获取UserMapper对象 从而再来进行增删改查重点解释了为什么在spring中看不到这个SqlSession对象的原因被框架封装管理了。4、介绍了SpingbootMYBatis的实践创建user表创建springboot项目引入依赖在yml文件中配置数据库连接信息mybatis相关配置创建实体类创建mapper接口可选的创建XML 映射文件复杂 SQL 场景创建ServiceController等上层逻辑在启动类上使用MapperScan接口以扫描Mapper接口包5、在创建User类实体类逻辑中指出可以使用Alt Insert来自动生成所有属性的 GettergetXxx()和 SettersetXxx()构造方法生成 toString () 方法又提出可以使用Lombok插件键替代所有方法生成并介绍了Data,NoArfConstructor,AllArgsConstructor,Getter,Setter,ToString等注解的作用并给出了给出使用了注解之后的实体类的形态6、在创建mapper逻辑中又介绍了创建mapper接口只需添加 Mapper 注解或全局扫描无需手动实现类且我们可以使用注解式SQL用Select, Insert, Update,Delete);或少数情况下在resources/mapper/ 目录下创建 XML 映射文件 UserMapper.xml进行映射式sql的书写原生框架的逻辑这便是XML 映射式 SQL(XML-based Mapping) 。进而又再横向对比了这两种模式且目前的应用场景。