Mybatis如何实现@Select等注解动态组合SQL语句

目录
  • 一、背景说明
  • 二、实现方案
  • 三、 实现自定义注解
    • 1、自定义Select注解
    • 2、自定义Select in注解
    • 3、自定义Update的注解
    • 4、自定义Insert的注解 同理
  • 四、注意事项&遇到的一些坑
  • 五、总结

一、背景说明

由于以前在项目中一直使用sqlmap.xml进行mybatis语句的编写和实现,其xml实现动态更新和查询较为方便,而目前由于技术框架所定,采用@Select、@Insert等注解方式来实现对应的持久化操作(MyBatis提供了简单的Java注解,使得我们可以不配置XML格式的Mapper文件,也能方便的编写简单的数据库操作代码)

对于简单的数据库操作基本能够满足日常需要,但注解对动态SQL的支持一直差强人意,即使MyBatis提供了InsertProvider等*Provider注解来支持注解的Dynamic SQL,也没有降低SQL的编写难度,甚至比XML格式的SQL语句更难编写和维护,实现较为复杂的语句时还是不那么方便,团队成员一直通过类来实现SQL语句的硬拼接

这样的硬语句编写的SQL语句很长又冗余,给维护和修改带来一定的成本且易读性差,为了提高效率,一次编写重复使用的原则,Mybatis在3.2版本之后,其实提供了LanguageDriver接口,就是便于使用该接口自定义SQL的解析方式。

故在这里将研究的MyBatis如何在注解模式下简化SQL语句的硬拼接实现动态组合版SQL的方案进行分享。

二、实现方案

我们先来看下LanguageDriver接口中的3个方法:

public interface LanguageDriver {
    ParameterHandler createParameterHandler(MappedStatement var1, Object var2, BoundSql var3);

    SqlSource createSqlSource(Configuration var1, XNode var2, Class<?> var3);

    SqlSource createSqlSource(Configuration var1, String var2, Class<?> var3);
}
  • createParameterHandler方法为创建一个ParameterHandler对象,用于将实际参数赋值到JDBC语句中
  • 将XML中读入的语句解析并返回一个sqlSource对象
  • 将注解中读入的语句解析并返回一个sqlSource对象

一旦实现了LanguageDriver,我们即可指定该实现类作为SQL的解析器,在不使用XML Mapper的形式下,我们可以使用@Lang注解

@Mapper
public interface RoleDAO {
    /**
     *  查询角色信息列表
     *
     * @param roleParam 查询参数
     * @return 角色列表
     */
    @Select("select id,name,description,enabled,deleted,date_created as dateCreated,last_modified as lastModified"
        + " from admin_role (#{roleParam})")
    @Lang(SimpleSelectLangDriver.class)
    List<RoleDO> findListRoleByPage(ListRoleParam roleParam);

LanguageDriver的默认实现类为XMLLanguageDriver和RawLanguageDriver;

分别为XML和Raw,Mybatis默认是XML语言,所以我们来看看XMLLanguageDriver中是怎么实现的:

public class XMLLanguageDriver implements LanguageDriver {
    public XMLLanguageDriver() {
    }

    public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
        return new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
    }

    public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
        XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
        return builder.parseScriptNode();
    }

    public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
        if(script.startsWith("<script>")) {
            XPathParser textSqlNode1 = new XPathParser(script, false, configuration.getVariables(),
                                       new XMLMapperEntityResolver());
            return this.createSqlSource(configuration, textSqlNode1.evalNode("/script"), parameterType);
        } else {
            script = PropertyParser.parse(script, configuration.getVariables());
            TextSqlNode textSqlNode = new TextSqlNode(script);
            return (SqlSource)(textSqlNode.isDynamic()?new DynamicSqlSource(configuration, textSqlNode)
                   :new RawSqlSource(configuration, script, parameterType));
        }
    }
}

