2011-12-09 3 views
25

저는 IN 절에 정의 된 기준에 따라 객체 컬렉션을 얻으려고 MyBatis에서 간단한 @Select 주석을 정의하려고합니다. SQL은 다음과 같습니다.List를 매개 변수로 MyBatis 매퍼에 전달할 수 있습니까?

SELECT * FROM employees WHERE employeeID IN (1, 2, 3); 

목록이 동적으로 생성되므로 목록의 개수가 얼마인지 알 수 없습니다.

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

나는 다음과 같이 호출 위의 주석이 정의 된 Mapper의 인스턴스를 생성하고 있어요 : 난 그냥 값의 List에 뭔가를 통과하고 싶습니다

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이 을 org.apache.ibatis.security.wrapException (ExceptionFactory.java:8) 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 (M apperMethod.java:65) com.mycompany.MySourceMapperDebug.testSelectSpecificEmployees에서 org.apache.ibatis.binding.MapperProxy.invoke (MapperProxy.java:35) $에서 Proxy23.selectSpecificProductTypes (알 수없는 소스) (MySourceMapperDebug.java에서 : 60)에서 sun.reflect.DelegatingMethodAccessorImpl.invoke sun.reflect.NativeMethodAccessorImpl.invoke (알 소스)에 sun.reflect.NativeMethodAccessorImpl.invoke0 (원시 메소드) (알 소스)에 에서 java.lang.reflect.Method의 .invoke (알 수없는 출처) at junit.framework.TestCase.runTest (TestCase.java:154) at junit.framework.TestCase.runBare (TestCase.java:127) at junit.framework.TestResult $ 1.protect (TestResult .java : 106)junit.framework.TestCase.run에서 junit.framework.TestResult.run (TestResult.java:109) 에서 junit.framework.TestResult.runProtected (TestResult.java:124) (TestCase.java:118) 에서 515,에서 (TestSuite.java:203) at junit.framework.TestSuite.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. org.apache에서 java.lang.NullPointerException이 : runner.RemoteTestRunner.run (RemoteTestRunner.java:390)는 org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main (RemoteTestRunner.java:197) 에 의해 발생 .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) at org. apache.ibatis.executor.statement.PreparedStatementHandler.parameterize (PreparedStatementHandler.java:61) at org.apache.ibatis.executor.statement.RoutingStatementHandler.parameterize (RoutingStatementHandler.java:43) at org.apache.ibatis.executor. SimpleExecutor.prepareStatement (SimpleExecutor.java:56) at 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) sun.reflect.NativeMethodAccessorImpl.invoke에서 sun.reflect.NativeMethodAccessorImpl.invoke0 (기본 방법) 에서 org.apache.ibatis.executor.CachingExecutor.query (CachingExecutor.java:72) (알 수없는 소스) 에서 에서 (알 수없는 소스) at org.apache.ibatis.plugin.Invocation.proceed (Invocation.java:31)
.. . 36 more

문제는 주석 자체에 있다고 생각합니다. 이것은 꽤 일반적인 요구 사항처럼 보입니다. List을 직접 String으로 변환하고 List<Integer> 대신 String 매개 변수로 전달해야합니까? 또는 List을 MyBatis 주석에 매개 변수로 전달하기위한 다른 구문이 있습니까?

+0

당신은 함께이 게시물을보고 XML과 어노테이션 기반 설정을 모두 사용할 수 있습니다 http://stackoverflow.com/questions/8788250/xml-annotation-based-configuration-for-mybatis/9076435 –

답변

39

전에 주석과 MyBatis를 사용한 적이 없습니다. 난 항상 xml 구성 파일을 라우트했습니다 (주석을 사용하는 데 문제가 있다는 것을 암시하지 않고 단지 내가 거기에서 당신을 도울 수 없다는 것을 설명합니다). 말했다되고 그건

, page 46 from the MyBatis user guide :

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는 요소는 매우 강력하고, 당신이하는 모음을 지정 내부 요소의 몸을 사용할 수있는 아이템 및 인덱스 변수를 선언 할 수 있습니다. 또한 여는 문자열과 닫는 문자열을 지정하고 반복 사이에 구분 기호를 추가 할 수 있습니다. 이 요소는 실수로 여분의 구분 기호를 추가하지 않는다는 점에서 현명합니다.

+0

덕분에이 나에게 다행 –

+0

를 도와주십시오 그것을 듣고! – Dave

+0

mybatis가 IN 절에서 한계를 처리 할 수 ​​있는지 알고 있습니까 (예 : oracle이 1000으로 제한됨). 나는 우리가 스스로 돌보아야한다고 생각한다. –

4

약간의 오버 헤드를 사용하면 JAVA를 사용하여 목록 처리 후 동적 문자열을 작성할 수 있습니다. com.data.sqlprovider.EmployeeSQLBuilder에서

@SelectProvider(type = com.data.sqlprovider.EmployeeSQLBuilder.class, method =  
"selectSpecificEmployees") 
List<Employee> selectSpecificEmployees(@Param("employeeIds") List<Integer> 
    employeeIds); 
  • :

    1. 는 동적 쿼리를 작성 할 수있는 공급자 선택을 정의합니다.클래스의 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

    가능한 employeeIds가 정수이기 때문에이 문제를 해결할 수 있습니다. 그러나 문자열 인 경우 문자열에 특수 문자가있는 경우 문제가 발생합니다. – kasdega

    +1

    SQL 문자열의 매개 변수 값을 연결하는 것이 잘못되었습니다. 그리고 매개 변수가 문자열 인 경우 SQL 주입을위한 방법입니다. – blackwizard

    0

    당신이, 당신이이 구문을 사용할 수 있습니다 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

    을 최근에 똑같은 문제에 직면하고 있습니다. 내 이해를 위해 XML 대신 Java 매퍼를 사용하는 것이 더 좋습니다. 여기서는 동일합니다.

    다음은 내가 사용하고있는 문제를 다루는 것입니다 : 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에서 쉽게 사용할 수 있습니다.

    관련 문제