如何使用带有iBatis(myBatis)的注释进行IN查询?

dir*_*ond 28 java annotations ibatis mybatis

我们只想使用MyBatis的注释; 我们真的想避免使用xml.我们正在尝试使用"IN"子句:

@Select("SELECT * FROM blog WHERE id IN (#{ids})") 
List<Blog> selectBlogs(int[] ids); 
Run Code Online (Sandbox Code Playgroud)

MyBatis似乎无法选择整数数组并将其放入生成的查询中.它似乎"软弱地失败",我们没有得到任何结果.

看起来我们可以使用XML映射来实现这一点,但我们真的想避免这种情况.是否有正确的注释语法?

Lor*_*igs 36

我相信答案与这个问题的答案是一致的.您可以通过执行以下操作在注释中使用myBatis Dynamic SQL:

@Select({"<script>",
         "SELECT *", 
         "FROM blog",
         "WHERE id IN", 
           "<foreach item='item' index='index' collection='list'",
             "open='(' separator=',' close=')'>",
             "#{item}",
           "</foreach>",
         "</script>"}) 
List<Blog> selectBlogs(@Param("list") int[] ids);
Run Code Online (Sandbox Code Playgroud)

<script>元素支持对注释进行动态SQL解析和执行.它必须是查询字符串的第一个内容.没有什么必须在它前面,甚至没有白色空间.

请注意,您可以在各种XML脚本标记中使用的变量遵循与常规查询相同的命名约定,因此如果您要使用"param1","param2"等其他名称引用您的方法参数...需要使用@Param注释为每个参数添加前缀.

  • 对于遇到此问题的任何人,我之前使用的是3.1.1版,但它无法正常工作.我更新到版本3.2.7,现在它可以工作,所以必须在这两个版本之间修复. (3认同)

小智 22

我相信这是jdbc准备好的陈述的细微差别,而不是MyBatis.有一个链接在这里解释这个问题,并提供各种解决方案.遗憾的是,这些解决方案都不适用于您的应用程序,但是,对于理解"IN"子句中预准备语句的限制仍然是一个很好的解读.可以在特定于DB的方面找到解决方案(可能是次优的).例如,在postgresql中,可以使用:

"SELECT * FROM blog WHERE id=ANY(#{blogIds}::int[])"
Run Code Online (Sandbox Code Playgroud)

"ANY"与"IN"相同,":: int []"是将参数类型转换为int数组.进入语句的参数应该类似于:

"{1,2,3,4}"
Run Code Online (Sandbox Code Playgroud)


yeg*_*ong 15

有一些关于这个主题的研究.

  1. mybatis的官方解决方案之一是将动态sql放入@Select("<script>...</script>").但是,在java注释中编写xml是非常不合适的.想一想@Select("<script>select name from sometable where id in <foreach collection=\"items\" item=\"item\" seperator=\",\" open=\"(\" close=\")\">${item}</script>")
  2. @SelectProvider工作良好.但阅读起来有点复杂.
  3. PreparedStatement不允许您设置整数列表.pstm.setString(index, "1,2,3,4")会让你的SQL像这样select name from sometable where id in ('1,2,3,4').Mysql会将字符转换'1,2,3,4'为数字1.
  4. FIND_IN_SET不适用于mysql索引.

查看mybatis动态sql机制,它已经实现了SqlNode.apply(DynamicContext).但是,没有<script></script>注释的@Select 将不会传递参数viaDynamicContext

也可以看看

  • org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
  • org.apache.ibatis.scripting.xmltags.DynamicSqlSource
  • org.apache.ibatis.scripting.xmltags.RawSqlSource

所以,

  • 解决方案1:使用@SelectProvider
  • 解决方案2:扩展LanguageDriver,它总是将sql编译为DynamicSqlSource.但是,你仍然需要\"到处写.
  • 解决方案3:扩展LanguageDriver,它可以将您自己的语法转换为mybatis.
  • 解决方案4:编写自己的LanguageDriver,使用一些模板渲染器编译SQL,就像mybatis-velocity项目一样.通过这种方式,您甚至可以整合groovy.

我的项目采用解决方案3,这是代码:

public class MybatisExtendedLanguageDriver extends XMLLanguageDriver 
                                           implements LanguageDriver {
    private final Pattern inPattern = Pattern.compile("\\(#\\{(\\w+)\\}\\)");
    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\" separator=\",\" >#{__item}</foreach>)");
        }
        script = "<script>" + script + "</script>";
        return super.createSqlSource(configuration, script, parameterType);
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

@Lang(MybatisExtendedLanguageDriver.class)
@Select("SELECT " + COLUMNS + " FROM sometable where id IN (#{ids})")
List<SomeItem> loadByIds(@Param("ids") List<Integer> ids);
Run Code Online (Sandbox Code Playgroud)

  • 在2019年,似乎仍然是最好的解决方案〜 (3认同)
  • 无法相信我们在2016年仍然必须这样做.我错过了什么吗? (2认同)

小智 6

我在代码中做了一个小技巧.

public class MyHandler implements TypeHandler {

public void setParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
    Integer[] arrParam = (Integer[]) parameter;
    String inString = "";
    for(Integer element : arrParam){
      inString = "," + element;
    }
    inString = inString.substring(1);        
    ps.setString(i,inString);
}
Run Code Online (Sandbox Code Playgroud)

我在SqlMapper中使用了这个MyHandler:

    @Select("select id from tmo where id_parent in (#{ids, typeHandler=ru.transsys.test.MyHandler})")
public List<Double> getSubObjects(@Param("ids") Integer[] ids) throws SQLException;
Run Code Online (Sandbox Code Playgroud)

它现在有效:)我希望这会对某人有所帮助.

叶夫根尼·

  • 这将不起作用,因为它创建了一个查询,如带有 1 个参数的“where id_parent in ('1,2,3')”,而不是带有 3 个参数的“where id_parent in (1,2,3)”。如果您使用字符串会更糟,因为这会创建带有 1 个参数的 `where name in ('''Tom'',''Dick'',''Harry''')` 并且引号会加倍转义。 (2认同)