发现其实mybatis已经帮忙写好了解析逻辑,而且发现如果是以开头的字符串传入后,会被以XML的格式进行解析。那么方案就可以确认了,我们继承XMLLanguageDriver这个类,并且重写其createSqlSource方法,按照自己编写逻辑解析好sql后,再调用父类的方法即可。

三、 实现自定义注解

本例中将给出一些常见的自定义注解的实现和使用方式。

1、自定义Select注解

在本例中的业务场景下,我们需要根据对象中的字段进行查询,就会写出硬SQL语句拼接类,如下代码:

    /**
     * 查询
     *
     * @param userParam 查询条件
     * @return 用户信息列表
     */
    @SelectProvider(type = UserSql.class, method = "listByPage")
    List<UserDO> listByPage(@Param(value = "userParam") ListUserParam userParam);
public class UserSql {
    /**
     * 拼接查询语句
     *
     * @param params 查询条件
     * @return 查询语句
     */
    public static String listByPage(Map params) {

        ListUserParam userParam = (ListUserParam)params.get("userParam");
        if (userParam == null) {
            return "";
        }
        long begin = (userParam.getPi() - 1) * userParam.getPs();
        long end = userParam.getPi() * userParam.getPs();

        String condition = "";

        StringBuffer sb = new StringBuffer(" with query as ( ");
        sb.append(
            " select row_number() over(order by user.last_modified desc, user.date_created desc) as row_nr, user.* ");
        sb.append(" from ( ");
        sb.append(" select * from admin_user where 1=1 ");
        condition = " and username like '%#{userParam.username}%'";
        sb.append(StringUtils.isBlank(userParam.getUsername()) ? "" : condition);
        condition = " and name like '%#{userParam.name}%'";
        sb.append(StringUtils.isBlank(userParam.getName()) ? "" : condition);
        condition = " and mobile like '%#{userParam.mobile}%'";
        sb.append(StringUtils.isBlank(userParam.getMobile()) ? "" : condition);
        condition = " and authorities like '%#{userParam.authorities}%'";
        sb.append(StringUtils.isBlank(userParam.getAuthorities()) ? "" : condition);
        condition = " and enabled = #{userParam.enabled}";
        sb.append(StringUtils.isBlank(userParam.getEnabled()) ? "" : condition);
        sb.append(" ) ");
        sb.append(" user) ");
        sb.append(" ");
        sb.append(" select ");
        sb.append(" id, username, password, name, mobile, authorities, enabled, deleted, ");
        sb.append(" creator_id as creatorId, creator, date_created as dateCreated, ");
        sb.append(" modifier_id as modifierId, modifier, last_modified as lastModified ");
        sb.append(" from query where row_nr > ");
        sb.append(begin);
        sb.append(" and row_nr <= ");
        sb.append(end);
        sb.append(" order by last_modified desc, date_created desc ");

        log.info("====UserSql.query====sb:{}", sb.toString());

        return sb.toString();
    }

}

对于这样硬拼接的SQL语句可读性较差,也不利用日常维护,我们可以通过实现LanguageDriver将where子句抽象化,以此来简化Select查询语句。

简化后代码如下( 在上述实现方案中已贴过代码):

/**
     *  查询角色信息列表
     *
     * @param roleParam 查询参数
     * @return 角色列表
     */
    @Select("select id,name,description,enabled,deleted,date_created as dateCreated,last_modified as lastModified"
        + " from admin_role (#{roleParam})")
    @Lang(SimpleSelectLangDriver.class)
    List<RoleDO> findListRoleByPage(ListRoleParam roleParam);

其SimpleSelectLangDriver的实现代码如下:

package com.szss.admin.common;

import java.lang.reflect.Field;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.session.Configuration;

import com.google.common.base.CaseFormat;

/**
 * @author Allen
 * @date 2018/3/9
 *
 * 自定义Select注解,用于动态生成Select语句
 */
public class SimpleSelectLangDriver extends XMLLanguageDriver implements LanguageDriver {

