Mybatis

相关概念

ResultMap 与 ResultType

  • 都表示的是一种映射关系,即只有当结果集需要映射(select)的时候才会使用;像新增、修改、删除只返回受影响行数,且框架默认会返回int类型,故可以不用显示声明ResultType="int"
  • 如果数据表字段和实体类是一一对应的,就使用ResultType,否则就使用ResultMap,因此两者是不能共存的
  • 实际开发中,对于一张表的映射其实通过驼峰命名等配置就可以实现,故ResultMap更多的是用在多张表的联合查询上

 

#{} 取参和 ${} 取参的区别

  • #{} 会将内容先替换为?占位符,并且自动进行参数类型转化;能够防止SQL注入,安全性更高
  • ${} 只是简单的字符串拼接
SELECT * FROM ${tableName}

SELECT * FROM user WHERE username = #{username}

 

配置文件

虽然有两种配置方式:mybatis-config.xml、application.yml, 两者有相同配置时,以mybatis-config.xml 文件为主(实践似乎并不是这样)。

推荐做法是两者结合使用:

  • 核心配置(MyBatis 原生功能):建议放在 mybatis-config.xml
  • Spring Boot 整合配置:建议放在 application.yml 中
application.yml
# MyBatis 配置
mybatis:
  # Mapper XML 文件的路径(可以是多个路径)
  mapper-locations: classpath:mapper/*.xml
  # MyBatis 配置文件(如果需要使用 mybatis-config.xml)
  config-location: classpath:mybatis-config.xml
  # 指定实体类包路径,避免每次都配置全限定名
  type-aliases-package: com.example.project.model
  # 自动映射行为(PARTIAL 或 FULL)
  configuration:
    map-underscore-to-camel-case: true  # 开启驼峰命名自动映射
    cache-enabled: true                 # 启用二级缓存
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印日志
mybatis-config.xml
 <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
  
  <!-- 全局配置 -->
  <settings>
    <!-- 是否启用二级缓存 -->
    <setting name="cacheEnabled" value="true" />
    <!-- 延迟加载 -->
    <setting name="lazyLoadingEnabled" value="true" />
    <!-- 多结果集是否允许延迟加载 -->
    <setting name="multipleResultSetsEnabled" value="true" />
    <!-- 使用列别名 -->
    <setting name="useColumnLabel" value="true" />
    <!-- 自动映射行为(NONE, PARTIAL, FULL) -->
    <setting name="autoMappingBehavior" value="PARTIAL" />
    <!-- 自动生成主键 -->
    <setting name="useGeneratedKeys" value="true" />
    <!-- 数据库字段与 Java 属性的映射是否使用驼峰命名法 -->
    <setting name="mapUnderscoreToCamelCase" value="true" />
    <!-- 查询超时时间(秒) -->
    <setting name="defaultStatementTimeout" value="30" />
    <!-- 默认抓取大小 -->
    <setting name="defaultFetchSize" value="100" />
  </settings>
  
  <!-- 类型别名 -->
  <typeAliases>
    <!-- 为包中的所有类定义别名 -->
    <package name="com.example.project.model" />
    <!-- 为特定类定义别名 -->
    <typeAlias alias="User" type="com.example.project.model.User" />
  </typeAliases>
  

</configuration>

注:Mybatis xml文件 常见配置项及默认值

 

缓存

sqlSession

Spring 管理的事务
 @Service
public class MyService {
    @Transactional
    public void myMethod() {
        myMapper.method1(); // 第一次 SQL 执行,SqlSession 开始
        myMapper.method2(); // 第二次 SQL 执行,使用相同的 SqlSession
        myMapper.method3(); // 第三次 SQL 执行,仍然使用相同的 SqlSession
    } // 事务提交,SqlSession 生命周期结束
}
手动管理 SqlSession
 SqlSession sqlSession = sqlSessionFactory.openSession();
try {
    MyMapper mapper = sqlSession.getMapper(MyMapper.class);
    mapper.method1(); // 第一次 SQL 执行
    mapper.method2(); // 第二次 SQL 执行
    mapper.method3(); // 第三次 SQL 执行
    sqlSession.commit(); // 提交
} finally {
    sqlSession.close(); // SqlSession 生命周期结束
}
无事务支持(每次调用单独管理)
 myMapper.method1(); // 第一次调用,SqlSession 生命周期开始和结束
myMapper.method2(); // 第二次调用,新的 SqlSession 开始和结束
myMapper.method3(); // 第三次调用,新创建的 SqlSession 生命周期开始和结束

 

一级缓存

默认开启,作用范围是同一个sqlSession对象下的相同sql查询

关闭方式:配置中localCacheScope设置为:STATEMENT。  即:<setting name="localCacheScope" value="STATEMENT"/>

二级缓存

作用范围:多个SqlSession之间需要共享缓存,其共享范围为Namespace

开启方式:

  1. 步骤一:配置setting,即:<setting name="cacheEnabled" value="true"/>
  2. 步骤二:指定namespace配置开启,即:<cache/>

 

 

传递实体参数

不加@Param注解,取值的时候直接写属性

加了@Param注解,取值必须使用对象.属性的方式

 

动态SQL

trim标签

insert into t_car
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="vin != null">vin,</if>
            <if test="deviceId != null">device_id,</if>
            <if test="fenceId != null">fence_id,</if>
         </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="vin != null">#{vin},</if>
            <if test="deviceId != null">#{deviceId},</if>
            <if test="fenceId != null">#{fenceId},</if>
         </trim>
</insert>

 

foreach标签

<foreach item="id" collection="array" open="(" separator="," close=")">
    #{id}
</foreach>

属性介绍

  • item:集合中元素迭代时的别名(必选)
  • collection:要做foreach的对象类型(必选)
  • index:用于表示在迭代过程中,每次迭代到的位置(可选)
  • open:开始符号,一般是(和close=")"合用。常用在in(),values()时(可选)
  • separator:元素之间的分隔符,在in()的时候,separator=","会自动在元素中间用“,“隔开,如in(1,2,)(可选)
  • close:关闭符号,一般是)和open="("合用。常用在in(),values()时(可选)

 

where标签

注:and关键字必须存在,且where标签只能去除第一个条件中出现的前置and关键字

<where>  
    <if test="simNo != null  and simNo != ''"> and sim_no = #{simNo}</if>
    <if test="deptId != null "> and dept_id = #{deptId}</if>
    <if test="status != null "> and status = #{status}</if>
</where>

 

异常处理

MyBatis框架自定义了一个异常基类,叫做PersistenceException, 而PersistenceException是RuntimeException的子类

故Mybatis没有的异常都是非检查异常,会自动向上抛出,所以直接使用全局异常处理器捕获即可

 

注释写法

使用 <!-- 注释 -->,千万别用 --(pgsql) 或者 #(mysql) 之类的

 

MybatisX 插件

 

拦截器

注:@Signature注解中的参数都是使用Mybatis中自定义好的值,与我们自定义的目标方法无关

package com.xz.bd.business.interceptor;

import com.xz.bd.business.entity.BaseEntity;
import com.xz.bd.common.utils.SecurityUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;

import java.util.Date;

@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class MybatisInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object parameter = args[1];

        // 只处理实体参数
        if (parameter instanceof BaseEntity) {
            BaseEntity entity = (BaseEntity) parameter;
            String operation = ms.getSqlCommandType().name();

            Date now = new Date();

            if ("INSERT".equals(operation)) {
                entity.setCreateTime(now);
                entity.setUpdateTime(now);
            } else if ("UPDATE".equals(operation)) {
                entity.setUpdateTime(now);
            }
        }

        // 执行原方法
        Object result = invocation.proceed();

        return result;
    }
}

参考链接 :Mybatis拦截器(Interceptor)的理解与实践

 

踩过的坑

Mybatis拦截器注册失败

问题描述

# 使用配置类的是否注册拦截器失败

@Configuration
public class MybatisConfig {

    @Bean
    public ConfigurationCustomizer mybatisConfigurationCustomizer() {
        return configuration -> {
            // 创建并添加拦截器实例到配置中
            configuration.addInterceptor(new MybatisInterceptor());
        };
    }

}

 

问题解决

项目中存在mybatis-config.xml文件,导致mybatis配置类失效,故强烈推荐只用一种配置方式

 

参考文章

【1】控制台打印SQL语句

【2】MyBatis的xml里的sql如何使用注释

posted @ 2024-01-12 15:38  先娶国王后取经  阅读(24)  评论(0)    收藏  举报