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

Bil*_*ard 31 java mybatis

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

SELECT * FROM employees WHERE employeeID IN (1, 2, 3);
Run Code Online (Sandbox Code Playgroud)

该列表是动态生成的,因此我不知道它将包含多少参数.我想传递一个List值,例如:

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

我正在创建一个Mapper定义上面注释的实例,并按如下方式调用它:

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

我发现这不起作用.

org.apache.ibatis.exceptions.PersistenceException:
###查询数据库时出错.原因:java.lang.NullPointerException
###错误可能涉及
com.mycompany.MySourceMapper.selectSpecificEmployees-Inline
###设置参数时发生错误###原因:org.apache.ibatis.exceptions中的java.lang.NullPointerException位于org.apache.ibatis.session.defaults.DefaultSqlSession.select上的org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:77)的.ExceptionFactory.wrapException(ExceptionFactory.java:8)(DefaultSqlSession.java) :69)org.apache.ibatis.binding.binperMethod.executeForList(MapperMethod.java:85)位于org.apache.ibatis.binding的org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:65).位于sun.reflect的sun.reflect.NativeMethodAccessorImpl.invoke0(本地方法)的com.mycompany.MySourceMapperDebug.testSelectSpecificEmployees(MySourceMapperDebug.java:60)处的$ Proxy23.selectSpecificProductTypes(未知来源)的MapperProxy.invoke(MapperProxy.java:35) sun.reflect中的.NativeMethodAccessorImpl.invoke(未知来源).在junit.framework.TestCase.runBare(testCase.java:127)的junit.framework.TestCase.runTest(TestCase.java:154)的java.lang.reflect.Method.invoke(未知来源)中委托MethodAethodAccessorImpl.invoke(未知来源) )在junit.framework.TestResult $ 1.protect(TestResult.java:106)的junit.framework.TestResult.runProtected(TestResult.java:124)junit.framework.TestResult.run(TestResult.java:109)junit. framework.TestCase.run(TestCase.java:118)位于junit.framework.TestSuite.runTest(TestSuite.java:208),位于org.eclipse.jdt.internal的junit.framework.TestSuite.run(TestSuite.java:203)位于org.eclipse.jdt.internal.junit的org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)的.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:130) .runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)atg.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)org.eclipse.jdt.internal.junit.runner.RemoteTestRunner .RUN(RemoteTestRu nner.java:390)at or.e.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)引起:org.apache.ibatis.type.UnknownTypeHandler.setNonNullParameter的java.lang.NullPointerException( UnknownTypeHandler.java:21)atg.apache.ibatis.type.BaseTypeHandler.setParameter(BaseTypeHandler.java:23)atg.apache.ibatis.executor.parameter.DefaultParameterHandler.setParameters(DefaultParameterHandler.java:73)org.apache位于org.apache.ibatis.executor.SimpleExecutor.prepareStatement的org.apache.ibatis.executor.statement.RoutingStatementHandler.parameterize(RoutingStatementHandler.java:43)中的.ibatis.executor.statement.PreparedStatementHandler.parameterize(PreparedStatementHandler.java:61) (SimpleExecutor.java:56)位于org.apache的org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:40)org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:216). org.apache.ibatis.executor上的ibatis.executor.BaseExecutor.query(BaseExecutor.java:95).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. org.apache.ibatis.plugin.Invocation.proceed(Invocation.java:31)中的lang.reflect.Method.invoke(未知来源)
... 36更多

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

Dav*_*ave 45

我之前从未使用过注释和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>
Run Code Online (Sandbox Code Playgroud)

foreach元素非常强大,允许您指定集合,声明可以在元素主体内使用的项和索引变量.它还允许您指定开始和结束字符串,并添加一个分隔符以放置在迭代之间.元素很聪明,因为它不会意外地附加额外的分隔符.


Arn*_*aud 6

如果您想使用foreachand 注释,可以使用以下语法:

@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);
Run Code Online (Sandbox Code Playgroud)

(从该答案复制)


hem*_*vsn 5

在处理List之后,您可以使用JAVA构建动态字符串.

  1. 定义一个Select Provider,您可以在其中构建动态查询:

    @SelectProvider(type = com.data.sqlprovider.EmployeeSQLBuilder.class, method =      
     "selectSpecificEmployees")
     List<Employee> selectSpecificEmployees(@Param("employeeIds") List<Integer> 
      employeeIds);
    
    Run Code Online (Sandbox Code Playgroud)
  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();
    }
    
    Run Code Online (Sandbox Code Playgroud)

  • 因为可能的employeeIds是整数,所以你可以逃避这一点.但是,如果这些是字符串,如果字符串中包含任何特殊字符,则会出现问题 (2认同)

Vig*_*gor 5

我最近面临完全相同的问题。就我的理解而言,您更喜欢使用Javamapper而不是XML,这是相同的。

以下是我使用SqlBuilder进行处理的方法。

sql builder类:

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

}
Run Code Online (Sandbox Code Playgroud)

映射器:

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

而已。

EmployeeSqlBuilder会自动生成SQL语句。我正在使用一个函数 getSqlConditionCollection来进行逻辑操作。当然,您可以将其getSqlConditionCollection作为静态函数封装在类中,这是我在实际项目中正在做的事情,然后可以从其他SqlBuilder中轻松使用它。