1. 程式人生 > >java 嵌入式數據庫H2

java 嵌入式數據庫H2

start hrd nsa tzu rap gfs anr 成功 rc4

H2作為一個嵌入型的數據庫,它最大的好處就是可以嵌入到我們的Web應用中,和我們的Web應用綁定在一起,成為我們Web應用的一部分。下面來演示一下如何將H2數據庫嵌入到我們的Web應用中。

一、搭建測試環境和項目

1.1、搭建JavaWeb測試項目

  創建一個【H2DBTest】JavaWeb項目,找到H2數據庫的jar文件,如下圖所示:

  技術分享圖片

  H2數據庫就一個jar文件,這個Jar文件裏面包含了使用JDBC方式連接H2數據庫時使用的驅動類,將"h2-1.4.183.jar"加入到【H2DBTest】項目中,如下圖所示:

  技術分享圖片

二、啟動H2數據庫

  既然是要將H2數據庫作為我們Web應用的一部分嵌入進來,那麽我們就要在Web應用中啟動H2數據庫的服務,這樣我們才能夠連接到H2數據庫,因此我們可以編寫一個專門用於啟動H2數據庫服務的監聽器(Listener),監聽器示例代碼如下:

技術分享圖片
 1 package me.gacl.web.listener;
 2 
 3 import java.sql.SQLException;
 4 import javax.servlet.ServletContextEvent;
 5 import javax.servlet.ServletContextListener;
 6 import org.h2.tools.Server;
 7 
 8 /**
 9 * @ClassName: H2DBServerStartListener
10 * @Description: 用於啟動H2數據庫服務的監聽器,在應用系統初始化時就啟動H2數據庫的服務
11 * @author: 孤傲蒼狼
12 * @date: 2014-12-20 下午11:43:39
13 *
14 */ 
15 public class H2DBServerStartListener implements ServletContextListener {
16 
17     //H2數據庫服務器啟動實例
18     private Server server;
19     /* 
20      * Web應用初始化時啟動H2數據庫
21      */
22     public void contextInitialized(ServletContextEvent sce) {
23         try {  
24             System.out.println("正在啟動h2數據庫...");
25             //使用org.h2.tools.Server這個類創建一個H2數據庫的服務並啟動服務,由於沒有指定任何參數,那麽H2數據庫啟動時默認占用的端口就是8082
26             server = Server.createTcpServer().start(); 
27             System.out.println("h2數據庫啟動成功...");
28         } catch (SQLException e) {  
29             System.out.println("啟動h2數據庫出錯:" + e.toString());  
30             e.printStackTrace();  
31             throw new RuntimeException(e);  
32         }  
33     }
34 
35     /* 
36      * Web應用銷毀時停止H2數據庫
37      */
38     public void contextDestroyed(ServletContextEvent sce) {
39         if (this.server != null) {
40             // 停止H2數據庫
41             this.server.stop();
42             this.server = null;
43         }
44     }
45 }
技術分享圖片

  監聽器寫好之後,我們在Web.xml文件中註冊這個監聽器,另外,因為我們要將H2數據庫嵌入到我們的Web應用當中,為了能夠方便訪問H2數據庫提供的Console,我們可以在Web.xml文件中配置用於訪問H2數據庫Console的Servlet。

Web.xml文件的配置如下:

技術分享圖片
 1 <!-- 使用監聽器啟動和停止數據庫 -->
 2       <listener>
 3         <listener-class>me.gacl.web.listener.H2DBServerStartListener</listener-class>
 4     </listener>
 5     
 6     <!-- 使用H2控制臺的Servlet H2控制臺是一個獨立的應用程序,包括它自己的Web服務器,但它可以作為一個servlet作為-->
 7     <servlet>
 8         <servlet-name>H2Console</servlet-name>
 9         <servlet-class>org.h2.server.web.WebServlet</servlet-class>