    /**
     * Pattern静态申明
     */
    private final Pattern inPattern = Pattern.compile("\\(#\\{(\\w+)\\}\\)");

    /**
     * 实现自定义Select注解
     * @param configuration 配置参数
     * @param script 入参
     * @param parameterType 参数类型
     * @return 转换后的SqlSource
     */
    @Override
    public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {

        Matcher matcher = inPattern.matcher(script);
        if (matcher.find()) {
            StringBuilder sb = new StringBuilder();
            sb.append("<where>");

            for (Field field : parameterType.getDeclaredFields()) {
                    String tmp = "<if test=\"_field != null\"> AND _column=#{_field}</if>";
                    sb.append(tmp.replaceAll("_field", field.getName()).replaceAll("_column",
                        CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, field.getName())));
            }

            sb.append("</where>");

            script = matcher.replaceAll(sb.toString());
            script = "<script>" + script + "</script>";
        }

        return super.createSqlSource(configuration, script, parameterType);
    }
}

上述代码实现了动态生成SQL语句的功能,但由于在VO实体类中可能有部分参数是我们不想加入到动态组合里面的或部分字段在数据库中并不存在相应的列(比如自动 生成的serialVersionUID等其他字段),这时我们就需要排除VO实体类的一些多余的不匹配的字段进行逻辑隐藏;我们增加一个自定义的注解,并且对Language的实现稍作修改即可。

新建一个注解,其代码如下:

package com.szss.admin.common;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author Allen
 * @date 2018/3/9
 *
 *  自定义的注解,用于排除多余的变量(自定义注解,过滤多余字段)
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Invisible {
}

然后在VO实体类中不需要加入的字段可进行引用该注解

package com.szss.admin.model.param;

import com.szss.admin.common.Invisible;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

/**
 * 角色查询参数
 *
 * @author Allen
 * @date 2018/3/8
 */
@Data
public class ListRoleParam {

    /**
     * 角色名称
     */
    @ApiModelProperty(value = "角色名称", example = "管理员", position = 1)
    private String name;

    /**
     * 是否启用:0-不可用,1-可用
     */
    @ApiModelProperty(value = "是否启用", example = "0", position = 2)
    private Boolean enabled;

    /**
     * 删除标示:0-未删除,1-已删除
     */
    @ApiModelProperty(value = "删除标示", example = "0", position = 3)
    private Boolean deleted;

    /**
     * 当前页码
     */
    @ApiModelProperty(value = "当前页码", example = "1", position = 4)
    @Invisible
    private long pi;
    /**
     * 当前页面大小
     */
    @ApiModelProperty(value = "当前页面大小", example = "10", position = 5)
    @Invisible
    private long ps;

}

最后需要对上述中的SimpleSelectLangDriver实现类中将被该注解声明过的字段排除操作,代码如下:

            for (Field field : parameterType.getDeclaredFields()) {
                // 排除被Invisble修饰的变量
                if (!field.isAnnotationPresent(Invisible.class)) {
                    String tmp = "<if test=\"_field != null\"> AND _column=#{_field}</if>";
                    sb.append(tmp.replaceAll("_field", field.getName()).replaceAll("_column",
                        CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, field.getName())));
                }
            }

如上所示,只是对SimpleSelectLangDriver类增加了if (!field.isAnnotationPresent(Invisible.class)) 这样的判断,已过滤多余的变量。

需要注意的是在使用Select的时候,传入的参数前无需加入@Param注解,否则会导致Mybatis找不到参数而抛出异常,如需加入 就需要绑定对象属性(如在语句中就需要使用param.name)。

2、自定义Select in注解

在使用Mybatis注解的时候,发现其对Select In格式的查询支持不是很友好,在字符串中输入十分繁琐,可以通过将自定义的标签转成格式;下面便通过我们自己实现的LanguageDriver来实现SQL的动态解析:

DAO接口层中代码如下:

@Select("SELECT * FROM admin_role WHERE id IN (#{roleIdList})")
@Lang(SimpleSelectInLangDriver.class)
List<RoleDO> selectRolesByRoleId(List<Integer> roleIdList);

LanguageDriver实现类如下:

package com.szss.admin.common;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.session.Configuration;

/**
 * @author Allen
 * @date 2018/3/9
 *
 * 自定义Select in 注解,用于动态生成Select in 语句
 */
public class SimpleSelectInLangDriver extends XMLLanguageDriver implements LanguageDriver {

    /**
     * Pattern静态申明
     */
    private final Pattern inPattern = Pattern.compile("\\(#\\{(\\w+)\\}\\)");

    /**
     * 实现自定义Select in 注解
     * @param configuration 配置参数
     * @param script 入参
     * @param parameterType 参数类型
     * @return 转换后的SqlSource
     */
    @Override
    public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {

        Matcher matcher = inPattern.matcher(script);
        if (matcher.find()) {
            script = matcher.replaceAll("<foreach collection=\"$1\" item=\"_item\" open=\"(\" "
                + "separator=\",\" close=\")\" >#{_item}</foreach>");
        }

        script = "<script>" + script + "</script>";

        return super.createSqlSource(configuration, script, parameterType);
    }
}

通过自己实现LanguageDriver,在服务器启动的时候,就会将我们自定义的标签解析为动态SQL语句,其等同于:

@Select("SELECT * " +
        "FROM admin_role " +
        "WHERE id IN " +
        "<foreach item='item' index='index' collection='list'open='(' separator=',' close=')'>" +
        "#{item}" +
        "</foreach>")
List<RoleDO> selectRolesByRoleId(List<Integer> roleIdList);

通过实现LanguageDriver,剥离了冗长的动态拼接SQL语句,简化了Select In的注解代码。

需要注意的是在使用Select In的时候,则与上述相反需务必在传入的参数前加@Param注解,否则会导致Mybatis找不到参数而抛出异常。

3、自定义Update的注解

在扩展update注解时,数据库每张表的字段和实体类的字段必须遵循一个约定(数据库中采用下划线命名法,实体类中采用驼峰命名法)。

当我们update的时候,会根据每个字段的映射关系,写出如下代码:

/**
     * 更新
     *
     * @param roleDO 角色信息
     * @return 影响行数
     */
    @Update("update admin_role set role_name = #{roleDO.roleName}, "
        + " enabled = #{roleDO.enabled}, deleted = #{roleDO.deleted}, modifierId = #{roleDO.modifierId},"
        + " modifier = #{roleDO.modifier}, last_modified = #{roleDO.lastModified} where id = #{roleDO.id}")
    int update(@Param(value = "roleDO") RoleDO roleDO);

上述的代码我们可以将实体类中的驼峰式代码转换为下划线式命名方式,这样就可以将这种映射规律自动化,但此代码存在一定的问题,就是当你在更新部分字段时其余所有字段原来的值必须传入,否则可能会将原有数据更新为null或空,亦或在更新时先查询原数据后将变更的数据进行操作,这样不仅增加了数据库查询操作且会造成代码冗余,而经过实现LanguageDriver后,注解代码如下:

    /**
     * 更新
     *
     * @param roleParam 角色信息
     */
    @Update("update admin_role (#{roleDO}) where id=#{id}")
    @Lang(SimpleUpdateLangDriver.class)
    void update(RoleParam roleParam);

相对于原始的代码量有很大的减少,尤其是对于一个类中字段越多,改善也就越明显。实现方式为:

package com.szss.admin.common;

import java.lang.reflect.Field;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.session.Configuration;

import com.google.common.base.CaseFormat;

/**
 * @author Allen
 * @date 2018/3/9
 *
 * 自定义Update注解,用于动态生成Update语句
 */
public class SimpleUpdateLangDriver extends XMLLanguageDriver implements LanguageDriver {

