1. 程式人生 > 實用技巧 >Java SSM入門(十三)——Oracle(二)(PLSQL)

Java SSM入門(十三)——Oracle(二)(PLSQL)

iwehdio的部落格園:https://www.cnblogs.com/iwehdio/

1、檢視和索引

  • 檢視:提供一個查詢的視窗,所有資料來自原表。改變檢視資料也會修改原表。
  • 建立檢視必須有dba許可權。
  • 跨使用者查詢建立表:create table emp as select * from scott.emp
  • 建立檢視:create view v_emp as select ename,job from emp
  • 查詢檢視:select * from v_emp
  • 修改檢視:update v_emp set job='CLERK' where ename = 'ALLEN',+ commit。
  • 檢視的作用:
    • 遮蔽一些敏感的欄位。
    • 保證總部和分部的資料及時統一。
  • 索引:在表的列上構建一個二叉樹,達到大幅增加查詢效率的目的,但是會影響增刪改的效率。
  • 單列索引:
    • 建立:create index idx_ename on emp(ename)
    • 單列索引觸發規則:條件必須是索引列中的原始值。單行函式、模糊函式不會觸發索引。
  • 複合索引:
    • 建立:create index idx_enamejob on emp(ename, job)
    • 複合索引中第一列為優先檢索列,如果要觸發複合索引,必須包含優先檢索列中的原始值。用 or 做查詢條件時不觸發。
    • 又是單列索引又是複合索引,先觸發單列索引。

2、PLSQL

  • PLSQL語言是對sql語言的擴充套件,使得其具有過程化程式設計的特性。主要用來編寫儲存過程和儲存函式等。

  • 宣告方法:

    declare
    -- 定義變數
    begin
    -- 處理業務邏輯
    end;
    
  • 賦值操作:

    • 定義變數必須在declare 下。

    • 賦值數字:i number(2) := 10;

    • 賦值字串:s varchar2(10) := '名'

    • 輸出:dbms_output.put_line(i)

    • 輸入:i number(3) := &i

    • 賦值指定表中列的型別(引用型變數):

      declare
             ena emp.ename%type;
      begin
             select ename into ena from emp where empno =7788;
             dbms_output.put_line(ena);
      end;
      
    • 賦值指定表中某一行的型別(記錄性變數):

      declare
             emprow emp%rowtype;
      begin
             select * into emprow from emp where empno =7788;
             dbms_output.put_line(emprow.ename || '的工作是' || emprow.job);
      end;
      
  • 流程控制語句:

    • 判斷語句格式:

      declare
      -- 定義變數
      begin
      	if i <18 then
      		dbms_output.put_line('a')
      	elsif i <60 then
      		dbms_output.put_line('b')
      	else
      		dbms_output.put_line('c')
      	end if;
      end;
      
    • 迴圈語句格式:

      -- 方法1
      while i <11 loop
      	dbms_output.put_line(i)
      	i := i+1;
      end loop;
      -- 方法2
      loop
      	exit when i >10;
      	dbms_output.put_line(i)
      	i := i+1;
      end loop;
      -- 方法3
      for i in 1..10 loop
      	dbms_output.put_line(i);
      end loop;
      
  • PLSQL中的遊標:

    • 類似於集合,可以儲存多行記錄。

    • 輸出emp表中所有員工的姓名:

      declare
             cursor c1 is select * from emp;
             emprow emp%rowtype;
      begin
             open c1;
             loop
               fetch c1 into emprow;
               exit when c1%notfound;
               dbms_output.put_line(emprow.ename);
             end loop;
             close c1;
      end;
      
    • 更改指定部門員工的工資:

      declare
             cursor c2(eno emp.deptno%type) is select empno from emp where deptno=eno;
             en emp.empno%type;
      begin
             open c2(10);
             loop
               fetch c2 into en;
               exit when c2%notfound;
               update emp set sal=sal+100 where empno=en;
               commit;
             end loop;
             close c2;
      end;
      
  • 儲存過程:

    • 就是提前已經編譯好的一段PLSQL語言,放置在資料庫端,可以直接被呼叫。一般都是固定步驟的業務。

    • 語法:

      create [or replace] procedure 過程名[(引數名 in/out 資料型別)]
      as
      begin
      	-- 業務程式;
      end;
      
    • 給指定員工漲工資的儲存過程:

      create or replace procedure p1(eno emp.empno%type)
      is
      begin
             update emp set sal=sal+100 where empno = eno;
             commit;
      end;
      
    • 測試儲存過程:

      declare
      begin
        p1(7788);
      end;
      
    • out 型別引數:

      • 定義:yearsal out number
      • 獲取輸出:p1(7788,yearsal)
      • 涉及到 into 或 := 賦值的引數都用out 來修飾。
  • 儲存函式:

    • 一般來說,過程和函式的區別在於函式可以有一個返回值,而過程沒有返回值。

    • 語法:

      create or replace function 函式名(引數名 資料型別) return 資料型別 is
      	結果變數 資料型別;
      begin
      	--業務程式
      	return(結果變數);
      end 函式名;
      
    • 計算指定員工年薪的儲存函式:

      create or replace function f_year(eno emp.empno%type) return number
      is
             s number(10);
      begin
             select sal*12 + nvl(comm,0) into s from emp where empno=eno;
             return s;
      end;
      
    • 測試儲存函式:

      declare
        s number(10);
      begin
        s:=f_year(7788);
        dbms_output.put_line(s);
      end;
      
  • 儲存過程和儲存函式的區別:

    • 語法區別:關鍵字不同。
    • 儲存函式比儲存過程多了兩個return。
    • 本質區別:儲存函式有返回值,而儲存過程沒有返回值。
    • 如果儲存過程需要返回值,需要out 型別引數。但本質也不是真有返回值,而是在儲存過程內部給out 引數賦值。
    • 儲存函式可以利用有返回值的特性,來自定義函式(如聚合函式),而儲存過程不能用來自定義函式。
  • 觸發器:

    • 制定一個規則,在進行增刪改操作的時候,只要滿足規則,自動觸發,無需呼叫。
    • 分為語句級觸發器和行級觸發器。
    • 行級觸發器中含有for each row,為了使用:oid 或 :new 物件。
    • 插入操作時,:old 所有欄位都是空,:new 是將要插入的資料。
    • 更新操作時,:old 是更新前該行的值,:new 是更新後的值。
    • 刪除操作時,:old 是刪除前該行的值,:new 所有欄位為空。
  • 建立觸發器:

    • 語句級觸發器:插入一條記錄,輸出一個新員工入職。

      create or replace trigger t1
      after		-- 在操作前還是操作後
      insert		-- 對應的操作
      on person	-- 對應的表
      declare
      
      begin
        dbms_output.put_line('入職');
      end;
      
    • 測試觸發器:

      insert into person values (2,'小綠');
      commit;
      select * from person;
      
    • 行級觸發器:不能給員工降薪。

      create or replace trigger t2
      before		-- 在操作之前
      update
      on emp
      for each row
      declare
      
      begin
        if :old.sal > :new.sal then
          raise_application_error(-20001,'不能降薪');
        end if;
      end;
      
    • 測試觸發器:

      update emp set sal = sal -100 where empno = 7788;
      commit;
      select * from emp where empno = 7788;
      
  • 觸發器實現主鍵自增:

    • 在使用者插入操作之前,拿到即將插入的資料,給資料的主鍵列賦值。使用序列。

      create or replace trigger auid
      before
      insert
      on person
      for each row
      declare
      
      begin
        select s_person.nextval into :new.pid from dual;
      end;
      
    • 測試觸發器:

      insert into person (pname) values ('a');
      commit;
      select * from person;
      

