H2(Java嵌入式数据库)

目录
  1. 下载H2数据库
  2. 在JavaWeb项目中使用H2
  3. 在JavaWeb项目中嵌入H2
  4. 向H2数据库注册自定义的数据库函数

简介

优势:
  1. H2采用纯Java编写,因此不受平台的限制。
  2. H2只有一个jar文件,非常适合作为嵌入式数据库(不需要安装独立的客户端和服务器端)。
  3. H2提供了一个十分方便的web控制台用于操作和管理数据库内容。

1. 下载H2数据库

  1. 下载H2数据库

下载最新版本的H2数据库(选择Platform-IndependentZip),并解压到指定目录(如:/Users/cx/H2Database)

H2数据库的目录结构

H2数据库的目录结构说明
  h2
  |---bin
  |    |---h2-2.1.214.jar     // H2数据库的jar包(驱动也在里面)
  |    |---h2.bat             // Windows控制台启动脚本
  |    |---h2.sh               // Linux/MAC控制台启动脚本
  |    |---h2w.bat             // Windows控制台启动脚本(不带黑屏窗口)
  |---docs                     // H2数据库的帮助文档(内有H2数据库的使用手册)
  |---service   // 通过wrapper包装成服务。
  |---src       // H2数据库的源代码
  |---build.bat // Windows构建脚本
  |---build.sh  // Linux构建脚本
  1. 启动H2数据库服务
在终端执行:
  cd /Users/cx/H2Database/h2/bin 
  sh h2.sh

在浏览器中访问H2数据库的WebConsole:http://localhost:8082
H2数据库的Web控制台
  1. 创建并连接数据库、创建表
点击顶部的绿色小图标,选择Create a new database

创建数据库,会在h2/bin目录下创建test.mv.db数据库文件

测试连接

点击连接,登录到test数据库的WebConsole,执行创建表SQL,在左侧可以看到创建的users表

2. 在JavaWeb项目中使用H2

1. 创建Web项目(Dynamic Web Project)
2. 导入H2依赖包
  将h2-xxx.jar复制到WEB-INF/lib目录下
3. 以嵌入式(本地)连接方式连接H2数据库(方式1)
  默认情况下只允许有一个客户端连接到H2数据库(有客户端连接到H2数据库之后,此时数据库文件就会被锁定,其他客户端无法再连接)。
  连接语法:jdbc:h2:[file:][<path>]<databaseName>
    例:
      jdbc:h2:~/test // 连接位于用户目录下的test数据库
      jdbc:h2:file:/data/sample
      jdbc:h2:file:E:/H2/test(Windows系统下)

===》Test.java
package com.sst.cx;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class Test {
    // 数据库连接URL
    private static final String JDBC_URL = "jdbc:h2:~/H2Database/h2/bin/test";
    // 连接数据库的用户名
    private static final String USER = "sa";
    // 连接数据库的密码
    private static final String PASSWORD = "密码";
    // 连接数据库的驱动类(H2数据库提供)
    private static final String DRIVER_CLASS="org.h2.Driver";
    public static void main(String[] args) throws Exception {
        // 加载H2数据库驱动
        Class.forName(DRIVER_CLASS);
        // 根据连接URL,用户名,密码获取数据库连接
        Connection conn = DriverManager.getConnection(JDBC_URL, USER, PASSWORD);
        Statement stmt = conn.createStatement();
        // 如果存在USERS表就先删除USERS表
        stmt.execute("DROP TABLE IF EXISTS USERS");
        // 创建users表
        stmt.execute("create table users("
                + "    id int primary key,"
                + "    name varchar(40),"
                + "    password varchar(40))");
        // 新增
        stmt.executeUpdate("INSERT INTO users VALUES(1,'张三','12')");
        stmt.executeUpdate("INSERT INTO users VALUES(2,'李四','34')");
        stmt.executeUpdate("INSERT INTO users VALUES(3,'王五','56')");
        stmt.executeUpdate("INSERT INTO users VALUES(4,'麻六','78')");
        stmt.executeUpdate("INSERT INTO users VALUES(5,'邹七','90')");
        // 删除
        stmt.executeUpdate("DELETE FROM users WHERE name='张三'");
        // 修改
        stmt.executeUpdate("UPDATE users SET name='李二' WHERE name='李四'");
        // 查询
        ResultSet rs = stmt.executeQuery("SELECT * FROM users");
        // 遍历结果集
        while (rs.next()) {
            System.out.println(rs.getString("id") + "," + rs.getString("name")+ "," + rs.getString("password"));
        }
        // 释放资源
        stmt.close();
        // 关闭连接
        conn.close();
    }
}
项目结构