    /**
     * Pattern静态申明
     */
    private final Pattern inPattern = Pattern.compile("\\(#\\{(\\w+)\\}\\)");

    /**
     * 实现自定义Update注解
     * @param configuration 配置参数
     * @param script 入参
     * @param parameterType 参数类型
     * @return 转换后的SqlSource
     */
    @Override
    public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {

        Matcher matcher = inPattern.matcher(script);
        if (matcher.find()) {
            StringBuilder sb = new StringBuilder();
            sb.append("<set>");

            for (Field field : parameterType.getDeclaredFields()) {
                // 排除被Invisble修饰的变量
                if (!field.isAnnotationPresent(Invisible.class)) {
                    String tmp = "<if test=\"_field != null\">_column=#{_field},</if>";
                    sb.append(tmp.replaceAll("_field", field.getName()).replaceAll("_column",
                        CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, field.getName())));
                }
            }

            sb.deleteCharAt(sb.lastIndexOf(","));
            sb.append("</set>");

            script = matcher.replaceAll(sb.toString());
            script = "<script>" + script + "</script>";
        }

        return super.createSqlSource(configuration, script, parameterType);
    }
}

注意此处在传入的参数前无需加入@Param注解。

4、自定义Insert的注解 同理

我们可以抽象化Insert操作,简化后的Insert注解为:

    /**
     * 插入
     *
     * @param roleParam 角色信息
     */
    @Insert("insert into admin_role (#{roleDO})")
    @Lang(SimpleInsertLangDriver.class)
    void insert(RoleParam roleParam);

SimpleInsertLanguageDriver实现类代码如下:

package com.szss.admin.common;

import java.lang.reflect.Field;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.session.Configuration;

import com.google.common.base.CaseFormat;

/**
 * @author Allen
 * @date 2018/3/9
 *
 * 自定义Insert注解,用于动态生成Insert语句
 */
public class SimpleInsertLangDriver extends XMLLanguageDriver implements LanguageDriver {

    /**
     * Pattern静态申明
     */
    private final Pattern inPattern = Pattern.compile("\\(#\\{(\\w+)\\}\\)");

    /**
     * 实现自定义Insert注解
     * @param configuration 配置参数
     * @param script 入参
     * @param parameterType 参数类型
     * @return 转换后的SqlSource
     */
    @Override
    public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {

        Matcher matcher = inPattern.matcher(script);
        if (matcher.find()) {
            StringBuilder sb = new StringBuilder();
            StringBuilder tmp = new StringBuilder();
            sb.append("(");

            for (Field field : parameterType.getDeclaredFields()) {
                if (!field.isAnnotationPresent(Invisible.class)) {
                    sb.append(
                        CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, field.getName())
                            + ",");
                    tmp.append("#{" + field.getName() + "},");
                }
            }

            sb.deleteCharAt(sb.lastIndexOf(","));
            tmp.deleteCharAt(tmp.lastIndexOf(","));
            sb.append(") values ("
                + tmp.toString() + ")");

            script = matcher.replaceAll(sb.toString());
            script = "<script>" + script + "</script>";
        }

        return super.createSqlSource(configuration, script, parameterType);
    }
}

至此我们完成了基本的@Select、@Update、@Insert自定义注解,简单化繁杂的拼接SQL语句的尴尬,但以上代码在SimpleSelectLangDriver中还有一定的局限性,比如对于一些字段我们需要使用like来进行查询,这时就需要对上述自定义Seleect注解进行完善以实现各种业务的场景。

四、注意事项&遇到的一些坑

  • 务必确保数据库中列名和实体类中字段能一一对应。
  • 在使用自定义SQL解析器的时候,只能传入一个参数,即相应的对象参数即可;传入多个参数会导致解析器中获得到的class对象改变,使得sql解析异常。
  • Update的实现能满足大部分的业务,但有些业务场景可以会遇到根据查询条件来更新查询参数的情况,比如Update user SET uesr_name = ‘王雷’  WHERE user_name = ‘小王’; 在这样的场景中请不要使用自定义的SQL解析器。
  • 请使用Mybatis 3.3以上版本。3.2以下版本会存在一些Bug,在本例中使用的为mybatis-spring-boot-starter1.3.1,其Mybatis为3.4.5。

