2016年4月10日 星期日

JSP 的指令元素 (page) (JSP 2.x 二)

參考JSP2.3的spec的JSP.1.10,上一篇有提供連結,語法如下
<%@ directive { attr=”value” }* %>

directive 有三種:page、tablib、include



※page

屬性有15種
{ language=”scriptingLanguage”}
{ extends=”className”}
{ import=”importList”}
{ session=”true|false”  }
{ buffer=”none|sizekb” }
{ autoFlush=”true|false”  }
{ isThreadSafe=”true|false”  }
{ info=”info_text” }
{ errorPage=”error_url” }
{ isErrorPage=”true|false” }
{ contentType=”ctinfo”}
{ pageEncoding=”peinfo”}
{ isELIgnored=”true|false” }

2.1(含)後又增加這兩個
{ deferredSyntaxAllowedAsLiteral=”true|false”}
{ trimDirectiveWhitespaces=”true|false”}


※除了import以外,<%@ page %>可以寫很多行,但屬性名不能重覆,否則會出「Page directive must not have multiple occurrences of pageencoding」的錯

※以下用2.3版測試


※contentType、pageEncoding

<%@ page language="java" contentType="application/msword; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Insert title here</title>
    </head>
    <body>
        <%
            response.setHeader("Content-Disposition", "Attachment; filename=xxx.doc");
        %>
        <table border="1">
            <tr><td>ascii</td></tr>
            <tr><td>氣气気</td></tr>
        </table>
    </body>
</html>

※contentType="text/html; charset=UTF-8"
在tomcat資料夾下的webapps\examples\WEB-INF\web.xml可以複製

※MIME預設就是text/html

※MIME是text/plain就會變成純文字檔,所以一點顏色都沒有,亂打會出現下載視窗

※MIME是application/msword,我用IE11會出錯,但用chrome是ok的,一樣會出現下載視窗,檔名依瀏覽器不同會不一樣,內容編碼吃的是meta的編碼

※如果要控制下載的檔名,可用response那一行,參考這裡

※charset 參考Servlet第二篇



※errorPage、isErrorPage

※test.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page errorPage="error.jsp"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Insert title here</title>
    </head>
    <body>
        <% int a = 1 / 0;%>
    </body>
</html>

※指定errorPage後,如果此網頁有錯,就會跳轉,且網址不變



※error.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page isErrorPage="true"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Insert title here</title>
    </head>
    <body>
        <h1>我錯了</h1>
        <%
            response.setStatus(200);
        %>
        <%=exception.getMessage()%>
    </body>
</html>

※isErrorPage是true,才能使用隱函物件exception

※網頁錯誤後,就會跳轉到error.jsp,多按幾次重整會發現有時候成功,有時候500,所以增加response那一行,200代表ok,都沒問題


還有一種方法是用web.xml的設定方式,在Servlet六的錯誤頁面有寫,但要注意IE有可能會有問題(我用IE11預設不行),將下面的勾取消即可


它預設有勾,它勾起來我反而不易懂好不好
上面還有「如果IE不是預設的網頁瀏覽器,請告訴我」,不想用的可取消


※language

文件寫的好長,我懶的看,反正寫java就對了


※extends

※xxx.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page extends="pkg.JSPDad"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    </head>
    <body>
        <%System.out.println("name=" + getName()); %>
        <%System.out.println("name2=" + getName2()); %>
        <h1>我跳</h1>
    </body>
</html>

※extends的類別,一定要繼承HttpServlet,不然會出「Unable to compile class for JSP」的錯

※沒加extends那一行時,getName()和getName2()當然不能用,「我跳」當然也有在畫面上
我一加extends那一行,就會跳到JSPDad的doGet方法,所以「我跳」沒看到很合理,但getName()和getName2()在控制台也沒有看到,不知道為什麼?


※JSPDad.java

public class JSPDad extends HttpServlet {
    private String name = "xxx";
    public static String name2 = "ooo";
    // setter/getter...
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("get");
        // resp.getWriter().println("<html><head></head><body><h1>Yeah!</h1></body></html>");
        // req.getRequestDispatcher("/first.jsp").include(req, resp);
        resp.sendRedirect(req.getContextPath() + "/first.jsp");
    }
}

※父類別一定要覆寫doGet方法,覆寫doPost沒用,沒覆寫就會405

※first.jsp自己隨便寫,是可以跳轉的

※如果跳轉又寫xxx.jsp會變成無限迴圈


※import

和java的import一樣,可以寫很多行或者寫一行,用「,」隔開,重覆import也是可以的


