[英]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 构建动态字符串。
定义一个 Select Provider,您可以在其中构建动态查询:
@SelectProvider(type = com.data.sqlprovider.EmployeeSQLBuilder.class, method = "selectSpecificEmployees") List<Employee> selectSpecificEmployees(@Param("employeeIds") List<Integer> employeeIds);
在 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.