30

MyBatis では注釈のみを使用したいと考えています。私たちは本当にxmlを避けようとしています. 「IN」句を使用しようとしています。

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

MyBatis は、int の配列を選択して結果のクエリに入れることができないようです。「そっと失敗」したようで、結果が返されません。

XML マッピングを使用してこれを達成できるように見えますが、それは避けたいと考えています。これには正しい注釈構文がありますか?

4

9 に答える 9

42

答えは、この質問で与えられたものと同じだと思います。次のようにして、アノテーションで 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 スクリプト タグで使用できる変数は、通常のクエリと同じ命名規則に従うことに注意してください。したがって、「param1」、「param2」など以外の名前を使用してメソッド引数を参照する場合は、各引数の前に @Param アノテーションを付ける必要があります。

于 2014-03-25T21:19:29.350 に答える
24

これは、MyBatis ではなく、jdbc の準備済みステートメントのニュアンスだと思います。この問題を説明し、さまざまな解決策を提供するリンクがここにあります。残念ながら、これらの解決策はどれもあなたのアプリケーションでは実行できませんが、「IN」節に関するプリペアド ステートメントの制限を理解するには、それでも良い読み物です。解決策 (最適ではないかもしれません) は、DB 固有の側面で見つけることができます。たとえば、postgresql では、次のように使用できます。

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

"ANY" は "IN" と同じで、"::int[]" は引数を int の配列にキャストする型です。ステートメントに入力される引数は次のようになります。

"{1,2,3,4}"
于 2010-08-09T16:35:45.287 に答える
18

このトピックに関するいくつかの調査がありました。

  1. mybatis からの公式の解決策の 1 つは、動的 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 は char'1,2,3,4'を numberに変換します1
  4. FIND_IN_SET は mysql インデックスでは機能しません。

mybatis 動的 SQL メカニズムを調べてください。これは によって実装されていますSqlNode.apply(DynamicContext)。ただし、<script></script>注釈のない @Select は、パラメーターを介して渡しませんDynamicContext

こちらもご覧ください

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

そう、

  • 解決策 1: @SelectProvider を使用する
  • 解決策 2: SQL を常にコンパイルする LanguageDriver を拡張しDynamicSqlSourceます。ただし、\"どこにでも書く必要があります。
  • 解決策 3: 独自の文法を mybatis に変換できる LanguageDriver を拡張します。
  • 解決策 4: mybatis-velocity プロジェクトと同じように、テンプレート レンダラーで SQL をコンパイルする独自の LanguageDriver を作成します。このようにして、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);
    }
}

そして使用法:

@Lang(MybatisExtendedLanguageDriver.class)
@Select("SELECT " + COLUMNS + " FROM sometable where id IN (#{ids})")
List<SomeItem> loadByIds(@Param("ids") List<Integer> ids);
于 2015-03-16T11:51:36.743 に答える
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);
}

そして、この 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;

それは今動作します:)これが誰かを助けることを願っています。

エフゲニー

于 2010-12-17T15:54:39.240 に答える
4

他のオプションは

    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);
        }
    }
于 2012-04-26T10:10:34.563 に答える
0

Oracle では、Tom Kyte のトークナイザーの変形を使用して、不明なリスト サイズを処理します (Oracle の IN 句に対する 1k 制限と、それを回避するために複数の IN を実行することの悪化を考えると)。これは varchar2 用ですが、数値に合わせて調整できます (または、'1' = 1 /shudder を知っている Oracle に頼ることもできます)。

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;
于 2016-06-24T12:54:53.930 に答える
0

私のプロジェクトでは、すでに Google Guava を使用しているため、簡単なショートカットは.

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));
    }
}
于 2016-05-23T15:41:27.247 に答える