Mybatis 是否支持延迟加载?实现原理是什么?

典型回答 MyBaits支持延迟加载,延迟加载允许在需要时按需加载关联对象,而不是在查询主对象时立即加载所有关联对象。这样做可以提高查询性能和减少不必要的数据库访问。 假设,我们有两张表,分别是订单表和商品项表,一个订单中可以关联多个商品项。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Order { private int id; private String orderNumber; private List<Item> items; // 关联的商品项列表 // 省略构造函数和getter/setter方法 } public class Item { private int id; private int orderId; private String itemName; private BigDecimal price; // 省略构造函数和getter/setter方法 } 当我们从数据库中查询Order的时候,如果同时把关联的Item都返回,这就不是延迟加载,如果在后面真正要用到Item的时候再查询加载,这就是延迟加载。 ...

March 22, 2026 · 1 min · santu

Mybatis可以实现动态SQL么?

典型回答 可以,动态SQL是指根据不同的条件生成不同的SQL语句,可以避免在编写SQL语句时出现重复的代码,提高代码的复用性和灵活性。 MyBatis中提供了一些标签来支持动态SQL的生成,常见的几个有: if标签:用于根据条件生成SQL语句的一部分。例如: 1 2 3 4 5 6 7 8 9 10 <select id="getUsers" resultType="User"> SELECT * FROM user WHERE <if test="name != null and name != ''"> name like #{name} </if> <if test="age != null"> and age = #{age} </if> </select> 使用if标签判断了查询条件中的name和age是否为空,如果不为空,则在SQL语句中加入相应的条件。 choose、when、otherwise标签:用于根据不同的条件选择不同的SQL语句块。例如: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <select id="getUsers" resultType="User"> SELECT * FROM user <where> <choose> <when test="name != null and name != ''"> and name like #{name} </when> <when test="age != null"> and age = #{age} </when> <otherwise> and sex = 'M' </otherwise> </choose> </where> </select> 使用choose、when、otherwise标签判断了查询条件中的name和age是否为空,如果不为空,则在SQL语句中加入相应的条件;否则加入默认的条件。 ...

March 22, 2026 · 1 min · santu

使用MyBatis如何实现分页?

典型回答 MyBatis中可以通过两种方式来实现分页:基于物理分页和基于逻辑分页。 所谓物理分页,指的是最终执行的SQL中进行分页,即SQL语句中带limit,这样SQL语句执行之后返回的内容就是分页后的结果。 所谓逻辑分页,就是在SQL语句中不进行分页,照常全部查询,在查询到的结果集中,再进行分页。 在MyBatis中,想要实现分页通常有四种做法: 1、在SQL中添加limit语句: 1 2 3 4 5 6 7 8 9 <select id="getUsers" resultType="User"> select * from user <where> <if test="name != null"> and name like CONCAT('%',#{name},'%') </if> </where> limit #{offset}, #{limit} </select> 2、基于PageHelper分页插件,实现分页: 在使用PageHelper时,只需要在查询语句前调用PageHelper.startPage()方法,然后再进行查询操作。PageHelper会自动将查询结果封装到一个PageInfo对象中,包含了分页信息和查询结果。 1 2 3 4 // Java代码中使用 PageHelper PageHelper.startPage(1, 10); List<User> userList = userMapper.getUsers(); PageInfo<User> pageInfo = new PageInfo<>(userList); 使用PageHelper时,不需要在mapper.xml文件中使用limit语句。 3、基于RowBounds实现分页 RowBounds是MyBatis中提供的一个分页查询工具,其中可以设置offset和limit用于分页。 ...

March 22, 2026 · 1 min · santu

MyBatis-Plus有什么用?

典型回答 MyBatis-Plus是一个增强的MyBatis框架,提供了许多实用的功能和工具,包括: 通用Mapper:提供了一组通用的Mapper接口和实现,可以快速进行增删改查操作,无需手写SQL语句。例如BaseMapper、ConditionMapper等: 1 2 public interface UserMapper extends BaseMapper<User> { } 分页插件:提供了一种简单易用的分页功能,可以根据传入的分页参数自动计算出分页信息,无需手动编写分页SQL语句。 1 2 3 public interface UserMapper extends BaseMapper<User> { List<User> selectUserPage(Page<User> page, @Param("name") String name); } 自动生成代码:可以根据数据库表自动生成实体类、Mapper接口、Mapper XML映射文件等代码,大大减少了开发人员的工作量。 1 2 3 4 5 6 AutoGenerator generator = new AutoGenerator(); generator.setDataSource(dataSourceConfig); generator.setPackageInfo(new PackageConfig().setParent("com.example.mybatisplus")); generator.setGlobalConfig(new GlobalConfig().setOutputDir(System.getProperty("user.dir") + "/src/main/java")); generator.setTemplateEngine(new FreemarkerTemplateEngine()); generator.execute(); Lambda表达式支持:提供了LambdaQueryWrapper和LambdaUpdateWrapper,可以使用Lambda表达式来构造查询条件和更新操作,使得代码更加简洁和易读。 1 2 3 LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(User::getName, "Tom").gt(User::getAge, 18); List<User> userList = userMapper.selectList(queryWrapper); SQL注入器:提供了自定义的SQL注入器功能,可以自由扩展MyBatis的SQL语句,实现更加灵活的SQL操作。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class CustomSqlInjector extends AbstractSqlInjector { @Override public List<AbstractMethod> getMethodList(Class<?> mapperClass) { List<AbstractMethod> methodList = new ArrayList<>(); methodList.add(new CustomInsert()); return methodList; } } public class CustomInsert extends InsertMethod { @Override public String getMethod(SqlSource sqlSource) { return "customInsert"; } } 性能分析插件:提供了性能分析插件,可以帮助开发人员分析SQL执行效率,优化数据库操作。 扩展知识 MyBatis-Plus的优缺点? 优点 ...

March 22, 2026 · 1 min · santu

RowBounds分页的原理是什么?

典型回答 MyBatis的RowBounds是一个用于分页查询的简单POJO类,它包含两个属性offset和limit,分别表示分页查询的偏移量和每页查询的数据条数。 **在使用RowBounds进行逻辑分页的时候,我们的SQL语句中是不需要指定分页参数的。**就正常的查询即可,如: 1 2 3 4 5 6 7 8 9 <select id="getUsers" resultType="User"> select * from user <where> <if test="name != null"> and name like CONCAT('%',#{name},'%') </if> </where> order by id </select> 然后,在查询的时候,将RowBounds当做一个参数传递: 1 2 3 4 int offset = 10; // 偏移量 int limit = 5; // 每页数据条数 RowBounds rowBounds = new RowBounds(offset, limit); List<User> userList = sqlSession.selectList("getUserList", null, rowBounds); 这样,实际上在查询的时候,将会先所有符合条件的记录返回,然后再在内存中进行分页,分页的方式是根据RowBounds中指定的offset和limit进行数据保留,即抛弃掉不需要的数据再返回。 ...

March 22, 2026 · 1 min · santu

MyBatis-Plus的分页原理是什么?

典型回答 MyBatis-Plus支持分页插件——PaginationInnerInterceptor PaginationInnerInterceptor采用的是物理分页方式,物理分页是在数据库中进行分页,即直接在SQL语句中加入LIMIT语句,只查询所需的部分数据。 物理分页的优点是可以减少内存占用,减轻数据库的负载,缺点是无法对结果进行任意操作,比如说在分页过程中做二次过滤、字段映射、json解析等。 PaginationInnerInterceptor这个分页插件就会自动拦截所有的SQL查询请求,计算分页查询的起始位置和记录数,并在SQL语句中加入LIMIT语句。 核心的操作在beforeQuery中: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 @Override public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { IPage<?> page = ParameterUtils.findPage(parameter).orElse(null); if (null == page) { return; } // 处理 orderBy 拼接 boolean addOrdered = false; String buildSql = boundSql.getSql(); List<OrderItem> orders = page.orders(); if (CollectionUtils.isNotEmpty(orders)) { addOrdered = true; buildSql = this.concatOrderBy(buildSql, orders); } // size 小于 0 且不限制返回值则不构造分页sql Long _limit = page.maxLimit() != null ? page.maxLimit() : maxLimit; if (page.getSize() < 0 && null == _limit) { if (addOrdered) { PluginUtils.mpBoundSql(boundSql).sql(buildSql); } return; } handlerLimit(page, _limit); IDialect dialect = findIDialect(executor); final Configuration configuration = ms.getConfiguration(); DialectModel model = dialect.buildPaginationSql(buildSql, page.offset(), page.getSize()); PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql); List<ParameterMapping> mappings = mpBoundSql.parameterMappings(); Map<String, Object> additionalParameter = mpBoundSql.additionalParameters(); model.consumers(mappings, configuration, additionalParameter); mpBoundSql.sql(model.getDialectSql()); mpBoundSql.parameterMappings(mappings); } 其中比较关键的就是第31行,buildPaginationSql方法。这里不同的数据库有不同的实现,我们看一下MySQL的实现: ...

March 22, 2026 · 2 min · santu

留言给博主