MyBatis之四:調用存儲過程含分頁、輸入輸出參數


  在前面分別講解了通過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轉義成&lt;,&gt;,&amp;,&apos;,&quot;)。

  5、resultType="com.mybatis.bean.StudentClassView"會決定測試代碼中session.selectList(statement, paramMap)的返回類型,是一一對應的關系。

  6、<select id="getClassStudentsProc" statementType="CALLABLE" resultMap="cvs2Map"> 

    如果這里使用resultMap,那么返回的結果可以自定義,同樣與session.selectOne(statement, paramMap)返回類型一致。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM