MyBatis学习知识点大全(二)

简介: 教程来源 https://app-abdss1rim1oh.appmiaoda.com 系统讲解MyBatis核心映射机制与高级特性:涵盖基础/关联/集合/鉴别器四种结果映射方式;注解开发(@Select、@Results、动态SQL等);以及缓存(一二级)、延迟加载、插件机制和批量操作等实战要点,助力高效数据持久层开发。

四、结果映射

4.1 基础映射

<!-- 简单映射 -->
<resultMap id="simpleMap" type="User">
    <id property="id" column="id"/>
    <result property="username" column="username"/>
    <result property="age" column="age"/>
</resultMap>

<!-- 类型处理器指定 -->
<resultMap id="typedMap" type="User">
    <id property="id" column="id" jdbcType="BIGINT"/>
    <result property="username" column="username" jdbcType="VARCHAR"/>
    <result property="createTime" column="create_time" 
            jdbcType="TIMESTAMP" javaType="java.util.Date"/>
</resultMap>

4.2 关联映射(一对一)

java

// 实体类
public class User {
    private Long id;
    private String username;
    private UserDetail detail;  // 一对一关联
    // getter/setter
}

public class UserDetail {
    private Long id;
    private Long userId;
    private String address;
    private String phone;
    // getter/setter
}
xml

<!-- 嵌套查询(延迟加载) -->
<resultMap id="userWithDetail" type="User">
    <id property="id" column="id"/>
    <result property="username" column="username"/>
    <association property="detail" column="id" 
                 select="com.example.mapper.UserDetailMapper.selectByUserId"/>
</resultMap>

<!-- 嵌套结果 -->
<resultMap id="userWithDetailResult" type="User">
    <id property="id" column="id"/>
    <result property="username" column="username"/>
    <association property="detail" javaType="UserDetail">
        <id property="id" column="detail_id"/>
        <result property="address" column="address"/>
        <result property="phone" column="phone"/>
    </association>
</resultMap>

<select id="selectUserWithDetail" resultMap="userWithDetailResult">
    SELECT u.id, u.username, d.id as detail_id, d.address, d.phone
    FROM user u
    LEFT JOIN user_detail d ON u.id = d.user_id
    WHERE u.id = #{id}
</select>

4.3 集合映射(一对多)

public class Department {
    private Long id;
    private String name;
    private List<User> users;  // 一对多关联
    // getter/setter
}
xml

<!-- 嵌套查询 -->
<resultMap id="deptWithUsers" type="Department">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <collection property="users" column="id" 
                select="com.example.mapper.UserMapper.selectByDeptId"
                ofType="User"/>
</resultMap>

<!-- 嵌套结果 -->
<resultMap id="deptWithUsersResult" type="Department">
    <id property="id" column="dept_id"/>
    <result property="name" column="dept_name"/>
    <collection property="users" ofType="User">
        <id property="id" column="user_id"/>
        <result property="username" column="username"/>
        <result property="age" column="age"/>
    </collection>
</resultMap>

<select id="selectDeptWithUsers" resultMap="deptWithUsersResult">
    SELECT d.id as dept_id, d.name as dept_name,
           u.id as user_id, u.username, u.age
    FROM department d
    LEFT JOIN user u ON d.id = u.dept_id
    WHERE d.id = #{id}
</select>

4.4 鉴别器映射

xml
<!-- 根据字段值映射不同的结果 -->
<resultMap id="vehicleMap" type="Vehicle">
    <id property="id" column="id"/>
    <result property="type" column="type"/>
    <discriminator javaType="int" column="type">
        <case value="1" resultMap="carMap"/>
        <case value="2" resultMap="truckMap"/>
    </discriminator>
</resultMap>

<resultMap id="carMap" type="Car" extends="vehicleMap">
    <result property="doorCount" column="door_count"/>
</resultMap>

<resultMap id="truckMap" type="Truck" extends="vehicleMap">
    <result property="loadCapacity" column="load_capacity"/>
</resultMap>

五、注解开发

5.1 基础注解

import org.apache.ibatis.annotations.*;
import java.util.List;

public interface UserAnnotationMapper {

