2015年7月9日 星期四

整合Spring、Hibernate和C3P0的配置

當我們只有使用Hibernate的時候,其中要獲取SessionFactory的話必須要使用Hibernate的API手動獲取,例如像這樣:

SessionFactory sessionFactory = AnnotationConfiguration().configure().buildSessionFactory();

如果配合Spring的話,就可以使用注入的概念將SessionFactory注入到要使用SessionFactory的類別中。

整合Spring與Hibernate有兩種方法:

  1. 在Spring配置中直接將原Hibernate的配置檔抓進來。
  2. 在Spring自己配置Hibernate的配置、連接池(例如C3P0)等,可以捨棄原來的Hibernate配置檔,也可把它抓進來,然後覆蓋部份配置。

讓我們再一次看一次原來的Hibernate配置檔,hibernate.cfg.xml:

2015年7月8日 星期三

Hibernate中C3P0連接池的設置

在一開始使用Hibernate做ORM(Object/Relational Mapping)時,如果我們沒有配置連接池(Connection Pool),可能會發生一些底層連接Database會碰到的問題,例如sessionFactory所產生的連接太久沒有動作而被Database端以Timeout為由強行關閉,而之後在使用連接時就會出錯(在Hibernate端似乎無法用SessionFactory的isClosed()來判斷),此時就可能要自己在做CRUD(Create,Read,Update,Delete)時手動測試連接是否還在等等。

如果配置了連接池,就可以簡單地受益於連接池帶來的許多好處,例如幫我們管理連線,最小連接數量、最大連接數量、定時檢測連線等等。

在Hibernate裡已經包含了一個很好用且有名的開源數據庫連接池,C3P0,如果你在NetBeans裡使用了Hibernate框架,那麼你就可以在Library中看到C3P0的jar,如下圖紅框內所示:

對於C3P0,以下的連結提供了不錯的介紹、範例與相闗資料:

  1. C3P0百度百科
  2. How to configure the C3P0 connection pool in Hibernate
  3. 關於MySQL的wait_timeout連接超時問題報錯解決方案
  4. c3p0 - JDBC3 Connection and Statement Pooling(官方網站)
在這邊,我把我使用的例子在這邊做了一個紀錄,在Hibernate的配置文件hibernate.cfg.xml中,加入如以下配置,這樣Hibernate就會自動偵測並使用C3P0了:
        <!--==============C3P0的配置=============-->
        <!-- 最小連接數 -->
        <property name="hibernate.c3p0.min_size">5</property>
        <!-- 最大連接數 -->
        <property name="hibernate.c3p0.max_size">20</property>
        <!-- 多久會把無用的連接視為timeout並移除到min_size的連接數量,單位毫秒 -->
        <property name="hibernate.c3p0.timeout">300</property>
        <!-- 最大的PreparedStatement的數量 -->
        <property name="hibernate.c3p0.max_statements">50</property>
        <!-- 多久進行空閒連接的檢查,確定連接還存在,例如有無被Database端關掉,單位是秒-->
        <property name="hibernate.c3p0.idle_test_period">3000</property>
        <!--======================================-->

注意:此處例出來的配置只是C3P0配置的一部份,其功能不只這些,其他的配置可以在上述的資料連接中查到。

2015年7月7日 星期二

Java - 在HttpGet及HttpPost中塞入資料參數的方法

當我們想用HttpClient的方式進行HttpGet及HttpPost連線時,常會需要在其中放入一些資料參數,在這邊紀錄整理了一些個人覺得不錯的、如何向連線放入參數的方法:

Note:
這裡使用了以下 Maven dependency,其中官方說明
commons-httpclient 官方已停止更新,
改由 org.apache.httpcomponents 繼續提供更新
(官方原文 :
The Commons HttpClient project is now end of life, and is no longer being developed. It has been replaced by the Apache HttpComponents project in its HttpClient and HttpCore modules, which offer better performance and more flexibility.
)

        <!-- https://mvnrepository.com/artifact/commons-httpclient/commons-httpclient -->
<dependency>
    <groupId>commons-httpclient</groupId>
    <artifactId>commons-httpclient</artifactId>
    <version>3.0.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
</dependency>

HttpGet:
HttpGet的參數都是放在Url後面的,例如"http://XXX.XXX.XXX.XXX?param1=1&param2=2",所以我們的目標就是創造出帶有參數的Url即可。
參考資料:How to add parameters to a HTTP GET request in Android?

  1. 最簡單的方式就是手動在HttpGet的Url後面加上要的參數資料(限文字),例如直接自行組合字串 (param 的值可能需要做 url encode):
  2. String url = "http://XXX.XXX.XXX.XXX?" + "param1=" + param1 + "&param2=" + param2;
    
    
  3. 使用 org.apache.http.client.utils.URIBuilder 來幫助我們產生Uri:
  4. URI uri = new URIBuilder("http://XXX.XXX.XXX.XXX:8080/path1/path2/xxx.jsp")
        .addParameter("param1", param1)
        .addParameter("param2", param2)
        .addParameters(listOfParameters)  //如果參數以NamerValuePair放在
        //List<NameValuePair>中的話可以
        //使用addParameters()
        .build();
    
    以上也可也拆成多個方法變成如下的樣子:
    URI uri = new URIBuilder()
        .setScheme("http")
        .setHost("XXX.XXX.XXX.XXX")
        .setPort(8080)
        .setPath("/path1/path2/xxx.jsp")
        .addParameter("param1", param1)
        .addParameter("param2", param2)
        .addParameters(listOfParameters)
        .build();
    
    因為 addParameter() 已經會對輸入值做 url encode ,所以不用再對資料做一次 url encode