10          <init-param>
11             <param-name>webAllowOthers</param-name>
12             <param-value></param-value>
13         </init-param>
14         <init-param>
15             <param-name>trace</param-name>
16             <param-value></param-value>
17         </init-param>
18         <load-on-startup>1</load-on-startup>
19     </servlet>
20     <!-- 映射H2控制臺的訪問路徑 -->
21     <servlet-mapping>
22         <servlet-name>H2Console</servlet-name>
23         <url-pattern>/console/*</url-pattern>
24     </servlet-mapping>
技術分享圖片

  配置好Listener和訪問Console的Servlet之後,我們就可以把H2數據庫當作是我們Web應用中的一部分來使用了。

  將Web應用部署到Tomcat服務器,當啟動Tomcat服務器時,在控制臺就可以看到H2數據庫啟動成功的消息,如下圖所示:

  技術分享圖片

  為了進一步驗證H2數據庫是否真的是通過監聽器正常啟動了,我們可以訪問一下H2數據庫的Console,輸入訪問地址:"http://localhost:8080/H2DBTest/console/"進行訪問,如下圖所示:

  技術分享圖片

  能夠看到H2數據庫Console的登錄頁面,說明了H2數據庫已經正常啟動了。

三、向H2數據庫註冊自定義的數據庫函數

  H2作為一個數據庫,和其他類型的數據庫一樣,會自帶有一些數據庫函數給我們使用,但是H2數據庫提供的數據庫函數有限,無法滿足我們開發中的需求,幸運的是,H2數據庫支持自定義數據庫函數的,因此我們可以根據開發中的實際應用場景編寫滿足我們需求的數據庫函數。

  下面就來說一下如何實現H2數據庫的自定義函數

  在MySQL數據庫中有一個UUID函數是用來生成UUID的,執行"SELECT UUID()"就可以看到UUID函數生成的UUID,如下圖所示:

  技術分享圖片

  而默認情況下,H2數據庫是沒有提供有UUID這個函數給我們使用的,如下圖所示:

  技術分享圖片

  那麽我們現在就來實現一個UUID函數,然後註冊到H2數據庫當中,這樣H2數據庫就支持UUID函數了,具體做法分為兩個步驟:

  (1) 使用Java實現自定義函數的方法。

    (2) 將Java的自定義函數註冊到H2數據庫中。

  首先我們來實現這個UUID函數,在java中,生成一個UUID的方法是使用java.util.UUID這個類裏面的一個randomUUID()方法生成的,封裝成一個uuid方法,代碼如下:

技術分享圖片
 1 package h2db.function.ext;
 2 
 3 import java.util.UUID;
 4 
 5 /**
 6 * @ClassName: H2DBFunctionExt
 7 * @Description: 針對H2數據庫函數的擴展
 8 * @author: 孤傲蒼狼
 9 * @date: 2014-12-20 下午11:20:34
10 *
11 */ 
12 public class H2DBFunctionExt {
13 
14     /**
15     * 用法:SELECT uuid();
16     * H2數據庫註冊uuid函數:CREATE ALIAS uuid FOR "h2db.function.ext.H2DBFunctionExt.uuid";
17     * @Method: uuid
18     * @Description: 實現MySQL數據庫的uuid函數,用於生成UUID
19     * @Anthor:孤傲蒼狼
20     *
21     * @return
22     */ 
23     public static String uuid(){
24         return UUID.randomUUID().toString();
25     }
26 }
技術分享圖片

  這樣,我們的uuid函數就算是編寫好了,需要註意的是,類和方法必須是公共(Public)的,且方法需為靜態(static)的,如方法中使用了Connection對象需將其關閉。

  接下來我們要將其註冊到H2數據庫中,須執行"CREATE ALIAS"語句,SQL語法如下:

1 CREATE ALIAS [IF NOT EXISTS] newFunctionAliasName [DETERMINISTIC] FOR classAndMethodName

  其中[]括起來的部分是可選的,本例須執行的語句為: CREATE ALIAS UUID FOR"h2db.function.ext.H2DBFunctionExt.uuid" ,執行結果如下圖所示:

  技術分享圖片

  這樣H2數據庫中就多了一個UUID函數可以使用了,我們再次執行"SELECT UUID()"語句就可以被H2數據庫正常解析了,執行結果如下圖所示:

  技術分享圖片

  以上就是針對H2數據庫函數的一個擴展,我們向H2數據庫新增加了一個UUID函數用於生成uuid。因此當H2數據庫提供的函數不滿足我們開發中的實際需求時,就可以使用這種方式來擴展H2數據庫的函數了。接下來演示一下一次性向H2數據庫擴展多個函數,我們編寫一個H2DBFunctionExt類,在類中編寫針對H2數據庫的擴展函數,代碼如下:

技術分享圖片
 1 package h2db.function.ext;
 2 
 3 import java.net.InetAddress;
 4 import java.net.UnknownHostException;
 5 import java.text.ParseException;
 6 import java.text.SimpleDateFormat;
 7 import java.util.Date;
 8 import java.util.UUID;
 9 
10 /**
11 * @ClassName: H2DBFunctionExt
12 * @Description: 針對H2數據庫函數的擴展
13 * @author: 孤傲蒼狼
14 * @date: 2014-12-20 下午11:20:34
15 *
16 */ 
17 public class H2DBFunctionExt {
18 
19     /**
20     * 用法:SELECT uuid();
21     * H2數據庫註冊uuid函數:CREATE ALIAS IF NOT EXISTS uuid FOR "h2db.function.ext.H2DBFunctionExt.uuid";
22     * @Method: uuid
23     * @Description: 實現MySQL數據庫的uuid函數,用於生成UUID
24     * @Anthor:孤傲蒼狼
25     *
26     * @return
27     */ 
28     public static String uuid(){
29         return UUID.randomUUID().toString();
30     }
31 
32     /**
33     * H2數據庫註冊currentTime函數:CREATE ALIAS IF NOT EXISTS currentTime FOR "h2db.function.ext.H2DBFunctionExt.now";
34     * @Method: now
35     * @Description: 實現MySQL數據庫的now()函數,用於生成當前系統時間
36     * @Anthor:孤傲蒼狼
37     *
38     * @return
39     */ 
40     public static String now(){
41         return new Date().toLocaleString();
42     }
43     
44     /**
45      * H2數據庫註冊IP函數:CREATE ALIAS IF NOT EXISTS IP FOR "h2db.function.ext.H2DBFunctionExt.getIp";
46     * @Method: getIp
47     * @Description: 
48     * @Anthor:孤傲蒼狼
49     *
50     * @return
51     */ 
52     public static String getIp(){
53         try {
54             InetAddress addr = InetAddress.getLocalHost();
55             //獲得本機IP
56             return addr.getHostAddress();
57         } catch (UnknownHostException e) {
58             e.printStackTrace();
59             return "未知的IP地址";
60         } 
61     }
62     
63     /**
64     * H2數據庫註冊date_format函數:CREATE ALIAS IF NOT EXISTS date_format FOR "h2db.function.ext.H2DBFunctionExt.date_format";
65     * @Method: date_format
66     * @Description: 實現MySQL數據庫的date_format()函數,用於格式化日期
67     * @Anthor:孤傲蒼狼
68     * @param date
69     * @param pattern
70     * @return
71     */ 
72     public static String date_format(String date,String pattern){
73         if (date != null) {
74             SimpleDateFormat sdf = new SimpleDateFormat(pattern);
75             try {
76                 Date temp = sdf.parse(date);
77                 return sdf.format(temp);
78             } catch (ParseException e) {
79                 e.printStackTrace();
80             }
81         }
82         return "";
83     }
84 }
技術分享圖片

  為了實現批量註冊H2數據庫的擴展函數,我們可以編寫一個Servlet,專門用於註冊擴展函數,代碼如下:

技術分享圖片
 1 package me.gacl.sys.init;
 2 
 3 
 4 import java.sql.Connection;
 5 import java.sql.Statement;
 6 
 7 import javax.servlet.ServletException;
 8 import javax.servlet.http.HttpServlet;
 9 
10 import me.gacl.util.JdbcUtil;
11 
12 /**
13 * @ClassName: RegisterH2ExtFuncServlet
14 * @Description:註冊H2數據庫的擴展函數
15 * @author: 孤傲蒼狼
16 * @date: 2014-12-20 下午11:47:03
17 *
18 */ 
19 public class RegisterH2ExtFuncServlet extends HttpServlet {
20 
21     /**
22     * @Field: serialVersionUID
23     */ 
24     private static final long serialVersionUID = 4379248469825545593L;
25 
26     public void init() throws ServletException {
27         //1、註冊uuid函數的SQL語句
28         String sql1 = "CREATE ALIAS IF NOT EXISTS uuid FOR \"h2db.function.ext.H2DBFunctionExt.uuid\"";
29         //2、註冊currentTime函數的SQL語句
30         String sql2 = "CREATE ALIAS IF NOT EXISTS currentTime FOR \"h2db.function.ext.H2DBFunctionExt.now\"";
31         //3、註冊IP函數的SQL語句
32         String sql3 = "CREATE ALIAS IF NOT EXISTS IP FOR \"h2db.function.ext.H2DBFunctionExt.getIp\"";
33         //4、註冊date_format函數的SQL語句
34         String sql4 = "CREATE ALIAS IF NOT EXISTS date_format FOR \"h2db.function.ext.H2DBFunctionExt.date_format\"";
35         Connection connection = null;
36         Statement stmt = null;
37         try {
38             //獲取數據庫連接
39             connection = JdbcUtil.getConnection();
40             //獲取Statement對象
41             stmt = connection.createStatement();
42             //添加要執行的SQL
43             stmt.addBatch(sql1);
44             stmt.addBatch(sql2);
45             stmt.addBatch(sql3);
46             stmt.addBatch(sql4);
47             //批量執行上述的4條SQL
48             stmt.executeBatch();
49             System.out.println("H2數據庫擴展函數註冊成功!");
50             stmt.clearBatch();
51         } catch (Exception e) {
52             System.out.println("H2數據庫擴展函數註冊失敗!");
53             e.printStackTrace();
54         }finally{
55             try {
56                 stmt.close();
57                 connection.close();
58             } catch (Exception e2) {
59                 e2.printStackTrace();
60             }
61         }
62     }
63 }
技術分享圖片

  在Web.xml中註冊RegisterH2ExtFuncServlet