    // @Select 注解
    @Select("SELECT * FROM user WHERE id = #{id}")
    User selectById(Long id);

    // 多个参数
    @Select("SELECT * FROM user WHERE username = #{username} AND age = #{age}")
    User selectByUsernameAndAge(@Param("username") String username, 
                                 @Param("age") Integer age);

    // @Insert 注解
    @Insert("INSERT INTO user (username, age, create_time) VALUES (#{username}, #{age}, NOW())")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int insert(User user);

    // @Update 注解
    @Update("UPDATE user SET username = #{username}, age = #{age} WHERE id = #{id}")
    int update(User user);

    // @Delete 注解
    @Delete("DELETE FROM user WHERE id = #{id}")
    int deleteById(Long id);

    // 返回 Map
    @Select("SELECT * FROM user WHERE id = #{id}")
    @MapKey("id")
    Map<Long, User> selectAsMap();

    // 动态 SQL 使用 Script 注解
    @Select({
        "<script>",
        "SELECT * FROM user",
        "<where>",
        "   <if test='username != null'>",
        "       AND username LIKE CONCAT('%', #{username}, '%')",
        "   </if>",
        "   <if test='age != null'>",
        "       AND age = #{age}",
        "   </if>",
        "</where>",
        "</script>"
    })
    List<User> findByCondition(@Param("username") String username, 
                                @Param("age") Integer age);
}

5.2 结果映射注解

// @Results 注解
@Select("SELECT id, username, age FROM user WHERE id = #{id}")
@Results(id = "userResultMap", value = {
    @Result(property = "id", column = "id", id = true),
    @Result(property = "username", column = "username"),
    @Result(property = "age", column = "age")
})
User selectById(Long id);

// 引用结果映射
@Select("SELECT * FROM user WHERE username = #{username}")
@ResultMap("userResultMap")
User selectByUsername(String username);

// 一对一关联
@Select("SELECT u.id, u.username, d.address FROM user u LEFT JOIN user_detail d ON u.id = d.user_id WHERE u.id = #{id}")
@Results({
    @Result(property = "id", column = "id"),
    @Result(property = "username", column = "username"),
    @Result(property = "detail", column = "id", 
            one = @One(select = "com.example.mapper.UserDetailMapper.selectByUserId"))
})
User selectUserWithDetail(Long id);

// 一对多关联
@Select("SELECT * FROM department WHERE id = #{id}")
@Results({
    @Result(property = "id", column = "id"),
    @Result(property = "name", column = "name"),
    @Result(property = "users", column = "id",
            many = @Many(select = "com.example.mapper.UserMapper.selectByDeptId"))
})
Department selectDeptWithUsers(Long id);

5.3 动态 SQL 注解

import org.apache.ibatis.annotations.SelectProvider;
import org.apache.ibatis.annotations.InsertProvider;
import org.apache.ibatis.annotations.UpdateProvider;
import org.apache.ibatis.annotations.DeleteProvider;

// SQL 构建器
public class UserSqlProvider {

    public String selectByCondition(Map<String, Object> params) {
        return new SQL() {
  {
            SELECT("*");
            FROM("user");
            if (params.get("username") != null) {
                WHERE("username LIKE #{username}");
            }
            if (params.get("age") != null) {
                WHERE("age = #{age}");
            }
            ORDER_BY("id DESC");
        }}.toString();
    }

    public String insert(User user) {
        return new SQL() {
  {
            INSERT_INTO("user");
            VALUES("username", "#{username}");
            VALUES("age", "#{age}");
            VALUES("create_time", "NOW()");
        }}.toString();
    }

    public String update(User user) {
        return new SQL() {
  {
            UPDATE("user");
            if (user.getUsername() != null) {
                SET("username = #{username}");
            }
            if (user.getAge() != null) {
                SET("age = #{age}");
            }
            SET("update_time = NOW()");
            WHERE("id = #{id}");
        }}.toString();
    }
}

// 使用 Provider
public interface UserProviderMapper {

    @SelectProvider(type = UserSqlProvider.class, method = "selectByCondition")
    List<User> selectByCondition(Map<String, Object> params);

    @InsertProvider(type = UserSqlProvider.class, method = "insert")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int insert(User user);

