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),监听器示例代码如下:

 package me.gacl.web.listener;

 import java.sql.SQLException;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.h2.tools.Server; /**
* @ClassName: H2DBServerStartListener
* @Description: 用于启动H2数据库服务的监听器,在应用系统初始化时就启动H2数据库的服务
* @author: 孤傲苍狼
* @date: 2014-12-20 下午11:43:39
*
*/
public class H2DBServerStartListener implements ServletContextListener { //H2数据库服务器启动实例
private Server server;
/*
* Web应用初始化时启动H2数据库
*/
public void contextInitialized(ServletContextEvent sce) {
try {
System.out.println("正在启动h2数据库...");
//使用org.h2.tools.Server这个类创建一个H2数据库的服务并启动服务,由于没有指定任何参数,那么H2数据库启动时默认占用的端口就是8082
server = Server.createTcpServer().start();
System.out.println("h2数据库启动成功...");
} catch (SQLException e) {
System.out.println("启动h2数据库出错:" + e.toString());
e.printStackTrace();
throw new RuntimeException(e);
}
} /*
* Web应用销毁时停止H2数据库
*/
public void contextDestroyed(ServletContextEvent sce) {
if (this.server != null) {
// 停止H2数据库
this.server.stop();
this.server = null;
}
}
}

  监听器写好之后,我们在Web.xml文件中注册这个监听器,另外,因为我们要将H2数据库嵌入到我们的Web应用当中,为了能够方便访问H2数据库提供的Console,我们可以在Web.xml文件中配置用于访问H2数据库Console的Servlet。

Web.xml文件的配置如下:

 <!-- 使用监听器启动和停止数据库 -->
<listener>
<listener-class>me.gacl.web.listener.H2DBServerStartListener</listener-class>
</listener> <!-- 使用H2控制台的Servlet H2控制台是一个独立的应用程序,包括它自己的Web服务器,但它可以作为一个servlet作为-->
<servlet>
<servlet-name>H2Console</servlet-name>
<servlet-class>org.h2.server.web.WebServlet</servlet-class>
<init-param>
<param-name>webAllowOthers</param-name>
<param-value></param-value>
</init-param>
<init-param>
<param-name>trace</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 映射H2控制台的访问路径 -->
<servlet-mapping>
<servlet-name>H2Console</servlet-name>
<url-pattern>/console/*</url-pattern>
</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方法,代码如下:

 package h2db.function.ext;

 import java.util.UUID;

 /**
* @ClassName: H2DBFunctionExt
* @Description: 针对H2数据库函数的扩展
* @author: 孤傲苍狼
* @date: 2014-12-20 下午11:20:34
*
*/
public class H2DBFunctionExt { /**
* 用法:SELECT uuid();
* H2数据库注册uuid函数:CREATE ALIAS uuid FOR "h2db.function.ext.H2DBFunctionExt.uuid";
* @Method: uuid
* @Description: 实现MySQL数据库的uuid函数,用于生成UUID
* @Anthor:孤傲苍狼
*
* @return
*/
public static String uuid(){
return UUID.randomUUID().toString();
}
}

  这样,我们的uuid函数就算是编写好了,需要注意的是,类和方法必须是公共(Public)的,且方法需为静态(static)的,如方法中使用了Connection对象需将其关闭。

  接下来我们要将其注册到H2数据库中,须执行"CREATE ALIAS"语句,SQL语法如下:

 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数据库的扩展函数,代码如下:

 package h2db.function.ext;

 import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID; /**
* @ClassName: H2DBFunctionExt
* @Description: 针对H2数据库函数的扩展
* @author: 孤傲苍狼
* @date: 2014-12-20 下午11:20:34
*
*/
public class H2DBFunctionExt { /**
* 用法:SELECT uuid();
* H2数据库注册uuid函数:CREATE ALIAS IF NOT EXISTS uuid FOR "h2db.function.ext.H2DBFunctionExt.uuid";
* @Method: uuid
* @Description: 实现MySQL数据库的uuid函数,用于生成UUID
* @Anthor:孤傲苍狼
*
* @return
*/
public static String uuid(){
return UUID.randomUUID().toString();
} /**
* H2数据库注册currentTime函数:CREATE ALIAS IF NOT EXISTS currentTime FOR "h2db.function.ext.H2DBFunctionExt.now";
* @Method: now
* @Description: 实现MySQL数据库的now()函数,用于生成当前系统时间
* @Anthor:孤傲苍狼
*
* @return
*/
public static String now(){
return new Date().toLocaleString();
} /**
* H2数据库注册IP函数:CREATE ALIAS IF NOT EXISTS IP FOR "h2db.function.ext.H2DBFunctionExt.getIp";
* @Method: getIp
* @Description:
* @Anthor:孤傲苍狼
*
* @return
*/
public static String getIp(){
try {
InetAddress addr = InetAddress.getLocalHost();
//获得本机IP
return addr.getHostAddress();
} catch (UnknownHostException e) {
e.printStackTrace();
return "未知的IP地址";
}
} /**
* H2数据库注册date_format函数:CREATE ALIAS IF NOT EXISTS date_format FOR "h2db.function.ext.H2DBFunctionExt.date_format";
* @Method: date_format
* @Description: 实现MySQL数据库的date_format()函数,用于格式化日期
* @Anthor:孤傲苍狼
* @param date
* @param pattern
* @return
*/
public static String date_format(String date,String pattern){
if (date != null) {
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
try {
Date temp = sdf.parse(date);
return sdf.format(temp);
} catch (ParseException e) {
e.printStackTrace();
}
}
return "";
}
}

  为了实现批量注册H2数据库的扩展函数,我们可以编写一个Servlet,专门用于注册扩展函数,代码如下:

 package me.gacl.sys.init;

 import java.sql.Connection;