技術分享圖片
 1 <servlet>
 2     <description>註冊H2數據庫的擴展函數</description>
 3     <servlet-name>RegisterH2DBExtFunction</servlet-name>
 4     <servlet-class>me.gacl.sys.init.RegisterH2ExtFuncServlet</servlet-class>
 5     <!-- 
 6     1、load-on-startup元素標記容器是否在啟動的時候就加載這個servlet(實例化並調用其init()方法)。
 7     2、它的值必須是一個整數,表示servlet應該被載入的順序
 8     3、當值為0或者大於0時,表示容器在應用啟動時就加載並初始化這個servlet;
 9     4、當值小於0或者沒有指定時,則表示容器在該servlet被選擇時才會去加載。
10     5、正數的值越小,該servlet的優先級越高,應用啟動時就越先加載。
11     6、當值相同時,容器就會自己選擇順序來加載。
12     所以,<load-on-startup>x</load-on-startup>,中x的取值1,2,3,4,5代表的是優先級,而非啟動延遲時間。
13      -->
14      <load-on-startup>1</load-on-startup>
15 </servlet>
技術分享圖片

  RegisterH2ExtFuncServlet要批量執行SQL語句,因此需要連接上H2數據庫才能夠執行,工具類JdbcUtil提供了獲取數據庫連接的方法,JdbcUtil的代碼如下:

技術分享圖片
 1 /**
 2  * 
 3  */
 4 package me.gacl.util;
 5 
 6 import java.io.InputStream;
 7 import java.sql.Connection;
 8 import java.util.Properties;
 9 import org.h2.jdbcx.JdbcConnectionPool;
10 
11 public class JdbcUtil {
12 
13     /**
14      * H2數據庫自帶的連接池
15      */
16     private static JdbcConnectionPool cp = null;
17     
18     static{
19         try {
20             //加載src目錄下的h2config.properties
21             InputStream in = JdbcUtil.class.getClassLoader().getResourceAsStream("h2config.properties");
22             Properties prop = new Properties();
23             prop.load(in);
24             //創建數據庫連接池
25             cp = JdbcConnectionPool.create(prop.getProperty("JDBC_URL"), prop.getProperty("USER"), prop.getProperty("PASSWORD"));
26         } catch (Exception e) {
27             System.out.println("連接池初始化異常");
28             e.printStackTrace();
29         }
30     }
31     
32     /**
33     * @Method: getConnection
34     * @Description:獲取數據庫連接
35     * @Anthor:孤傲蒼狼
36     * @return
37     * @throws Exception
38     */ 
39     public static Connection getConnection() throws Exception{
40         return cp.getConnection();
41     }
42 
43     public static JdbcConnectionPool getCp() {
44         return cp;
45     }
46 }
技術分享圖片

  h2config.properties的配置信息如下:

JDBC_URL=jdbc:h2:tcp://localhost/~/h2db
USER=gacl
PASSWORD=123

  當web應用啟動時,就會執行RegisterH2ExtFuncServlet這個Servlet中的init方法,init方法內部的處理就是通過JdbcUtil工具類獲取一個H2的數據庫連接,然後創建Statement對象,再由Statement對象批量執行SQL向H2數據庫註冊擴展函數。

  RegisterH2ExtFuncServlet執行的過程中如果沒有出現任何錯誤,那就說明所有的針對H2數據庫的擴展函數都註冊成功了,我們可以到H2的Console去驗證一下上述的4個擴展函數,如下圖所示:

  技術分享圖片

  關於在Web應用中嵌入使用H2數據庫,以及針對H2數據庫函數的擴展的內容就講解這麽多了。

java 嵌入式數據庫H2