一個使用MyBatis調用Oracle數據庫存儲過程的例子

個人電腦操做系統版本爲Win7旗艦版(ServicePack1),Oracle版本爲Oracle11gjava

程序使用的jar包有:mybatis-3.2.2.jar、ojdbc14-10.2.0.2.0.jarsql

本例中使用的配置文件mybatis-config.xml,能夠參見個人另外一篇Blog《一個簡單的MyBatis鏈接Oracle數據庫的例子》(http://my.oschina.net/Tsybius2014/blog/626206數據庫


先說下本文中描述的場景。apache

現有一些產品,每一個產品都有一些相關聯的崗位,這些崗位可能由不一樣的人擔任。人員信息表(person_info)表中記錄了每一個人的代碼(person_code)和名稱(person_name),人員任職表(time_on_duty)記錄了每一個產品的產品代碼(item_code)、崗位類型(duty_type)、相關人(person_code&person_name)、在崗狀況(is_on_duty),對於已離職的人,還須要記錄離職的日期和時間。產品相關人員的變更、增減是從另外一個系統推送來的,每次推送的數據,只有產品代碼(item_code)、崗位1任職人、崗位2任職人、崗位3任職人……等等。瀏覽器

建表語句(init_data.sql)以下所示,time_on_duty的主鍵(id)使用序列(seq_time_on_duty)來賦值:session

-- 人員信息表
create TABLE PERSON_INFO
(
    id number(12,0) PRIMARY KEY,
    person_code varchar2(20),
    person_name varchar2(50)
);

-- 插入人員信息基礎數據
insert into person_info (id, person_code, person_name) values (1, '10001', 'Tsybius');
insert into person_info (id, person_code, person_name) values (2, '10002', 'Galatea');
insert into person_info (id, person_code, person_name) values (3, '10003', 'Gaius');
insert into person_info (id, person_code, person_name) values (4, '10004', 'Quintus');
insert into person_info (id, person_code, person_name) values (5, '10005', 'Atia');
commit;

-- 人員任職表
create TABLE TIME_ON_DUTY
(
    id number(12,0) PRIMARY KEY,
    item_code varchar2(20),
    person_code varchar2(20),
    person_name varchar2(50),
    duty_type varchar2(20),
    is_on_duty varchar2(5),
    exit_date number(10,0) DEFAULT 0,
    exit_time number(10,0) DEFAULT 0,
    input_date number(10,0) DEFAULT to_number(to_char(sysdate,'yyyymmdd')),
    input_time number(10,0) DEFAULT to_number(to_char(sysdate,'hh24miss')),
    update_date number(10,0) DEFAULT to_number(to_char(sysdate,'yyyymmdd')),
    update_time number(10,0) DEFAULT to_number(to_char(sysdate,'hh24miss'))
);

-- 建立人員信息表序列
CREATE SEQUENCE SEQ_TIME_ON_DUTY
INCREMENT BY 1
START WITH 1
MAXVALUE 999999999999999999999999999
CYCLE 
CACHE 20;

創建存儲過程,對於每個崗位的輪替,僞代碼以下:mybatis

if (對應崗位人員 != null)
{
    bool 是否已更新 = false
    for (人員 : 當前產品同一崗位中全部的歷史任職人員)
    {
        if (老數據當前已離職 && 老數據的人 == 新數據的人)
        {
            老人員從新上崗
            是否已更新 = true
        } 
        else if (老數據當前有效&&老數據的人 != 新數據的人)
        {
            老人下崗,更新離職時間
        }
        else if (老數據當前有效&&老數據的人 == 新數據的人)
        {
            更新下更新日期
            是否已更新 = true
        }
    }
    if (!是否已更新)
    {
        新人上崗
        是否已更新 = true
    }
}
else 
{
    當前產品對應崗位全部的歷史任職人員,如還在職,所有標爲離職,同時更新離職時間
}

寫出的Oracle存儲過程(refresh_new_data.sql)代碼以下:app

-- 創建存儲過程 - 更新項目相關人
  CREATE OR REPLACE PROCEDURE refresh_new_data(p_item_code     IN VARCHAR2,
                                               p_duty_1        IN VARCHAR2,
                                               --p_duty_2        IN VARCHAR2,
                                               --p_duty_3        IN VARCHAR2,  -- 若有更多相關人在此繼續加入
                                               p_error_no      OUT NUMBER,   -- 錯誤號
                                               p_error_info    OUT VARCHAR2, -- 錯誤提示
                                               p_error_id      OUT NUMBER,   -- 錯誤序號
                                               p_error_sysinfo OUT VARCHAR2  -- 系統錯誤信息
                                               ) AS
  c_duty_type_1 CONSTANT VARCHAR2(2) := '1';
  --c_duty_type_2 CONSTANT VARCHAR2(2) := '2';
  --c_duty_type_3 CONSTANT VARCHAR2(2) := '3'; -- 若有更多相關人在此繼續加入
  c_exit    CONSTANT VARCHAR2(3) := '0';
  c_on_duty CONSTANT VARCHAR2(3) := '1';
  v_duty_1_is_refresh VARCHAR2(5);
  --v_duty_2_is_refresh VARCHAR2(5);
  --v_duty_3_is_refresh VARCHAR2(5); -- 若有更多相關人在此繼續加入
  v_id_tmp          time_on_duty.id % TYPE;
  v_item_code_tmp   time_on_duty.item_code % TYPE;
  v_person_code_tmp time_on_duty.person_code % TYPE;
  v_duty_type_tmp   time_on_duty.duty_type % TYPE;
  v_is_on_duty_tmp  time_on_duty.is_on_duty % TYPE;
  CURSOR c_duty_1 IS
    SELECT id, item_code, person_code, duty_type, is_on_duty
    FROM   time_on_duty
    WHERE  item_code = p_item_code AND
           duty_type = c_duty_type_1;
  --CURSOR c_duty_2 IS
  --  SELECT id, item_code, person_code, duty_type, is_on_duty
  --  FROM   time_on_duty
  --  WHERE  item_code = p_item_code AND
  --         duty_type = c_duty_type_2;
  --CURSOR c_duty_3 IS
  --  SELECT id, item_code, person_code, duty_type, is_on_duty
  --  FROM   time_on_duty
  --  WHERE  item_code = p_item_code AND
  --         duty_type = c_duty_type_3; -- 若有更多相關人在此繼續加入
  -- 當前日期&當前時間
  v_curr_date NUMBER(10, 0) := TO_NUMBER(TO_CHAR(SYSDATE, 'yyyymmdd'));
  v_curr_time NUMBER(10, 0) := TO_NUMBER(TO_CHAR(SYSDATE, 'hh24miss'));
BEGIN
  dbms_output.put_line('----------- PROCUDURE START -----------');
  -- ITEM CODE 不能爲空
  IF p_item_code IS NULL
  THEN
    BEGIN
      dbms_output.put_line('ITEM CODE IS NULL');
      p_error_no      := 101;
      p_error_info    := 'ITEM CODE IS NULL';
      p_error_id      := SQLCODE;
      p_error_sysinfo := 'CUSTOM DEFECT TYPE';
      RETURN;
    END;
  END IF;
  -- 相關人更新 START --
  --
  -- 更新 duty_1
  --
  IF p_duty_1 IS NOT NULL
  THEN
    BEGIN
      -- 若是傳入的新人不爲空,須要作進一步判斷
      dbms_output.put_line('CHECK DUTY_1');
      v_duty_1_is_refresh := 'no';
      -- 使用遊標遍歷相同產品相同職務的人
      OPEN c_duty_1;
      LOOP
        FETCH c_duty_1
          INTO v_id_tmp, v_item_code_tmp, v_person_code_tmp, v_duty_type_tmp, v_is_on_duty_tmp;
        EXIT WHEN c_duty_1 % NOTFOUND;
        dbms_output.put_line('==ITEM FOUND==');
        dbms_output.put_line('id          : ' || v_id_tmp);
        dbms_output.put_line('item_code   : ' || v_item_code_tmp);
        dbms_output.put_line('person_code : ' || v_person_code_tmp);
        dbms_output.put_line('duty_type   : ' || v_duty_type_tmp);
        dbms_output.put_line('is_on_duty  : ' || v_is_on_duty_tmp);
        IF v_is_on_duty_tmp = c_exit AND
           p_duty_1 = v_person_code_tmp
        THEN
          -- 老人當前失效且老人與新人是同一人的狀況:從新啓用老人
          UPDATE time_on_duty
          SET    is_on_duty = c_duty_type_1, update_date = v_curr_date, update_time = v_curr_time
          WHERE  id = v_id_tmp;
          v_duty_1_is_refresh := 'yes';
        ELSIF v_is_on_duty_tmp = c_on_duty AND
              p_duty_1 <> v_person_code_tmp
        THEN
          -- 老人當前有效且老人與新人不是同一人的狀況:老人離職
          UPDATE time_on_duty
          SET    is_on_duty  = c_exit,
                 exit_date   = v_curr_date,
                 exit_time   = v_curr_time,
                 update_date = v_curr_date,
                 update_time = v_curr_time
          WHERE  id = v_id_tmp;
        ELSIF v_is_on_duty_tmp = c_exit AND
              p_duty_1 = v_person_code_tmp
        THEN
          -- 老人當前有效且老人與新人是同一人的狀況:更新下更新時間字段便可
          UPDATE time_on_duty
          SET    update_date = v_curr_date, update_time = v_curr_time
          WHERE  id = v_id_tmp;
          v_duty_1_is_refresh := 'yes';
        END IF;
      END LOOP;
      CLOSE c_duty_1;
      -- 遊標使用結束,關閉遊標
      -- 如新人之前未擔任過本產品的對應職務則插入一條新記錄
      IF v_duty_1_is_refresh = 'no'
      THEN
        INSERT INTO time_on_duty
          (id,
           item_code,
           person_code,
           person_name,
           duty_type,
           is_on_duty,
           exit_date,
           exit_time,
           input_date,
           input_time,
           update_date,
           update_time)
          SELECT seq_time_on_duty.NEXTVAL,
                 a.item_code,
                 a.person_code,
                 b.person_name,
                 a.duty_type,
                 a.is_on_duty,
                 a.exit_date,
                 a.exit_time,
                 a.input_date,
                 a.input_time,
                 a.update_date,
                 a.update_time
          FROM   (SELECT p_item_code   AS item_code,
                         p_duty_1      AS person_code,
                         c_duty_type_1 AS duty_type,
                         c_on_duty     AS is_on_duty,
                         0             AS exit_date,
                         0             AS exit_time,
                         v_curr_date   AS input_date,
                         v_curr_time   AS input_time,
                         v_curr_date   AS update_date,
                         v_curr_time   AS update_time
                  FROM   dual) a
          LEFT   JOIN (SELECT person_code, person_name
                       FROM   person_info) b ON a.person_code = b.person_code;
        v_duty_1_is_refresh := 'yes';
      END IF;
    END;
  ELSE
    -- 若是傳入的新人爲空,則認爲老人離職,無人補缺
    UPDATE time_on_duty
    SET    is_on_duty  = c_exit,
           exit_date   = v_curr_date,
           exit_time   = v_curr_time,
           update_date = v_curr_date,
           update_time = v_curr_time
    WHERE  is_on_duty = c_on_duty AND
           item_code = p_item_code AND
           duty_type = c_duty_type_1;
  END IF;
  -- 若有更多相關人在此繼續加入
  -- 相關人更新 FINISH --
  dbms_output.put_line('----------- PROCUDURE END -----------');
  p_error_no      := 0;
  p_error_info    := 'EXECUTE SUCCESS';
  p_error_id      := SQLCODE;
  p_error_sysinfo := SQLERRM;
EXCEPTION
  WHEN OTHERS THEN
    p_error_no      := 999;
    p_error_info    := '存儲過程執行錯誤';
    p_error_id      := SQLCODE;
    p_error_sysinfo := SQLERRM;
END refresh_new_data;
/

在PL/SQL中使用命令窗口創建存儲過程時,若是存儲過程寫得有問題,就會報錯:函數

這種狀況下,能夠經過下面的方式查看存在哪裏:測試

一、找到PL/SQL中的「瀏覽器」窗口,在存儲過程當中找到咱們新創建的存儲過程(上面標記了紅色的×,說明腳本執行出錯了),在右鍵菜單中找到「查看」

二、在「查看」界面中,出錯的行會用黃色標記,下面有詳細的報錯信息

準備工做完成後,創建文件MyBatisTestMapper.java:

import java.util.Map;

public interface MyBatisTestMapper {
    void refreshNewData(Map<String, Object> paramMap);
}

創建對應的Mapper文件MyBatisTestMapper.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="MyBatisTestMapper">

    <parameterMap id="paramMap4NewData" type="java.util.Map">
       <parameter property="itemCode" javaType="java.lang.String" jdbcType="VARCHAR" mode="IN"/>
       <parameter property="duty1" javaType="java.lang.String" jdbcType="VARCHAR" mode="IN"/>
       <!-- 若有更多相關人在此繼續加入 -->
       <!-- <parameter property="duty2" javaType="java.lang.String" jdbcType="VARCHAR" mode="IN"/> -->
       <!-- <parameter property="duty3" javaType="java.lang.String" jdbcType="VARCHAR" mode="IN"/> -->
       <parameter property="errorNo" javaType="java.lang.Long" jdbcType="NUMERIC" mode="OUT"/>
       <parameter property="errorInfo" javaType="java.lang.String" jdbcType="VARCHAR" mode="OUT"/>
       <parameter property="errorId" javaType="java.lang.Long" jdbcType="NUMERIC" mode="OUT"/>
       <parameter property="errorSysInfo" javaType="java.lang.String" jdbcType="VARCHAR" mode="OUT"/>
    </parameterMap>

    <select id="refreshNewData" statementType="CALLABLE" parameterMap="paramMap4NewData">
       { call refresh_new_data(?, ?, ?, ?, ?, ?) }
    </select>

</mapper>

main函數的實現以下:

import java.io.InputStream;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

/**
 * MyBatis使用測試
 * @author Tsybius2014
 * @date 2016年3月26日
 * @time 上午12:29:10
 * @remark
 *
 */
public class MyBatisTest {
    
    public static void main(String[] args) {
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            SqlSession session = sqlSessionFactory.openSession();
            try {
                MyBatisTestMapper mapper = session.getMapper(MyBatisTestMapper.class);
                Map<String, Object> paramMap = new HashMap<String, Object>();
                paramMap.put("itemCode", "item00X");
                paramMap.put("duty1", "10001");
                paramMap.put("duty2", "10001");
                paramMap.put("duty3", "10002");
                mapper.refreshNewData(paramMap);
                
                long errorNo = (Long) paramMap.get("errorNo");
                String errorInfo = (String) paramMap.get("errorInfo");
                long errorId = (Long) paramMap.get("errorId");
                String errorSysInfo = (String) paramMap.get("errorSysInfo");
                System.out.println(errorNo);
                System.out.println(errorInfo);
                System.out.println(errorId);
                System.out.println(errorSysInfo);
                
            } finally {
                session.commit();
                session.close();
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

輸出結果以下,存儲過程執行成功:

0
EXECUTE SUCCESS
0
ORA-0000: normal, successful completion

END

相關文章
相關標籤/搜索