我正在尝试在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内联
###设置参数时发生错误 ###原因:java.lang.NullPointerException at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:8) 在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) 在$ Proxy23.selectSpecificProductTypes(未知来源) 在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.lang.reflect.Method.invoke(Unknown Source) 在junit.framework.TestCase.runTest(TestCase.java:154) 在junit.framework.TestCase.runBare(TestCase.java:127) 在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) 在junit.framework.TestSuite.run(TestSuite.java:203) 在org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:130) 在org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) 在org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) 在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.lang.NullPointerException at org.apache.ibatis.type.UnknownTypeHandler.setNonNullParameter(UnknownTypeHandler.java:21) at org.apache.ibatis.type.BaseTypeHandler.setParameter(BaseTypeHandler.java:23) 在org.apache.ibatis.executor.parameter.DefaultParameterHandler.setParameters(DefaultParameterHandler.java:73) at 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) at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:216) 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) 在org.apache.ibatis.plugin.Invocation.proceed(Invocation.java:31)
......还有36个
我认为问题在于注释本身。这似乎是一个相当普遍的要求。我是否需要自己将List
转换为String
并将其作为String
参数而不是List<Integer>
传递?或者是否有其他语法将List
作为参数传递给MyBatis注释?
答案 0 :(得分:44)
我之前从未使用过注释和MyBatis;我总是走xml配置文件路径(并不意味着使用注释有任何问题;只是解释我无法帮助你)。
话虽如此,page 46 from the MyBatis user guide:
<强>的foreach 强>
动态SQL的另一个常见需求是需要迭代a 收集,通常建立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元素非常强大,允许你指定一个 集合,声明可以在里面使用的项目和索引变量 元素的主体。它还允许您指定打开和 关闭字符串,并在迭代之间添加一个分隔符。 元素很聪明,因为它不会意外地追加额外的 分隔符。
答案 1 :(得分:5)
在处理List之后,您可以使用JAVA构建动态字符串。
定义一个可以构建动态查询的选择提供程序:
@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();
}
答案 2 :(得分:4)
我最近面临着完全相同的问题。根据我的理解,您更喜欢使用Java
mapper而不是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中使用它。
答案 3 :(得分:1)
如果您希望使用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);
(从answer复制)
答案 4 :(得分:0)
MyBatis支持直接列出参数。
假设这是您的图层:
public List<User> getUsersByIds(List<Integer> ids);
您要传递ID列表。 然后,您可以在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
}
答案 5 :(得分:-1)
尽管这个问题是很多年前的,@ 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);