※session

可不可以使用session,預設是true
上一篇有說9種隱含物件,它就是其中一種,如果這個選項設false,那這一頁就不能使用session了


※buffer、autoFlush

buffer為設定buffer大小,預設是8kb
autoFlush為buffer滿時,是否自動強制輸出,預設是true

如果超出buffer大小
autoFlush設false,會出「IOException: Error: JSP Buffer overflow」
autoFlush設true,還是可以正常輸出,內容也不會變少

buffer設定成0kb或none
autoFlush設false,會出「jsp.error.page.badCombo」
autoFlush設true,還是可以正常輸出,內容也不會變少


※isThreadSafe

在Servlet2.4已過時了,tomcat5.5、Servlet就是2.4,而JSP是2.0,看tomcat連結
預設是true,表示此jsp是安全的,一次允許多個request
如果是false,則一次只允許一個request

※info

設定訊息,可以透過getServletInfo()取得
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" info="xxx"%>
<html>
    <head></head>
    <body><%=getServletInfo()%></body>
</html>


※isELIgnored

要不要忽略EL,就是長「${}」的東東,以後會講



※以下兩個屬性是2.1(含)才有的

※deferredSyntaxAllowedAsLiteral

是否允許在頁面上有「#{}」,預設不允許,所以一打會出現編譯錯誤(Spring的EL就長這樣)

※官方文件有寫說page的作者說,在web.xml寫如下的設定可以覆蓋
<jsp-config>
    <jsp-property-group>
        <url-pattern>*.jsp</url-pattern>
        <deferred-syntax-allowed-as-literal>false</deferred-syntax-allowed-as-literal>
    </jsp-property-group>
</jsp-config>

※我試的結果根本沒覆蓋


※trimDirectiveWhitespaces

※jsp頁面

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page trimDirectiveWhitespaces="false" isELIgnored="false"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
</head>
    
<body>
    <% String s = "s"; %>
    <b>hey man!</b>
    ${user}
    <%=s%>
</body>

※這個要在網頁按右鍵,檢視-->原始檔才看得出來,如下:

※上面的圖是設成false或沒設時,下面的圖是true

※官方文件有寫說page的作者說,在web.xml寫如下的設定可以覆蓋
<jsp-config>
    <jsp-property-group>
        <url-pattern>*.jsp</url-pattern>
        <trim-directive-whitespaces>true</trim-directive-whitespaces>
    </jsp-property-group>
</jsp-config>

※我在web.xml全不寫當然是ok的,但奇怪的是我把這裡改成false,我試的結果還是run的好好的,根本就沒覆蓋啊!

完整性約束的型態 (DDL 三)

參考官網,用法和範例可參考這連結,總共分成六種:
1.NOT NULL:禁止放null

2.UNIQUE:同一欄位只能唯一,但允許放null

3.PRIMARY KEY(PK):NOT NULL + UNIQUE

4.FOREIGN KEY(FK):欄位指定FK後,表示此欄位的值必需是另外一張表的欄位值的其中之一

5.CHECK:符合指定的條件

6.REF:參考到另外一個物件,個人覺得這個比較不像約束,比較像自定型態,很多書寫完整性約束時也沒有提到這個,不過官網有就是了

※NOT NULL只能放在宣告欄位的最後面,其他的除了這種方式,還有一種是為約束命名的寫法,這個是官方說的,可是FK我試的結果只能用約束命名的寫法(而且只能使用CONSTRAINT獨立一行),官網也沒有提供不寫約束名稱的範例

※以下以這個範例來練習,先寫沒有約束名稱的寫法
DROP TABLE xxx PURGE;
CREATE TABLE xxx (
    id number(5) PRIMARY KEY,
    name varchar2(20) UNIQUE,
    color varchar2(10) NOT NULL,
    price number(5,2) DEFAULT 0 CHECK(price BETWEEN 0 AND 1000),
    make_date timestamp DEFAULT sysdate UNIQUE
);
    
    
CREATE TABLE ooo (
    oid number(5) PRIMARY KEY,
    oname varchar2(20) UNIQUE,
    xid number(5),
    CONSTRAINT fk_xid FOREIGN KEY(xid) REFERENCES xxx(id)
);

※DEFAULT一定要在約束前面,如果有DEFAULT,INSERT時可以不塞值


※NOT NULL

INSERT INTO xxx(id, color) VALUES (1, null);
INSERT INTO xxx(id) VALUES (1);

※預設不打就是null,所以這兩條SQL是一樣的

※因為color是null,所以會出「ORA-01400: 無法將 NULL 插入 ("帳號"."XXX"."NAME")」的錯



※UNIQUE

INSERT INTO xxx(id, name, color, price) VALUES (1, 'apple', 'red', 50);
INSERT INTO xxx(id, name, color, price) VALUES (2, 'apple', 'blue', 40);

※第一條SQL新增成功,但第二條的name值一樣,所以會出「ORA-00001: 違反必須為唯一的限制條件」的錯

※null可以重覆,官網有說明



※PRIMARY KEY

一筆記錄的唯一值,所以是NOT NULL + UNIQUE



※CHECK

INSERT INTO xxx(id, name, color, price) VALUES (1, 'apple', 'red', 1002);

※因為price的範圍是0~1000,1002已經超過了,所以會出「ORA-02290: 違反檢查條件 (C##SCOTT.SYS_C0010287)」的錯



※FOREIGN KEY

一定要有兩張表而且FK對應的欄位必需有UNIQUE(所以PK也可以),而且型態也要一樣

只要欄位有FK,那就表示它的值有多筆,對應到另外一個table的欄位值(除非另外一個table的欄位值只有一種)
因為這樣的原因,所以刪除時,預設也必須先刪除子項(FK)的欄位,才能刪除父項(PK)的欄位


INSERT INTO xxx(id, name, color) VALUES (1, 'aaa', 'red');
INSERT INTO xxx(id, name, color) VALUES (2, 'bbb', 'blue');
    
    
INSERT INTO ooo(oid, oname, xid) VALUES (2, 'qoo', 3);

※xxx新增兩條SQL,而ooo的xid是對應到xxx的id,而xxx的id值,全部的值有1和2,所以ooo的xid不塞1或2,就會發生「ORA-02291: 違反完整性限制條件 (C##SCOTT.FK_XID) - 找不到父項索引鍵」的錯

※null是可以的



※REF

-- 1.新增TYPE
DROP TYPE aaa;
CREATE TYPE aaa AS OBJECT (
    p1 varchar2(40),
    p2 varchar(2)
);
    
-- 2.新增Table,並使用REF型態
DROP TABLE bbb PURGE;
CREATE TABLE bbb (
    id number, 
    ref_name REF aaa
);
    
-- 3.新增另一張Table,並引用aaa TYPE,順便塞值
DROP TABLE table_a PURGE;
CREATE TABLE table_a OF aaa;
INSERT INTO table_a VALUES('1', 'a');
INSERT INTO table_a VALUES('2', 'b');
    
-- 4.新增資料
INSERT INTO bbb(id, ref_name)VALUES(1, 
    (SELECT REF(t) FROM table_a t WHERE P1 = '1')
);
INSERT INTO BBB(id, ref_name)VALUES(2, 
    (SELECT REF(t) FROM table_a t WHERE P1 = '2')
);

※主要是2的bbb表,它要使用一個TYPE,所以1才要新增TYPE
4要新增時,不知道怎麼塞值,所以3就引用的aaa TYPE
所以使用REF是不能直接在裡面塞值的,要像3這樣引用才可以

※修改可參考PL/SQL 三十三



※以上的6種約束可以混用,用空格隔開如「NOT NULL UNIQUE」,但不是全部都可以,詳情要看官網的說明,例如PRIMARY KEY UNIQUE就不行




※有約束名稱的寫法

CREATE TABLE xxx (
    id number(5),
    name varchar2(20),
    color varchar2(10) NOT NULL,
    price number(5,2) DEFAULT 0,
    make_date timestamp DEFAULT sysdate UNIQUE,
    
    CONSTRAINT pk_id PRIMARY KEY(id),
    CONSTRAINT uk_name UNIQUE(name),
    CONSTRAINT ck_price CHECK(price BETWEEN 0 AND 1000)
);
    
    
CREATE TABLE xxx (
    id number(5) CONSTRAINT pk_id PRIMARY KEY,
    name varchar2(20) CONSTRAINT uk_name UNIQUE,
    color varchar2(10) NOT NULL,
    price number(5,2) DEFAULT 0 CONSTRAINT ck_price CHECK(price BETWEEN 0 AND 1000),
    make_date timestamp DEFAULT sysdate UNIQUE
);

※PK、UNIQUE、CHECK都可以獨立成一行或宣告欄位的最後面

※獨立一行還可以設定多個欄位,下面會介紹



※PK、FK、UNIQUE多個欄位

DROP TABLE xxx PURGE;
CREATE TABLE xxx (
    id number(5),
    name varchar2(20),
    color varchar2(10) NOT NULL,
    price number(5,2) DEFAULT 0 UNIQUE,
    make_date timestamp DEFAULT sysdate UNIQUE,
    
    CONSTRAINT pk_id_name PRIMARY KEY(id, name),
    CONSTRAINT uk_name UNIQUE(name),
    CONSTRAINT ck_price CHECK(price BETWEEN 0 AND 1000)
);
    
DROP TABLE ooo PURGE;
CREATE TABLE ooo (
    oid number(5),
    oname varchar2(20),
    xid number(5),
    xname varchar2(20),
    
    CONSTRAINT pk_oid_xid PRIMARY KEY(oid, xid),
    CONSTRAINT fk_xid_xname FOREIGN KEY(xid, xname) REFERENCES xxx(id, name),
    CONSTRAINT uk_oname_xid UNIQUE(oname, xid)
);

※主要是看ooo表,而FK對應的表欄位兩個欄位也要有UNIQUE(所以PK也可以)



※新增/刪除約束

-- 1
ALTER TABLE xxx MODIFY(color varchar2(25));
    
-- 2
ALTER TABLE ooo DROP CONSTRAINT fk_xid;
ALTER TABLE xxx DROP CONSTRAINT pk_id;
ALTER TABLE xxx ADD CONSTRAINT pk_id PRIMARY KEY(id);
ALTER TABLE ooo ADD CONSTRAINT fk_xid FOREIGN KEY(xid) REFERENCES xxx(id);
    
-- 3
ALTER TABLE xxx DROP CONSTRAINT ck_price;
ALTER TABLE xxx ADD CONSTRAINT ck_price CHECK(price BETWEEN 500 AND 2000);
    
-- 4
ALTER TABLE xxx DISABLE /*ENABLE*/ CONSTRAINT ck_price;

※1是NOT NULL用的

※2我在官網沒有看到修改約束和修改約束名稱的功能,所以只好先刪除再新增了

※2和3是PK、FK、CHECK

※4是將約束啟用/停用的功能

※因為有這個功能,所以有些地方是先新增表,創建完了才用這種語法加一些限制條件



※FK還有重要的功能,下一篇再寫

2016年4月9日 星期六

ALTER、COMMENT (DDL 二)

這一篇還是DDL,所以ALTER、COMMENT執行完也會將前面沒有commit的進行commit

前一篇是針對表的增刪改,下面是針對表裡的欄位的增刪改
ALTER TABLE <table> ADD <field> <type> [default];-- 增加一個欄位
ALTER TABLE <table> ADD (<field> <type> [default], <field> <type> [default]);-- 增加多個欄位
    
ALTER TABLE <table> DROP COLUMN <field>;-- 刪除一個欄位
ALTER TABLE <table> DROP (<field>, <field>);-- 刪除多個欄位
    
ALTER TABLE <table> MODIFY <field> <type> [default] [not null];-- 修改欄位,不包括欄位重命名
ALTER TABLE <table> RENAME COLUMN <old_field> TO <new_field>;-- 欄位重命名用這個

※官網連結

※删除约束可用「ALTER TABLE <table> Drop CONSTRAINT <約束名稱>;」
※增加UK:ALTER TABLE <table> ADD CONSTRAINT <約束名稱> UNIQUE(欄位);
※增加PK:ALTER TABLE <table> ADD CONSTRAINT <約束名稱> PRIMARY KEY(欄位);
※增加複合主鍵:前面和「增加PK」一樣,最後是PRIMARY KEY(欄位,欄位...);
※增加FK:ALTER TABLE <table> ADD CONSTRAINT <約束名稱> FOREIGN KEY(欄位) REFERENCES 另一張表名稱(另一張表欄位);


※UNUSED

如果資料太多,會刪的很慢,所以可以先設定成不使用,等到沒什麼人用資料庫時再刪,例如凌晨時再刪

變成不使用的狀態後,SELECT看不到不使用的欄位


ALTER TABLE xxx SET UNUSED COLUMN price; -- 將一個欄位設為不使用
ALTER TABLE xxx SET UNUSED(name); -- 將一個欄位設為不使用
ALTER TABLE xxx SET UNUSED(name, id); -- 將多個欄位設為不使用
ALL_UNUSED_COL_TABS -- 這張表可以得知表裡有幾個不使用欄位
ALTER TABLE xxx DROP UNUSED COLUMNS; -- 刪除不使用的欄位

※官網我沒有看到恢復UNUSED變成使用狀態,所以小心使用

※如果想要恢復還可以用下面要講的VISIBLE



※VISIBLE/INVISIBLE

這個功能是Oracle 12c才有的,可以用V$VERSION這張表,看資料庫版本

在新增時,如果欄位名稱不夠,會編譯錯誤,而這個功能可以讓SELECT、DESC時,看不到這個欄位,新增也不需要用到這個欄位,它會直接給null


ALTER TABLE xxx MODIFY(name invisible);
    
CREATE TABLE xxx (
    id number(5),
    name varchar2(20) INVISIBLE
);

※第一條SQL是讓name欄位看不見,要回復只要改成VISIBLE即可

※也可以像第二條SQL那樣,在新增表時就使用

※雖然看不見欄位,但用下面要講的COMMENT,USER_COL_COMMENTS 這張表就能看到了



※COMMENT

就是為表和欄位增加註解,讓人家知道這個表和欄位是做什麼用的


COMMENT ON TABLE <table> IS 'xxx';
COMMENT ON COLUMN <table>.<field> IS 'ooo';
    
SYS.USER_TAB_COMMENTS -- 看所有表的註解
SYS.USER_COL_COMMENTS -- 看所有欄位的註解

CREATE、DROP、RENAME、TRUNCATE、回收表 (DDL 一)

※DDL

之前講的都是DML(資料處理語言),針對表的增刪改查,做完要commit,其他的session才會看的到
而DDL(資料定義語言),是創建、刪除、修改表,把它想成執行完會自動commit,所以待會講到CREATE、DROP、RENAME、TRUNCATE、FLASHBACK、PURGE(增刪改)時,假設有做update表的動作,但沒有commit,只要一執行DDL,那就會自動commit了,這點要注意


※新增

CREATE TABLE chess (
    id number(5),
    name varchar2(20),
    price number(5,2) DEFAULT 0,
    make_date1 date DEFAULT sysdate,
    make_date2 timestamp,
    pic clob
);

※chess要表名稱,自己想一個,「()」裡最左邊是欄位名稱,自己想名字,右邊為資料型態

※官網連結,內鍵的有23種

※常用的就是下面那幾種了
.number:
可用int和float代替,number(5,2),5為全部位數,不包括小數點; 2為小數點位數
所以123.45是ok的;number(3, 0)等同number(3),沒有小數點的意思,上面官網連結的Table 2-2有NUMBER的說明,已經很清楚了

.varchar、varchar2、char:
char(5)就是長度是5,varchar2(5)就是最多長度是5,如果有個值是abc,那char取出來後面會包括兩個空格,varchar和varchar2不會,所以才叫var(變動)
varchar和varchar2目前都一樣,官方推薦用varchar2

.date、timestamp:
date年月日;timestamp年月日時分秒

.clob、blob:
c就是character,所以放的是文字;b就是byte,所以放的是圖片

.nXXX:
如nchar、nvarchar2、nclob,主要是存中文字,因為你輸入長度3,它可能內部會將你輸入的長度乘2或3,看編碼而定

※複製表

CREATE TABLE xxx AS SELECT * FROM emp;
CREATE TABLE xxx AS SELECT * FROM emp WHERE 1=0;

※也就是從emp這張表整個複製一份出來變成xxx

※第二條SQL的where永遠不成立,所以只會複製表結構,裡面沒有內容,算是一種運用吧!

※要注意CONSTRAINT 不複製,所以PK、FK都沒有,過幾篇會說明

※AS一定要打,而且不能打IS



※修改

RENAME xxx TO ooo;

※xxx是舊表名,ooo是新表名,就算有PK、FK也可以改



※刪除

DROP TABLE chess;
DROP TABLE chess PURGE;

※兩種方式都可以,差別在下面的資源回收表有說



※清空表

TRUNCATE TABLE xxx;
DELETE FROM xxx;

※TRUNCATE為DDL;DELETE為DML

※DDL比較快;但DML因為還沒commit,所以多一個反悔的機會



※查看表結構

DESC ooo;

※只能看到欄位名稱、欄位類型、是否接受null



※查看目前帳號的表

SELECT * FROM tab;

※資料字典分為兩類
.動態資料字典:隨著資料庫運行而更新的表,通常以「V$」開頭

.靜態資料字典又分成以下三種:
user_開頭:目前帳號的表
all_開頭:目前帳號可以存取的表
dba_開頭:所有的表,必需有權限



※回收表

就是將表刪除後,類似Windows的資源回收桶,還有還原的機會


DROP TABLE xxx;--1
SELECT * FROM tab;--2
SELECT * FROM recyclebin;--3
FLASHBACK TABLE xxx TO BEFORE DROP;--4

※第一步刪除完後,看第二步的表,會發現TNAME多一個「BIN$」開頭,「==$0」結尾的,表示已經到回收表裡了

※第三步的表就是回收表,從ORIGINAL_NAME也會發現剛剛刪除的xxx,如果有多個,那就表示之前刪除了很多,剛好名稱也是xxx

※如果名稱有多個,它還會幫我們多兩個記錄,
「SYS_IL」開頭,「$$」結尾和「SYS_LOB」開頭,「$$」結尾的
而且CREATETIME時間欄位和刪除的表一樣

※第四步使用這種語法能恢復表,它會恢復最接近目前時間且名稱和ORIGINAL_NAME一樣的(例如有三筆資料:1分鐘前刪的、2分鐘前刪的、3分鐘前刪的,會恢復1分鐘前刪的)

※恢復完成後,第二和第三步的表都會少一行

※如果不想進資源回收表,就使用DROP TABLE xxx PURGE;



※刪除回收表

回收表沒辦法用DELETE和TRUNCATE刪的,要用以下的語法


PURGE TABLE xxx;
PURGE recyclebin;

※第一條SQL是刪除一筆,如果有「SYS_IL」開頭,「$$」結尾和「SYS_LOB」開頭,「$$」結尾的,而且CREATETIME時間欄位和刪除的表一樣的,也會一起刪除
刪除的是離目前時間最遠的那一筆(例如有三筆資料:1分鐘前刪的、2分鐘前刪的、3分鐘前刪的,會刪除3分鐘前刪的)

※一筆一筆刪除太累了,所以第二條SQL是清空整個回收表用的

2016年4月7日 星期四

&、DEFINE、ACCEPT (DML 十三)

互動變數並不是Oracle SQL裡,是在一本叫SQL*PlusR User's Guide and Reference裡面
我這篇的最後一張圖,去官網往下拉一點就會看到

首先要啟用互動變數的功能,其實預設已經啟用,但我不確定每個版本都預設啟用
官網有說明,我的這篇PL/SQL也有講到一些,如果不懂PL/SQL就看截圖就好,這篇就不截圖了

只要打&xxx,xxx自己隨便取,放在SQL的任何地方都可以,反正就是個字串,一執行就會跳出畫面,這時你打什麼就是什麼,但如果沒啟用就會出「ORA-01008: 部分變數未被連結」的錯誤



※啟用/停用 換符號

SET DEFINE ON/OFF;
SELECT * FROM emp where deptno = &p1;
    
SET DEFINE &c;

※第一行就是啟用/停用,第二行就可以用了,預設要用「&」

※如果不想用預設的「&」,那就打最後一行,一打會跳出視窗,你輸入什麼就用什麼

※官網的DEF[INE]表示打DEF和DEFINE都可以,「[]」不是必要的,下面介紹的語法還有很多都是這樣



※&、&&

SELECT * FROM emp where deptno = &p1;
SELECT * FROM emp where deptno = &&p1;

※第一行每次執行都要打一次

※第二行打一次就可以執行很多次,如果要清除下面會介紹

※第二行的變數名稱和第一行一樣,所以執行過第二行,第一行的變數值也有值了,就不會再跳出畫面了



※清除

SELECT &p, sum(sal) FROM emp GROUP BY &p;
SELECT &&p, sum(sal) FROM emp GROUP BY &p;

※第一行兩個變數一樣,但要打兩次,所以改成第二行的寫法

※第二行的&&要寫在前面,後面的變數才知道不用再打了

※變數存起來只是暫時的,重登入就消失了,或者執行以下的語法
UNDEFINE p;
    
UNDEFINE p p1;

※第一行清除一個變數的值

※第二行是要清除多個變數,中間用空格隔開



※DEFINE

DEFINE xxx = 20;
SELECT * FROM emp WHERE deptno = &xxx;

※除了用「&」,也可以用DEFINE,看官網介紹



※ACCEPT

前本介紹的互動還不夠好,ACCEPT可以有文字,看官網介紹
語法如下:
ACC[EPT] variable [NUM[BER] | CHAR | DATE | BINARY_FLOAT | BINARY_DOUBLE] [FOR[MAT] format] [DEF[AULT] default] [PROMPT text|NOPR[OMPT]] [HIDE]


--1
ACCEPT yyy DATE FOR 'YYYY-MM-DD' NOPROMPT
SELECT * FROM emp WHERE hiredate < to_date('&yyy', 'YYYY-MM-DD');
    
--2
ACCEPT xxx DEFAULT 30 PROMPT '請輸入部門編號:'
SELECT * FROM emp WHERE deptno = &xxx;
    
--3
ACCEPT ooo FORMAT A2 PROMPT '請輸入部門編號:' HIDE
SELECT * FROM emp WHERE deptno = &ooo;

※第一組的NOPROMPT(NOPROMPT不寫也是一樣)和上面介紹的一樣,沒有文字訊息
但這一組有設定日期的格式

※第二組有預設值,但跳出的視窗有確定和取消,要按確定才有用

※第三組後本有個HIDE,表示打出來的字變成「*」號

2016年4月6日 星期三

JSP 和 Servlet 的關係 (JSP 2.x 一)

Oracle的JSP spec,各版本連結 2.0、2.1、2.2、2.3
在spec的1.8.3 有9個隱含物件(就是不用new了,直接拿來使用)

.request:javax.servlet.http.HttpServletRequest
.response:javax.servlet.http.HttpServletResponse
.pageContext:javax.servlet.jsp.PageContext
.session:javax.servlet.http.HttpSession
.application:javax.servlet.ServletContext
.out:javax.servlet.jsp.JspWriter
.config:javax.servlet.ServletConfig
.page:java.lang.Object
.exception:java.lang.Throwable

以上的9個的scope如下:
request 為 request scope
session 為 session scope
application 為 application scope
其他都是page scope



※測試準備

<h1>JSP路徑</h1>
<%=request.getServletContext().getAttribute("javax.servlet.context.tempdir")%>

※以上的程式碼在Servlet 四有提過

※此行印出
D:\eclipse-jee-mars-R-win32-x86_64\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\work\Catalina\localhost\TestJSP,最後的TestJSP是專案名稱

※不同的container可能不一樣,我是用tomcat8

※到目錄後還要再進去org\apache\jsp,這樣才會看到由servlet變出來的.java和.class



※測試
<%!int i = 0;%>
<%
    int j = 10;
    i++;
    j++;
%>
<%=i%><br />
<%=j%>

※寫這樣然後去剛剛的.java看一下,大部分都是自動產生出來的,我把自動的去除以後,大概如下的樣子:
public final class exercise_jsp extends HttpJspBase implements JspSourceDependent, JspSourceImports {
    int i = 0;
    
    try {
        out.write('很多HTML');
        
        int j = 10;
        i++;
        j++;
        
        out.write('很多HTML');
        out.print(i);
        out.print(j);
    } catch (Throwable t) {
        // ...
    } finally {
        // ...
    }
}

※可以看出JSP一樣有_jspInit、_jspDestroy、_jspService

※exercise.jsp是我取的名字,會變成 exercise_jsp.java,也就是將「.」變成「_」,然後產生.java,最後編譯成.class

※類別是final的

※繼承和實作都是org.apache.jasper.runtime 包
繼承 HttpJspBase,實作 JspSourceDependent 和 JspSourceImports

※<%! %>:寫全域變數和方法的地方

※<% %>:寫區域變數和方法內容的地方,看生出來的.java就可以知道,所以方法前沒辦法加synchronized

※<%= %>:印出變數,類似servlet的PrintWriter,要注意最後沒有「;」

※<%-- --%>:註解,和以下三種相同
<%// ... %>
<%
  /*
...
*/
%>
<%
/**
...
*/
%>
因為<% %>和<%! %>本來就是寫java的地方,所以當然可以用這三種註解,以上這幾種註解叫伺服器端註解

<!-- ... -->:這是HTML的註解,不是JSP的,這叫用戶端註解
和伺服器端註解比較起來,不同的地方是按右鍵-->檢視原始碼時,伺服器端的看不到

※剛剛沒有在<%! %>裡面寫方法,這裡寫一下
<%!
    public String xxx(){
        return "abc";
    }
%>
<%
    String s = this.xxx() + "def";
%>
<%=s%>

※寫static也可以,但不能用exercise_jsp.xxx(),直接呼叫就好

※如果將方法寫在<% %>就會編譯錯誤

登入跳轉 (Servlet 九)

跳轉之前講的不太清楚,這裡寫一個登入功能會比較容易了解

我看了API後,理解如下:
HttpServletResponse的sendRedirect
參數不是「/」開頭,為相對路徑
是「/」開頭,表示ServletContext路徑
如果resp已commit,會拋出IllegalStateException
1.瀏覽器req給servlet
2.servlet將設定的路徑回傳給瀏覽器
3.瀏覽器依這個路徑再req
4.servlet再回傳
所以有兩次的req
因為是resp,所以網址會改變


ServletRequest的getRequestDispatcher取得javax.servlet的RequestDispatcher
有以下兩個方法

1.forward(主控權是別人)
轉發給servlet、JSP、HTML
如果resp已commit,會拋出IllegalStateException

2.include(主控權是自己)
將servlet、JSP、HTML包括到自己的servlet
所包含的servlet不能改變resp狀態碼或設定header,如果有改變將被忽略。

這兩個方法都是req,所以網址不會改變


※web.xml

<filter>
    <filter-name>zzz</filter-name>
    <filter-class>org.apache.catalina.filters.SetCharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>zzz</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>



※LoginServlet.java

@WebServlet(urlPatterns = { "/xxx" })
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String enc = super.getServletConfig().getInitParameter("encoding");
        resp.setContentType("text/html; charset=" + enc);
    
        String username = req.getParameter("username");
        String password = req.getParameter("password");
    
        if (username.trim().equals("aaa") && password.trim().equals("bbb")) {
            String fruit = req.getParameter("ooo");
            String[] xxx = req.getParameterValues("xxx");
            List<String> tl = Arrays.asList("ttt", "lll");
    
            if (xxx != null) {
                for (String s : req.getParameterValues("xxx")) {
                    // 老虎獅子吃香蕉
                    if (tl.contains(s) && "bbb".equals(fruit)) {
                        // forward 轉發給設定的servlet、jsp、HTML,主導權也送出去
                        System.out.println("before:" + req.getServletPath());
                        req.getRequestDispatcher("/abc").forward(req, resp);
                        // req.getRequestDispatcher("/WEB-INF/success.jsp").forward(req,
                        // resp);
                        System.out.println("after:" + req.getServletPath());
    
                        // include包含設定的servlet、jsp、HTML,主導權並沒有送出去
                        // System.out.println("before:" + req.getServletPath());
                        // req.getRequestDispatcher("/abc").include(req, resp);
                        // req.getRequestDispatcher("/WEB-INF/success.jsp").include(req,
                        // resp);
                        // System.out.println("after:" + req.getServletPath());
                        break;
                    } else {
                        if ("rrr".equals(s)) {
                            resp.sendRedirect(req.getContextPath() + "/fail.jsp");
                            System.out.println("456");// 有執行到
                        } else {
                            continue;
                        }
                    }
                }
            } else {
                resp.sendRedirect("http://www.google.com");
            }
        } else {
            // sendRedirect是兩次的request,所以req.setAttribute抓不到
            // req.setAttribute("fail", "帳密錯誤");
            req.getSession().setAttribute("fail", "帳密錯誤");
            resp.sendRedirect(req.getContextPath() + "/login.jsp");
        }
    }
}

※sendRedirect也可以直接呼叫一般的網址,而WEB-INF是特殊的資料夾,resp當然不能拿來用

※include、forward 差在 getRequestDispatcher 是另一個 servlet 時
forward 後,主導權不在,include 還在
所以 forward 的 response 不會執行;而 include 跳轉的頁面返回後,繼續執行 response

假設有 ServletA 和 ServletB
使用 forward 跳頁後就不管了
使用 include 跳頁執行完後,會再回來繼續執行其他的部分


※TestAaa.java

@WebServlet(urlPatterns = { "/abc" })
public class TestAaa extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("in:" + req.getServletPath());// include進來是xxx,forward進來是abc
    }
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("get接收");
    }
}

※include和forward呼叫這支servlet時,會發現不一樣的地方

※如果login.jsp呼叫doGet,那abc也會呼叫doGet



※login.jsp

<form action="xxx" method="post">
    <font color="red">${fail}</font><br />
    使用者:<input type="text" name="username" value="user" /><br />
    
    密碼:<input type="password" name="password" value="pass" /><br />
    
    登入密語:
    <input type="checkbox" name="xxx" value="ttt" />tiger
    <input type="checkbox" name="xxx" value="rrr" />rabit
    <input type="checkbox" name="xxx" value="lll" />lion<br />
    
    <input type="radio" name="ooo" value="aaa" />apple
    <input type="radio" name="ooo" value="bbb" />banana
    <input type="radio" name="ooo" value="ppp" />pineapple<br />
    <input type="reset" />
    <input type="submit" />
</form>

※fail.jsp和success.jsp自己隨便寫

※有些書用servlet寫jsp,可是現在工作時都在用MVC,所以現在應該很少人這樣寫了,檢核使用者輸入的值也都是前端用jQuery、ajax了,所以我全部沒寫