MyBatis 参数映射机制实践记录

 更新时间:2024年12月21日 10:25:56   作者:山高自有客行路  
这篇文章主要介绍了MyBatis 参数映射机制实践记录,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

核心组件

1. SqlSessionFactory

MyBatis 的入口点是 SqlSessionFactory,它负责创建 SqlSession 实例。每个 SqlSession 都代表一个与数据库的会话,并且在该会话中可以执行 SQL 操作。

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
try (SqlSession session = sqlSessionFactory.openSession()) {
    // 使用 session 进行 CRUD 操作
}

2. SqlSession

SqlSession 提供了用于执行语句、提交或回滚事务以及获取映射器实例的方法。它是线程不安全的,因此应该在方法作用域内使用(即每次调用时创建和关闭)。

UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.getUserById(100);

3. Mapper Interface

Mapper 接口定义了与数据库交互的方法,这些方法通常对应于 SQL 语句。MyBatis 通过动态代理的方式实现了这些接口,使得你可以在代码中像调用普通 Java 方法一样调用它们。

public interface UserMapper {
    @Select("SELECT * FROM users WHERE id = #{id}")
    User getUserById(@Param("id") int id);
}

参数处理的具体流程

ParameterHandler 和 TypeHandler

  • ParameterHandler:如前所述,这是 MyBatis 处理 SQL 语句参数的核心组件。它根据 SQL 语句中的占位符设置相应的值。
  • TypeHandler:用于将 Java 类型转换为 JDBC 类型,反之亦然。你可以注册自定义的 TypeHandler 来支持特定的数据类型。

内部工作原理

  • 解析 SQL 语句:当 MyBatis 解析 XML 或注解定义的 SQL 语句时,它会识别出所有的 #{}${} 占位符,并记录下它们的位置。
  • 创建 ParameterHandler:对于每一个需要执行的 SQL 语句,MyBatis 都会创建一个 ParameterHandler 实例。这个实例包含了如何填充 SQL 语句中占位符的信息。
  • 设置参数值ParameterHandler 会遍历所有占位符,并根据传入的参数对象(可以是简单类型、Java Bean、Map 或集合)来设置对应的值。如果参数是一个复杂类型,MyBatis 会递归地访问其属性,直到找到匹配的值。
  • 应用 TypeHandler:对于每一个参数值,MyBatis 会查找并应用适当的 TypeHandler 来确保正确的数据类型转换。
  • 执行 SQL:最后,带有正确参数值的 SQL 语句被发送到数据库进行执行。

参数映射的实现细节

单个参数

对于单个参数的情况,MyBatis 可以直接使用参数名或默认名称(如 param1)。如果你希望提高代码的可读性,建议总是使用 @Param 注解明确指定参数名。

@Select("SELECT * FROM users WHERE id = #{userId}")
User getUserById(@Param("userId") int id);

多个参数

当有多个参数时,使用 @Param 注解是非常重要的,因为它可以让 MyBatis 知道每个参数的名字,从而在 SQL 中通过名字引用它们。在springBoot的1.x版本/单独使用mybatis(使用@Param注解来指定SQL语句中的参数名),因为在编译时,生成的字节码文件当中,不会保留Mapper接口中方法的形参名称,而是使用var1、var2、...这样的形参名字,此时要获取参数值时,就要通过@Param注解来指定SQL语句中的参数名。

@Select("SELECT * FROM orders WHERE user_id = #{userId} AND status = #{status}")
List<Order> findOrdersByUserIdAndStatus(@Param("userId") int userId, @Param("status") String status);

Java Bean 或 Map 参数

对于复杂的参数类型,如 Java Bean 或 Map,MyBatis 会自动解析对象的属性或 Map 的键值对,允许你在 SQL 中通过属性名或键名直接引用这些值。

@Select("SELECT * FROM products WHERE category = #{category} AND price < #{maxPrice}")
List<Product> findProducts(ProductCriteria criteria);

数组和集合参数

数组和集合类型的参数可以通过 <foreach> 标签来处理,这使得你可以遍历集合并在 SQL 中插入多值条件。

<select id="getCategoriesByIds" resultType="Category">
  SELECT * FROM categories
  WHERE id IN
  <foreach item="id" collection="ids" open="(" separator="," close=")">
    #{id}
  </foreach>
</select>

动态 SQL

MyBatis 支持动态 SQL,允许根据运行时条件构造 SQL 语句。这包括使用 <if>, <choose>, <when>, <otherwise>, <trim>, <where>, <set>, 和 <foreach> 标签。

<select id="findActiveBlogWithTitleLike" resultType="Blog">
  SELECT * FROM BLOG
  WHERE state = 'ACTIVE'
  <if test="title != null">
    AND title like #{title}
  </if>
</select>

高级特性

自定义 TypeHandler

有时你需要自定义数据类型之间的转换逻辑。例如,处理枚举类型或自定义日期格式。你可以通过实现 TypeHandler 接口并注册到 MyBatis 中来完成这一需求。

public class EnumTypeHandler<E extends Enum<E>> implements TypeHandler<E> {
    private final Class<E> type;
    public EnumTypeHandler(Class<E> type) {
        if (type == null) throw new IllegalArgumentException("Type argument cannot be null");
        this.type = type;
    }
    @Override
    public void setParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
        if (parameter == null) {
            ps.setNull(i, Types.VARCHAR);
        } else {
            ps.setString(i, parameter.name());
        }
    }
    @Override
    public E getResult(ResultSet rs, String columnName) throws SQLException {
        return rs.getString(columnName) == null ? null : Enum.valueOf(type, rs.getString(columnName));
    }
    @Override
    public E getResult(ResultSet rs, int columnIndex) throws SQLException {
        return rs.getString(columnIndex) == null ? null : Enum.valueOf(type, rs.getString(columnIndex));
    }
    @Override
    public E getResult(CallableStatement cs, int columnIndex) throws SQLException {
        return cs.getString(columnIndex) == null ? null : Enum.valueOf(type, cs.getString(columnIndex));
    }
}

