2011-12-09 26 views
25

Tôi đang cố gắng xác định chú thích đơn giản @Select trong MyBatis để có được tập hợp các đối tượng dựa trên tiêu chí được xác định bởi mệnh đề IN. SQL trông giống như sau:Tôi có thể chuyển Danh sách dưới dạng thông số cho một người lập bản đồ MyBatis không?

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

Danh sách được tạo động, vì vậy tôi không biết có bao nhiêu thông số sẽ có. Tôi muốn chỉ cần vượt qua trong một List các giá trị, một cái gì đó như:

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

tôi là tạo ra một thể hiện của các Mapper nơi chú thích trên được xác định và gọi đó là như sau:

List<Integer> empIds = Arrays.asList(1, 2, 3); 
List<Employee> result = mapper.selectSpecificEmployees(empIds); 

Tôi đã phát hiện ra rằng điều này không hoạt động.

org.apache.ibatis.exceptions.PersistenceException:
### Lỗi truy vấn cơ sở dữ liệu. Nguyên nhân: java.lang.NullPointerException
### Các lỗi có thể liên quan
com.mycompany.MySourceMapper.selectSpecificEmployees-Inline
### Các lỗi xảy ra trong khi thiết thông số ### Nguyên nhân: java.lang.NullPointerException tại org.apache.ibatis.exceptions.ExceptionFactory.wrapException (ExceptionFactory.java:8) tại org.apache.ibatis.session.defaults.DefaultSqlSession.selectList (DefaultSqlSession.java:77) tại org.apache.ibatis.session .defaults.DefaultSqlSession.selectList (DefaultSqlSession.java:69) tại org.apache.ibatis.binding.MapperMethod.executeForList (MapperMethod.java:85) tại org.apache.ibatis.binding.MapperMethod.execute (M apperMethod.java:65) tại org.apache.ibatis.binding.MapperProxy.invoke (MapperProxy.java:35) tại $ Proxy23.selectSpecificProductTypes (Nguồn không xác định) tại com.mycompany.MySourceMapperDebug.testSelectSpecificEmployees (MySourceMapperDebug.java: 60) tại sun.reflect.NativeMethodAccessorImpl.invoke0 (Native Method) tại sun.reflect.NativeMethodAccessorImpl.invoke (Unknown Source) tại sun.reflect.DelegatingMethodAccessorImpl.invoke (Unknown Source) tại java.lang.reflect.Method .invoke (Nguồn không xác định) tại junit.framework.TestCase.runTest (TestCase.java:154) tại junit.framework.TestCase.runBare (TestCase.java:127) tại junit.framework.TestResult $ 1.protect (TestResult .java: 106) tại junit.framework.TestResult.runProtected (TestResult.java:124) tại junit.framework.TestResult.run (TestResult.java:109) tại junit.framework.TestCase.run (TestCase.java:118) at junit.framework.TestSuite.runTest (TestSuite.java:208) tại junit.framework.TestSuite.run (TestSuite.java:203) tại org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run (JUnit3TestReference.java:130) tại org.eclipse.jdt.internal.junit.runner.TestExecution.run (TestExecution.java:38) tại org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests (RemoteTestRunner. java: 467) tại org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests (RemoteTestRunner.java:683) tại org.eclipse.jdt.internal.junit. runner.RemoteTestRunner.run (RemoteTestRunner.java:390) tại org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main (RemoteTestRunner.java:197) Gây ra bởi: java.lang.NullPointerException tại org.apache .ibatis.type.UnknownTypeHandler.setNonNullParameter (UnknownTypeHandler.java: 21) tại org.apache.ibatis.type.BaseTypeHandler.setParameter (BaseTypeHandler.java:23) tại org.apache.ibatis.executor.parameter.DefaultParameterHandler.setParameters (DefaultParameterHandler.java:73) tại tổ chức. apache.ibatis.executor.statement.PreparedStatementHandler.parameterize (PreparedStatementHandler.java:61) tại org.apache.ibatis.executor.statement.RoutingStatementHandler.parameterize (RoutingStatementHandler.java:43) tại org.apache.ibatis.executor. SimpleExecutor.prepareStatement (SimpleExecutor.java:56) tại org.apache.ibatis.executor.SimpleExecutor.doQuery (SimpleExecutor.java:40) tại org.apache.ibatis.executor.BaseExecutor.queryFromDatabase (BaseExecutor.java:216) tại org.apache.ibatis.executor.BaseExecutor.query (BaseExecutor.java: 95) tại org.apache.ibatis.executor.CachingExecutor.query (CachingExecutor.java:72) tại sun.reflect.NativeMethodAccessorImpl.invoke0 (Phương pháp gốc) tại sun.reflect.NativeMethodAccessorImpl.invoke (Nguồn không xác định) tại sun.reflect.DelegatingMethodAccessorImpl.invoke (Nguồn không rõ) tại java.lang.reflect.Method.invoke (Nguồn không xác định) tại org.apache.ibatis.plugin.Invocation.proceed (Invocation.java:31)
.. 36 thêm

