2015年6月30日 星期二

證明unicode和UTF-8各佔幾Byte

首先創建三個文字文件,可以另存新檔選編碼

如下是結果圖,分別是ANSI.txt、Unicode.txt、UTF-8.txt

這三個檔裡面都是空的,分別按右鍵內容看一下大小(不是磁碟大小),磁碟大小是電腦一存就是4k的意思,選檔案系統時決定的,這時會看到三個檔案的大小不同,如上圖是Unicode.txt的大小。
第一次-->三個檔案都打一個ASCII code有的字,然後存檔看一下大小的變化,不要按到空白或Enter
第二次-->三個檔案都打一個中文字,然後存檔看一下大小的變化

這時就可以整理出一張表,注意要扣掉空白檔案的大小:
                ANSI        Unicode(UTF-16)        UTF-8
空檔          0B               2B                              3B
ASCII       1B                2B                              1B
中文         2B             2B、4B                      3B~6B

ANSI就是系統編碼,像我是Big5,看你用什麼地區安裝的會不一樣

unicode 就是 unicode16LE,也就是 UTF-16LE
UTF 是 Unicode Transfor Format,UTF-8、UTF-16、UTF-32 是對 Unicode 的轉換,因為以上面整理出的表會發現美國人大部分都是用英文 (ASCII),用了 Unicode 後,容量增大一倍,這時用 UTF-8 就會和原來是一樣的,而且還支援其他國家的符號

UTF-8 有1~6 Byte,如「𠃡𠃢𠃣𠃤𠃥𠃦」在 UTF-8 都是 4Byte,輸入法應該是打不出來,但可以複製, java 和 javascript 都只支援到 4Byte,都是到 unicode 的 10FFFF,可看我的這一篇文章比較詳細
可在 cmd 打 chcp (change code page),微軟文件維基

437          ASCII
936          GBK
950          Big5
65000 UTF-7
65001 UTF-8

可以改,但只會在當前的 cmd 生效,不會影響預設值,想改預設值要改機碼



2015年6月29日 星期一

Struts2 HelloWorld(Struts2.3.x 一)

※XML設定

hello world struts2
這個網址為官方教學,蠻清楚的,我針對不清楚的部分說明,它是用maven,我用eclipse創建

struts-2.x/docs/docs/create-struts-2-web-application-using-maven-to-manage-artifacts-and-to-build-the-application.html 這個是下載下來的目錄裡,比較準確,因為和你下載的版本完全一樣

1.先new一個Maven Project

2.直接Next

3.選webapp的選項

4.Artifact Id就是專案名稱,官方的第一步是叫basic_struts

5.mvnrepository.com為maven網站,官方的第三和第四步會用到,自行搜尋一下

6.將網站找到的貼到pom.xml,<finalName>為官方的第一步,因為我們用maven,第四部就做好了,這邊會自動出現

7.官方的Step2 5 6 7都做一下,run起專案,叫會看到index.jsp的內容了,如果jsp<%@ page 前面有錯的話,注意訊息,我的部分是要加servlet-api.jar,將tomcat build進來即可。

jsp的「s:」必須在上面加上<%@ taglib prefix="s" uri="/struts-tags" %>,兩支都有用到,如果不想用,可以自己打hello.action,才會連到另外一頁

再來最下面會有Hello World Using Struts 2,點進去,也寫的非常清楚,照著做即可
<a href="<s:url action='hello'/>">Hello World</a>-->hello會連到struts.xml的name,如下:
<action name="hello" class="helloworld.action.HelloWorldAction" method="execute">
所以它會去call這個class的execute方法
做完事後,會回傳success,然後到HelloWorld.jsp
<result name="success">/HelloWorld.jsp</result>
最後用<s:property value="messageStore.message" />struts2的專用標籤語法,顯示內容
我把我這次做的程式碼放在下面好了

HelloWorldAction.java,類似controller,永遠繼承ActionSupport
package helloworld.action;
import helloworld.model.MessageStore;
import com.opensymphony.xwork2.ActionSupport;
public class HelloWorldAction extends ActionSupport {
    private static final long serialVersionUID = 1L;
    private MessageStore messageStore;

    @Override
    public String execute() throws Exception {
        messageStore = new MessageStore();
        return SUCCESS;
    }

    public MessageStore getMessageStore() {
        return messageStore;
    }

    public void setMessageStore(MessageStore messageStore) {
        this.messageStore = messageStore;
    }
}
MessageStore.java
package helloworld.model;
public class MessageStore {
    private String message;

    public MessageStore() {
        this.setMessage("Hello Struts User");
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
log4j.xml,其實不加也沒關係,和struts2沒有絕對關係
<!DOCTYPE log4j:configuration PUBLIC "-//log4j/log4j Configuration//EN" "log4j.dtd">

    
        
            <param name="ConversionPattern" value="%d %-5p %c.%M:%L - %m%n" />
        
    

    
    
        <level value="DEBUG" />
    

    
        <level value="DEBUG" />
    

    
    
        <priority value="INFO" />
        <appender-ref ref="STDOUT" />
    

struts.xml-->struts-2.3.dtd",copy官網是2.0,不過我的是2.3,所以我改成2.3,這個檔案放在src/main/java或src/main/resources都可以
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">

    <constant name="struts.devMode" value="true" />
    
        
            /index.jsp
        
        
            /HelloWorld.jsp
        
    

web.xml
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" >

    Archetype Created Web Application
    
        struts2
        org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
    
    
        struts2
        /*
    

HelloWorld.jsp
<%@ page language="java" contentType="text/html; charset=BIG5" pageEncoding="BIG5"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!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=BIG5">
<title>Hello World!</title>
</head>

<body>
    <h1>HelloWorld.jsp</h1>
    <h2>
        <s:property value="messageStore.message" />
    </h2>
</body>
</html>
index.jsp
<%@ page language="java" contentType="text/html; charset=BIG5" pageEncoding="BIG5"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<!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=BIG5">
<title>Struts2</title>
</head>
<body>
    <h1>看到了</h1>
    <p>
        <a href="<s:url action='hello'/>">Hello World</a>
    </p>
</body>
</html>

※以下是annotation設定

anotation struts2,這邊可以參考。
因為是annotation設定,所以可以替換xml,當然也可以留著,只是待會測就測不出來了,先註解掉好了,根據網站的說法, struts.xml,只剩下一行(其實web.xml的init-param和這裡的constant根本不用,但第一次就先以官方為主好了)
<struts>
    <constant name="struts.devMode" value="true" />
</struts>

※然後要下載一個叫struts2-convention-plugin的jar,web.xml要設定init-param,都從官網copy就可以了

※但我有遇到一個問題一裝完啟動會出「java.lang.NoClassDefFoundError: org/apache/commons/lang/xwork/StringUtils」的錯,最好的解決方法就是,版本和struts2-core的版本一樣就可以了

※還有一個問題,官網寫說action類別一定要放在包名叫struts、struts2、action、actions四者之一,否者就會出「There is no Action mapped for namespace [/] xxx」的錯,只有annotation會這樣

以下是action類,我覺得它寫得不清楚 如下:
package helloworld.action;
import helloworld.model.MessageStore;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.Result;
import com.opensymphony.xwork2.ActionSupport;

@Namespace(value = "/")
public class HelloWorldAction extends ActionSupport {
    private static final long serialVersionUID = 1L;
    private MessageStore messageStore;

    @Override
    @Action(value = "hello", results = { @Result(name = "success", location="/HelloWorld.jsp") })
    public String execute() throws Exception {
        messageStore = new MessageStore();
        return SUCCESS;
    }

    public MessageStore getMessageStore() {
        return messageStore;
    }

    public void setMessageStore(MessageStore messageStore) {
        this.messageStore = messageStore;
    }
}

對照一下原本的xml
<package name="basicstruts2" extends="struts-default">
    <action name="index">
        <result>/index.jsp</result>
    </action>
    <action name="hello" class="helloworld.action.HelloWorldAction" method="execute">
        <result name="success">/HelloWorld.jsp</result>
    </action>
</package>

package 裡面本來就有一個屬性namespace了,不打就是「/」,所以@Namespace也可以不打

quartz和java Mail範例

官網教學
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.2.3</version>
</dependency>



import java.util.Date;
import java.util.Properties;
import javax.mail.AuthenticationFailedException;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class SendBugzillaMail {
    public void sendBugzillaMail() {
        InternetAddress[] address = null;
        InternetAddress[] address2 = null;
        String mailserver = "SMTP server";// SMTP server
        String from = "mail";// 寄件者mail
        String to = "mail";// 收件者mail
        String cc = "mail";// 收件者mail
        String subject = "subject";// 主旨
        String messageText = "test";// 內容

        Properties props = System.getProperties();
        props.put("mail.smtp.host", mailserver);
        props.put("mail.smtp.auth", "true");
        AuthAccountPassword msec = new AuthAccountPassword("user", "password");
        Session mailSession = Session.getDefaultInstance(props, msec);
        mailSession.setDebug(false);
        Message msg = new MimeMessage(mailSession);// 創建文件信息

        try {
            msg.setFrom(new InternetAddress(from)); // 設置傳送郵件的發信人
            address = InternetAddress.parse(to, false); // 指定收信人的信箱
            address2 = InternetAddress.parse(cc, false); // 指定收信人的信箱
            msg.setRecipients(Message.RecipientType.TO, address); // 向指定郵箱發送
            msg.setRecipients(Message.RecipientType.CC, address2); // 向指定郵箱發送
            msg.setSubject(subject);
            msg.setSentDate(new Date()); // 設定寄件時間
            msg.setText(messageText);
        } catch (AddressException e1) {
            e1.printStackTrace();
        } catch (MessagingException e1) {
            e1.printStackTrace();
        }

        try {
            System.out.println("驗證中…");
            Transport.send(msg, msg.getAllRecipients());
            System.out.println("send over!");
        } catch (AuthenticationFailedException e) {
            System.err.println("account or password error!");
        } catch (MessagingException e) {
            System.err.println("SMTP sever error!");
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("send fail...");
        }
    }
}

import org.quartz.CronScheduleBuilder;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;

public class SimpleSchedule implements Job {
    public static void main(String[] args) {
        JobDetail jobDetail = JobBuilder.newJob(SimpleSchedule.class).build();

        Trigger trigger = TriggerBuilder.newTrigger().withSchedule(CronScheduleBuilder.cronSchedule("0 30 18 ? * MON-FRI")).startNow().build();

        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = null;
        try {
            // 取得一個調度器
            scheduler = schedulerFactory.getScheduler();
            scheduler.scheduleJob(jobDetail, trigger);
            scheduler.start();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void execute(JobExecutionContext paramJobExecutionContext) throws JobExecutionException {
        System.out.println("start schedule...");
        SendBugzillaMail sbm = new SendBugzillaMail();
        sbm.sendBugzillaMail();
        System.out.println("end schedule!");

    }
}

import javax.mail.Authenticator;
import javax.mail.PasswordAuthentication;

public class AuthAccountPassword extends Authenticator {
    private String name;
    private String password;

    public AuthAccountPassword(String name, String password) {
        super();
        this.name = name;
        this.password = password;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    protected PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication(this.name, this.password);
    }
}

排程 (Timer和TimerTask)

Timer的schedule、scheduleAtFixedRate主要差別是
schedule(4個)設定完後,下次會開始
scheduleAtFixedRate(2個)設定完後,連同之前的都會做

※TimerTask是個抽象類別,而且還implements Runnable,所以是個執行緒,要寫run(),也就是排程要執行的工作


public class Xxx extends TimerTask{
    public static void main(String[] args) {
        // 第一個參數為"欲執行的工作",會呼叫對應的run() method
        // 第二個參數為第一次執行的時間(Date)或延遲的時間(long)
        // 第三個參數為每幾毫秒做一次(是個選項,沒加就是只執行一次)
        new Timer().schedule(new Xxx(), new Date(), 3000L);
    }
    
    @Override
    public void run() {
        System.out.println(new Date());
    }
}
※也就是每3秒印一行

※如果沒有第三個參數,就是只執行一次
第二個參數如果是Date,表示哪時開始;如果是long,表示延遲多少毫秒才開始
schedule(TimerTask task, Date time)
schedule(TimerTask task, long delay)
schedule(TimerTask task, Date firstTime, long period)
schedule(TimerTask task, long delay, long period)


※匿名寫法

public static void main(String[] args) {
    new Timer().schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println(new Date());
        }
    }, new Date(), 3000L);
}


※schedule和scheduleAtFixedRate的差別

public static void main(String[] args) {
    Calendar cal = new GregorianCalendar(2016, Calendar.MARCH, 9, 13, 0, 0);
    System.out.println(new Date());
    // new Timer().schedule(new LoadOnStartup(), cal.getTime(), 2000);
    new Timer().scheduleAtFixedRate(new LoadOnStartup(), cal.getTime(), 2000);
}

※我測的時候是3月9號,我定的時間往前幾個小時,會補之前沒做的,但時間當然是現在的時間做

※Timer和TimerTask都有Cancel()可以取消排程



※單數次2秒執行、偶數次3秒執行

也就是第一次2秒執行,第二次3秒執行,第三次2秒執行,第四次3秒執行…依此類推

public class Test {
    static int i = 0;
    
    public static void main(String[] args) {
        class MyTimerTask extends TimerTask {
            @Override
            public void run() {
                i = (++i) % 2;
                System.out.println("ooo");
                // 2秒或3秒
                new Timer().schedule(new MyTimerTask(), 2000 + 1000 * i);
            }
        }
    
        new Timer().schedule(new MyTimerTask(), 2000);
    
        new Thread(() -> {
            for (;;) {
                try {
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println(new Date().getSeconds());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

※由於內部類別和方法內部不能寫 static 變數,所以寫到最外面

※改成 % 3 可以 2、3、4 秒循環

常考的面試題:畫星星(共八種)

畫這個星星有很種方式,我個人覺得這種方式比較不容易忘
行   1   2   3
星   3   2   1
第一行有三顆星,第二行有兩顆星,第三行有一顆星。
想辦法想出一個公式,譬如3=1*3也等於1+2,也等於根號9…等
三行只是視意,要動態的方式想,譬如
行   1   2   3   4   5
星   5   4   3   2   1
,我想到的是:最後一行 + 1 - 行
先看最上面的行那一行:
第一行-->3+1-1=3(星數),第二行-->3+1-2=2(星數),第三行-->3+1-3=1(星數)
同樣的,五行也是一樣

做法:

第一個迴圈負責行,第二個迴圈負責星,將公式放到星數那一行
***
**
*
private static void start1(int startCount) {
    System.out.println();
    for (int i = 1; i <= startCount; i++) {// 行數
        for (int j = 1; j <= startCount + 1 - i; j++) {// 星數
            System.out.print("*");
        }
        System.out.println();
    }
}
行   1   2   3
星   1   2   3
公式:行
*
**
***
private static void start2(int startCount) {
    System.out.println();
    for (int i = 1; i <= startCount; i++) {// 行數
        for (int j = 1; j <= i; j++) {// 星數
            System.out.print("*");
        }
        System.out.println();
    }
}
這裡開始會有輸入空格的情形,也就是要多一個迴圈
行   1   2   3
空   0   1   2
星   3   2   1
空格公式:行 - 1
星號公式:最後一行 + 1 - 行
***
 **
  *
private static void start3(int startCount) {
    System.out.println();
    for (int i = 1; i <= startCount; i++) {// 行數
        for (int j = 1; j <= i-1; j++) {// 空格數
            System.out.print(" ");
        }
        for (int j = 1; j <= startCount + 1 - i; j++) {// 星數
            System.out.print("*");
        }
        System.out.println();
    }
}
行   1   2   3
空   2   1   0
星   1   2   3
空格公式:最後一行 - 行
星號公式:行
  *
 **
***
private static void start4(int startCount) {
    System.out.println();
    for (int i = 1; i <= startCount; i++) {// 行數
        for (int j = 1; j <= startCount-i; j++) {// 空格數
            System.out.print(" ");
        }
        for (int k = 1; k <= i; k++) {// 星數
            System.out.print("*");
        }
        System.out.println();
    }
}
行   1   2   3
空   2   1   0
星   1   3   5
空格公式:最後一行 - 行
星號公式:行 * 2 - 1
  *
 ***
*****
private static void start5(int startCount) {
    System.out.println();
    for (int i = 1; i <= startCount; i++) {// 行數
        for (int j = 1; j <= startCount-i; j++) {// 空格數
            System.out.print(" ");
        }
        for (int k = 1; k <= i*2-1; k++) {// 星數
            System.out.print("*");
        }
        System.out.println();
    }
}
行   1   2   3
空   0   1   2
星   5   3   1
空格公式:行 - 1
星號公式:(最後一行 - 行) * 2 + 1
*****
 ***
  *
private static void start6(int startCount) {
    System.out.println();
    for (int i = 1; i <= startCount; i++) {// 行數
        for (int j = 1; j <= i-1; j++) {// 空格數
            System.out.print(" ");
        }
        for (int k=1;k<=2*(startCount-i)+1;k++) {// 星數
            System.out.print("*");
        }
        System.out.println();
    }
} 
上三角形:
行   1   2   3
空   2   1   0
星   1   3   5
空格公式:最後一行 - 行
星號公式:行 * 2 - 1

下三角形(中間不算):
行   1   2
空   1   2
星   3   1
空格公式:行
星號公式:(最後一行 - 行) * 2 + 1
  *
 ***
*****
 ***
  *
private static void start7(int startCount) {
    System.out.println();
    for (int i = 1; i <= startCount; i++) {// 行數
        for (int j = 1; j <= startCount-i; j++) {// 空格數
            System.out.print(" ");
        }
        for (int k = 1; k <= i*2-1; k++) {// 星數
            System.out.print("*");
        }
        System.out.println();
    }
 
    startCount -= 1;//扣中間那一行
    for (int x = 1; x <= startCount; x++) {// 行數
        for (int y = 1; y <= x; y++) {// 空格數
            System.out.print(" ");
        }
        for (int z=1;z<=2*(startCount-x)+1;z++) {// 星數
            System.out.print("*");
        }
        System.out.println();
    }
}
上三角形:
行   1   2   3
空   0   1   2
星   5   3   1
空格公式:行 - 1
星號公式:(最後一行 - 行) * 2 + 1

下三角形(中間不算):
行   1   2
空   1   0
星   3   5
空格公式:最後一行 - 行
星號公式:行 * 2 + 1
*****
 ***
  *
 ***
*****
private static void start8(int startCount) {
    System.out.println();
    for (int i = 1; i <= startCount; i++) {// 行數
        for (int j = 1; j <= i-1; j++) {// 空格數
            System.out.print(" ");
        }
        for (int k=1;k<=2*(startCount-i)+1;k++) {// 星數
            System.out.print("*");
        }
        System.out.println();
    }
 
    startCount -= 1;//扣中間那一行
    for (int x = 1; x <= startCount; x++) {// 行數
        for (int y = 1; y <= startCount-x; y++) {// 空格數
            System.out.print(" ");
        }
        for (int z = 1; z <= x*2+1; z++) {// 星數
            System.out.print("*");
        }
        System.out.println();
    }
}
總結:用這種方法,什麼ijkxyz永遠都是1,永遠都是<=,永遠都是ijkxyz++,看起來一目了然,比較舒服

2015年6月28日 星期日

java類:

public static void main(String[] a){}

Map、properties-->key, value
List、Linked-->順序
Set-->不重覆
Sorted、Tree-->排序
Hash-->雜物

oracle類:

INSERT INTO <table>(<many field>)VALUES(<many value>);
INSERT INTO <table> VALUES(<many value>);
INSERT INTO <table1> (<many field>) SELECT many field FROM <table2> WHERE...;

DELETE FROM <table>;

UPDATE <table_name> SET <column> = <value> WHERE...;

SELECT * FROM <table>;
CREATE TABLE <table> (
    <field> <type> [not null],
    CONSTRAINT <pk_name> PRIMARY KEY(<field>),
    CONSTRAINT <fk_name> FOREIGN KEY (<field>) REFERENCES <table>(<field>) [ON DELETE CASCADE]
);
CREATE SEQUENCE XXX_SEQUENCE
    INCREMENT BY 1  -- 每次加幾個
    START WITH 1    -- 從1開始計數
    NOMAXVALUE      -- 不設置最大值
    NOCYCLE         -- 一直累加,不循環
    CACHE 10;
XXX_SEQUENCE.NEXTVAL
XXX_SEQUENCE.CURRVAL

COMMENT ON TABLE <table> IS 'xxx';
COMMENT ON COLUMN <table>.<field> IS 'ooo';
ALTER TABLE <table> ADD <field> <type>;

ALTER TABLE <table> DROP COLUMN <field>;

ALTER TABLE <table> MODIFY <field> <type> [not null];--修改欄位,不包括重命名

ALTER TABLE <table> RENAME COLUMN <old_field> TO <new_field>;--重命名用這個


--看版本
SELECT * FROM V$VERSION;

--看所有表的comment SYS.USER_TAB_COMMENTS
--看所有欄的comment SYS.USER_COL_COMMENTS
--看所有表 SYS.USER_TABLES;

--某個表的某個欄包括ABC,只要知道有一張全表查詢的表叫USER_TAB_COLUMNS就夠了
SELECT * FROM SYS.USER_TAB_COLUMNS
WHERE TABLE_NAME = '<table>'
AND REGEXP_LIKE (COLUMN_NAME, '*ABC*');

java的equals與==

看個程式碼先
Long l1 = new Long(100);
Long l2 = new Long(100);
Long l3 = new Long("100");
Long l4 = 100l;
Long l5 = 100L;
System.out.println("-------------一般比較-------------");
System.out.println(l1 == l2);// false
System.out.println(l1 == l3);// false
System.out.println(l1.equals(l2));// true
System.out.println(l1.equals(l3));// true,注意
System.out.println(l4 == l5);// true
System.out.println(l4.equals(l5));// true

String s1 = "xxx";
String s2 = "xxx";
String s3 = new String("xxx");
System.out.println("-------------字串比較-------------");
System.out.println("s1 == s2:" + s1 == s2 + "s1 == s2:");// false,注意
System.out.println(s1 == s2);// true
System.out.println(s1 == s3);// false
System.out.println(s1.equals(s2));// true
System.out.println(s1.equals(s3));// true

第17行要小心
因為會將寫死的字串加上xxx,然後==後面的xxx+寫死的字串,結果當然是false,
必需加圓括號先做運算才可以,如下:
"s1 == s2:" + (s1 == s2) + "s1 == s2:"

一般都是像Long那樣,但String因為有字串池的原故,所以如果不用new時就不太一樣
如果比較的不是xxx,是個數字,這時如果用String.valueOf(1),結果等同有new
除非String.valueOf("1"),因為本來就是字串,強轉還是字串,這時不會new


重點是如果是自己定義的類別呢?,先寫兩個測試類別
public class Compare1 {
    int i = 100;

    public int getI() {
        return i;
    }

    public void setI(int i) {
        this.i = i;
    }
}
public class Compare2 {
    int i = 100;

    public int getI() {
        return i;
    }

    public void setI(int i) {
        this.i = i;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof Compare2)) {
            return false;
        }
        return this.i == ((Compare2) obj).getI();
    }
}
Compare1 ca1 = new Compare1();
Compare1 ca2 = new Compare1();
Compare2 cb1 = new Compare2();
Compare2 cb2 = new Compare2();

System.out.println("-------------自定比較-------------");
System.out.println("ca1 == ca2:" + (ca1 == ca2));
System.out.println("cb1 == cb2:" + (cb1 == cb2));
System.out.println("ca1 equals ca2:" + ca1.equals(ca2));
System.out.println("cb1 equals cb2:" + cb1.equals(cb2));
結果為:
-------------自定比較-------------
ca1 == ca2:false
cb1 == cb2:false
ca1 equals ca2:false
cb1 equals cb2:true

如果有new關鍵字一定是不同的記憶體空間,Object的equals方法最原始為
public boolean equals(Object obj) {
    return (this == obj);
}
所以就是比較記憶體,但有覆寫當然就把它變成我們想要的比較內容

注意:就算是實作Cloneable,然後使用clone(),這樣子記憶體還是不一樣的,也就是結果還是和cb1和cb2一樣

結論:

== 因為長得像記憶體,所以就是比較記憶體;equals 就比較內容。
但沒覆寫equals方法,就是比較記憶體,因為Object就是this == obj


※注意

沒有小數點的 Byte、Short、Integer、Long 和 Character,裡面有個內部類 XxxCache
如 Integer 內部有個類別,IntegerCache,會將 -128~127 cache 起來 (Character 是 0 ~127)
所以在這個範圍裡,如果內容一樣且不用 new,然後用 == 判斷的,是沒問題的,如
Character c1 = 'y', c2 = 'y';   P.S:y 的 ASCII 是 121
c1 == c2 會是 true

※Integer status = 1;
status == 1,這是可以判斷的,但要小心 null == 1,int 是沒有 null 的,會報錯


※Enum的equals、==

public class EnumEqal {
    public enum EnumABC {
        A, B, C;
    }
    
    private EnumABC enumAbc;
    // setter/getter...
    
    public static void main(String[] args) {
        EnumEqal ee = new EnumEqal();
        ee.setEnumAbc(EnumABC.C);
    
        System.out.println(EnumABC.C.equals(ee.getEnumAbc()));// true
        System.out.println(EnumABC.C == ee.getEnumAbc());// true
    
        System.out.println(EnumABC.C.equals(ee.getEnumAbc().name()));// false
    
        System.out.println(EnumABC.C.toString().equals(ee.getEnumAbc().name()));// true
        System.out.println(EnumABC.C.toString() == ee.getEnumAbc().name());// true
    }
}

※new一個物件,然後比較,所以不管是equals和==都會是true,就算將enumAbc改成static,然後setter/getter也改成static,結果依然是一樣的

※此例除非另外 new,否則一定都是 true


※List 的 equals

仍然是比對物件的內容和型態,不要被角括號影響了



List<Integer> l1 = new ArrayList<>();
List<String> l2 = new ArrayList<>();
    
List l3 = new ArrayList();
List l4 = new ArrayList();
    
List<Integer> l5 = new ArrayList<>();
l5.add(8);
List<Integer> l6 = new ArrayList<>();
l6.add(8);
    
System.out.println(l1.equals(l2)); // true
System.out.println(l3.equals(l4)); // true
System.out.println(l5.equals(l6)); // true

※只有內容或內容的型態一樣才會是 true,其他都是 false

※Set、Map 也是同樣的道理,Map 裡的 key 或 value,其中一個不一樣就會是 false 了


※String 的 intern

指的是 java 7 之後的,此例是用 java 8 跑的
intern 表示將字串的內容放到字串池,如果沒有就放;有就什麼也不做
@Test
public void test1() {
String s1 = "a"; // s1 s2 如果為 final,結果會是 true
String s2 = "b";
String s3 = "ab";
System.out.println(s3 == s1 + s2); // false,變數相加要看變數佔哪個記憶體空間
System.out.println(s3 == "ab"); // true
System.out.println(s3 == "a" + "b"); // true,優化成 "ab"
System.out.println(s3 == s1 + "b"); // false,無法優化成 "ab",除非 s1 final
}
@Test
public void test2() {
String s1 = new String("123"); // stack s1heap 123,字串池123。 字串的 123 會放在字串池
// String s1 = new StringBuilder("123").toString();
s1.intern(); // s1 123 放到字串池,但一開始就已經有 123 了,所以不會有動作
String s2 = "123";
System.out.println(s1 == s2); // false,如果想變 true,可用 s1 = s1.intern();
}

@Test
public void test3() {
String s1 = new String("1") + new String("2") + new String("3"); // 字串的 123 會放在字串池
// String s1 = new StringBuilder("1").append("23").toString();
s1.intern(); // s1 123 放到字串池,一開始並沒有 123 ,所以會放進去
String s2 = "123";
System.out.println(s1 == s2); // true
// 總共產生幾個物件?
// new String 底層會用 new StringBuilder,最後還會 toString,因為是 new,所以最少有 5 個
// 1、2、3 如果字串池都沒有也會產生三個,最後 intern() 如果沒有 123 也會產生一個,所以最多 9 個物件
}

@Test
public void test4() {
String s1 = String.valueOf(123); // stack s1heap 123,字串池沒有
s1.intern(); // s1 123 放到字串池,一開始並沒有 123 ,所以會放進去
String s2 = "123";
System.out.println(s1 == s2); // true
}


@Test
public void testKeyWord() {
// 多個 String 相加,底層會用 StringBuilder 連起來
String str1 = new StringBuilder("aaa").append("bbb").toString();
System.out.println(str1.intern() == str1); // true
String str2 = new StringBuilder("ja").append("va").toString();
System.out.println(str2.intern() == str2); // false,因為 java 是字串池內鍵已有的
// 內鍵的字串池可看 VersionProps
}

Hello World (Spring3.x 一)

首先先建立一個專案,然後把jar檔copy進去,我用得jar如下,如果是maven,只要抓Spring core和Spring Context就會抓到下面7個jar檔了

然後用spring tools裡的工具,我也不是很會用這個工具

選一個地方放xml,就選src吧,名稱大部人都會取applicationContext.xml
如果是 maven 或 gradle 就選 src\main\java 以下,我用 ..(上一層)還不行呢!

選需要的namespace,注意下面很有子選項,我是3.2版,所以我全部選3.2
但如果選的是第一個沒有版本的,表示任何版本都可用

如果沒選到,之後也可以選下方的活頁標籤的Namespaces來改

寫程式測試一下,建一個interface Paper,它被兩個子類繼承,Book和Comic
一樣有分兩種配置,XML和annotation

※XML配置

package book.vo;
public class Book implements Paper {
    private String bookName;
    private int bookMoney;
    //setter/getter...
    @Override
    public String getInfo() {
        return "書名:" + this.bookName + ", 書錢:" + this.bookMoney;
    }
}


而applicationContext.xml如下
<?xml version="1.0" encoding="UTF-8"?>

      
    <bean class="book.vo.Book" id="book">
        <!--方法一 -->
        <property name="bookName" value="Spring in Action" />
        <!--方法二 -->
        <property name="bookMoney">
            <value>500</value>
        </property>
    </bean>


※id和name都可以,差在哪後續章節再說

測試類為
public class Test {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
Book book = (Book) ctx.getBean("book");
System.out.println(book.getInfo());
((ClassPathXmlApplicationContext) ctx).close();
結果為:
書名:Spring in Action, 書錢:500 漫畫書名:七龍珠, 漫畫書錢:65


※Annotation


Annotation配置又分成兩種,一種是Java配置,另一種就是Annotation配置
可以和XML配置互相使用


Java配置

@Configuration
public class Book {
    @Value(value = "Spring in Action")
    private String bookName;
    
    @Value(value = "500")
    private int bookMoney;
    
    //setter/getter...
    
    @Bean
    public String getInfo() {
        return "書名:" + this.bookName + ", 書錢:" + this.bookMoney;
    }
}

ApplicationContext ctx = new AnnotationConfigApplicationContext(Book.class);
System.out.println(ctx.getBean("getInfo"));
((AnnotationConfigApplicationContext) ctx).close();

※@Bean 回傳什麼就用什麼接,如回傳一個自訂的 Book 類別,就可以點裡面的方法,只要回傳的物件裡面有值,那取出來就有值;此例回傳 String,所以可以直接印出來

※注意使用annotation時@Bean不能回傳void,否則會出「Error creating bean with name 'getInfo' defined in class book.vo.Book: Invalid factory method 'getInfo': needs to have a non-void return type!」的錯

※@Value可以寫在屬性或setter上面,但getter不行,和Hibernate一樣

※如果在 xml 設定檔想讀 annotation,那就要寫 context:component-scan

※如果想使用 @ComponentScan(basePackages = { "包路徑", "包路徑" }),那就只能抓annotation 的 bean,測試時就一定要使用 AnnotationConfigApplicationContext(Xxx.class),但要注意會包括子包的,如寫 xxx.ooo,那 xxx.ooo 以下的包也會掃的到,如果不想掃再加屬性 filter 即可

※如果annotation 想讀 xml 設定檔,那就要寫 @ImportResource("classpath:/applicationContext.xml")

※Annotation配置

開頭可以是@Component、@Controller、@Service、@Repository、@Named 其中之一
@Component("book")
public class Book {
    @Value(value = "Spring in Action")
    private String bookName;
    
    @Value(value = "500")
    private int bookMoney;
    
    // setter/getter...
    
    public String getInfo() {
        return "書名:" + this.bookName + ", 書錢:" + this.bookMoney;
    }
}

※測試類和XML的一樣

2015年6月26日 星期五

在Eclipse增加Spring tools

※在Eclipse增加Spring tools
一開始先確定沒有sp開頭的東西

確定沒有後,先記住Eclipse的版本

然後去Eclipse菜市場下載同版本的

這邊有很多,我也不太懂,就全載了吧!

等一下後,要接受一些有的沒的,然後會有提示重啟Eclipse



然後再次確認有沒有成功


2015年6月25日 星期四

DECODE與CASE WHEN區別

兩者很像,都是if else
※DECODE
SELECT product_id,
DECODE (warehouse_id, 
        1, 'Southlake',
        2, 'San Francisco',
        3, 'New Jersey',
        4, 'Seattle',
        'Non domestic') "Location"
FROM inventories;
這是官方的範例,如果warehouse_id欄位是1就顯示Southlake,以此類推,如果都不是就顯示Non domestic

※CASE WHEN
換成CASE WHEN的寫法
SELECT product_id,
(
    CASE product_id
    WHEN 1 THEN 'Southlake'
    WHEN 2 THEN 'San Francisco'
    WHEN 3 THEN 'New Jersey'
    WHEN 4 THEN 'Seattle'
    ELSE 'Non domestic'
    END
) "Location"
FROM inventories;


※但如果有兩個以上要顯示一樣,CASE WHEN 會比較方便,因為它有第二種寫法,CASE後面不接欄位,欄位變成在WHEN後面

SELECT product_id,
(
    CASE
    WHEN product_id IN(1, 2, 4) THEN 'Southlake'
    WHEN product_id = 3 THEN 'New Jersey'
    ELSE 'Non domestic'
    END
) "Location"
FROM inventories;
如果用DECODE就會出現重復的值,它沒辦法有條件,會出錯
DECODE (warehouse_id, 
        1, 'Southlake',
        2, 'Southlake',
        3, 'New Jersey',
        4, 'Southlake',
        'Non domestic') "Location"
FROM inventories;



※判斷不同欄位

SELECT product_id,
(
    CASE 
    WHEN product_id=1 THEN 'Southlake'
    WHEN product_name=2 THEN 'San Francisco'
    WHEN product_code=3 THEN 'New Jersey'
    WHEN product_title=4 THEN 'Seattle'
    ELSE 'Non domestic'
    END
) "Location"
FROM inventories;

※如果要判斷的欄位都一樣,就將欄位放在case後面會比較方便,如果不一樣就要用這種寫法

※注意ORA-00932: 不一致的資料類型: 應該是 xxx型態, 但為 xxx型態

SELECT
(
    CASE WHEN  1=2
           THEN '1'
           ELSE 2
    END
) TEST
FROM dual;
這樣會報錯,要把整個CASE視為一個整體,THEN和ELSE回傳的型態要統一就不會報這個錯了

Oracle的RPAD/LPAD遇到中文的問題

R/L PAD
R就是right右邊,L就是left左邊,PAD就是墊板

SELECT RPAD('123', 5, 'x') TEST FROM DUAL;
如這行有5個位置的墊板,不夠的已x替代,並放在右邊,所以答案為
123xx

SELECT RPAD('1二3', 5, 'x') TEST  FROM DUAL;
但如果有中文答案卻是
1二3x


它把中文設為兩格(根據編碼會有所不同),但如果要讓它當成一格呢?

SELECT '1二3' || RPAD('x', 5, 'x')TEST FROM DUAL;
我的想法是把想顯示的字加上全部都是一樣的墊板,所以答案是
1二3xxxxx
字後面有5個x

SELECT SUBSTR('1二3' || RPAD('x', 5, 'x'), 1, 5) TEST FROM DUAL;
最後再用SUBSTR取出即可,所以答案為
1二3xx

寫個StoreProcedure
CREATE OR REPLACE FUNCTION CHI_SPACE(STR IN VARCHAR2, LEN IN PLS_INTEGER, PAD IN VARCHAR2)  RETURE VARCHAR2

AS BEGIN

RETURN SUBSTR(STR || RPAD(PAD, LEN, PAD), 1, LEN);--依照最後一個SQL的公式

END CHI_SPACE;

這樣以後就不管有沒有中文都可以統一用
SELECT CHI_SPACE('1二3', 5, 'x') FROM DUAL;

Hibernate3.x的Hello World (使用Hibernate tools)(Hibernate3.x一)

Hibernate3.xHello World
maven創一個空專案後,去http://mvnrepository.com,將hibernatejavassistdependency貼到pom.xml裡,如果不下載javassist執行會有錯誤提示,還有資料庫的驅動程式也要加



設定好後,寫一個對應DBjava bean

以下分兩個部分,XML設定和Annotation設定

XML設定
Add Class,然後選剛剛新建的java bean

會自動生成xml,看以下講解,有不對的在修改一下就可以了,class name裡面的路徑可以按Ctrl鍵,然後滑鼠移過去,有出現底線表示有連成功

設定完後要讓hibernate.cfg.xml知道,所以要下<mapping resource>,一樣用Ctrl去偵測看看有沒有連成功

寫測試類,configure()裡面,可以設指定的路徑去讀xml,也就是不一定要叫hibernate.cfg.xml
hbm.xml有個<generator>,如果要變成流水號的形式
oracle可以這樣用
<generator class="sequence">
<param name="sequence">SEQ_HIBERNATE_USERS</param>
</generator>
SEQ_HIBERNATE_USERS這些字是因為DB的設定

CREATE SEQUENCE SEQ_HIBERNATE_USERS
    INCREMENT BY 10  -- 每次加幾個
    START WITH 10        -- 從10開始計數
    NOMAXVALUE        -- 不設置最大值
    NOCYCLE                  -- 一直累加,不循環
    CACHE 10;
所以我們要取得流水號才會用SEQ_HIBERNATE_USERS.NEXTVAL或SEQ_HIBERNATE_USERS.CURRVAL
其他DB和方法可以參考這裡Hibernate标识符属性(主键)生成策略全析

雖然Oracle12c有支援自增主鍵了(看這篇的最下面),但使用Hibernate時,我給identity,還是出現不支援的錯誤,但我只試 3.6.10 版



Annotation設定

因為是annotation設定,所以不用hbm.xml
但也是要讓hibernate.cfg.xml知道,所以要設<mapping class>,剛剛的xml設定是<mapping resource>,不要搞錯了,設完後,一樣用Ctrl測試

Java bean 如下設定,注意@開頭的都是javax.persistence.*裡面,不是hibernate的喔!
@Entity表示可以連到DB的類
@IDDB主鍵
@ColumnDB的欄位
要完全和DB的欄位一模一樣才可以
annotation也可以寫在setter/getter上面

DB常常有底線,所以java連不到,所以還可以這樣寫
@GeneratedValue是設定流水號之類用的

這樣子java的屬性就可以亂打了,但唯一就是@Id,沒有name可以設定,我目前也沒找到其他方法,所以目前就Id先和DB的欄位名稱一樣,其他隨便,測試類和XML設定一樣

值得一提的就是,如果使用hbm.xml,而且annotation也寫了,而且還亂寫,還是能執行成功,感覺應該是annotation沒作用而已

 ※用功具生成class

剛剛打的persistent class 打得太累了,所以有個工具專門做這件事的,首先要有一個console,然後reverse轉換,最後Generation,三合一的

※新增一個Hibernate Console設定






Console的名稱,預設為專案名稱,選對應的版本,然後專案名稱,還有資料庫連線,及設定檔的位置,大部分都自動長出來了






設定完後,用這個路徑打開看一下





注意要按下Database的三角形,能出現table名稱,才表示ok






這邊我卡超久的,按下Database的三角形後,出現Pending…,等到我都拉完屎了,它還在Pending…,最後還錯誤。我來我發現,在cfg.xml多設定一個叫hibernate.default_schema,就可以了,一定要大寫,不然什麼都沒有

※TABLE SCHEMA:

指Table有關欄位名稱、長度、約束條件、主鍵.....等等有關Table的資訊

※SCHEMA:
可以把Database看作是一個大倉庫,倉庫分了很多很多的房間,Schema就是其中的房間,一個Schema代表一個房間,Table可以看作是每個Schema中的床,Table(床)就被放入每個房間中。
然後床上可以放置很多物品,就好比Table上可以放置很多列和行一樣,數據庫中存儲數據的基本單元是Table,現實中每個倉庫放置物品的基本單位就是床, User就是每個Schema的主人(所以Schema包含的是Object,而不是User)。

一個用戶一般對應一個schema,該用戶的schema名等於用戶名,並作為該用戶預設schema。這也就是在企業管理器的方案下看到schema名都為資料庫用戶名的原因。






※新增Hibernate Reverse Engineering File


問你生成的reveng.xml要放哪,就放src下好了


先選Console configuration裡剛剛設定好的console,然後Refresh,左邊就會出現資料庫的table,選要轉換的table後按Include,讓它到右邊後完成
 
經過辛苦的設定後,想不到內容這麼少



Hibernate Code Generation設定
Open Perspective,不是Show View
Hibernate可以選



這樣以後就有Hibernate(右圖)的圖,不用在Open Perspective了,要切回去時,只要按Java EE圖示即可。
選這個專用的圖示(左圖),我之前因為不知道Open Perspective,所以找不到這個圖示,好可怕




左邊先選最下面的框,才能選上面的「+」圖示,右邊的Name,給個名字,通常是專案名稱加_configurationreveng.xml選已存在的





選剛剛建立的reveng.xml






切換到Exporters頁籤,使用java5以上的泛型,看要不要用annotation,要在勾。如果不用annotation,就要用hbm.xmlDomain codepersistent classDAO code為建資料庫的程式碼,有需要就可以勾,再來按下Apply後按Run即完成


 

正當我高興時,天殺的,想不到居然發生一個有衝突的例外訊息





忽然很想殺人啊~~~!找了好久,終於找到解法,就是Console改成3.5的即可,但Hibernate Configurations隨便按一下就跳到Error Log頁籤,說我是3.6版的,這時不用管它,只要按到有table出來就表示ok了,後來的操作都一樣,所以我應該一開始就始用3.5就不會出錯了吧!
hibernate現在已經到5.x了還這樣,真是火大沒處發,只好用饅頭丟狗




以下是產生出來的檔案,有可能須要微調一下,自行修改即可


------------------------------------------------------------------------------------------------------------

※Configuration還有一些其他的用法


在src下增加一個叫hibernate.properties,將cfg.xml的內容變成key=value形式
hibernate.connection.driver_class=oracle.jdbc.driver.OracleDriver
hibernate.connection.password=your password
hibernate.connection.url=jdbc:oracle:thin:@localhost:1521:db name
hibernate.connection.username=your username
hibernate.dialect=org.hibernate.dialect.OracleDialect
hibernate.show_sql=true
hibernate.format_sql=true
hibernate.default_schema=XXX

new Configuration()就會把hibernate.properties讀進來,但因為沒有hbm.xml,所以要在.addClass(); 如果要在configure()也可以,cfg.xml會把properties的內容覆蓋,但如果要addClass()只能選擇其一,看是要將cfg.xml的mapping註解或是不addClass(),否則會出現Duplicate collection role mapping 的錯誤
Configuration cfg = new Configuration().addClass(Dept2.class).addClass(Emp2.class);
還有一種是什麼檔都不用,直接寫在程式上,如下:
Configuration cfg = new Configuration()
.addClass(Dept2.class)
.addClass(Emp2.class)
.setProperty("hibernate.connection.driver_class", "oracle.jdbc.driver.OracleDriver")
.setProperty("hibernate.connection.password", "your password")
.setProperty("hibernate.connection.url", "jdbc:oracle:thin:@localhost:1521:db name")
.setProperty("hibernate.connection.username", "your username")
.setProperty("hibernate.dialect", "org.hibernate.dialect.OracleDialect")
.setProperty("hibernate.show_sql", "true")
.setProperty("hibernate.format_sql", "true")
.setProperty("hibernate.default_schema", "XXX");

綜合以上的寫法,最好用的還是cfg.xml配合hbm.xml,所以這種方式最為廣泛
※一對多,多對一
員工對部門是多對一
public class Emp2 {
    private Integer empno;
    private String ename;
    private String job;
    private Date hiredate;
    private Float sal;
    private Float comm;
    // 多對一
    private Dept2 deptno;
    //...
    
    

部門對員工是一對多
public class Dept2 {
    private Integer deptno;
    private String dname;
    private String loc;
    // 一對多
    private Set<Emp2> emps = new HashSet<>();
    
    
    
    //...

最後用hibernate tools使用hbm.xml的工具會直接產生
<many-to-one name="deptno" class="vo.Dept2" fetch="join">
    <column name="DEPTNO" />
</many-to-one>

<set name="emps" table="EMP2" inverse="false" lazy="true">
    <key>
        <column name="DEPTNO" />
    </key>
    <one-to-many class="vo.Emp2" />
</set>

如果改成List會變成以下的樣子,多個<list-index>
<list name="emps" inverse="false" table="EMP2" lazy="true">
    <key>
        <column name="DEPTNO" />
    </key>
    <list-index></list-index>
    <one-to-many class="vo.Emp2" />
</list>

如果沒有主鍵,會自動變成複合主鍵,composite-id裡面合起來就是複合主鍵,最外層又包了一層

@EmbeddedId表示複合主鍵ID
@AttributeOverrides包住@AttributeOverride,裡面放複合屬性

※NUMBER(precision, scale),precision為全部位數,不包括小數點; scale為小數點後長度
number(5, 2)-->123.45,如果最後是0不顯示(這我不確定)
number(3, 0)-->789


@Embeddable表示為複合主鍵類
 

我這一篇又更詳細一點-->複合主鍵 (Hibernate3.x 十五)