Java嵌入式数据库H2学习总结(三)——在Web应用中嵌入H2数据库
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数据库函数的扩展的内容就讲解这么多了。