mybatis-plus – 2

1.在自定义SQL中 $ 和 # 的区别是什么?

#{} 是mybatis-plus采用预编译SQL语句,也就是将SQL语句转换成类似:

select * from user where name = ?;

那么使用#{name}就会将name直接传入,变成name = ‘name’;能够有效防止SQL注入

${}是直接将字符串替换,不应该直接用于查询的字段中,而是应该用于标识是否降序这种固定字段上

2.如何只更新指定的字段而不是更新整个实体类对应字段

2.1使用UpdateWrapper,比如:

UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();

updateWrapper.eq("id",userId).set("password",newPassword);

userMapper.update(updateWrapper);

2.2 使用@Param注解在XML中自定义SQL

<update id = "setUserPassword">
UPDATE user
SET password = #{password}
WHERE id = #{userId};
</update>
int setUserPassword(@Param("userId") Long userId , @Param("newPassword") String newPassword);

3.查询时如何只查询自己需要的字段而不是查询实体类所有的字段

3.1QueryMapper的select

QueryMapper<User> queryMapper = new QueryMapper<>();

queryMapper.eq("id",userId).select("subject_name");

userMapper.select(queryMapper);

3.2使用selectMaps方法返回Map列表

Map<String,Object> getUserSubjectTimeByIdReturnMap(Long userId){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();

userMapper.selectMaps(queryWrapper).stream().findFirst(). //获取第一个Map
orElse(null);// 如果没有结果,返回null
}

4.如何将某个字段更新为null

使用 UpdateWrapper 的 setSql() 方法可以将字段更新为 null

UpdateWrapper<UserPo> updateWrapper = new UpdateWrapper<>();
    updateWrapper.eq("id", userId)
                 .setSql("subject_time = null"); // 使用 setSql 设置字段为 null

5. 自定义mapper方法中如何访问查询器QueryWrapper

List<UserPo> selectByCustom(QueryWrapper<UserPo> qw);
<select id = "selectByCustom" resultType = "User">
SELECT * FROM user
${ew.customSqlSegment}
</select>

6. SqlProvider作用

public class UserSqlProvider {
    public String selectUserPageByCondition(Map<String, Object> params) {
        String tableName = (String) params.get("tableName");
        Page page = (Page) params.get("page");
        QueryWrapper wrapper = (QueryWrapper) params.get("ew");

        SQL sql = new SQL();
        sql.SELECT("*");
        sql.FROM(tableName);
        if (wrapper != null && !StringUtils.isEmpty(wrapper.getCustomSqlSegment())) {
            sql.WHERE(wrapper.getCustomSqlSegment());
        }
        // ... 添加分页 SQL (根据数据库类型) ...

        return sql.toString();
    }
}
import org.apache.ibatis.annotations.SelectProvider;

public interface UserMapper {
    @SelectProvider(type = UserSqlProvider.class, method = "selectUserPageByCondition")
    List<UserPo> selectUserPageByCondition(Map<String, Object> params);
}

7. 自定义的插入方法如何返回插入数据的主键ID

MyBatis-Plus 默认的 insert() 方法会自动回填主键 ID (如果数据库支持自动生成主键,例如 MySQL 的自增主键,Oracle 的 Sequence 等)。 对于自定义的插入方法,也可以实现主键回填。

使用 MyBatis-Plus 的 @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")

public interface UserMapper {
    @Insert("INSERT INTO user_po (name, password) VALUES (#{name}, #{password})")
    @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id") // 开启主键回填
    int insertUser(UserPo userPo);
}

useGeneratedKeys = true: 开启主键回填功能。
keyProperty = "id": 指定实体类中接收主键值的属性名,这里是 id。
keyColumn = "id": 指定数据库表中的主键列名,这里是 id。

8. 一对多查询如何实现

MyBatis-Plus 可以通过 ResultMap 和关联查询来实现一对多查询。

9.xml中 \<include>、\<if>、\<foreach> 等标签的作用

<sql id="userColumns"> <!-  定义 SQL 片段 -->
    id, name, password, subject_time
</sql>

<select id="selectAllUsers" resultType="UserPo">
    SELECT <include refid="userColumns"/> <!-  引用 SQL 片段 -->
    FROM user_po
</select>

<select id="selectUserById" resultType="UserPo">
    SELECT <include refid="userColumns"/> <!-  引用 SQL 片段 -->
    FROM user_po
    WHERE id = #{userId}
</select>
<select id="selectUsersByCondition" resultType="UserPo">
    SELECT * FROM user_po
    <where> <!-  使用 <where> 标签可以智能处理 WHERE 关键字和 AND/OR 连接符 -->
        <if test="name != null and name != ''"> <!-  判断 name 参数是否不为空 -->
            AND name LIKE #{name}
        </if>
        <if test="age != null"> <!-  判断 age 参数是否不为 null -->
            AND age = #{age}
        </if>
    </where>
</select>
<select id="selectUsersInIds" resultType="UserPo">
    SELECT * FROM user_po
    WHERE id IN
    <foreach collection="ids" item="id" open="(" separator="," close=")"> <!-  遍历 ids 集合 -->
        #{id}
    </foreach>
</select>

10.ResultMap和ResultType的区别

ResultMap 和 ResultType 都是 MyBatis 中用于结果映射的配置,但它们的作用和适用场景不同。

<select id="getUserCount" resultType="Integer"> <!-  resultType="Integer" -->
    SELECT COUNT(*) FROM user_po
</select>

<select id="getUserById" resultType="UserPo"> <!-  resultType="UserPo" -->
    SELECT * FROM user_po WHERE id = #{userId}
</select>
<resultMap id="UserResultMap" type="UserPo"> <!-  定义 ResultMap -->
    <id property="id" column="id"/> <!-  主键映射 -->
    <result property="name" column="user_name"/> <!-  属性名与列名不一致的映射 -->
    <result property="password" column="pwd"/>
    <result property="subjectTime" column="subject_time"/>
</resultMap>

<select id="getUserById" resultMap="UserResultMap"> <!-  resultMap 引用 ResultMap -->
    SELECT id, user_name, pwd, subject_time FROM user_po WHERE id = #{userId}
</select>

11.如何在没有实体类的情况下执行sql

MyBatis-Plus 提供了 SqlRunner 工具类,可以执行原始 SQL 语句,而无需实体类。

@Component
public class DbUtils {

    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    public boolean truncateTableByName(String tableName) {
        try (SqlSession sqlSession = sqlSessionFactory.openSession()) { <!-  获取 SqlSession -->
            String sql = "TRUNCATE TABLE " + tableName;
            return SqlRunner.db().session(sqlSession).execute(sql); <!-  使用 SqlRunner 执行 SQL -->
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}

14. mybatis拦截器大致流程和适用场景

MyBatis 拦截器 (Interceptor) 是 MyBatis 框架提供的一种插件机制,允许您在 SQL 执行过程中的特定点 进行拦截和增强处理,例如在 SQL 语句执行前、执行后、结果集处理时等。

大致流程:

定义拦截器类: 创建一个 Java 类,实现 org.apache.ibatis.plugin.Interceptor 接口。
实现 Interceptor 接口的 intercept() 方法: 在 intercept() 方法中编写拦截逻辑。
Invocation invocation 参数包含了拦截点的信息,例如目标对象 (Executor, StatementHandler, ParameterHandler, ResultSetHandler)、方法参数等。
invocation.proceed(): 调用 invocation.proceed() 方法会继续执行原始的 SQL 执行流程。 如果不调用 proceed(),则会 阻止 原始流程的执行。
使用 @Intercepts 和 @Signature 注解声明拦截点: 在拦截器类上使用 @Intercepts 注解,并使用 @Signature 注解指定要拦截的目标对象、方法名和参数类型。
在 MyBatis 配置中注册拦截器: 在 mybatis-config.xml 或 Spring Boot 的 MyBatis-Plus 配置类中注册自定义的拦截器。
适用场景:

SQL 性能监控和分析: 在 SQL 执行前后记录执行时间、SQL 语句等信息,用于性能分析和监控。
数据权限控制: 根据用户身份动态修改 SQL 语句,添加数据权限过滤条件。
数据脱敏: 在查询结果返回前,对敏感数据进行脱敏处理。
自动分页: 虽然 MyBatis-Plus 已经提供了分页插件,但您也可以自定义拦截器来实现更特殊的分页逻辑。
参数修改: 在 SQL 执行前,动态修改方法参数。
结果集处理: 在结果集返回前,对结果集进行统一处理,例如数据转换、数据加密等。
自定义日志记录: 实现更灵活和定制化的 SQL 日志记录。
拦截点 ( @Signature 注解的 type 属性):

Executor: 拦截 Executor 接口的方法,例如 query, update, commit, rollback 等,可以拦截 SQL 执行的各个阶段。
StatementHandler: 拦截 StatementHandler 接口的方法,例如 prepare, parameterize, query, update, batch 等,可以拦截 SQL 语句的准备、参数化、执行等阶段。
ParameterHandler: 拦截 ParameterHandler 接口的 getParameterObject 和 setParameters 方法,可以拦截参数对象的获取和参数的设置过程。
ResultSetHandler: 拦截 ResultSetHandler 接口的 handleResultSets, handleOutputParameters 方法,可以拦截结果集的处理过程。

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import java.util.Properties;

@Intercepts({@Signature(type = StatementHandler.class, method = "query", args = {java.sql.Statement.class, org.apache.ibatis.executor.resultset.ResultSetHandler.class})})
public class SqlCostInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        long startTime = System.currentTimeMillis();
        try {
            return invocation.proceed(); // 执行原始的 SQL 查询
        } finally {
            long endTime = System.currentTimeMillis();
            long costTime = endTime - startTime;
            StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
            String sql = statementHandler.getBoundSql().getSql();
            System.out.println("SQL: " + sql + ", 执行耗时: " + costTime + "ms");
        }
    }

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

    @Override
    public void setProperties(Properties properties) {
        // 可以接收配置参数
    }
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MybatisPlusConfig {
    // ... 其他配置 ...

    @Bean
    public SqlCostInterceptor sqlCostInterceptor() {
        return new SqlCostInterceptor();
    }
}