2014年8月27日 星期三

繼承struts select tag 改label

為解和前篇一樣的問題,置換的內容除了property外,還有select。
所以只好再code一個select出來了~

原則上還是以原本架構為主體,但是把資料的model(就是selectTag裡的 list 內容換掉),因為又不能改bean/vo,就不能set回去...所以將以現成的request的setAttribute/getAttribute來放改過的資料~就不用動其他的程式

1. 繼承SelectTag
public class MySelecStrutsTag extends SelectTag  {

2. 定一個tag attribute來接原始資訊(即原本寫在list的那一項改到這邊來)
private String orginList = "";

3. override populateParams,別忘了super(它做了很多要set的東西),要偷換資料的內容就接在底下做
    public void populateParams() {       
        super.populateParams();

4. 先用 orginList 裡找出原值(fVal),改一改再塞回去(lst)
        String expr = ComponentUtils.stripExpressionIfAltSyntax(getStack(), this.orginList);
        Object fVal = this.findValue(expr);
       
        //......................
        String exprNew = ComponentUtils.stripExpressionIfAltSyntax(getStack(), this.list);
        getStack().getContext().put(exprNew, lst);

5. 前端要設定tld,除了自訂的attribute要加進去外,別忘了去找一下struts tld裡面的SelectTag,有一大串長落落的也attribute也要copy進去

6. 重起後就可以在jsp上面呼叫囉~因為是用request的attribute來暫存,所以tag attribute的寫法要寫成 list="#request.tmpData" ,#request表示調用request.getAttribute


過程是有點血淚..因為原本想要塞值回原bean,後來發現原本的那個bean沒做setter,再怎樣也擠不回去Orz...然後為了把值丟在struts能find的方式~也是把一堆setXXX全拿來試了一遍~\囧rz...
    
   

2014年8月26日 星期二

繼承struts tag取值

這篇是與前一篇有關的(簡易解析struts property tag 取值 )~

前一篇是用JSP的概念去parser struts的格式資訊,只需要J2EE的method就可以~
阿這一篇就是採用struts自己的功能來取,就是call struts的method...
(雖然有點懶惰~但還是去看了一下原始碼來用用~之後還是得靠他自己內部來call取值,在loop時,用原始方式就沒辦法拿到外部變數了Orz..得靠struts tag內的暫存才有辦法取得到)

1.  先找一個struts tag來繼承,先選最簡單的來練習
public class MyStrutsTag extends PropertyTag {

2. 通常有繼承,constructor照跑比較安全
public MyStrutsTag()  {
    super();
}

3. override doStartTag() ,這裡想了有點久,因為在原始碼內,doStartTag其實會call protected void populateParams(),看原始碼大家通常是改在這裡,但是因為我需要改寫最後輸出的out的內容,後來決定就在這邊先跳掉,讓流程SKIP_BODY

public int doStartTag() throws JspException {
        //前面這裡都照抄,讓該讀的資料都進來,也不知道有沒有用到,只是萬一要用就有
        ValueStack stack = getStack();
        component = getBean(stack, (HttpServletRequest) pageContext.getRequest(), (HttpServletResponse) pageContext.getResponse());
        Container container = (Container) stack.getContext().get(ActionContext.CONTAINER);
        container.inject(component);
       
        super.populateParams();

4. 開始改寫加入自己的東西,調用struts內的值,val要先處理到只剩取值表示法那樣:example: "result.dt",正常的話fVal就應該會是像struts tag取出的值了~

                  val = this.findPropertyValue(val);   
                  String expr = ComponentUtils.stripExpressionIfAltSyntax(this.getStack(), val);
                  Object fVal = this.findValue(expr);

                  if(fVal != null)  {
                      val = fVal.toString();
                  }


其實重點也就是只有,使用ComponentUtils,還有暫存資訊的getStack()...
另一點是還沒看到它解tag的attribute,所以~解tag的方式我還是用string->dom的方式取value

簡易解析struts property tag 取值

起因在於有莫名的堅持要弄一個純"Tag"的網頁...因為這郭系統是不做js/ajax的,很傳統的java網站(就是輸入值都要submit到server做檢查的那種~~)
然後講一講後端邏輯的部分不改,就只改前端的view.....造成只好要用自訂tag( custom tag)包掉原本的資料~

原本的資料本來就是動態的撈出來的~所以原本值就已經被struts的tag包起來,學struts包struts那樣子,想說包兩層應該很簡單(包兩層只要一個用小撇('),一個用("),他就會分出來了)~

可是我忘了偶自己寫的不是struts tag阿阿阿阿阿~~~~~
實作自訂Tag時,JSP也很乖的送了一字不漏的struts tag的文字進來了Orz...
最後想不出辦法來,只好硬解tag內容抓值了,就以會傳入的型式自己寫parser了Orz...

1. 先解析struts tag,反正就只抓property 的value
2. 輸入值抓出來後,就依其定義在JSP裡面抓值,只處理像這種情況的取值result.startDt.substring(0, 4),只有一層bean,也不處理array情況,bean也只能get一次,原本的定義應該是用while loop可以一直抓下去的~
request.getAttribute() --> bean --> getter --> substring
3. 前端呼叫的寫法
<my:year format='YYYY' value='<s:property value="result.startDt.substring(0, 4)" />' />
4. tld別忘了要做tag設定才會被讀入~

String getStrutsPropertyValue(String val)  {
        String re = "";
        Document doc = null;
       
        ByteArrayInputStream bis = null;
        try  {
            val = "<root>"+val+"</root>"; //append to a normal xml format
            bis = new ByteArrayInputStream(val.getBytes());
           
            DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
            doc = dBuilder.parse(bis);
           
            NodeList nl = doc.getElementsByTagName("s:property");
            if(nl.getLength() > 0)  {
                String theValue = nl.item(0).getAttributes().getNamedItem("value").getNodeValue();
              
               String[] sp =  StringUtils.split(theValue,".");
                if(sp.length > 0)  {
                    Object obj0 = pageContext.getRequest().getAttribute(sp[0]);
                    if(obj0 != null && sp.length > 1)  { //attribute is bean
                        String getter = "get"+sp[1].substring(0,1).toUpperCase()+sp[1].substring(1);
                        Object obj1 = obj0.getClass().getMethod(getter).invoke(obj0, null);
                        if(obj1 != null)  {
                            re = obj1.toString();
                        }
                        if(sp.length > 2 && obj1 != null)  {  //string operation with bean property
                            if(sp[2].indexOf("substring") >= 0)  {  //support substring
                                String[] agrs = StringUtils.split(sp[2].substring( sp[2].indexOf("(")+1, sp[2].indexOf(")")),",");
                                Object obj2 = null;
                                if(agrs.length == 1)  {
                                    obj2 = obj1.getClass().getMethod("substring", int.class).invoke(obj1, Integer.parseInt(agrs[0].trim()));
                                }  else if(agrs.length == 2)  {
                                    obj2 = obj1.getClass().getMethod("substring", int.class, int.class).invoke(obj1, Integer.parseInt(agrs[0].trim()), Integer.parseInt(agrs[1].trim()));
                                }
                                if(obj2 != null)  {
                                    re = obj2.toString();
                                }
                            }
                           
                        }
                       
                    }  else if(obj0 != null)  {  //only attribute value
                        re = obj0.toString();
                    }
                }
            }
           
        }  catch(Exception e)  {
            e.printStackTrace();
        }  finally  {
            if(bis != null)  {
                try  {  bis.close();  }  catch(Exception ee)  {}
            }
        }
        return re;
    }

很久沒用java.lang.reflect,是有點手癢XD...那個package真的很有意思~~

PS:雖然說 jdk後來同意 Integer, int 這種變數的型態可以互通,就是寫程式的時候大I,小i亂塞是被complire認同的~但是在Class的定義上~ Integer.class =/= int.class~所以在找method的時候~ 還是得用正確的型態才能找到喔!

PS:java內建的split還是常常不好用,用apache.commons.lang.StringUtils安定!

PS:原本也想直接找struts的Tag做繼承,不過它看起來還是得自己去解tag字串,要仿做一個完整的就變成要去研究它的source code和呼叫關係才行~這~實在有點麻煩就先用爛解處理了一.一a...反正能動就好!

2014年8月19日 星期二

Crystal Report-小筆記

純筆記小東西~
外國的東西中文版通常會更難用~都是因為那個奇怪的翻譯Orz...
在google的時後也會造成困難~因為都看不懂他們是在說那個Orz...

###群組,日期
群組就新增群組應該沒什麼問題~但是若是該欄位為日期時要注意...
它group by 的項目default是周...(為什麼是周阿阿阿阿阿阿~超故意)
要記得去改他,一般最常用應該是日,月,年才是~

###打SQL的地方
資料庫專家(這翻譯一看就超不專業的~)--> 命令(右鍵修改)
--> 修改命令視窗

左邊區塊就是打SQL的,照規定打就好沒啥問題~
重點在右邊~ ~若左邊有where條件的話,通常都會有一堆$變數傳入的地方~~
傳入的變數"一定"要在右邊"建立"一個與變數一模一樣的項目~才能真正的傳值進去...

確定後~如果沒啥問題~就會跳出變數資訊輸入值給你填(就算沒報錯,也不一定表示一定對,只是有過語法而已)~記得這個與報表設計右下的"欄位總管"的參數是迷有關係的!!雖然,在資料庫建完右邊參數後,欄位總管會出現看起來一樣的東西,但是用途是不一樣的!!
我猜是把輸入DB的參數再代入一般變數到ap(reprot)裡使用,這邊已經是後段了,與DB的條件內容輸入無關~改"欄位總管"的項目是不會改變DB的查詢條件的!!

###撰寫function(公式)
其實很簡單的~右鍵新增或隨便找個東西編輯修改~
就會跳出"公式工作區-公式編輯器",重點在於中間與右上方default會有"報表欄位"、"函式"、"運算子"。樹狀點開後~可以直接把該項目用滑鼠拖拉到下方的打字編輯區,就會帶出該項的template!

對於拉變數欄位還蠻方便的~不用去記,因為欄位/參數/公式會全在 "報表欄位"裡~
而函式這東西就是讓你看看有沒有什麼可以用的...找不到就是沒有就對了!早點死心Orz...
運算子也還算實用~實用的原因是~它的語法真的太奇怪了阿阿阿~~不知道是那位天才想出來的,很奇怪的function概念~

在我目前的感覺上~嗯一"一a...因為偶又沒看過書,沒上過課,直接當沒說明書的遊戲在打王...就~
以var變數上來說,定義時不要給init值,當有要用到時,沒值他會自己起一個default值(總之要用的時後不會是null啦~)
function裡,其實他理論上像是 string xxxx(), int xxxx()的那種樣子,會要求你一定要給回傳值讓他顯示在頁面上~但是也沒有return的寫法,就是...只要隨便用個變數給個值~或是給它個純字串純數字~他最後就會把最後的那個值顯示在報表上~只能說"天才"Orz...

###數字轉文字時加三位一撇~
ToText( cnt ,'##,###,###,##0', 0);

###判別null...在crystal report內沒有null這種東西,是用預設值在看的~
http://stackoverflow.com/questions/18691542/how-to-check-for-null-condition-on-shared-variable-in-crystal-reports

Number: 0
Currency: $0
String: "" //The empty string
Date: Date(0,0,0) //The null date value
Time: None or null //The isnull() function won't work
Datetime: None or null //The isnull() function won't work

所以以日期舉例來看為~
if  ({命令.MAX_DATE} <> Date(0,0,0)) then
    ToText ({命令.MIN_DATE},'yyyy-MM-dd')
else
    ''

### 日期轉民國年
(其實,後來發現內建的"欄位格式",就可以改成民國年format了,不過要是資料源是字串,還是得自己處理)
if  ({命令.MIN_DATE} <> Date(0,0,0) And {命令.MAX_DATE} <> Date(0,0,0)) then
   ToText(ToNumber(Year ({命令.MIN_DATE}))-1911,'####')+'.'
    +(Mid(ToText ({命令.MIN_DATE},'yyyy.MM.dd'), 6,5))
    +' ~ '
    +ToText(ToNumber(Year ({命令.MAX_DATE}))-1911,'####')+'.'
    +(Mid(ToText ({命令.MAX_DATE},'yyyy.MM.dd'), 6,5))
else
    ''
### 動態where 時間區間,若原值可能有null,用IFNULL,default 起迄可以用Date的最大值或最小值~或是實務上的時間。動態數字,原理一樣(處理<=, >=)
....
and IFNULL(CREATE_DATE,'1800-01-01') >= CASE
      WHEN '{?CREATE_DATE_START}' != '' THEN '{?CREATE_DATE_START}'
      ELSE '1800-01-01' 
END
...

### 動態where string(處理=)
....
and TYPE = CASE
      WHEN '{?TYPE}' != '' THEN '{?TYPE}'
      ELSE TYPE
END
...

### 設計軟體的XLS對齊....
一般如果只是拉固定頁面排版或是純文字的話, 應該是沒什麼好講的...
但是如果是要拉成像XLS那種格子的話....就...

因為crystal report並沒有提供table類型的排版,所以就是用框線(口)加上直線(一|)這種東西組合出來Orz...

表頭基本上就是外面一個大框(口),加上豎線(|)。如果表頭多列的話就再加橫(一)畫出來,做成最上面的區段(表頭或是群組的區段)。

資料的話,就是在細目畫出左下右框線(|_|),上框線要空的,因為表頭或前一列會提供上框線...

相連的區段,可以用區段調整,把區段的空隙消除,就會黏在一起了~

PS: 後來發現如果用大框(口)其實也可以接成xls的樣子,但是有個前提是,大框的左右兩邊必須都是對齊的狀態,在看預覽時,就會發現有對齊的格子跟沒對齊的格子畫法不一樣O_oa...
然後實際上跑的時候,橫線有時會黏在一起,有時不會,真的超級Orz...
接著試出最後一招....在區段,右鍵「排列各行」。接著就會在邊邊出現對齊用的小三角形(然後版通常會亂一下),開始拉小三角形到定點,還有調最底下的那個往上(通常那個就是指大框(口)的底)...接著最後調整區段清掉空白(調整區段後,應該還要可以看到最底下的小三角形...就是要把那個拉比區段高一點點)...然後T__T他終於會把線黏在一起了~不過...是會比較粗啦~但至少不會空在那裡更怪~_~|||....

然後常會碰到很機車的,在那邊打XY,長度寬度怎麼打數字都會故意亂跳,自以為很聰明但是全都不對!...按左右移格的位置還都很奇怪(就是為了那0.01...)
這時候就到工具列"檔案"--> "選項" 裡,貼齊格線的勾拿掉...
http://stackoverflow.com/questions/17529383/crystal-reports-object-size-and-position-not-working
接下來後,就可以用滑鼠拖拉對齊了,但是再怎麼打數字就完全不鳥你(管你打20, 30聞風不動,一切靠200%+滑鼠...)....雖然很難用,但是這個方法至少讓我可以移動傳說中的0.01位置...

### 設計軟體字被裁切
這個可能是pdf版本所延伸出來的問題,然後中文字又喜歡長得特別胖,很容易看軟體美美的,等產出後整個走經=3=...

所以就是一開始的文字高度一定要定高出來多點,如果外框剛剛好,一定會被切到底部...
然後目前Browser看到的樣子,與另存新檔開出來的樣子也會有差別(聽說連螢幕解析度也有影響)...
如果user很龜毛的話(user通常也不懂資訊,能有個能說嘴的通常都龜毛的跟什麼一樣...)
最好還是乖乖的依實用案例量身打造囉~

Crystal Report-自訂Array動態接值

剛看到一項限制~補充一下。陣列大小最大為 1000 個元素 。
看來array真的不好用@@~

因為報表本身並不會幫你處理RAW data...雖然在產生報表時,一定是有吃到~但是卻沒有提供給人隨意取資訊的method...所以真的很難用阿阿阿(回音)

繼前篇~那是對內容每份做個小計~
但是其實在最後的部分還得給一個全部的統計~
所以......眼看又要GG了~其實最後還是GG先用subquery解決~只是因為去db撈兩次感覺還好~GG的原因是因為這報表沒有提供Sort的function,硬要刻真是太難為我了Orz...

不過還是留一下寫出來的function~
排版示意圖:

細目      @sum_add
....
報表尾  (@sum_sort) @sum_show

作法,其實就也是建兩個array,一個放名稱(all_name),一個放累計筆數(all_cnt),另一個則放array的size(idx),其兩個array就是1:1做對應。

add時,會先比名稱,有的就累計舊的,沒有就兩個array各擴充一筆
最底下的 Sum (all_cnt);是拿來做debug用的~
@sum_add
Shared stringVar array all_name;
Shared NumberVar array all_cnt;
Shared NumberVar idx;

stringVar tmp := {XXXXXXXXX} ;
Local NumberVar i;
booleanVar isContains := false ;
For i := 1 To idx Do  (
    if all_name[i] = tmp then (
        all_cnt[i] := all_cnt[i]+1;
        isContains := true ;
    )       
);

if isContains = false then  (
    idx := idx+1;
    redim preserve all_name [idx];
    all_name[idx] := tmp;
    redim preserve all_cnt [idx];
    all_cnt[idx] := 1;
   
    Sum (all_cnt);
) else
Sum (all_cnt);


最後顯示相對簡單的~就照順序拿出來,但是就在這個摩門~我想起來這邊應該要依項目排序的Orz...
@sum_show
Shared stringVar array all_name;
Shared NumberVar array all_cnt;
Shared NumberVar idx;

stringVar showData := '';
NumberVar i;
for i := 1 to idx do (
    showData := showData +Chr(13)+Chr(10)+ all_name[i] +':'+all_cnt[i];
);
showData;

過了幾天~想一想還是做一個醜醜的排序好了~排序要在取值前先排。原理用最簡單的把資料拿起來一個一個排就是了,反正就賭他項目不會很多~
PS:有發現他的中文的順序和DB的算法不一樣~但是看起來還是有照筆畫就算了@@...反正之後有被念再說|||Orz...
雖說function裡有用MID(substring)來取第一個字排,不過偶發現跟用AscW來取的話~好像都一樣?!好像都一樣只會回傳第一個字的碼,就總之有被念後再想辦法改好了=_=|||...
@sum_sort
Shared stringVar array all_name;
Shared NumberVar array all_cnt;
Shared NumberVar idx;
Shared stringVar array sort;

NumberVar i;
NumberVar j;
NumberVar k;
StringVar output;
StringVar tmp;
sort := all_name;
for j := 1 to idx do (
    for i := 1 to j do (
        if (MID(sort[j],1) < MID(sort[i],1)) then (
            tmp := sort[j];
            for k := j to i+1 step -1 do (
                sort[k] := sort[k-1];
            );
            sort[i] := tmp;
            exit for;
        );
    );
);

for i := 1 to idx do (
    output := output+sort[i]+',';
);
output;

在排序後要取值的話~先比對有值後再顯示~
@sum_show
Shared stringVar array all_name;
Shared NumberVar array all_cnt;
Shared stringVar array sort;
Shared NumberVar idx;

stringVar showData := '';
NumberVar i;
NumberVar j;
for i := 1 to idx do (
    for j := 1 to idx do (
        if sort[i] = all_name[j] then (
            showData := showData +Chr(13)+Chr(10)+ all_name[j]+':'+ToText(all_cnt[j],'##,###,###,##0',0);
            exit for;
        );
    );  
);
showData;

Crystal Report-把goupName放在一起顯示

首先~讓我嘆N口氣後再說~水晶報表真是一個無言的產品...

它把grouping的東西當迴圈的概念做進來,把RAW data用resultSet一樣只能next的一筆筆抓偶也接受,是還好沒什麼意見,但是在報表中,底下的小統計分析明明就是很常見的放置做法~口訴它居然沒有提供直接的方式來做就是讓人覺得不爽~

然後偶同事跟我說再去DB做個subquery...這個解法也真是挺爛的~因為這個小型統計硬要query的話~要查個N百次才查得完~而且他一次又要撈很多表一定超慢的~要是可以在Java的作法上,只要把RAW Data丟進Map/SortedMap做分類~ 再撈出來統計數字~用用memory就可以解決的小問題(因為這個list量也還好一般,不會讓list的size爆掉,只是它join不少的table和長長一大串的where而已~)~

總之~因為在google上一時找不到解法,所以就想了個很爛的方式解套先Orz...
先以sql的概念來說~其實不過就是想實現
select count(AAA), AAA from tbl_XXX
group by AAA
這麼簡單的問題><~但是為啥會搞得這麼複雜呢!?

報表的設計配置示意如下:

群組首#1          (隱藏但可計)@group_reset
    群組首#2      (隱藏但可計)
        細目          (顯示)
    群組尾#2      (隱藏但可計) #group_count   @group_set
群組尾#1         (顯示) @group_show_name   @group_show_cnt

先設定累計項目(#group_count)為,計算群組2的項目筆數,記得要在群組變更時reset,就可以得到 該首#2的個數累計。

其實若把隱藏的都show出來,就可以得到對的數據了(只是排版不一樣Orz...為啥咪只是想把原本在"群組尾#2"的項目全部擺在同一區會這麼血淚...)~
因為user是想要先看完明細後才看小計~為了這個排版搞死人一_一"...

報表的邏輯~我看是像resultset next的作法一樣,跑過就不見~擺在那就只能看到最後一筆,所以乾脆做變數來把他都先接起來,然後最後再一起顯示~

變數採用名稱跟數量分開~是為了結果排版好看啦XD...
還有要記得一起換行(文字模式用 Chr(13)+Chr(10) )!!這樣才會對齊~
ToText({#group_count}, 0)-->統計會自帶小數點,讓它變整數~

@group_reset 清空變數
Shared stringVar groupDisplay := '';
Shared stringVar groupDisplayCnt := '';

@group_set 把資訊保存
Shared stringVar groupDisplay;
Shared stringVar groupDisplayCnt;

groupDisplay := groupDisplay +Chr(13)+Chr(10)+ GroupName ({@XXX})  ;
groupDisplayCnt := groupDisplayCnt +Chr(13)+Chr(10)+ ToText({#group_count}, 0) ;

@group_show_name
Shared stringVar groupDisplay;

stringVar output := groupDisplay;
output;

2014年8月11日 星期一

html置中排版~

之前好像有寫過~不過最近有找到一個更漂亮的寫法,就記錄一下~

這次大多練習用css排,有單獨使用小調整的才寫在code裡,
規劃上就是以body, div來置中,div則走list區塊的方式在排,若是一個區塊裡又分左右的話,再考慮在div裡面開table或是div/span來處理。總之由上往下整體上都是一條條的div,這樣就不會亂跑了!

內容上主要分 h1(大標), h2(副標), .content(內文文字)
因為上次聽說search上h1會比較容易被查到,所以就配合一下。
.content 主要處裡文字縮排,左右空隙,不過因為margin-right好像很難用沒反應,所以乾脆就用width來算,就齊了一"一b...

這次其實是做的是EDM...(電子DM的版,所以跟網站不太一樣XD)
字大小都僅量用百分比在微調,還蠻好用的!

多個class,指定時用" "(空白隔開就可以了)
<div class="content text_bottom text_grey div_border" >

/**body 和 div要一起配合才會一起置中! div也可以不要用tag, 指定一個包很大的 id元素也可以~*/
body  {
    font-family: Microsoft JhengHei;   /**字型改正黑體*/
    margin: 0;
    text-align: center;
}

div {
    position: relative;
    margin: 0 auto;
    width: 800px;   /**版面寬度,主要配合banner圖*/
    text-align: left;
}


h1  {
    font-size: 32px;
    margin-left: 50px;
}

h2  {
    color:red;
    font-size: 24px;
    margin-left: 50px;
    margin-top: -20px;
}

.content  {
    margin-left: 30px;    /**內文縮排,width為全寬減掉右邊縮排的距離*/
    width: 740px;
}

.text_red  {
    color:red;
}

.text_small  {
    font-size: 70%;    /**字型比率(%),之前比較愛用+1, -1,不過有些微妙變化的字用百分比好用*/
}

.text_bottom  {
    margin:30px;     /**包在.content裡面,就可以縮排兩次了*/
    text-align:center;    /**字的位置,置中*/
}

.text_grey  {
    color:grey;
}



.div_border  {     /**div的外框線,一定要設定border-style才會跑出來*/
    border-color:grey;
    border-size: 1px;
    border-style: solid;
}

其他:
圖片要整張背景圖出來,用height限制最剛好,不會有換行縮排div太多或太少的問題
background-image: url('imgs/edm-07-01.png'); height:500px


unicode string to UTF-8

這是個有點蠢的問題~不過居然一時忘了怎麼解~
卡了兩個小時真是豬頭T_T...

總之~java有個cmd可以把內容轉成unicode--> native2ascii
通常用於語系properties檔內~
然後那個檔裡面的中文字就開始變成\uXXXX的東西~


於是偶就只是想要看一下中文是什麼~然後就卡住了|||Orz...
因為檔案是為UTF-8的編碼~所以讀檔進來就變成一堆英文字~
也當然不能把他用UTF-16讀~因為降子一讀進來的字就錯了~

結果是~請出好用的apache commons lang來一行解決他!
StringEscapeUtils.unescapeJava(str);
就~這樣很簡單的就把檔給寫回原本的中文了T^T...

public void process(String input, String output) throws Exception {
        FileInputStream fis = new FileInputStream(input);
        FileOutputStream fos = new FileOutputStream(output);
       
       
        BufferedReader br = new BufferedReader(new InputStreamReader(fis, "utf-8"));
        while(br.ready())  {
            String row = br.readLine();
            String str = StringEscapeUtils.unescapeJava(row);
//System.out.println("str="+str);           
            str += "\n";
            fos.write(str.getBytes("UTF-8"));
        }
       
        fis.close();
        fos.close();
}


2014年8月7日 星期四

DB2 語法備忘

因為DB2常跟別人的語法不一樣~
備註依有用到時更新~

@@ 限制回傳筆數 ==> limit 3
FETCH FIRST 3 ROWS ONLY

@@ 判斷null時給default值 ==> ISNULL(col, 0)
IFNULL(numcol,0)
IFNULL(strcol,'')

@@動態判斷是否要篩選條件 IFNULL(isNull)
SELECT ... FROM ...
WHERE ...
And STATUS = IFNULL('{?STATUS_P}', STATUS)
 @@CASE 應用
CASE    
     WHEN test-condition THEN value
     WHEN test-condition THEN value
     ELSE value
END    
 
@@ 動態判斷 空值->不篩選, 非空值->條件
常見大家會說用IFNULL來做,但因為用到的地方,它無法傳NULL進來,沒值是傳空字串,所以得用CASE來做
SELECT ... FROM ...
WHERE ...
And STATUS =
CASE
      WHEN '{?STATUS_P}' != '' THEN '{?STATUS_P}'
      ELSE STATUS 
END


@@ 用動態的方式,把三個條件is not null, is null, 不篩選做成一個SQL...
用CASE 來將符合條件的資料給成一個新的欄位 DATA_CHECK,符合給YES,不合給NO,然後再從中挑出DATA_CHECK='YES'的就可以成功了!其中

example:
SELECT a.* FROM (
SELECT XXX.ID, YYY.ID as ID2,
CASE
           WHEN ('{?TYPE_P}'  = '1' And YYY.ID is not Null) THEN 'YES'
           WHEN ('{?TYPE_P}' = '2' And YYY.ID is Null) THEN 'YES'
           WHEN ('{?TYPE_P}' = '0') THEN 'YES'
           ELSE 'NO'
END as DATA_CHECK,
......From XXX, YYY
Where ....
FETCH FIRST 10 ROWS ONLY ) a
WHERE a.DATA_CHECK = 'YES'