    @UpdateProvider(type = UserSqlProvider.class, method = "update")
    int update(User user);
}

六、高级特性

6.1 缓存机制
一级缓存(SqlSession 级别):

public class FirstLevelCacheDemo {

    public void testFirstLevelCache() {
        try (SqlSession session = MyBatisUtil.getSqlSession()) {
            UserMapper mapper = session.getMapper(UserMapper.class);

            // 第一次查询,从数据库查询
            User user1 = mapper.selectById(1L);

            // 第二次查询,从一级缓存获取
            User user2 = mapper.selectById(1L);

            // 返回同一个对象
            System.out.println(user1 == user2); // true

            // 执行更新操作会清空缓存
            mapper.update(new User(1L, "新名字", 30));

            // 再次查询,从数据库查询
            User user3 = mapper.selectById(1L);

            // 手动清空缓存
            session.clearCache();
        }
    }
}

二级缓存(Mapper 级别):

xml

<!-- 开启二级缓存 -->
<configuration>
    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>
</configuration>

<!-- Mapper XML 中配置二级缓存 -->
<mapper namespace="com.example.mapper.UserMapper">
    <!-- 基础缓存配置 -->
    <cache/>

    <!-- 高级缓存配置 -->
    <cache 
        eviction="LRU"           <!-- 缓存淘汰策略 -->
        flushInterval="60000"    <!-- 刷新间隔 60秒 -->
        size="512"               <!-- 缓存对象数量 -->
        readOnly="false"/>       <!-- 是否只读 -->
</mapper>
java

// 实体类需要实现 Serializable
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    // ...
}

// 在 Mapper 接口上配置缓存
@CacheNamespace(eviction = FifoCache.class, flushInterval = 60000, size = 512)
public interface UserMapper {
    // ...
}

// 使用参照缓存
@CacheNamespaceRef(UserMapper.class)
public interface RoleMapper {
    // 使用 UserMapper 的缓存配置
}

6.2 延迟加载

xml
<!-- 配置延迟加载 -->
<configuration>
    <settings>
        <!-- 开启延迟加载 -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 关闭积极加载 -->
        <setting name="aggressiveLazyLoading" value="false"/>
        <!-- 延迟加载触发方法 -->
        <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
    </settings>
</configuration>
xml
<!-- 映射文件中配置延迟加载 -->
<resultMap id="userWithOrders" type="User">
    <id property="id" column="id"/>
    <result property="username" column="username"/>
    <!-- fetchType 覆盖全局配置 -->
    <collection property="orders" column="id" 
                select="com.example.mapper.OrderMapper.selectByUserId"
                fetchType="lazy"/>
</resultMap>
java
// 使用代理对象触发加载
public class LazyLoadingDemo {
    public void testLazyLoading() {
        try (SqlSession session = MyBatisUtil.getSqlSession()) {
            UserMapper mapper = session.getMapper(UserMapper.class);
            User user = mapper.selectById(1L);

            // 此时只加载了用户信息
            System.out.println(user.getUsername());

            // 访问订单时触发加载
            List<Order> orders = user.getOrders();  // 执行额外查询

            // 判断是否已加载
            boolean loaded = PersistenceUtils.isLoaded(user.getOrders());
        }
    }
}

6.3 插件机制

java
// 实现 Interceptor 接口
@Intercepts({
    @Signature(
        type = Executor.class,
        method = "query",
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
    )
})
public class PageInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 获取参数
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object parameter = args[1];
        RowBounds rowBounds = (RowBounds) args[2];

        // 分页逻辑
        if (parameter instanceof Page) {
            Page page = (Page) parameter;
            // 获取原始 SQL
            BoundSql boundSql = ms.getBoundSql(parameter);
            String sql = boundSql.getSql();

            // 添加分页 SQL
            String pageSql = sql + " LIMIT " + page.getOffset() + "," + page.getLimit();

            // 创建新的 BoundSql
            BoundSql newBoundSql = new BoundSql(
                ms.getConfiguration(), pageSql, boundSql.getParameterMappings(), parameter);

            // 创建新的 MappedStatement
            MappedStatement newMs = copyMappedStatement(ms, newBoundSql);
            args[0] = newMs;
        }

        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        // 读取配置
        String dialect = properties.getProperty("dialect");
    }

    private MappedStatement copyMappedStatement(MappedStatement ms, BoundSql boundSql) {
        // 复制 MappedStatement 的实现
        // ...
        return ms;
    }
}

