内存数据库(In-Memory Database,简称IMDB)是一种将数据主要存储在计算机的主存(RAM)中,而不是存储在磁盘上的数据库系统。
对于某些场景,例如:快速原型开发、测试、高性能操作、只读数据库,可能不需要持久化数据或持久化对数据的更改,此时使用内存数据库较为合适。
H2 数据库支持内存模式,即不持久化数据。要开启内存数据库模式是通过在 JDBC URL 地址设置关键信息来实现,格式如下:
// 连接到默认名称的内存数据库 jdbc:h2:mem: // 连接到名为 databaseName 的内存数据库 jdbc:h2:mem:<databaseName>
示例:
jdbc:h2:mem:test_mem
上面地址将连接到名为 test_mem 的内存数据库。
注意事项:
(1)在某些情况下,只需要与内存数据库建立一个连接。这意味着要打开的数据库是私有的。在这种情况下,可以使用默认名称的内存数据库,数据库的 URL 为 jdbc:h2:mem:。如果在同一虚拟机中打开两个连接,意味着打开两个不同的(私有)数据库。例如:
// 创建连接 Connection connection1 = DriverManager.getConnection("jdbc:h2:mem:"); Statement statement1 = connection1.createStatement(); statement1.execute("CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(255))"); statement1.executeQuery("SELECT * FROM users"); // org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "USERS" not found (this database is empty); SQL statement Connection connection2 = DriverManager.getConnection("jdbc:h2:mem:"); Statement statement2 = connection2.createStatement(); statement2.executeQuery("SELECT * FROM users");
(2)有时需要多个连接,连接到同一个内存数据库。在这种情况下,数据库 URL 必须包含名称。例如:jdbc:h2:mem:db1。注意:使用此 URL 访问同一数据库只能在同一虚拟机和类加载器环境中进行。
(3)要从其他进程或其他计算机访问内存数据库,需要在创建内存数据库的同一进程中启动 TCP 服务器。然后,其他进程需要通过 TCP/IP 或 TLS 访问数据库,如:jdbc:h2:tcp://localhost/mem:db1。例如:
// 进程1:创建内存数据库,且启动服务 Connection connection = DriverManager.getConnection("jdbc:h2:mem:test-db"); Statement statement = connection.createStatement(); statement.execute("CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(255))"); statement.execute("INSERT INTO users VALUES (1, '张三')"); // 查询结果 ResultSet resultSet = statement.executeQuery("SELECT * FROM users"); while (resultSet.next()) { System.out.println(resultSet.getInt("id") + " - " + resultSet.getString("name")); } // 启动H2服务,让其他进程远程连接该内存数据库 Server.createTcpServer("-tcp","-web","-ifNotExists").start(); // 进程2:远程访问内存数据库 Connection connection = DriverManager.getConnection("jdbc:h2:tcp://localhost/mem:test-db"); Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("SELECT * FROM users"); while (resultSet.next()) { System.out.println(resultSet.getInt("id") + " - " + resultSet.getString("name")); }
(4)默认情况下,关闭数据库的最后一个连接,H2 会关闭数据库。对于内存数据库,这意味着内容丢失。要保持数据库不被关闭,可在数据库 URL 中添加 ;DB_CLOSE_DELAY=-1。例如: jdbc:h2:mem:test;DB_CLOSE_DELAY=-1。注意:这可能会造成内存泄漏,需要删除数据库时,请使用 SHUTDOWN 命令。例如:
不使用 DB_CLOSE_DELAY 的效果:
// 创建连接 { Connection connection = DriverManager.getConnection("jdbc:h2:mem:test-db"); Statement statement = connection.createStatement(); statement.execute("CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(255))"); statement.execute("INSERT INTO users VALUES (1, '张三')"); statement.close(); connection.close(); // 关闭连接,此时数据库内存中的数据会被清空 } // 重新打开连接 { Connection connection = DriverManager.getConnection("jdbc:h2:mem:test-db"); Statement statement = connection.createStatement(); // 报错:因为数据库内存中的数据被清空了,所以找不到表 // org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "USERS" not found (this database is empty); SQL statement: ResultSet resultSet = statement.executeQuery("SELECT * FROM users"); while (resultSet.next()) { System.out.println(resultSet.getInt("id") + " - " + resultSet.getString("name")); } }
使用 DB_CLOSE_DELAY 的效果:
// 创建连 { Connection connection = DriverManager.getConnection("jdbc:h2:mem:test-db;DB_CLOSE_DELAY=-1"); Statement statement = connection.createStatement(); statement.execute("CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(255))"); statement.execute("INSERT INTO users VALUES (1, '张三')"); statement.close(); connection.close(); // 关闭连接,此时数据库内存中的数据不会被清空 } // 重新打开连接 { Connection connection = DriverManager.getConnection("jdbc:h2:mem:test-db"); Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("SELECT * FROM users"); while (resultSet.next()) { System.out.println(resultSet.getInt("id") + " - " + resultSet.getString("name")); } statement.execute("shutdown"); // 关闭数据库,此时内存中的数据会被清空 statement.close(); resultSet.close(); connection.close(); // 关闭连接,此时数据库内存中的数据会被清空 } // 再次打开连接 { Connection connection = DriverManager.getConnection("jdbc:h2:mem:test-db"); Statement statement = connection.createStatement(); // 报错:因为内存中的数据库被清理了 // org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "USERS" not found (this database is empty); SQL statement: ResultSet resultSet = statement.executeQuery("SELECT * FROM users"); while (resultSet.next()) { System.out.println(resultSet.getInt("id") + " - " + resultSet.getString("name")); } statement.close(); resultSet.close(); connection.close(); }