import java.sql.Statement; import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet; import me.gacl.util.JdbcUtil; /**
* @ClassName: RegisterH2ExtFuncServlet
* @Description:注册H2数据库的扩展函数
* @author: 孤傲苍狼
* @date: 2014-12-20 下午11:47:03
*
*/
public class RegisterH2ExtFuncServlet extends HttpServlet { /**
* @Field: serialVersionUID
*/
private static final long serialVersionUID = 4379248469825545593L; public void init() throws ServletException {
//1、注册uuid函数的SQL语句
String sql1 = "CREATE ALIAS IF NOT EXISTS uuid FOR \"h2db.function.ext.H2DBFunctionExt.uuid\"";
//2、注册currentTime函数的SQL语句
String sql2 = "CREATE ALIAS IF NOT EXISTS currentTime FOR \"h2db.function.ext.H2DBFunctionExt.now\"";
//3、注册IP函数的SQL语句
String sql3 = "CREATE ALIAS IF NOT EXISTS IP FOR \"h2db.function.ext.H2DBFunctionExt.getIp\"";
//4、注册date_format函数的SQL语句
String sql4 = "CREATE ALIAS IF NOT EXISTS date_format FOR \"h2db.function.ext.H2DBFunctionExt.date_format\"";
Connection connection = null;
Statement stmt = null;
try {
//获取数据库连接
connection = JdbcUtil.getConnection();
//获取Statement对象
stmt = connection.createStatement();
//添加要执行的SQL
stmt.addBatch(sql1);
stmt.addBatch(sql2);
stmt.addBatch(sql3);
stmt.addBatch(sql4);
//批量执行上述的4条SQL
stmt.executeBatch();
System.out.println("H2数据库扩展函数注册成功!");
stmt.clearBatch();
} catch (Exception e) {
System.out.println("H2数据库扩展函数注册失败!");
e.printStackTrace();
}finally{
try {
stmt.close();
connection.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}

  在Web.xml中注册RegisterH2ExtFuncServlet

 <servlet>
<description>注册H2数据库的扩展函数</description>
<servlet-name>RegisterH2DBExtFunction</servlet-name>
<servlet-class>me.gacl.sys.init.RegisterH2ExtFuncServlet</servlet-class>
<!--
1、load-on-startup元素标记容器是否在启动的时候就加载这个servlet(实例化并调用其init()方法)。
2、它的值必须是一个整数,表示servlet应该被载入的顺序
3、当值为0或者大于0时,表示容器在应用启动时就加载并初始化这个servlet;
4、当值小于0或者没有指定时,则表示容器在该servlet被选择时才会去加载。
5、正数的值越小,该servlet的优先级越高,应用启动时就越先加载。
6、当值相同时,容器就会自己选择顺序来加载。
所以,<load-on-startup>x</load-on-startup>,中x的取值1,2,3,4,5代表的是优先级,而非启动延迟时间。
-->
<load-on-startup>1</load-on-startup>
</servlet>

  RegisterH2ExtFuncServlet要批量执行SQL语句,因此需要连接上H2数据库才能够执行,工具类JdbcUtil提供了获取数据库连接的方法,JdbcUtil的代码如下:

 /**
*
*/
package me.gacl.util; import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
import org.h2.jdbcx.JdbcConnectionPool; public class JdbcUtil { /**
* H2数据库自带的连接池
*/
private static JdbcConnectionPool cp = null; static{
try {
//加载src目录下的h2config.properties
InputStream in = JdbcUtil.class.getClassLoader().getResourceAsStream("h2config.properties");
Properties prop = new Properties();
prop.load(in);
//创建数据库连接池
cp = JdbcConnectionPool.create(prop.getProperty("JDBC_URL"), prop.getProperty("USER"), prop.getProperty("PASSWORD"));
} catch (Exception e) {
System.out.println("连接池初始化异常");
e.printStackTrace();
}
} /**
* @Method: getConnection
* @Description:获取数据库连接
* @Anthor:孤傲苍狼
* @return
* @throws Exception
*/
public static Connection getConnection() throws Exception{
return cp.getConnection();
} public static JdbcConnectionPool getCp() {
return cp;
}
}

  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学习总结(三)——在Web应用中嵌入H2数据库的更多相关文章

  1. C#实现多级子目录Zip压缩解压实例 NET4.6下的UTC时间转换 [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了 asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程 asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案 .NET Core开发日志

    C#实现多级子目录Zip压缩解压实例 参考 https://blog.csdn.net/lki_suidongdong/article/details/20942977 重点: 实现多级子目录的压缩, ...

  2. 【转】MyBatis学习总结(三)——优化MyBatis配置文件中的配置

    [转]MyBatis学习总结(三)——优化MyBatis配置文件中的配置 一.连接数据库的配置单独放在一个properties文件中 之前,我们是直接将数据库的连接配置信息写在了MyBatis的con ...

  3. Java嵌入式数据库H2学习总结(二)——在Web应用程序中使用H2数据库

    一.搭建测试环境和项目 1.1.搭建JavaWeb测试项目 创建一个[H2DBTest]JavaWeb项目,找到H2数据库的jar文件,如下图所示: H2数据库就一个jar文件,这个Jar文件里面包含 ...

  4. java网络爬虫基础学习(三)

    尝试直接请求URL获取资源 豆瓣电影 https://movie.douban.com/explore#!type=movie&tag=%E7%83%AD%E9%97%A8&sort= ...

  5. Java Web项目中连接Access数据库的配置方法

    本文是对前几天的"JDBC连接Access数据库的几种方式"这篇的升级.因为在做一些小项目的时候遇到的问题,因此才决定写这篇博客的.昨天已经将博客公布了.可是后来经过一些验证有点问 ...

  6. PHP学习(3)—在HTML中嵌入PHP

    我们以一个提交订单和显示订单信息的例子为学习PHP的开始.这个例子包含两个文件.一个提交订单的html文件:orderform.html,一个显示订单信息的php文件:processorder.php ...

  7. [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了

    [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了 本文首发自:博客园 文章地址: https://www.cnblogs.com/yilezhu/p/ ...

  8. SpringBoot学习(七)-->SpringBoot在web开发中的配置

    SpringBoot在web开发中的配置 Web开发的自动配置类:在Maven Dependencies-->spring-boot-1.5.2.RELEASE.jar-->org.spr ...

  9. Java集合源码学习(三)LinkedList分析

    前面学习了ArrayList的源码,数组是顺序存储结构,存储区间是连续的,占用内存严重,故空间复杂度很大.但数组的二分查找时间复杂度小,为O(1),数组的特点是寻址容易,插入和删除困难.今天学习另外的 ...

随机推荐

  1. AF_INET域与AF_UNIX域socket通信原理对比

    原文 1.  AF_INET域socket通信过程 典型的TCP/IP四层模型的通信过程. 发送方.接收方依赖IP:Port来标识,即将本地的socket绑定到对应的IP端口上,发送数据时,指定对方的 ...

  2. Linux驱动技术(五) _设备阻塞/非阻塞读写【转】

    转自:http://www.cnblogs.com/xiaojiang1025/p/6377925.html 等待队列是内核中实现进程调度的一个十分重要的数据结构,其任务是维护一个链表,链表中每一个节 ...

  3. linux中断申请之request_threaded_irq【转】

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=21977330&id=3755609 在linux里,中断处理分 ...

  4. Visual Studio 2013/2015/2017快捷键(转)

    英文原文:19 Must-Know Visual Studio Keyboard Shortcuts 项目相关的快捷键 Ctrl + Shift + B = 生成项目 Ctrl + Alt + L = ...

  5. 004_加速国内docker源下载速度

    docker下载慢的不行.国内加速器地址 http://355dbe53.m.daocloud.iohttps://docker.mirrors.ustc.edu.cn https://hub-mir ...

  6. 解决依赖的moduleBuildConfig.DEBUG总是未false的问题

    Android 开发中一般会通过 BuildConfig.DEBUG 判断是否是 Debug 模式,从而做一些在 Debug 模式才开启的特殊操作,比如打印日志.这样好处是不用在发布前去主动修改,因为 ...

  7. google地图的url参数

    Google Maps Intents for Android The Google Maps app for Android exposes several intents that you can ...

  8. FormData介绍

    FormData XMLHttpRequest Level 2添加了一个新的接口FormData.利用FormData对象,我们可以通过JavaScript用一些键值对来模拟一系列表单控件,我们还可以 ...

  9. net mvc中angular

    把angular项目整合到.net mvc中   之前的开发选择的是完全舍弃服务端,仅保留最简单web服务器提供angular经打包的静态资源,此外所有的业务与数据请求都访问一个分离的WebApi来实 ...

  10. 应用Mongoose开发MongoDB(3)控制器(controllers)

    控制器的基本构成与如何通过路由调用 控制器中通过建立函数并导出,实现前端对数据库的查询.新建.删除与修改的需求,并使之可以在路由中调用,完成API的封装.本文着重于结构之间的关系,具体问题解决方法将在 ...