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();
}
}