是否可以将 List 作为参数传递给 MyBatis 映射器?

2022-09-01 05:56:49

我试图在MyBatis中定义一个简单的注释,以根据IN子句定义的条件获取对象的集合。SQL 看起来像这样:@Select

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:
### Error query database.原因: java.lang.NullPointerException
### 该错误可能涉及
com.mycompany.MySourceMapper.selectSpecificEmployees-Inline
### 设置参数时发生错误 原因: java.lang.NullPointerException at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:8) at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:77) atorg.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 $Proxy 23.selectSpecificProductTypes(Unknown Source) at com.mycompany.MySourceMapperDebug.testSelectSpecificEmployees(MySourceMapperDebug.java:60) atsun.reflect.nativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at junit.framework.TestCase.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) atorg.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) Caused by: java.lang.NullPointerException atorg.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) at org.apache.ibatis.executor.statement.PreparedStatementHandler.parameterize(PreparedStatementHandler.java:61) atorg.apache.ibatis.executor.statement.RoutingStatementHandler.parameterize(RoutingStatementHandler.java:43) at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:56) at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:40) at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:216) at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:95) at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:95)org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:72) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.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.continue(Invocation.java:31)
...另外 36

我认为问题出在注释本身。这似乎是一个相当普遍的要求。我是否需要将 转换为我自己并将其作为参数而不是参数传入?或者是否有其他语法可以将 a 作为参数传递给 MyBatis 注释?ListStringStringList<Integer>List


答案 1

我以前从未使用过注释和MyBatis;我一直采用xml配置文件路由(并不意味着使用注释有什么问题;只是解释我无法帮助你)。

话虽如此,MyBatis用户指南中的第46页

前言

动态 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 元素非常强大,允许您指定集合、声明可在元素主体内部使用的项和索引变量。它还允许您指定开始和结束字符串,并添加分隔符以放置在迭代之间。该元素很聪明,因为它不会意外地附加额外的分隔符。


答案 2

在处理列表后,您可以使用 JAVA 构建动态字符串。

  1. 定义可在其中生成动态查询的选择提供程序:

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

推荐