HttpPost:
HttpPost的參數都是放在Entity中的,所以我們只要用List<NameValuePair>的型式把資料放到HttpPost的Entity中就行了。
參考資料:How to add parameters to a HTTP GET request in Android?

import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

String url = "http://XXX.XXX.XXX.XXX:8080/path1/path2/xxx.jsp";

//建立HttpClient
//DefaultHttpClient httpClient = new DefaultHttpClient(); //舊方法,現已 Deprecated
CloseableHttpClient httpClient = HttpClients.createDefault();
//建立HttpPost
HttpPost httpPost = new HttpPost( url ) ;
//建立要送的資料參數
ArrayList<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
params.add(new BasicNameValuePair("param1" , param1)) ;
params.add(new BasicNameValuePair("param2" , param2)) ;
//將資料參數放到HttpPost的Entity裡面,並指定編碼,如果是要塞純文字 body (例如 Json String),
//可以用 StringEntity
httpPost.setEntity(new UrlEncodedFormEntity(params, StandardCharsets.UTF_8));
//Execute連線發送訊息並取得回應
CloseableHttpResponse response = httpClient.execute(httpPost);
System.out.println(EntityUtils.toString(response.getEntity(), "UTF-8"));
或是使用PostMethod
final HttpClient client = new HttpClient();
final PostMethod postMethod = new PostMethod("http://XXX.XXX.XXX.XXX:8080/path1/path2/xxx.jsp");
postMethod.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET,"utf-8");  
postMethod.addParameter("param1", param1);
postMethod.addParameter("param2", param2);
//如果想將文字資料直接放到HttpPost的Entity裡面,可以這樣
//postMethod.setRequestEntity(new StringRequestEntity("", ContentType.APPLICATION_JSON.toString(), StandardCharsets.UTF_8.name()));

int statusCode = client.executeMethod(postMethod);
System.out.println(postMethod.getStatusLine());
System.out.println(postMethod.getResponseBodyAsString());

JUnit與Mockito

在程式進行測試時,常常會使用JUnit進行測試,其在NetBeans中已有內建。

而Mockito是一個需要下載的工具(下載jar後引入library中使用),它可以很方便地讓我們模擬還未撰寫完成的模組單元(類別還未實作完的類別),用模擬的單元來進行測試。

在單元測試中,我們有時會碰到一個單元要使用另一個單元的情況,例如單元A要使用單元B,而我們想在假設單元B一定正確的前提下測試單元A、或者是單元B根本就還沒實作完成(不過方法介面等要先出來),這時我們就會需要在單元A的測試中模擬出一個虛擬的單元B幫助測試。

在這裡"JUnit + Mockito 單元測試"有不錯的示例與說明可以參考。

我們在這裡演示一個簡單的例子:

有一個有兩個類別,Singer與Song,其中在Singer的建構子中我們注入一個Song給它,並在Singer的sing()方法中呼叫Song的getLyrics(),sing()方去會回傳從getLyrics()得到的lyrics,而lyrics的值就是Song建構子要傳入的參數,兩個類別的內容如下所示。

Gradle Excpetion - Could not find property XXX on root project 'XXX' 的解決辨法

今天用Gradle 2.4寫了一個最簡單的Hello World的Task,竟然無法執行成功,錯誤如下:


而build.gradle的內容只是很簡單的Hello World:

task hello << {
    println 'Hello world!'
}

後來Google了一下,在這篇文 "Gradle Hello World Error" 得到了靈感,可能是編碼造成的,才想到當初程式碼是用記事本寫的,並用UTF-8格式存的。馬上把編碼換成ANSI再存一次,果然這次就成功了,如下圖:

2015年7月2日 星期四

用Eclipse開發Android實機測試時,遇到The connection to adb is down, and a severe error has occured. 的錯誤時之解決辦法

關掉tfadb的進程,為風行網等軟件搞的鬼。

Java - HttpClient 的基本身份認證

在HttpClient中,如果要使用有基本身份認證的連線時,必須在程式碼中加入身認證的Code,如下面所示:

//建立認證提供者
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
//建立其本帳號密碼對的身份認證
UsernamePasswordCredentials urseNamePasswordCredentials = new UsernamePasswordCredentials(Admin, Pass);
//將身份認證交給認證提供者
credentialsProvider.setCredentials(AuthScope.ANY, urseNamePasswordCredentials );
//建立HttpClient連線
DefaultHttpClient defaultHttpclient = new DefaultHttpClient();
//設定HttpClient連線的認證提供者
defaultHttpclient.setCredentialsProvider(credentialsProvider );