五、总结

通过实现Language Driver,我们可以很方便的自定义自己的注解。在遵循一些约定的情况下(数据库下划线命名,实体驼峰命名),我们可以大幅度的减少SQL的编写量,并且可以完全的屏蔽掉麻烦的XML编写方式,再也不用再编写复杂的拼接动态SQL的烦恼,简化工作,提高开发效率。

    //简洁的数据库操作
    /**
     *  查询角色信息列表
     *
     * @param roleParam 查询参数
     * @return 角色列表
     */
    @Select("select id,name,description,enabled,deleted,date_created as dateCreated,last_modified as lastModified"
        + " from admin_role (#{roleParam})")
    @Lang(SimpleSelectLangDriver.class)
    List<RoleDO> findListRoleByPage(ListRoleParam roleParam);

    /**
     * 插入
     *
     * @param roleParam 角色信息
     */
    @Insert("insert into admin_role (#{roleDO})")
    @Lang(SimpleInsertLangDriver.class)
    void insert(RoleParam roleParam);

    /**
     * 更新
     *
     * @param roleParam 角色信息
     */
    @Update("update admin_role (#{roleDO}) where id=#{id}")
    @Lang(SimpleUpdateLangDriver.class)
    void update(RoleParam roleParam);

通过@Lang注解以及自定义LanguageDriver类实现来简化数据库操作,不仅代码减少便于可读的同时,还避免了在更新时需要获取原数据的操作。