然后在 MyBatis 配置文件中注册这个 TypeHandler

<typeHandlers>
  <typeHandler javaType="com.example.MyEnum" handler="com.example.EnumTypeHandler"/>
</typeHandlers>

插件机制

MyBatis 还提供了插件机制,允许开发者拦截和修改 MyBatis 的内部行为,如拦截 SQL 执行、结果映射等。这对于性能监控、日志记录等功能非常有用。

@Intercepts({@Signature(type= Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
public class ExamplePlugin implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 在这里添加你的逻辑
        return invocation.proceed();
    }
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
    @Override
    public void setProperties(Properties properties) {
        // 设置插件属性
    }
}

性能优化和最佳实践

1. 批量操作

对于大批量的数据操作,考虑使用批量插入、更新或删除,以减少数据库交互次数。可以使用 <foreach> 标签结合批处理功能。

<insert id="batchInsertUsers">
  INSERT INTO users (name, email)
  VALUES
  <foreach collection="list" item="user" separator=",">
    (#{user.name}, #{user.email})
  </foreach>
</insert>

2. 缓存策略

合理配置一级缓存和二级缓存,避免不必要的重复查询。一级缓存是基于 SqlSession 的,而二级缓存可以在多个 SqlSession 之间共享。

<mapper namespace="com.example.UserMapper">
  <cache/>
  <!-- 其他映射语句 -->
</mapper>

3. 减少不必要的参数传递

只传递必要的参数,以减少内存消耗和提高性能。对于复杂对象,尽量只传递需要的字段。

4. 文档化和一致性

保持代码风格一致,并为接口和方法添加适当的文档说明,有助于团队协作和维护。

错误处理与调试

1. SQL 日志输出

开启 SQL 日志输出可以帮助你调试和优化 SQL 语句。MyBatis 支持多种日志框架,如 Log4j、SLF4J 等。

# log4j.properties
log4j.logger.org.apache.ibatis=DEBUG
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

2. 异常处理

确保在捕获异常时提供足够的信息,以便快速定位问题。可以使用 AOP 或者手动捕获异常,并记录详细的错误信息。

try {
    // 数据库操作
} catch (PersistenceException e) {
    logger.error("Database operation failed", e);
    throw e;
}

到此这篇关于MyBatis 参数映射机制的文章就介绍到这了,更多相关MyBatis 参数映射内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java 集合实现分页的方法(业务代码实现分页)

    Java 集合实现分页的方法(业务代码实现分页)

    在Java开发中,有些场景比较复杂,受限制,不好在sql查询层面实现分页,需要在查询的list结果后,将list分页返回,如何实现呢,带着这个问题一起通过本文学习吧
    2025-02-02
  • 关于Mybatis和JDBC的使用及区别

    关于Mybatis和JDBC的使用及区别

    这篇文章主要介绍了关于Mybatis和JDBC的使用及区别,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-05-05
  • 聊聊springboot静态资源加载的规则

    聊聊springboot静态资源加载的规则

    这篇文章主要介绍了springboot静态资源加载的规则,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • Java中的Fork/Join框架使用详解

    Java中的Fork/Join框架使用详解

    这篇文章主要介绍了Java中的Fork/Join框架使用详解,Fork/Join 框架:就是在必要的情况下,将一个大任务,进行<BR>拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个<BR>的小任务运算的结果进行 join 汇总,需要的朋友可以参考下
    2024-01-01
  • java读取文件显示进度条的实现方法

    java读取文件显示进度条的实现方法

    当读取一个大文件时,一时半会儿无法看到读取结果,就需要显示一个进度条,是程序员明白已经读了多少文件,可以估算读取还需要多少时间,下面的代码可以实现这个功能
    2014-01-01
  • Java 使用openoffice进行word转换为pdf的方法步骤

    Java 使用openoffice进行word转换为pdf的方法步骤

    这篇文章主要介绍了Java 使用openoffice进行word转换为pdf的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • Java JDBC数据库连接失败的7种常见原因及解决方案

    Java JDBC数据库连接失败的7种常见原因及解决方案

    Java JDBC(Java Database Connectivity)是Java平台中用于执行SQL语句的标准API,它为开发者提供了与各种关系型数据库进行交互的能力,本文为大家介绍了Java JDBC数据库连接失败的7种常见原因及解决方案,需要的朋友可以参考下
    2025-11-11
  • JAVA基础之数组和集合区别对比分析

    JAVA基础之数组和集合区别对比分析

    文章主要介绍了Java中数组和集合的基本概念、使用方法以及它们之间的区别,文章还探讨了不可变集合的创建方式及其线程安全和不可篡改的优势,感兴趣的朋友跟随小编一起看看吧
    2025-11-11
  • Java中的FutureTask源码解析

    Java中的FutureTask源码解析

    这篇文章主要介绍了Java中的FutureTask源码解析,FutureTask是一个可取消的异步计算,这个类是Future的实现类,有开始和取消一个计算的方法,如果一个计算已经完成可以查看结果,需要的朋友可以参考下
    2023-12-12
  • Java基础第四篇 封装与接口

    Java基础第四篇 封装与接口

    本文将对介绍Java 的封装与接口进行介绍,将要封装(encapsulation)对象的成员(成员包括数据成员和方法),从而只允许从外部调用部分的成员。利用封装,我们可以提高对象的易用性和安全性。想具体了解的小伙伴请参考下面文章的内容
    2021-09-09

最新评论