繁体   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