注:上述通过@Lang及实现LanguageDriver类的方法目前已基本不建议采用了,可采取Mybatis Plus来进行取代更为方便和快捷,具体可参加本博客的另一篇文章<Spring Boot环境下Mybatis Plus的快速应用>

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Mybatis动态SQL的实现示例

    场景 在实际应用开发过程中,我们往往需要写复杂的 SQL 语句,需要拼接,而拼接SQL语句又稍微不注意,由于引号,空格等缺失可能都会导致错误. Mybatis提供了动态SQL,也就是可以根据用户提供的参数,动态决定查询语句依赖的查询条件或SQL语句的内容. 动态SQL标签 if 和 where 标签 <!--动态Sql : where / if--> <select id="dynamicSql" resultType="com.lks.domain.Use

  • mybatis3使用@Select等注解实现增删改查操作

    1.需要的jar包 2.目录树 3.具体代码 一.需要的jar包 第一个:mybatis的jar包 第二个:mysql数据的驱动 二.目录树 三.具体代码 使用框架,配置文件先行! conf.xml:(配置 登录数据库,映射文件) <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN

  • mybatisplus @Select注解中拼写动态sql异常问题的解决

    目录 mybatisplus @Select注解中拼写动态sql异常 出现原因 解决方案 在注解上使用动态SQL(@select使用if) 用script标签包围 用Provider去实现SQL拼接 说明 mybatisplus @Select注解中拼写动态sql异常 使用mybatisplus后,手写SQL语句很少了,偶尔使用@Select时, 之前一直用实体类传递参数,完全能够正常使用,今天换成了参数传递,报下面的错误 @Select("<script>" +"

  • 在Mybatis @Select注解中实现拼写动态sql

    现在随着mybatis plus的应用,越来越多的弱化了SQL语句,对于单表操作可以说几乎不需要进行自己编写SQL语句了,但对于多表查询操作目前mybatis plus还没有很好的支持,还需要自己编写SQL语句,如: import java.util.List; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.anno

  • Mybatis如何实现@Select等注解动态组合SQL语句

    目录 一.背景说明 二.实现方案 三. 实现自定义注解 1.自定义Select注解 2.自定义Select in注解 3.自定义Update的注解 4.自定义Insert的注解 同理 四.注意事项&遇到的一些坑 五.总结 一.背景说明 由于以前在项目中一直使用sqlmap.xml进行mybatis语句的编写和实现,其xml实现动态更新和查询较为方便,而目前由于技术框架所定,采用@Select.@Insert等注解方式来实现对应的持久化操作(MyBatis提供了简单的Java注解,使得我们可以不配

  • 动态组合SQL语句方式实现批量更新的实例

    Default.aspx 复制代码 代码如下: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Index.aspx.cs" Inherits="Index" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w

  • Mybatis基于注解形式的sql语句生成实例代码

    对其做了些优化,但此种sql生成方式仅适用于复杂程度不高的sql,所以实用性不是很高,仅仅是写着玩的,知道点mybatis的注解形式的使用方式,可能以后会逐渐完善起来.第一次写博客,写的简单点. package com.bob.config.mvc.mybatis; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retenti

  • Mybatis如何使用ognl表达式实现动态sql

    本文讲述在mybatis中如何使用ognl表达式实现动态组装sql语句 新建Users实体类: public class Users { private Integer uid; private String userName; private String tel; //添加上面私有字段的get.set方法 } 新建一个Dao接口类,mybatis配置文件在配置namespace属性时需要加入这个类的完整类名,找到这个类里的方法执行: public interface UserDao { /*

  • mybatis的mapper特殊字符转移及动态SQL条件查询小结

    目录 前言 条件查询 快速入门 if标签 where标签 choose when otherwise标签 foreach标签 场景案例 前言 我们知道在项目开发中之前使用数据库查询,都是基于jdbc,进行连接查询,然后是高级一点jdbcTemplate进行查询,但是我们发现还是不是很方便,有大量重复sql语句,与代码偶合,效率低下,于是就衍生出来ORM框架,如Mybatis,Hibernate,还有SpringBoot的,Spring Data JPA 条件查询 我们知道在mybatis map

  • SQL SERVER 中构建执行动态SQL语句的方法

    1 :普通SQL语句可以用exec执行 Select * from tableName exec('select * from tableName') exec sp_executesql N'select * from tableName' -- 请注意字符串前一定要加N 2:字段名,表名,数据库名之类作为变量时,必须用动态SQL declare @fname varchar(20) set @fname = 'FiledName' --Select @fname from tableName

  • mybatis对于list更新sql语句的写法说明

    目录 对于list更新sql语句的写法 批量更新 mybatis动态更新sql语句 注意参数是实体 对于list更新sql语句的写法 批量更新 <update id="updateConfigureNames" parameterType="java.util.List">     <foreach collection="list" item="item" index="index" o

  • 学习SQL语句(强大的group by与select from模式)

    强大的group by 复制代码 代码如下: select stdname, isnull(sum( case stdsubject when ' 化学 ' then Result end), 0 ) [化学], isnull(sum( case stdsubject when ' 数学 ' then Result end), 0 ) [数学], isnull(sum( case stdsubject when ' 物理 ' then Result end), 0 ) [物理], isnull(

  • Mybatis打印替换占位符后的完整Sql教程

    利用mybtis插件打印完整的sql,将占位符?替换成实际值 import org.apache.ibatis.cache.CacheKey; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.Paramete

  • mybatis注解动态sql注入map和list方式(防sql注入攻击)

    目录 网上的教程 我的教程(防sql注入攻击) 注入Map 注入List 封装foreach mybatis防止sql注入的循环map写法 网上的教程 配置xml 注解中写xml脚本@Select() 使用Java类中的Java方法拼写sql语句(不防sql注入攻击的纯字符串拼接) 我的教程(防sql注入攻击) 注入Map Mapper层代码 @Repository public interface ManageMapper { @SelectProvider(type = ManageProv

随机推荐