MyBatis中动态SQL&&以及分页查询的几种方式详解

一:首先是动态sql

        MyBatis支持动态SQL,即根据不同的情况生成不同的SQL语句。这种机制可以帮助开发者编写更加灵活的SQL语句,从而满足不同的需求。MyBatis的动态SQL主要有以下几种方式:(基于xml方式可以实现,注解的方式本人不推荐)

1.<if>

2.<where>

3.<set>

4.<sql><include>

接下来逐个详细的解释

  1. If语句:可以使用if语句根据不同的条件控制SQL语句的执行。如果条件成立,则拼接相应的SQL语句;否则忽略该语句段。

举例来说,假设需要查询某个表中年龄在18到30之间的人员信息,则可以使用如下SQL语句

SELECT * FROM user WHERE 1=1 
<if test="age!=null">
    AND age BETWEEN #{minAge} AND #{maxAge}
</if>

如果用户指定了最小年龄和最大年龄,则会生成下面的SQL语句:

SELECT * FROM user WHERE 1=1 AND age BETWEEN ? AND ?

如果用户未指定年龄,则只会生成下面的SQL语句:

SELECT * FROM user WHERE 1=1

        2.Where语句:可以使用where语句根据不同的条件拼接不同的SQL语句。如果第一个条件为true,则添加where子句;否则添加and子句。

        例如,需要查询某个表中符合条件的人员信息,但是可能存在多种查询条件,则可以使用如下SQL语句:

SELECT * FROM user
<where>
    <if test="name!=null">
        AND name=#{name} 
    </if>
    <if test="age!=null">
        AND age=#{age} 
    </if>
</where>

如果用户指定了姓名和年龄,则会生成下面的SQL语句

SELECT * FROM user WHERE name=? AND age=?

如果用户只指定了姓名,则会生成下面的SQL语句:

SELECT * FROM user WHERE name=?

如果用户只指定了年龄,则会生成下面的SQL语句:

SELECT * FROM user WHERE age=?

set标签用于动态生成UPDATE语句中的SET子句。下面是对set标签的详细解释:

<sql><include>标签都是MyBatis中动态SQL的一部分。

<sql>标签用于定义可重用的SQL片段,可以在任何地方引用这些片段。使用<sql>标签定义SQL片段后,可以在其他语句中使用<include>标签来插入该片段。

<include>标签用于将一个定义好的SQL片段包含在另一个SQL语句中,以实现SQL语句的复用。使用<include>标签时,需要指定引用的SQL片段的id属性值,例如:

<sql id="userColumns">
  username, password, email
</sql>

<select id="selectUsers" resultType="map">
  select 
    <include refid="userColumns"/>
  from users
</select>

上示例中,<sql>标签定义了一个名为userColumns的SQL片段,包含了用户表中的三个列。在<select>语句中,使用<include>标签将这个SQL片段包含在了查询语句中。在执行查询时,MyBatis会将<include>标签替换成对应的SQL片段,从而生成完整的SQL语句。

使用<include>标签可以有效地减少SQL语句的冗余,提高代码的可维护性和复用性。

二实现分页查询的两种方式:

MyBatis 实现分页查询通常有两种方式:基于数据库的分页和基于应用程序的分页。

  1. 基于数据库的分页

基于数据库的分页是通过 SQL 语句中添加 LIMIT 或者 OFFSET 子句来实现的。例如:

<select id="selectUsers" resultType="User">
  SELECT * FROM users LIMIT #{offset}, #{limit}
</select>

其中,#{offset} 表示偏移量(即从第几条记录开始),#{limit} 表示每页显示的记录数。在进行分页查询时,应用程序首先计算出 offset 和 limit 的值,然后将它们传递给 MyBatis 执行相应的 SQL 语句。

优点:性能较好,因为只返回了需要显示的数据。