3、Java呼叫儲存過程/函式

  • Oracle10g的驅動包為ojdbc14.jar,Oracle11g的驅動包為ojdbc6.jar。

  • maven 座標:

    <dependencies>
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc14</artifactId>
            <version>10.2.0.4.0</version>
            <scope>runtime</scope>
        </dependency>
    
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
  • 測試查詢:

    @Test
    public void test1() throws Exception {
        //載入資料庫驅動
        Class.forName("oracle.jdbc.driver.OracleDriver");
        //獲取連線
        Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@iwehdio:1521:orcl", "iwehdio0", "hhh");
        //得到預編譯物件並賦值
        PreparedStatement preparedStatement = connection.prepareStatement("select * from person where pid = ?");
        preparedStatement.setObject(1, "21");
        ResultSet resultSet = preparedStatement.executeQuery();
        while (resultSet.next()) {
            System.out.println(resultSet.getString("pname"));
        }
        resultSet.close();
        preparedStatement.close();
        connection.close();
    }
    
  • 呼叫儲存過程:

    @Test
    public void test2() throws Exception {
        //載入資料庫驅動
        Class.forName("oracle.jdbc.driver.OracleDriver");
        //獲取連線
        Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@iwehdio:1521:orcl", "scott", "tiger");
        //得到物件並賦值
        CallableStatement callableStatement = connection.prepareCall("{call p1(?)}");
        callableStatement.setObject(1, "7788");
        callableStatement.execute();
        callableStatement.close();
        connection.close();
    
    }
    
  • 呼叫儲存函式:

    @Test
    public void test3() throws Exception {
        //載入資料庫驅動
        Class.forName("oracle.jdbc.driver.OracleDriver");
        //獲取連線
        Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@iwehdio:1521:orcl", "scott", "tiger");
        //得到物件並賦值
        CallableStatement callableStatement = connection.prepareCall("{? = call f_year(?)}");
        callableStatement.setObject(2, "7788");
        callableStatement.registerOutParameter(1, OracleTypes.NUMBER);
        callableStatement.execute();
        //獲得第一個引數並輸出
        System.out.println(callableStatement.getObject(1));
        callableStatement.close();
        connection.close();
    }
    
  • 呼叫儲存過程時,sql語句寫{call 過程名(引數)}

  • 呼叫儲存函式時,sql語句寫{? = call 函式名(引數)}

    • 儲存函式的第一個?屬於引數,需要註冊指定為Oracle中的那個型別。
    • 使用.getObject(第幾個)方法獲得返回值。

iwehdio的部落格園:https://www.cnblogs.com/iwehdio/