Java-03 深入浅出 MyBatis 增删改查与映射配置详解

TL;DR

  • 场景:Java开发者学习MyBatis框架进行数据库操作
  • 结论:MyBatis通过XML映射和注解两种方式实现持久层操作,INSERT/UPDATE/DELETE均需显式提交事务
  • 产出:完整的增删改查代码示例、Mapper XML配置规范、事务与数据源配置说明

版本矩阵

功能 版本/配置 说明
MyBatis版本 3.5.16 文档发布日期2024年4月2日
参数占位符 #{} MyBatis原生语法
事务管理 JDBC / MANAGED JDBC需手动commit,MANAGED由容器管理
数据源类型 UNPOOLED / POOLED / JNDI POOLED为生产环境推荐
Mapper加载 resource / url / class / package 四种方式灵活配置
插入操作API sqlSession.insert() 需配合sqlSession.commit()
更新操作API sqlSession.update() 需配合sqlSession.commit()
删除操作API sqlSession.delete() 需配合sqlSession.commit()

快速入门

续接上节,完成了基本的 MyBatis 的配置,在不依赖 Spring 的情况下。 这里开始对增删改的内容进行补充。

新增数据

xml 复制代码
<insert id="insert" parameterType="icu.wzk.model.UserInfo">
    INSERT INTO
        user_info
    VALUES(0, #{username}, #{password}, #{age})
</insert>

添加结果如下图所示: 编写新增代码如下所示:

java 复制代码
public class WzkIcu02 {
    public static void main(String[] args) throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
                .build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        UserInfo userInfo = UserInfo
                .builder()
                .username("wzk2")
                .password("icu2")
                .age(25)
                .build();
        sqlSession.insert("icu.wzk.mapper.UserInfoMapper.insert", userInfo);
        sqlSession.commit();
        sqlSession.close();
    }
}

编写代码如下所示: 执行结果部分日志如下:

shell 复制代码
24/11/11 15:16:49 DEBUG UserInfoMapper.insert: ==>  Preparing: INSERT INTO user_info VALUES(0, ?, ?, ?)
24/11/11 15:16:49 DEBUG UserInfoMapper.insert: ==> Parameters: wzk2(String), icu2(String), 25(Integer)
24/11/11 15:16:49 DEBUG UserInfoMapper.insert: <==    Updates: 1
24/11/11 15:16:49 DEBUG jdbc.JdbcTransaction: Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@475e586c]

控制台输出结果如下所示: 注意的问题:

  • 插入语句使用 INSERT 标签
  • 在映射文件中使用 parameterType 属性指定要插入的数据类型
  • SQL 语句使用 #{} 来引入实体中的属性值
  • 插入操作使用的 API 是 sqlSession.insert("命名空间.id",实体对象)
  • 插入操作涉及到数据库数据变化,所以要使用 sqlSession 对象显示的提交事务,即 sqlSession.commit()

更新数据

xml 复制代码
<update id="update" parameterType="icu.wzk.model.UserInfo">
    UPDATE
        user_info
    SET
        username=#{username}, password=#{password}, age=#{age}
    WHERE
        id=#{id}
</update>

编写的情况下如下所示:

java 复制代码
public class WzkIcu03 {
    public static void main(String[] args) throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
                .build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        UserInfo userInfo = UserInfo
                .builder()
                .id(2L)
                .username("wzk2-update")
                .password("icu2-update")
                .age(25)
                .build();
        sqlSession.update("icu.wzk.mapper.UserInfoMapper.update", userInfo);
        sqlSession.commit();
        sqlSession.close();
    }
}

编写代码如下所示: 运行之后控制台输出部分日志如下:

shell 复制代码
24/11/11 15:24:29 DEBUG UserInfoMapper.update: <==    Updates: 1
24/11/11 15:24:29 DEBUG jdbc.JdbcTransaction: Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@475e586c]

对应的截图如下所示:

注意的问题

  • 修改语句需要使用 update 标签
  • 操作的是 sqlSession.update("命名空间.id",实体对象)

