簡體   English   中英

我可以將 List 作為參數傳遞給 MyBatis 映射器嗎?

[英]Can I pass a List as a parameter to a MyBatis mapper?

我試圖在 MyBatis 中定義一個簡單的@Select注釋,以根據 IN 子句定義的條件獲取對象集合。 SQL 看起來像:

SELECT * FROM employees WHERE employeeID IN (1, 2, 3);

列表是動態生成的,所以我不知道它會有多少參數。 我只想傳入一個值List ,例如:

@Select("SELECT * FROM employees WHERE employeeID IN( #{employeeIds} )")
List<Employee> selectSpecificEmployees(@Param("employeeIds") List<Integer> employeeIds);

我正在創建Mapper一個實例,其中定義了上面的注釋並按如下方式調用它:

List<Integer> empIds = Arrays.asList(1, 2, 3);
List<Employee> result = mapper.selectSpecificEmployees(empIds);

我發現這行不通。

org.apache.ibatis.exceptions.PersistenceException:
### 查詢數據庫時出錯。 原因:java.lang.NullPointerException
###錯誤可能涉及
com.mycompany.MySourceMapper.selectSpecificEmployees-內聯
### 設置參數時出錯### 原因:org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:8) 處的java.lang.NullPointerException at org.apache.ibatis.session.defaults.DefaultSqlSession .selectList(DefaultSqlSession.java:77) at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:69) at org.apache.ibatis.binding.MapperMethod.executeForList(MapperMethod.java:85) at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:65) at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:35) at $Proxy23.selectSpecificProductTypes(Unknown Source) at com.mycompany .MySourceMapperDebug.testSelectSpecificEmployees(MySourceMapperDebug.java:60) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java. junit.framework.Te 上的 lang.reflect.Method.invoke(Unknown Source) stCase.runTest(TestCase.java:154) at junit.framework.TestCase.runBare(TestCase.java:127) at junit.framework.TestResult$1.protect(TestResult.java:106) at junit.framework.TestResult.runProtected( TestResult.java:124) at junit.framework.TestResult.run(TestResult.java:109) at junit.framework.TestCase.run(TestCase.java:118) at junit.framework.TestSuite.runTest(TestSuite.java:208) ) at junit.framework.TestSuite.run(TestSuite.java:203) at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:130) at org.eclipse.jdt.internal。 junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) at org.eclipse.jdt.internal.junit.runner。 RemoteTestRunner.runTests(RemoteTestRunner.java:683) 在 org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) 在 org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main( RemoteTestRunner.java:197) 引起:java.lan g.NullPointerException at org.apache.ibatis.type.UnknownTypeHandler.setNonNullParameter(UnknownTypeHandler.java:21) at org.apache.ibatis.type.BaseTypeHandler.setParameter(BaseTypeHandler.java:23) at org.apache.ibatis.executor。 parameter.DefaultParameterHandler.setParameters(DefaultParameterHandler.java:73) 在 org.apache.ibatis.executor.statement.PreparedStatementHandler.parameterize(PreparedStatementHandler.java:61) 在 org.apache.ibatis.executor.statement.RoutingStatementHandler.parameterize(RoutingStatementHandler. java:43) 在 org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:56) 在 org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:40) 在 org.apache.ibatis.executor .BaseExecutor.queryFromDatabase(BaseExecutor.java:216) at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:95) at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:72) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflec t.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.apache.ibatis.plugin.Invocation.proceed(Invocation.爪哇:31)
... 36 更多

我認為問題出在注釋本身。 這似乎是一個相當普遍的要求。 我是否需要自己將List轉換為String並將其作為String參數而不是List<Integer>傳入? 或者是否有其他語法可以將List作為參數傳遞給 MyBatis 注釋?

我以前從未使用過注解和 MyBatis; 我總是走 xml 配置文件路線(並不暗示使用注釋有什么問題;只是解釋我無法幫助你)。

話雖如此, MyBatis 用戶指南的第 46 頁

foreach

動態 SQL 的另一個常見需求是需要迭代集合,通常是為了構建 IN 條件。 例如:

<select id="selectPostIn" resultType="domain.blog.Post">
    SELECT *
    FROM POST P
    WHERE ID in
    <foreach item="item" index="index" collection="list"
        open="(" separator="," close=")">
          #{item}
    </foreach>
  </select>

foreach 元素非常強大,它允許您指定一個集合、聲明可以在元素主體內使用的項目和索引變量。 它還允許您指定開始和結束字符串,並添加分隔符以放置在迭代之間。 該元素很聰明,因為它不會意外地附加額外的分隔符。