查询结果
3. 使用TCP/IP的服务器模式(远程连接)方式连接H2数据库(方式2  推荐) 
  连接方式和其他数据库类似(基于Service的形式进行连接),因此允许多个客户端同时连接到H2数据库。
  连接语法:jdbc:h2:tcp://<server>[:<port>]/[<path>]<databaseName>
  例:
    jdbc:h2:tcp://localhost/~/test

===》Test2.java
package com.sst.cx;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class Test2 {
   // 数据库连接URL
   private static final String JDBC_URL = "jdbc:h2:tcp://127.0.0.1/~/H2Database/h2/bin/test";
   // 连接数据库的用户名
   private static final String USER = "sa";
   // 连接数据库的密码
   private static final String PASSWORD = "密码";
   // 连接数据库的驱动类(由H2数据库提供)
   private static final String DRIVER_CLASS="org.h2.Driver";
   public static void main(String[] args) throws Exception {
       // 加载H2数据库驱动
       Class.forName(DRIVER_CLASS);
       // 根据连接URL,用户名,密码获取数据库连接
       Connection conn = DriverManager.getConnection(JDBC_URL, USER, PASSWORD);
       Statement stmt = conn.createStatement();
       //如果存在users表就先删除users表
       stmt.execute("DROP TABLE IF EXISTS USERS");
       // 创建users表
       stmt.execute("create table users("
            + "    id int primary key,"
            + "    name varchar(40),"
            + "    password varchar(40))");
       // 新增
       stmt.executeUpdate("INSERT INTO users VALUES(1,'张三','12')");
       stmt.executeUpdate("INSERT INTO users VALUES(2,'李四','34')");
       stmt.executeUpdate("INSERT INTO users VALUES(3,'王五','56')");
       stmt.executeUpdate("INSERT INTO users VALUES(4,'麻六','78')");
       stmt.executeUpdate("INSERT INTO users VALUES(5,'邹七','90')");
       // 删除
       stmt.executeUpdate("DELETE FROM users WHERE name='张三'");
       // 修改
       stmt.executeUpdate("UPDATE users SET name='李二' WHERE name='李四'");
       // 查询
       ResultSet rs = stmt.executeQuery("SELECT * FROM users");
       // 遍历结果集
       while (rs.next()) {
           System.out.println(rs.getString("id") + "," + rs.getString("name")+ "," + rs.getString("password"));
       }
       // 释放资源
       stmt.close();
       // 关闭连接
       conn.close();
   }
}

3. 在JavaWeb项目中嵌入H2

1. 创建Web项目(Dynamic Web Project)
2. 导入依赖包
  将h2-xxx.jar、servlet-api.jar复制到WEB-INF/lib目录下
3. 创建一个专门用于启动H2数据库服务的监听器

===》H2DBServerStartListener.java
package com.sst.cx.web.listener;
import java.sql.SQLException;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.h2.tools.Server;
// 用于启动H2数据库服务的Listener
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;
        }
    }
}
4. 在web.xml文件中添加
    <!-- 配置监听器:启动和停止H2数据库服务 -->
    <listener>
        <listener-class>com.sst.cx.web.listener.H2DBServerStartListener</listener-class>
    </listener>
    <!-- 配置用于访问H2数据库Console的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>
5. 测试

部署到Tomcat,并启动Tomcat。
在浏览器中输入http://localhost:8080/hello/console/ 可访问H2的Web控制台。

4. 向H2数据库注册自定义的数据库函数

1. 实现自定义数据库函数
  创建一个普通类,创建一个public static修饰的方法并实现。
  例:
    public class Hello{
      public static String hi(){
        return "hello";    // 根据自身需求,编写功能
      }
    }
2. 注册到H2数据库中(在H2的Web控制台中执行)
  CREATE ALIAS [IF NOT EXISTS] newFunctionAliasName [DETERMINISTIC] FOR classAndMethodName
  例:
    CREATE ALIAS hello FOR "com.sst.cx.Hello.hi"
3. 测试
  select hello()
注册到H2数据库

测试

例(一次性向H2数据库注册多个函数)

===》1. H2DBFunctionExt.java(定义数据库函数)
package com.sst.cx;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
public class Hello {
    public static String hi(){
        return "hello";
    }
    public static String my_uuid(){
        return "com.sst.cx"+UUID.randomUUID().toString();
    }
    public static String now(){
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = new Date();
        String dateStr = simpleDateFormat.format(date);
        return dateStr;
    }
    public static String getIp(){
        try {
            InetAddress addr = InetAddress.getLocalHost();
            // 获得本机IP
            return addr.getHostAddress();
        } catch (UnknownHostException e) {
            e.printStackTrace();
            return "未知的IP地址";
        }
    }
    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 "";
    }
}

===》2. RegisterH2ExtFuncServlet.java(注册数据库函数)
package com.sst.cx;
import java.sql.Connection;
import java.sql.Statement;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
public class RegisterH2ExtFuncServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    public void init() throws ServletException {
        // 0、注册hi函数的SQL语句
        String sql0 = "CREATE ALIAS IF NOT EXISTS hello FOR \"com.sst.cx.H2DBFunctionExt.hi\"";
        // 1、注册uuid函数的SQL语句
        String sql1 = "CREATE ALIAS IF NOT EXISTS my_uuid FOR \"com.sst.cx.H2DBFunctionExt.my_uuid\"";
        // 2、注册currentTime函数的SQL语句
        String sql2 = "CREATE ALIAS IF NOT EXISTS currentTime FOR \"com.sst.cx.H2DBFunctionExt.now\"";
        // 3、注册IP函数的SQL语句
        String sql3 = "CREATE ALIAS IF NOT EXISTS IP FOR \"com.sst.cx.H2DBFunctionExt.getIp\"";
        // 4、注册date_format函数的SQL语句
        String sql4 = "CREATE ALIAS IF NOT EXISTS date_format FOR \"com.sst.cx.H2DBFunctionExt.date_format\"";
        Connection connection = null;
        Statement stmt = null;
        try {
            // 获取数据库连接
            connection = JdbcUtil.getConnection();
            // 获取Statement对象
            stmt = connection.createStatement();
            // 添加要执行的SQL
            stmt.addBatch(sql0);
            stmt.addBatch(sql1);
            stmt.addBatch(sql2);
            stmt.addBatch(sql3);
            stmt.addBatch(sql4);
            // 批量执行
            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();
            }
        }
    }
}

===》3. 在web.xml文件中,添加
    <servlet>
        <description>注册H2数据库的扩展函数</description>
        <servlet-name>RegisterH2DBExtFunction</servlet-name>
        <servlet-class>com.sst.cx.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>

===》4. JdbcUtil.java(提供JDBC连接)
package com.sst.cx;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
import org.h2.jdbcx.JdbcConnectionPool;
public class JdbcUtil {
   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();
       }
   }
   public static Connection getConnection() throws Exception{
       return cp.getConnection();
   }
   public static JdbcConnectionPool getCp() {
       return cp;
   }
}
===》5. 创建h2config.properties文件(src目录下)
JDBC_URL=jdbc:h2:tcp://127.0.0.1/~/H2Database/h2/bin/test
USER=sa
PASSWORD=密码
测试
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339

推荐阅读更多精彩内容