配置插件:

xml
<configuration>
    <plugins>
        <plugin interceptor="com.example.plugin.PageInterceptor">
            <property name="dialect" value="mysql"/>
        </plugin>
    </plugins>
</configuration>

6.4 批量操作

java
public class BatchDemo {

    // 使用 ExecutorType.BATCH
    public void batchInsert(List<User> users) {
        try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
            UserMapper mapper = session.getMapper(UserMapper.class);

            for (User user : users) {
                mapper.insert(user);
            }

            session.commit();
        }
    }

    // 使用 foreach 批量插入
    public void batchInsertWithForeach(List<User> users) {
        try (SqlSession session = sqlSessionFactory.openSession()) {
            UserMapper mapper = session.getMapper(UserMapper.class);
            mapper.batchInsert(users);
            session.commit();
        }
    }

    // 批量更新
    public void batchUpdate(List<User> users) {
        try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
            UserMapper mapper = session.getMapper(UserMapper.class);

            for (User user : users) {
                mapper.update(user);
            }

            session.commit();
        }
    }

    // 批量删除
    public void batchDelete(List<Long> ids) {
        try (SqlSession session = sqlSessionFactory.openSession()) {
            UserMapper mapper = session.getMapper(UserMapper.class);
            mapper.deleteByIds(ids);  // 使用 foreach 实现
            session.commit();
        }
    }
}

来源:
https://app-abdss1rim1oh.appmiaoda.com

相关文章
|
4天前
|
人工智能 JSON 机器人
让龙虾成为你的“公众号分身” | 阿里云服务器玩Openclaw
本文带你零成本玩转OpenClaw:学生认证白嫖6个月阿里云服务器,手把手配置飞书机器人、接入免费/高性价比AI模型(NVIDIA/通义),并打造微信公众号“全自动分身”——实时抓热榜、AI选题拆解、一键发布草稿,5分钟完成热点→文章全流程!
10568 53
让龙虾成为你的“公众号分身” | 阿里云服务器玩Openclaw
|
9天前
|
人工智能 JavaScript API
解放双手!OpenClaw Agent Browser全攻略(阿里云+本地部署+免费API+网页自动化场景落地)
“让AI聊聊天、写代码不难,难的是让它自己打开网页、填表单、查数据”——2026年,无数OpenClaw用户被这个痛点困扰。参考文章直击核心:当AI只能“纸上谈兵”,无法实际操控浏览器,就永远成不了真正的“数字员工”。而Agent Browser技能的出现,彻底打破了这一壁垒——它给OpenClaw装上“上网的手和眼睛”,让AI能像真人一样打开网页、点击按钮、填写表单、提取数据,24小时不间断完成网页自动化任务。
2393 5
|
23天前
|
人工智能 JavaScript Ubuntu
5分钟上手龙虾AI!OpenClaw部署(阿里云+本地)+ 免费多模型配置保姆级教程(MiniMax、Claude、阿里云百炼)
OpenClaw(昵称“龙虾AI”)作为2026年热门的开源个人AI助手,由PSPDFKit创始人Peter Steinberger开发,核心优势在于“真正执行任务”——不仅能聊天互动,还能自动处理邮件、管理日程、订机票、写代码等,且所有数据本地处理,隐私完全可控。它支持接入MiniMax、Claude、GPT等多类大模型,兼容微信、Telegram、飞书等主流聊天工具,搭配100+可扩展技能,成为兼顾实用性与隐私性的AI工具首选。
23996 121
|
3天前
|
人工智能 IDE API
2026年国内 Codex 安装教程和使用教程:GPT-5.4 完整指南
Codex已进化为AI编程智能体,不仅能补全代码,更能理解项目、自动重构、执行任务。本文详解国内安装、GPT-5.4接入、cc-switch中转配置及实战开发流程,助你从零掌握“描述需求→AI实现”的新一代工程范式。(239字)
2239 126

热门文章

最新文章