通過一些開銷,您可以在處理列表后使用 JAVA 構建動態字符串。

  1. 定義一個 Select Provider,您可以在其中構建動態查詢:

     @SelectProvider(type = com.data.sqlprovider.EmployeeSQLBuilder.class, method = "selectSpecificEmployees") List<Employee> selectSpecificEmployees(@Param("employeeIds") List<Integer> employeeIds);
  2. 在 com.data.sqlprovider.EmployeeSQLBuilder.class 中,使用 StringBuilder,生成查詢

     public String selectSpecificEmployees(Map<String, Object> parameters) { List<Integer> employeeIds = (List<Integer>) parameters.get("employeeIds"); StringBuilder builder = new StringBuilder("SELECT id, name FROM employees where id IN ("); for (int i : employeeIds) { builder.append(i + ","); } builder.deleteCharAt(builder.length() - 1); builder.append(")"); System.out.println(builder.toString()); return builder.toString(); }

我最近面臨着完全相同的問題。 根據我的理解,您更喜歡使用Java映射器而不是XML ,這在這里是相同的。

以下是我正在使用以下方法處理它: SqlBuilder

sql 構建器類:

public class EmployeeSqlBuilder {

    public String getEmployees(final List employeeIds) {

        String strSQL = new SQL() {{
            SELECT("*");
            FROM("employees");
            if (employeeIds != null) {
                WHERE(getSqlConditionCollection("employeeID", employeeIds));
            }
        }}.toString();

        return strSQL;
    }

    private String getSqlConditionCollection(String field, List conditions) {
        String strConditions = "";
        if (conditions != null && conditions.size() > 0) {
            int count = conditions.size();
            for (int i = 0; i < count; i++) {
                String condition = conditions.get(i).toString();

                strConditions += condition;
                if (i < count - 1) {
                    strConditions += ",";
                }
            }
            return field + " in (" + strConditions + ")";
        } else {
            return "1=1";
        }
    }

}

映射器:

@SelectProvider(type = EmployeeSqlBuilder.class, method = "getEmployees")
List<RecordSubjectEx> getEmployees(@Param("employeeIds") List employeeIds);

就是這樣。

EmployeeSqlBuilder會動態生成sql 語句。 我正在使用函數getSqlConditionCollection進行邏輯操作。 當然你可以把getSqlConditionCollection封裝成一個類中的靜態函數,這也是我在實際項目中所做的,然后你就可以從其他SqlBuilder中輕松使用它。

如果您希望使用foreach和注解,您可以使用以下語法:

@Select("<script>" +
         "SELECT * FROM employees WHERE employeeID IN " +
           "<foreach item='item' index='index' collection='employeeIds'" +
             " open='(' separator=',' close=')'>" +
             " #{item}" +
           "</foreach>" +
         "</script>") 
List<Employee> selectSpecificEmployees(@Param("employeeIds") List<Integer> employeeIds);

(從那個答案復制)

MyBatis 直接支持 List 參數。

假設這是你的 dao 層:

public List<User> getUsersByIds(List<Integer> ids);

你想傳遞一個 ids 列表。 然后,在您的 mapper.xml 中,您可以使用它們:

    <select id="getUsersByIds" resultType="entity.User">
      select * from user
      where id = #{list[0]} or id = #{list[1]}
    </select>

您可以使用#{list[0]}#{list[1]}來獲取列表值。

    @Test
    public void getUsersByIds(){
        List<Integer> ids = new ArrayList<Integer>();
        ids.add(1);
        ids.add(2);
        List<User> users = userDao.getUsersByIds(ids);
        // you will get correct result
    }

雖然這個問題是多年前的,@Dave 的回答是正確的,但今天我想知道如何通過 java 而不是 xml config 解決它,我得到了它並與你們中的一些人分享。

    @Autowired
    PersonMapper personMapper;

    @Test
    public void testListParams(){
        String[] ids = {"1","2","3","4"};
        List<String> idList = Arrays.asList(ids);
        String idListToString = idList.toString().substring(1, idList.toString().length() - 1);
        List<Person> people = personMapper.selectByIds(idListToString);
        people.forEach(System.out::println);
    }

在 PersonMapper.java 中

    @Select("select id,name,age from person where id in ( ${ids} )") // attention: here is $ not #, $ is use to replace with string, # is use to replace ? with param
    List<Person> selectByIds(@Param("ids") String ids);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM