接口绑定机制

93 阅读3分钟

MyBatis 的 接口绑定机制 是其核心功能之一,允许开发者通过定义 Java 接口直接操作数据库,而无需手动编写实现类。以下是接口绑定的详细解析:

一、接口绑定的核心原理

1. 动态代理生成实现类

  • 机制:MyBatis 在运行时通过 JDK 动态代理,为 Mapper 接口生成代理对象。
  • 触发时机:当调用 SqlSession.getMapper(Class<T> type) 方法时,生成接口的代理实例。
  • 代理逻辑:代理对象拦截接口方法调用,根据方法名和参数,查找对应的 SQL 语句并执行。

2. 接口与 SQL 的绑定规则

  • XML 映射文件

    • namespace 必须与接口的全限定名一致。
    • SQL 标签的 id 必须与接口方法名一致。
    <!-- UserMapper.xml -->
    <mapper namespace="com.example.UserMapper">
        <select id="selectUserById" resultType="User">
            SELECT * FROM user WHERE id = #{id}
        </select>
    </mapper>
    
  • 注解方式: 在接口方法上直接使用 @Select, @Insert 等注解定义 SQL。

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

3. XML 与注解的优先级

  • 默认规则

    • 若同一方法同时存在 XML 和注解配置,XML 优先级高于注解
    • 若仅有一种配置方式,MyBatis 按配置加载。

二、接口方法的参数映射

1. 参数传递规则

  • 单个参数

    • 可直接通过 #{参数名} 引用,参数名任意(如 #{id})。
    User selectUserById(int id);
    
  • 多个参数

    • 需使用 @Param 注解指定参数名,否则按索引(arg0, arg1)或 param1, param2 引用。
    User selectUserByNameAndAge(@Param("name") String name, @Param("age") int age);
    
  • 对象参数

    • 直接通过属性名引用(如 #{user.name})。
    void insertUser(@Param("user") User user);
    

2. 参数类型处理

  • MyBatis 自动处理基本类型、POJO、Map、集合等参数类型。
  • 复杂参数(如集合)可通过 <foreach> 动态生成 SQL。

三、返回结果映射

1. 返回类型配置

  • resultType:指定返回的 Java 类型(如 resultType="User")。

  • resultMap:通过自定义映射规则处理复杂结果集。

    <resultMap id="userMap" type="User">
        <id property="id" column="user_id"/>
        <result property="name" column="user_name"/>
    </resultMap>
    <select id="selectUser" resultMap="userMap">
        SELECT user_id, user_name FROM user
    </select>
    

2. 返回集合类型

  • 接口方法可直接返回 List<T>Map 类型,MyBatis 自动处理。

    List<User> selectAllUsers();
    

3. 返回复杂对象

  • 支持嵌套对象(association)和集合(collection)映射。

    <resultMap id="userWithOrdersMap" type="User">
        <collection property="orders" ofType="Order">
            <id property="orderId" column="order_id"/>
        </collection>
    </resultMap>
    

四、高级特性

1. 注解扩展

  • @Options:设置执行选项(如超时、自增主键)。

    @Options(useGeneratedKeys = true, keyProperty = "id")
    @Insert("INSERT INTO user(name) VALUES(#{name})")
    void insertUser(User user);
    
  • @ResultMap:引用 XML 中定义的 resultMap

    @ResultMap("userMap")
    @Select("SELECT * FROM user")
    List<User> selectAll();
    

2. 动态 SQL 支持

  • 在接口方法中结合 @SelectProvider, @UpdateProvider 等注解,动态生成 SQL。

    @SelectProvider(type = UserSqlBuilder.class, method = "buildSelectByName")
    List<User> selectByName(String name);
    ​
    // SQL 构建类
    class UserSqlBuilder {
        public static String buildSelectByName(String name) {
            return new SQL()
                .SELECT("*")
                .FROM("user")
                .WHERE("name = #{name}")
                .toString();
        }
    }
    

五、错误处理与注意事项

1. 方法未绑定 SQL

  • 启动时报错:若接口方法未在 XML 或注解中定义 SQL,MyBatis 会在初始化时抛出 BindingException

    org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)
    

2. 接口默认方法

  • 支持情况:MyBatis 3.5+ 支持接口默认方法,但默认方法不能绑定 SQL。

    public interface UserMapper {
        default User defaultSelectUser() {
            // 需手动调用其他方法
            return selectUserById(1);
        }
    }
    

六、与 Spring 集成

1. 接口扫描配置

  • 在 Spring 中通过 @MapperScan 注解扫描 Mapper 接口。

    @Configuration
    @MapperScan("com.example.mapper")
    public class MyBatisConfig {}
    

2. 事务管理

  • 结合 Spring 的 @Transactional 注解管理事务。

    @Transactional
    public void updateUser(User user) {
        userMapper.updateUser(user);
    }
    

七、总结

  • 核心机制:动态代理实现接口与 SQL 的绑定,XML 和注解两种配置方式。
  • 参数处理:支持简单类型、对象、集合等参数,通过 @Param 注解解决多参数问题。
  • 结果映射:灵活使用 resultTyperesultMap 处理简单或复杂结果集。
  • 高级功能:注解扩展、动态 SQL、与 Spring 无缝集成。
  • 适用场景:适用于需要精细控制 SQL 且追求代码简洁性的项目。