删除数据

xml 复制代码
<delete id="delete" parameterType="icu.wzk.model.UserInfo">
  DELETE FROM
      user_info
  WHERE
      id=#{id}
</delete>

对应的 mapper 截图如下所示: 编写代码:

java 复制代码
public class WzkIcu04 {
    public static void main(String[] args) throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
                .build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        UserInfo userInfo = UserInfo
                .builder()
                .id(2L)
                .build();
        sqlSession.update("icu.wzk.mapper.UserInfoMapper.delete", userInfo);
        sqlSession.commit();
        sqlSession.close();
    }
}

执行结果如下所示: 删除的时候需要注意:

  • 删除使用 delete
  • 操作的是 sqlSession.delete("命名空间.id",实体对象)

映射概述

核心概念

MyBatis 的映射主要由 XML 映射文件 和 注解方式 实现,将数据库的表结构映射到 Java 对象上。MyBatis 映射通过 XML 和注解方式提供了灵活的 SQL 控制能力,适合需要手动编写 SQL 的场景。XML 映射文件适合复杂逻辑,而注解方式更适合简单场景。掌握 MyBatis 的映射能力,可以更高效地完成数据持久化操作,同时保持代码的可维护性和可读性。

XML 映射文件

XML 是 MyBatis 映射的核心组件,通常以 .xml 文件形式存在,与映射的 Java 类对应。它包含了 SQL 语句、映射关系以及其他配置。

注解方式

通过 Java 注解的形式,将 SQL 直接嵌入到 Mapper 接口中,适合简单的 CRUD 操作。

核心配置

重要标签

  • mapper:顶级标签,声明该文件为 Mapper 文件,namespace 对应 Java 接口的全限定名。
  • resultMap:自定义对象与数据库字段的映射关系,支持复杂映射。
  • select:定义查询语句,可通过 resultType 或 resultMap 映射结果。
  • insert:定义插入语句,使用 #{} 占位符传递参数。
  • update:定义更新语句。
  • delete:定义删除语句。

常用配置

enviroment

数据库环境配置,支持对环境配置 其中,事务管理器(transactionManager)类型有两种:

  • JDBC :JDBC 事务管理方式直接使用了 Java 数据库连接(JDBC)的事务控制机制。它通过从数据源获取的 Connection 对象来管理事务作用域,开发者需要显式调用 connection.commit() 提交事务或 connection.rollback() 回滚事务。这种模式适用于简单的 JDBC 应用场景,需要开发人员自行处理事务边界。例如:

    java 复制代码
    Connection conn = dataSource.getConnection();
    try {
        conn.setAutoCommit(false); // 开启事务
        // 执行SQL操作
        conn.commit(); // 提交事务
    } catch (SQLException e) {
        conn.rollback(); // 发生异常时回滚
    } finally {
        conn.close();
    }
  • MANAGED :MANAGED 事务管理方式将事务管理完全委托给容器(如 J2EE 应用服务器)处理。它不会主动提交或回滚事务,而是由容器根据上下文环境自动管理事务的生命周期。这种模式适用于 JEE 应用服务器环境,如 WebLogic、WebSphere 等。默认情况下,MANAGED 会在事务结束时关闭连接,但某些容器可能不希望这样,可以通过设置 closeConnection 属性为 false 来禁用默认的关闭行为。配置示例:

xml 复制代码
  <transactionManager type="MANAGED">
      <property name="closeConnection" value="false"/>
  </transactionManager>

这种模式通常用于需要容器提供分布式事务支持(XA)的场景,或者当应用运行在具有完整JTA支持的EJB容器中时。

