在前面分別講解了通過mybatis執行簡單的增刪改,多表聯合查詢,那么自然不能缺少存儲過程調用,而且還帶分頁功能。
注意:表結構參見上篇講解聯合查詢的表。
一、查詢某班級以及該班級下面所有學生的記錄
上面這個查詢可以用sql語句表示為:
select c.class_id,c.class_name,s.s_id,s.s_name from Class c left join ClassStudent cs on c.class_id = cs.class_id left join Student s on cs.student_id = s.s_id where c.class_id = 1
查詢出來的結果是:
那么如果這個班級里面學生超級多,要通過分頁查詢,並且用存儲過程該怎么通過mybatis調用了???
二、在sql2008r2里面新建一個分頁查詢的存儲過程,如下
create proc [dbo].[SP_PAGE_GetClassStudent] ( @ClassID int, --班級編碼 @PageSize int, --每頁顯示多少條 @PageIndex int , --查詢第多少頁 @PageCount int output, --總頁數輸出 @RecordCount int output --總記錄數輸出 ) as begin declare @startIndex int declare @endIndex int declare @sqlString varchar(4000) declare @QueryPageSql nvarchar(4000) declare @QueryCountString nvarchar(4000) --1、拼接sql語句 SET @sqlString ='(select *,ROW_NUMBER() over (order by t.s_id) as rowId from ( select c.class_id,c.class_name,s.s_id,s.s_name from Class c left join ClassStudent cs on c.class_id = cs.class_id left join Student s on cs.student_id = s.s_id where c.class_id = '+str(@ClassID)+' )t) temp' --2、計算總頁數,總記錄數 SET @QueryCountString = 'select @RecordCount=count(1),@PageCount=CEILING((COUNT(1)+0.0)/' + CAST(@PageSize AS NVARCHAR)+') from ' +@SqlString ; --PRINT(@QueryCountString) EXEC SP_EXECUTESQL @QueryCountString,N'@RecordCount int output,@PageCount int output', @RecordCount output, @PageCount output --3、判斷輸入的當前頁數大於實際總頁數,則把實際總頁數賦值給當前頁數 IF @PageIndex > CEILING((@RecordCount+0.0)/@PageSize) BEGIN SET @PageIndex = CEILING((@RecordCount+0.0)/@PageSize) END SET @startIndex=(@PageIndex-1)*@PageSize+1 SET @endIndex=@PageIndex*@PageSize --4、執行分頁查詢 SET @QueryPageSql ='select * from ' + @SqlString + ' where rowId between ' + str(@startIndex) + ' and ' + str(@endIndex); --PRINT(@QueryPageSql) EXEC (@QueryPageSql) end
不難看出,這個存儲過程一共有5個參數,3個輸入參數,2個輸出參數,我們通過sql2008r2自帶的工具執行這個存儲過程,如下:

點擊“確定”會在sql查詢窗體中出現如下sql語句塊:
USE [mybatisDB] GO DECLARE @return_value int, @PageCount int, @RecordCount int EXEC @return_value = [dbo].[SP_PAGE_GetClassStudent] @ClassID = 1, @PageSize = 2, @PageIndex = 5, @PageCount = @PageCount OUTPUT, @RecordCount = @RecordCount OUTPUT SELECT @PageCount as N'@PageCount', @RecordCount as N'@RecordCount' SELECT 'Return Value' = @return_value GO
仔細一看,怎么多出了一個輸出參數“@return_value”了,可是在設計存儲過程的時候只有2個輸出參數,此處先埋下伏筆。
三、在原先項目下新建com.mybatis.proc這個包,在包里面建一個procMapper.xml的映射文件,之后需要在該項目的mybatis的配置文件conf.xml下面對這個procMapper.xml文件進行注冊,具體是:
<!-- 注冊映射文件 --> <mappers> <!-- 通過xml方式映射 --> <mapper resource="com/mybatis/proc/procMapper.xml"/> </mappers>
四、填充procMapper.xml,編寫測試代碼
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.mybatis.proc.procMapper"> <!-- 1、測試錯誤,待解決 --> <!-- ### Error querying database. Cause: com.microsoft.sqlserver.jdbc.SQLServerException: '@P0' 附近有語法錯誤。 ### The error may exist in com/mybatis/proc/procMapper.xml ### The error may involve com.mybatis.proc.procMapper.getCSPMap ### The error occurred while setting parameters ### SQL: CALL SP_PAGE_GetClassStudent(?,?,?,?,?) --> <!-- <select id="getClassStudentsProc" statementType="CALLABLE" parameterMap="getCSPMap" resultType="com.mybatis.bean.StudentClassView"> CALL SP_PAGE_GetClassStudent(?,?,?,?,?) </select> <parameterMap type="java.util.Map" id="getCSPMap"> <parameter property="ClassID" mode="IN" jdbcType="INTEGER" /> <parameter property="PageSize" mode="IN" jdbcType="INTEGER" /> <parameter property="PageIndex" mode="IN" jdbcType="INTEGER" /> <parameter property="PageCount" mode="OUT" jdbcType="INTEGER" /> <parameter property="RecordCount" mode="OUT" jdbcType="INTEGER" /> </parameterMap> --> <!--2、測試通過,返回數據有冗余 --> <!-- <select id="getClassStudentsProc" statementType="CALLABLE" resultType="com.mybatis.bean.StudentClassView"> <![CDATA[ {#{rval,mode=OUT,jdbcType=INTEGER} = CALL SP_PAGE_GetClassStudent( #{ClassID,mode=IN,jdbcType=INTEGER}, #{PageSize,mode=IN,jdbcType=INTEGER}, #{PageIndex,mode=IN,jdbcType=INTEGER}, #{PageCount,mode=OUT,jdbcType=INTEGER}, #{RecordCount,mode=OUT,jdbcType=INTEGER} )} ]]> </select> --> <!-- 3、測試通過,一對多格式 --> <select id="getClassStudentsProc" statementType="CALLABLE" resultMap="cvs2Map"> <![CDATA[ {#{rval,mode=OUT,jdbcType=INTEGER} = CALL SP_PAGE_GetClassStudent( #{ClassID,mode=IN,jdbcType=INTEGER}, #{PageSize,mode=IN,jdbcType=INTEGER}, #{PageIndex,mode=IN,jdbcType=INTEGER}, #{PageCount,mode=OUT,jdbcType=INTEGER}, #{RecordCount,mode=OUT,jdbcType=INTEGER} )} ]]> </select> <resultMap type="com.mybatis.bean.StudentClassView2" id="cvs2Map"> <id property="class_id" column="class_id" /> <result property="class_name" column="class_name" /> <result property="rowId" column="rowId" /> <collection property="students" ofType="com.mybatis.bean.Student"> <id property="studentid" column="s_id"/> <result property="studentname" column="s_name"/> </collection> </resultMap> </mapper>
我最開始的寫法是按照注釋里面的那種方式,也就是第1種,可是在執行的時候卻報錯“ '@P0' 附近有語法錯誤。 ”,查了n久,不知道是那里問題,如是將sql追蹤器打開,檢測到該存儲過程在數據庫中的執行代碼如下:

