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資料庫函數的擴充的内容就講解這麼多了。