其中,数据源有三种不同的配置方式,每种方式适用于不同的应用场景:

  1. UNPOOLED(非池化数据源):

    • 工作原理:每次数据库请求时都会创建一个新的连接,使用完毕后立即关闭
    • 特点:简单直接,没有连接复用
    • 适用场景:适合小型应用或测试环境
    • 示例:在开发测试阶段使用UNPOOLED可以快速验证功能
    • 缺点:频繁创建和销毁连接会导致性能问题
  2. POOLED(池化数据源):

    • 工作原理:维护一个连接池,预先创建多个连接并缓存起来
    • 特点:连接可以被复用,避免了频繁创建和销毁的开销
    • 适用场景:适合生产环境中的高并发应用
    • 实现细节:
      • 最小连接数:池中保持的最小连接数
      • 最大连接数:池中允许的最大连接数
      • 连接超时:获取连接的最大等待时间
    • 优点:显著提高性能,减少系统资源消耗
  3. JNDI(Java命名和目录接口)数据源:

    • 工作原理:通过JNDI服务查找和使用容器管理的数据源
    • 特点:由应用服务器统一管理连接
    • 适用场景:适合EJB或应用服务器环境
    • 典型配置:
      • 在Tomcat的context.xml中配置数据源
      • 在WebLogic等应用服务器中配置
    • 优点:
      • 可以实现连接资源的集中管理
      • 便于应用间的资源共享
      • 支持分布式事务
    • 使用方式:通过InitialContext.lookup()方法获取数据源

每种数据源类型都有其特定的配置参数,开发者需要根据应用的实际需求和运行环境选择合适的数据源类型。在性能要求高的生产环境中,通常推荐使用POOLED或JNDI数据源。

mapper

作用是加载映射,加载方式有如下几种: 使用相对于类路径的资源应用:

xml 复制代码
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>

使用完全限定资源定位符(URL),例如:

xml 复制代码
<mapper url="file:///var/mappers/AuthorMapper.xml"/>

使用映射接口实现类的完全限定类名,例如:

xml 复制代码
<mapper class="org.mybatis.builder.AuthorMapper"/>

将包内的映射接口实现全部注册为映射器,例如:

xml 复制代码
<package name="org.mybatis.builder"/>

核心框架速查

框架 核心要点
增删改共同点 均需sqlSession.commit()显式提交事务
数据源选择 生产环境用POOLED,开发测试用UNPOOLED
事务管理 JDBC需手动commit,MANAGED由容器管理
mapper加载方式 resource/url/class/package四种方式
参数占位符 #{}是MyBatis原生占位符语法

错误速查卡

症状 根因 定位 修复
插入数据后查询不到 未调用sqlSession.commit() 检查代码是否遗漏commit调用 插入后添加sqlSession.commit()
更新/删除操作无效 同样未commit或使用了错误API 检查是否调用了正确的insert/update/delete API 使用sqlSession.update()/delete()并配合commit
连接未关闭导致池耗尽 使用UNPOOLED且未关闭连接 观察连接池状态 使用try-finally确保sqlSession.close()
namespace.id找不到 Mapper XML的namespace与Java调用不匹配 检查命名空间路径是否一致 确保sqlSession.insert("全限定命名空间.id", obj)
参数绑定失败 #{}占位符名称与实体属性名不一致 检查SQL中#{username}与UserInfo.username是否匹配 确保属性名一致或使用resultMap手动映射

作者:武子康的个人博客

相关推荐
图像僧5 小时前
vs2019中的属性页使用说明
java·开发语言·jvm
百度Geek说5 小时前
网盘存量代码迁移实战:我们如何用三层架构管住 AI 的输出
后端
静心观复5 小时前
.puml文件是什么,怎么用
java
YOU OU5 小时前
SpringBoot 日志
java·开发语言
江南十四行5 小时前
并发编程(二)
java·开发语言
qingfeng154155 小时前
企业微信 API 自动化开发指南:从消息回调到智能运营实战
java·开发语言·python·自动化·企业微信
jonyleek6 小时前
性能就是生命线?规则引擎如何支撑实时决策
java·开发语言·数据库
ZFSS6 小时前
Midjourney Shorten API 的集成与使用
java·前端·数据库·人工智能·ai·midjourney·ai编程