怎么這里第一個參數就是輸出參數,與之前在sql編輯器里面一樣的現象,看來這種寫法是不行的,可是我找不到解決方案,如果誰發現了希望能告訴我,在這里謝謝。
如是按照另一種方式,執行卻ok了。測試代碼如下:
先定義一個實體類接收存儲過程返回的結果
package com.mybatis.bean; public class StudentClassView { private int s_id; private String s_name; private int class_id; private String class_name; private int rowId;
//private List<Student> students; 如果啟用這個屬性,則將s_id,s_name注釋掉,將類名稱修改為StudentClassView2
public int getRowId() { return rowId; } public void setRowId(int rowId) { this.rowId = rowId; } public int getS_id() { return s_id; } public void setS_id(int s_id) { this.s_id = s_id; } public String getS_name() { return s_name; } public void setS_name(String s_name) { this.s_name = s_name; } public int getClass_id() { return class_id; } public void setClass_id(int class_id) { this.class_id = class_id; } public String getClass_name() { return class_name; } public void setClass_name(String class_name) { this.class_name = class_name; } @Override public String toString() { return "studentclass2 [s_id=" + s_id + ", s_name=" + s_name + ", class_id=" + class_id + ", class_name=" + class_name + ", rowId=" + rowId + "]"; } }
具體測試代碼
package com.mybatis.proc; import java.util.*; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import com.mybatis.bean.Student; import com.mybatis.bean.StudentClassView; import com.mybatis.bean.StudentClassView2; import com.mybatis.util.MybatisUtils; public class testproc { public static void main(String[] args) { SqlSessionFactory factory = MybatisUtils.getFactory(); SqlSession session = null; try { session = factory.openSession(true); Map<String, Integer> paramMap = new HashMap<>(); paramMap.put("ClassID", 1); paramMap.put("PageSize", 2); paramMap.put("PageIndex", 5); String statement = "com.mybatis.proc.procMapper.getClassStudentsProc"; //1、數據冗余 // List<StudentClassView> scv = session.selectList(statement, paramMap); // for(int i = 0;i<scv.size();i++){ // System.out.println(scv.get(i)); // } //2、一對多展現 StudentClassView2 scv2 = session.selectOne(statement, paramMap); List<Student> students = scv2.getStudents(); System.out.println("排序字段:"+scv2.getRowId() +";班級編號:"+scv2.getClass_id()+";班級名稱:"+scv2.getClass_name()); for(int i = 0;i<students.size();i++){ System.out.println(students.get(i)); } Integer pageCount = paramMap.get("PageCount"); Integer recordCount = paramMap.get("RecordCount"); System.out.println("總頁數:"+pageCount); System.out.println("總記錄數:"+recordCount); } catch (Exception e) { e.printStackTrace(); }finally{ session.close(); } } }
在控制台中輸出的結果如下:

到此測試成功。
五、需要注意的問題
1、存儲過程的output參數,只能通過傳入的map獲取
2、存儲過程的return結果需要使用 參數x=call procName(?,?...)的第一個參數接收,需要指定對應的mode為OUT類型
3、<select id="getClassStudentsProc" statementType="CALLABLE" resultType="com.mybatis.bean.StudentClassView">
resultType是映射數據查詢出來的記錄,存儲過程必須標明類型為CALLABLE
4、<![CDATA[ sql語句 ]]> 作用是避免不必要的麻煩(如<,>,&,'," 會被xml轉義成<,>,&,',")。
5、resultType="com.mybatis.bean.StudentClassView"會決定測試代碼中session.selectList(statement, paramMap)的返回類型,是一一對應的關系。
6、<select id="getClassStudentsProc" statementType="CALLABLE" resultMap="cvs2Map">
如果這里使用resultMap,那么返回的結果可以自定義,同樣與session.selectOne(statement, paramMap)返回類型一致。