Tôi nghĩ rằng vấn đề nằm trong chính chú thích. Điều này có vẻ như nó sẽ là một yêu cầu khá phổ biến. Tôi có cần phải tự chuyển đổi số List thành một số String và chuyển số đó thành thông số String thay vì số List<Integer> không? Hoặc có một số cú pháp khác để chuyển một số List làm tham số cho chú thích MyBatis không?

+0

Bạn có thể sử dụng cả cấu hình dựa trên xml và chú thích cùng nhau xem bài đăng này, vui lòng http://stackoverflow.com/questions/8788250/xml-annotation-based-configuration-for-mybatis/9076435 –

Trả lời

39

Tôi chưa bao giờ sử dụng chú thích và MyBatis trước đây; Tôi đã luôn luôn đi các tuyến đường tập tin cấu hình xml (không ngụ ý có bất cứ điều gì sai trái với việc sử dụng các chú thích; chỉ giải thích tôi không thể giúp bạn có).

Điều đó đang được nói, page 46 from the MyBatis user guide:

foreach

Một cần thiết chung cho SQL động là sự cần thiết để lặp qua một bộ sưu tập , thường để xây dựng một tình trạng IN. Ví dụ:

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

Yếu tố foreach là rất mạnh mẽ, và cho phép bạn chỉ định một bộ sưu tập , khai báo các biến mục và chỉ số có thể được sử dụng bên trong cơ thể của nguyên tố này. Nó cũng cho phép bạn chỉ định mở và chuỗi đóng và thêm dấu phân cách để đặt giữa các lần lặp. Yếu tố thông minh ở chỗ nó sẽ không vô tình thêm thêm các dấu tách .

+0

cảm ơn điều này đã giúp tôi –

+0

Vui mừng nghe nó! – Dave

+0

Bạn có biết nếu mybatis có thể xử lý các giới hạn trong mệnh đề IN (như oracle được giới hạn ở 1000). Tôi đoán chúng ta nên tự lo cho bản thân mình –

4

Với một chút chi phí, bạn có thể sử dụng JAVA để tạo Chuỗi động sau khi xử lý Danh sách.

  1. Xác định Chọn nhà cung cấp mà bạn có thể xây dựng truy vấn động của bạn:

    @SelectProvider(type = com.data.sqlprovider.EmployeeSQLBuilder.class, method =  
    "selectSpecificEmployees") 
    List<Employee> selectSpecificEmployees(@Param("employeeIds") List<Integer> 
        employeeIds); 
    
  2. Trong com.data.sqlprovider.EmployeeSQLBuilder.lớp, Sử dụng StringBuilder, tạo ra các truy vấn

    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

Bởi vì các nhân viên có thể là số nguyên, bạn có thể thoát khỏi điều này. Tuy nhiên, nếu đây là những chuỗi bạn có một vấn đề nếu chuỗi có bất kỳ ký tự đặc biệt trong nó – kasdega

+1

nối các giá trị tham số trong chuỗi SQL là điều ác. Và nếu tham số là chuỗi, đây là công thức cho SQL injection. – blackwizard

0

Nếu bạn muốn sử dụng foreach và chú thích, bạn có thể sử dụng cú pháp sau:

@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); 

(sao chép từ answer đó)

4

Tôi đang đối mặt với các vấn đề tương tự chính xác gần đây. Theo hiểu biết của tôi, bạn thích sử dụng công cụ ánh xạ Java thay vì XML, giống như ở đây.

Sau đây là những gì tôi đang làm để giải quyết vấn đề này bằng cách sử dụng: SqlBuilder.

Lớp builder 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"; 
     } 
    } 

} 

Các mapper:

@SelectProvider(type = EmployeeSqlBuilder.class, method = "getEmployees") 
List<RecordSubjectEx> getEmployees(@Param("employeeIds") List employeeIds); 

Vậy là xong.

EmployeeSqlBuilder sẽ tự động tạo câu lệnh sql. Tôi đang sử dụng hàm getSqlConditionCollection để thực hiện thao tác logic. Tất nhiên bạn có thể đóng gói getSqlConditionCollection như một hàm tĩnh trong một lớp, đó là những gì tôi đang làm trong dự án thực, sau đó bạn có thể sử dụng nó dễ dàng từ SqlBuilder khác.

Các vấn đề liên quan