My節で単純な@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-Inline
###パラメータの設定中にエラーが発生しました###原因:org.Apache.ibatisでorg.Apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.Java:8)でJava.lang.NullPointerExceptionが発生しました。 org.Apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.Java:69)のorg.Apache.ibatis.binding.MapperMethod.executeForList(MapperMethod。のsession.defaults.DefaultSqlSession.selectList(DefaultSqlSession.Java:77) 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 )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)at junit.fram junit.framework.TestCase.runBare(TestCase.Java:127)のjunit.framework.TestResult $ 1.protect(TestResult.Java:106)のjunit.framework.TestResultでework.TestCase.runTest(TestCase.Java:154) 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)org.Eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.Java:130)at org.Eclipse.jdtでjunit.framework.TestSuite.run(TestSuite.Java:203)で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)at org.Eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.Java:390)at org.Eclipse.jdt.internal.junit.runner.RemoteTestRunner。 main(RemoteTestRunner.Java:197)原因: org.Apache.ibatis.type.BaseTypeHandler.setParameter(BaseTypeHandler.Java:23)のorg.Apache.ibatis.type.UnknownTypeHandler.setNonNullParameter(UnknownTypeHandler.Java:21)のorg.Apache.ibatisでのJava.lang.NullPointerException org.Apache.ibatis.executor.statement.PreparedStatementHandler.parameterize(PreparedStatementHandler.Java:61)at org.Apache.ibatis.executor.statement.RoutingStatementHandler.parameterize(でexecutor.parameter.DefaultParameterHandler.setParameters(DefaultParameterHandler.Java:73) RoutingStatementHandler.Java:43)org.Apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.Java:56)at org.Apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.Java:40)at org.Apache.ibatis org.Apache.ibatis.executor.BaseExecutor.query(BaseExecutor.Java:95)at org.Apache.ibatis.executor.CachingExecutor.query(CachingExecutor.Java:72)の.executor.BaseExecutor.queryFromDatabase(BaseExecutor.Java:216) )Su。のSun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)でn.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.proceed( Invocation.Java:31)
...さらに36
問題は注釈自体にあると思います。これはかなり一般的な要件のようです。 List
を自分でString
に変換し、List<Integer>
ではなくString
パラメーターとして渡す必要がありますか?または、List
をパラメータとしてMyBatisアノテーションに渡すための構文は他にありますか?
これまでにアノテーションとMyBatisを使用したことはありません。私は常にxml構成ファイルのルートを行ってきました(注釈の使用に問題があることを意味するわけではありません。
つまり、 MyBatisユーザーガイドの46ページ :
foreach
動的SQLのもう1つの一般的な必要性は、多くの場合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を使用して、リストの処理後に動的な文字列を作成できます。
動的クエリを作成できる選択プロバイダーを定義します。
@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);
(そこからコピー answer )
MyBatisはList paramsを直接サポートします。
これがdaoレイヤーであると仮定します。
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
}