缺点:SQL 语句比较复杂,不利于调试和维护;如果使用了多表连接或者复杂的查询条件,可能会导致性能下降。

        2.基于应用程序的分页

基于应用程序的分页是在查询结果集合中进行分页的。例如,在查询所有用户信息之后,应用程序通过 Java 代码进行分页处理:

int startIndex = (pageNum - 1) * pageSize;
List<User> users = userDao.selectAll();
List<User> pageUsers = new ArrayList<>();
for (int i = startIndex; i < startIndex + pageSize && i < users.size(); i++) {
    pageUsers.add(users.get(i));
}

其中,pageNum 表示当前页码,pageSize 表示每页显示的记录数。首先,应用程序调用 selectAll() 方法查询所有用户信息,然后根据 pageNum 和 pageSize 计算出需要显示的数据范围,并将符合条件的用户信息存入 pageUsers 集合中。

优点:代码简单易懂,易于调试和维护;适用于所有情况,不会因为复杂的 SQL 语句而影响性能。

缺点:在大量数据的情况下可能会占用较多系统内存。

综上所述,基于数据库的分页和基于应用程序的分页各有优缺点,具体使用哪种方式应根据实际情况进行选择。

3.基于插件的分页查询

        MyBatis 还提供了一种基于插件的分页查询方式。通过自定义 MyBatis 插件,在查询数据时截获 SQL 语句并进行修改,从而实现分页查询。

具体实现方式如下:

  1. 自定义一个 MyBatis 插件,并实现 Interceptor 接口。在 intercept() 方法中,可以获取到当前执行的 SQL 语句,然后根据传入的分页参数对 SQL 语句进行修改。例如:

 

@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class PageInterceptor implements Interceptor {
  @Override
  public Object intercept(Invocation invocation) throws Throwable {
    StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
    MetaObject metaObject = MetaObject.forObject(statementHandler, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY);
    MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
    if (!mappedStatement.getId().matches(".+ByPage$")) {
      return invocation.proceed();
    }
    BoundSql boundSql = statementHandler.getBoundSql();
    String sql = boundSql.getSql();
    Page page = null;
    Object parameterObject = boundSql.getParameterObject();
    if (parameterObject instanceof Page) {
      page = (Page) parameterObject;
    } else {
      Field[] fields = parameterObject.getClass().getDeclaredFields();
      for (Field field : fields) {
        if (field.getType() == Page.class) {
          field.setAccessible(true);
          page = (Page) field.get(parameterObject);
          break;
        }
      }
    }
    if (page == null) {
      return invocation.proceed();
    }
    String countSql = "SELECT COUNT(*) FROM (" + sql + ") tmp_count";
    Connection connection = (Connection) invocation.getArgs()[0];
    PreparedStatement countStatement = connection.prepareStatement(countSql);
    BoundSql countBoundSql = new BoundSql(mappedStatement.getConfiguration(), countSql, boundSql.getParameterMappings(), parameterObject);
    setParameters(countStatement, mappedStatement, countBoundSql, parameterObject);
    ResultSet rs = countStatement.executeQuery();
    int totalCount = 0;
    if (rs.next()) {
      totalCount = rs.getInt(1);
    }
    rs.close();
    countStatement.close();
    page.setTotalCount(totalCount);
    String pageSql = generatePageSql(sql, page);
    metaObject.setValue("delegate.boundSql.sql", pageSql);
    return invocation.proceed();
  }

  private void setParameters(PreparedStatement preparedStatement, MappedStatement mappedStatement, BoundSql boundSql, Object parameter) throws SQLException {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      Configuration configuration = mappedStatement.getConfiguration();
      TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
      MetaObject metaObject = parameter == null ? null : configuration.newMetaObject(parameter);
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          Object value;
          String propertyName = parameterMapping.getProperty();
          PropertyTokenizer prop = new PropertyTokenizer(propertyName);
          if (parameter == null) {
            value = null;
          } else if (typeHandlerRegistry.hasTypeHandler(parameter.getClass())) {
            value = parameter;
          } else if (boundSql.hasAdditionalParameter(propertyName)) {
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (propertyName.startsWith(ForEachSqlNode.ITEM_PREFIX) && boundSql.hasAdditionalParameter(prop.getName())) {
            value = boundSql.getAdditionalParameter(prop.getName());
            if (value != null) {
              value = configuration.newMetaObject(value).getValue(propertyName.substring(prop.getName().length()));
            }
          } else {
            value = metaObject == null ? null : metaObject.getValue(propertyName);
          }
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          if (typeHandler == null) {
            throw new ExecutorException("There was no TypeHandler found for parameter " + propertyName + " of statement " + mappedStatement.getId());
          }
          typeHandler.setParameter(preparedStatement, i + 1, value, parameterMapping.getJdbcType());
        }
      }
    }
  }

  private String generatePageSql(String sql, Page page) {
    StringBuffer pageSql = new StringBuffer();
    pageSql.append(sql);
    pageSql.append(" limit ").
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
MyBatis 分页插件可以帮助我们在使用 MyBatis 进行分页查询时,更加方便地编写分页查询语句。下面是 MyBatis 分页插件的详细介绍以及使用方法。 ## 什么是 MyBatis 分页插件? MyBatis 分页插件是一个用于简化 MyBatis 分页查询的工具,它可以自动拦截分页查询语句,并根据传入的分页参数进行分页处理,最终返回分页结果。 MyBatis 分页插件支持多种数据库,包括 MySQL、Oracle、SQL Server 等。同时,它还提供了丰富的配置选项,可以让我们根据实际需求进行灵活配置。 ## 如何使用 MyBatis 分页插件? 使用 MyBatis 分页插件需要进行以下几个步骤: 1. 引入 MyBatis 分页插件的依赖 可以使用 Maven 或 Gradle 等工具,将 MyBatis 分页插件的依赖添加到项目。以 Maven 为例,需要添加以下依赖: ```xml <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.11</version> </dependency> ``` 2. 配置 MyBatis 分页插件 在 MyBatis 的配置文件,需要配置 MyBatis 分页插件。以下是一个示例配置: ```xml <plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <property name="helperDialect" value="mysql"/> <property name="reasonable" value="true"/> <property name="supportMethodsArguments" value="true"/> <property name="params" value="count=countSql"/> </plugin> </plugins> ``` 其,`interceptor` 指定了使用的插件类,`helperDialect` 指定了数据库类型,`reasonable` 指定了是否开启合理化查询,`supportMethodsArguments` 指定了是否支持方法参数作为分页参数,`params` 指定了参数映射规则。 3. 在 Mapper 编写分页查询语句 在 Mapper 编写分页查询语句时,需要使用 MyBatis 分页插件提供的分页参数。以下是一个示例: ```xml <select id="getUsers" resultMap="userResultMap"> select * from users <where> <if test="name != null and name != ''"> and name like concat('%', #{name}, '%') </if> </where> order by id <if test="pageSize != null and pageNum != null"> limit #{pageSize} offset #{pageSize * (pageNum - 1)} </if> </select> ``` 其,`pageSize` 和 `pageNum` 分别表示每页大小和当前页码。 4. 调用分页查询方法 最后,在 Service 调用分页查询方法时,需要传入分页参数。以下是一个示例: ```java PageHelper.startPage(pageNum, pageSize); List<User> userList = userMapper.getUsers(name); PageInfo<User> pageInfo = new PageInfo<>(userList); ``` 其,`PageHelper.startPage()` 方法用于启动分页查询,`PageInfo` 用于封装分页结果。 ## 总结 MyBatis 分页插件是一个非常实用的工具,可以大大简化 MyBatis 分页查询的编写和调用过程。在使用 MyBatis 进行分页查询时,推荐使用 MyBatis 分页插件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值