我们想只能使用注释与实际;我们真的想要避免xml。我们正在试图使用"在"条款:

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

实际上似乎并不能够挑选出列整数,并把这些纳入所得的查询。它似乎"不轻轻的"和我们没有得到任何结果。

它看起来像我们可以完成这一使用XML映,但我们真的想要避免的。是否有一个正确的注释法为这个?

有帮助吗?

解决方案

我相信这是JDBC的准备语句,而不是MyBatis的的细微差别。有解释这个问题,并提供各种解决方案这里。不幸的是,这些解决方案都是可行的应用程序,但是,它仍然是一个很好看的懂准备语句的局限与问候到“IN”的条款。将(可能不理想)可以对事物的DB-具体一侧找到。例如,PostgreSQL中,可以使用:

"SELECT * FROM blog WHERE id=ANY(#{blogIds}::int[])"

“ANY”是相同的“IN”和“:: INT []”的类型是铸造参数转换为整数的数组。被送入语句的参数应该是这个样子:

"{1,2,3,4}"

其他提示

我相信答案是相同的这个问题。您可以使用MyBatis的动态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);

<script>元件能够用于注解动态SQL分析和执行。它必须是查询字符串的第一个内容。没有必须是在它的前面,甚至没有空白空间。

请注意这两个变量,你可以在不同的XML脚本标记使用遵循相同的命名约定定期查询,所以如果你想使用的不是“参数1”等名称,“参数2”来称呼你的方法参数等。 ..需要前缀的每个参数与@Param注解

有一些研究关于这一主题。

  1. 一个官方的解决方案,从实际上是把你的动态sql @Select("<script>...</script>").但是,写xml在java注释是相当非正常.想想这个 @Select("<script>select name from sometable where id in <foreach collection=\"items\" item=\"item\" seperator=\",\" open=\"(\" close=\")\">${item}</script>")
  2. @SelectProvider 工作正常。但这是有点复杂阅读。
  3. 预处理不能让你集合名单的整数。 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索引。

看来实际上的动态sql机构,它已经实现的 SqlNode.apply(DynamicContext).然而,@没有选择 <script></script> 注释将不会通过的参数通过 DynamicContext

也参看

  • 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其可以将自己的语法来很之一。
  • 解决方案4:写你自己的LanguageDriver其汇编SQL与一些模板渲染,就像很速项目。在这种方式,可以甚至整合常规。

我的项目采取解决方案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);
    }
}

和使用情况:

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

我在我的代码做了一个小把戏。

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

和我用这个在MyHandler的SqlMapper:

    @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;

它现在:) 我希望这会帮助别人。

叶夫

其他选项可以是

    public class Test
    {
        @SuppressWarnings("unchecked")
        public static String getTestQuery(Map<String, Object> params)
        {

            List<String> idList = (List<String>) params.get("idList");

            StringBuilder sql = new StringBuilder();

            sql.append("SELECT * FROM blog WHERE id in (");
            for (String id : idList)
            {
                if (idList.indexOf(id) > 0)
                    sql.append(",");

                sql.append("'").append(id).append("'");
            }
            sql.append(")");

            return sql.toString();
        }

        public interface TestMapper
        {
            @SelectProvider(type = Test.class, method = "getTestQuery")
List<Blog> selectBlogs(@Param("idList") int[] ids);
        }
    }

在我的项目中,我们已经在使用谷歌番石榴,所以一个快捷键是。

public class ListTypeHandler implements TypeHandler {

    @Override
    public void setParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, Joiner.on(",").join((Collection) parameter));
    }
}

在Oracle中,我使用的汤姆凯特的分词器处理未知列表的大小(给定的IN子句和做多插件来绕过它的加重甲骨文1K限制)。这是为VARCHAR2,但它可以用于数字被调整(或你可以只依赖Oracle知道“1” = 1 /颤动)。

假设你传递或执行的MyBatis咒语得到ids为字符串,使用它:

select @Select("SELECT * FROM blog WHERE id IN (select * from table(string_tokenizer(#{ids}))")

代码:

create or replace function string_tokenizer(p_string in varchar2, p_separator in varchar2 := ',') return sys.dbms_debug_vc2coll is
    return_value SYS.DBMS_DEBUG_VC2COLL;
    pattern varchar2(250);
begin
    pattern := '[^(''' || p_separator || ''')]+' ;

    select
        trim(regexp_substr(p_string, pattern, 1, level)) token
    bulk collect into
        return_value
    from
        dual
    where
        regexp_substr(p_string, pattern, 1, level) is not null
    connect by
        regexp_instr(p_string, pattern, 1, level) > 0;

    return return_value;
end string_tokenizer;

您可以使用自定义类型处理器做到这一点。例如:

public class InClauseParams extends ArrayList<String> {
   //...
   // marker class for easier type handling, and avoid potential conflict with other list handlers
}

注册以下类型处理器在你的MyBatis配置(或在注释具体说明):

public class InClauseTypeHandler extends BaseTypeHandler<InClauseParams> {

    @Override
    public void setNonNullParameter(final PreparedStatement ps, final int i, final InClauseParams parameter, final JdbcType jdbcType) throws SQLException {

        // MySQL driver does not support this :/
        Array array = ps.getConnection().createArrayOf( "VARCHAR", parameter.toArray() );
        ps.setArray( i, array );
    }
    // other required methods omitted for brevity, just add a NOOP implementation
}

然后,您可以使用它们像这样

@Select("SELECT * FROM foo WHERE id IN (#{list})"
List<Bar> select(@Param("list") InClauseParams params)

然而,这将的作为MySQL的工作,这是因为连接器的MySQL确实准备语句不支持setArray()

为MySQL一种可能的解决方法是使用FIND_IN_SET代替IN

@Select("SELECT * FROM foo WHERE FIND_IN_SET(id, #{list}) > 0")
List<Bar> select(@Param("list") InClauseParams params)

和您的类型处理器变为:

@Override
    public void setNonNullParameter(final PreparedStatement ps, final int i, final InClauseParams parameter, final JdbcType jdbcType) throws SQLException {

        // note: using Guava Joiner! 
        ps.setString( i, Joiner.on( ',' ).join( parameter ) );
    }

请注意:我不知道FIND_IN_SET的表现,所以测试这一点,如果是很重要的。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top