H2数据库地址:http://www.h2database.com/html/main.html
H2是一个开源的嵌入式(非嵌入式设备)数据库引擎,它是一个用Java开发的类库,可直接嵌入到应用程序中,与应用程序一起打包发布,不受平台限制。
1.1 与其他开源数据库比较
H2与Derby、HSQLDB、MySQL、PostgreSQL等开源数据库相比,H2的优势为:a.存Java开发,不受平台限制;b.H2只有一个jar包,占用空间小,适合嵌入式数据库;c.有web控制台,用于管管理数据库。具体特征如下:
特征 | H2 | Derby | HSQLDB | MySQL | PostgreSQL |
---|---|---|---|---|---|
纯Java | yes | yes | yes | no | no |
支持内存模式 | yes | yes | yes | no | no |
支持数据库加密 | yes | yes | yes | no | no |
支持ODBC驱动 | yes | no | no | yes | yes |
支持全文检索 | yes | no | no | yes | yes |
支持多版本并发控制 | yes | no | yes | yes | yes |
占用空间(jar/dll) | ~1M | ~2M | ~1M | ~4M | ~6M |
* 1.2 H2数据库连接方式
H2数据库支持如下三种连接方式:
连接方式 | 描述 |
---|---|
嵌入式模式 | 本地JDBC连接 |
服务器模式 | JDBC或基于tcp/ip的ODBC远程连接 |
混合模式 | 本地或远程同时连接 |
注:三种模式都支持内存、持久化到文件两种数据存储方式。三种模式对同时开启的数据库数量和数据库连接数量没有限制。
嵌入式模式
嵌入式模式是最简单最快捷的一种连接方式,嵌入式模式下,应用在JVM中启动H2数据库并通过JDBC连接。该模式同时支持数据持久化和内容两种方式,对同时开启的数据库数量和数据库连接数量没有限制。示意图如下:
服务器模式
服务器模式下,应用通过JDBC或ODBC API远程开启数据库。该模式下,H2数据库可以部署在不同的JVM或不同的物理机中,多个应用可以通过连接H2服务器同时连接到H2数据库。因为数据需要通过TCP/IP协议远程传输,因此服务器模式获取数据比嵌入式模式慢。服务器模式示意图如下:
混合模式
混合模式结合了嵌入式模式和服务器模式的特点,第一个应用通过嵌入式模式打开H2数据库,同时将数据库开启服务器模式,其他应用可以远程连接到数据库。数据库服务器的开启和关闭都在第一个应用中完成。混合模式示意图如下:
1.3 H2数据库JDBC URL格式
H2数据库支持多种连接方式和连接设置,连接URL格式如下,URL中的设置大小写不敏感。
主题 | URL格式 | 范例 |
---|---|---|
本地嵌入式连接 | jdbc:h2:[file:][]< databaseName> | jdbc:h2:~/test jdbc:h2:file:/data/sample jdbc:h2:file:C:/data/sample (Windows only) |
内存模式(private) | jdbc:h2:mem: | |
内存模式(named) | jdbc:h2:mem:< databaseName> | jdbc:h2:mem:test_mem |
服务器模式(TCP/IP) | jdbc:h2:tcp://[:]/[]< databaseName> | jdbc:h2:tcp://localhost/~/test jdbc:h2:tcp://dbserv:8084/~/sample jdbc:h2:tcp://localhost/mem:test |
服务器模式(TLS) | jdbc:h2:ssl://[:]/< databaseName> | jdbc:h2:ssl://localhost:8085/~/sample; |
加密方式 | jdbc:h2:< url>;CIPHER=AES | jdbc:h2:ssl://localhost/~/test;CIPHER=AES jdbc:h2:file:~/secure;CIPHER=AES |
文档锁定 | jdbc:h2:< url>;FILE_LOCK={FILE|SOCKET|NO} | jdbc:h2:file:~/private;CIPHER=AES;FILE_LOCK=SOCKET |
仅存在时打开 | jdbc:h2:< url>;IFEXISTS=TRUE | jdbc:h2:file:~/sample;IFEXISTS=TRUE |
VM存在时不关闭数据库 | jdbc:h2:< url>;DB_CLOSE_ON_EXIT=FALSE | |
用户名、密码 | jdbc:h2:< url>[;USER=< username>][;PASSWORD=< value>] | jdbc:h2:file:~/sample;USER=sa;PASSWORD=123 |
调试日志设置 | jdbc:h2:< url>;TRACE_LEVEL_FILE=< level 0..3> | jdbc:h2:file:~/sample;TRACE_LEVEL_FILE=3 |
忽略不明设置 | jdbc:h2:;IGNORE_UNKNOWN_SETTINGS=TRUE | |
用户文件访问 | jdbc:h2:;ACCESS_MODE_DATA=rws | |
zip格式数据库文件 | jdbc:h2:zip:< zipFileName>!/< databaseName> | jdbc:h2:zip:~/db.zip!/test |
兼容模式 | jdbc:h2:< url>;MODE=< databaseType> | jdbc:h2:~/test;MODE=MYSQL |
自动重新连接 | jdbc:h2:< url>;AUTO_RECONNECT=TRUE | jdbc:h2:tcp://localhost/~/test;AUTO_RECONNECT=TRUE |
自动混合模式 | jdbc:h2:< url>;AUTO_SERVER=TRUE | jdbc:h2:~/test;AUTO_SERVER=TRUE |
页面大小 | jdbc:h2:< url>;PAGE_SIZE=512 | |
修改其他设置 | jdbc:h2:< url>;< setting>=< value>[;< setting>=< value>…] | jdbc:h2:file:~/sample;TRACE_LEVEL_SYSTEM_OUT=3 |
H2控制台应用允许通过浏览器的方式连接到H2数据库,示意图如下。这是典型Client/Server模式,因此同时需要服务器和客户端。
H2控制台在不同的操作系统下有不同的启动方式,笔者系统是Mac os,下文通过命令行启动,如下:
java -jar h2*.jar
H2数据库服务器启动后会自动打开web控制台,也可以通过:http://localhost:8082 访问。控制台界面如下:
可以在H2控制台设置数据库连接模式,本文设置为服务器模式,首次进入可以设置用户名和密码,第一次测试连接后生效,连接进入到数据库控制界面,如下。在该界面下可执行数据库相关的DDL、DML语句。
注:如果数据库开启方式为嵌入式模式,则不允许其他应用在启动控制台时同时连接到数据库;如果开启模式为服务器模式或混合模式,则允许其他应用同时连接到数据库
Spring+Mybatis+Mysql数据库的相关配置参考:Spring事务管理-编程式事务、声明式事务,本文介绍Spring+Mybatis+H2的数据库访问实践。Spring+Mybatis配置参考上一篇文章,本次事件新添加H2数据库依赖:
...
com.h2database
h2
1.4.190
...
H2数据库属性文件配置如下,本文采用内存模式访问H2数据库:
driver=org.h2.Driver
# 内存模式
url=jdbc:h2:mem:testdb;MODE=MYSQL;DB_CLOSE_DELAY=-1
# 持久化模式
#url= jdbc:h2:tcp://localhost/~/test1;MODE=MYSQL;DB_CLOSE_DELAY=-1
H2数据库访问的Spring配置文件为:
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd">
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:config.propertiesvalue>
list>
property>
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.xiaofan.test" />
bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis_config.xml"/>
<property name="mapperLocations" value="classpath:user_mapper.xml"/>
bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${driver}" />
<property name="url" value="${url}" />
bean>
<jdbc:initialize-database data-source="dataSource" ignore-failures="DROPS">
<jdbc:script location="classpath:sql/ddl.sql" />
<jdbc:script location="classpath:sql/dml.sql" />
jdbc:initialize-database>
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
beans>
其中初始化数据库的DDL语句文件为:
CREATE TABLE `user` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`age` int(11) NOT NULL,
PRIMARY KEY (`id`)
);
初始化数据库的DML语句文件为:
insert into `user` (`id`,`name`,`age`) values (1, 'Jerry', 27);
insert into `user` (`id`,`name`,`age`) values (2, 'Angel', 25);
编写测试文件,如下:
/**
* Created by Jerry on 17/7/30.
*/
@ContextConfiguration(locations = {"classpath:config.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class Test extends AbstractJUnit4SpringContextTests{
@Resource
UserDAO userDAO;
@org.junit.Test
public void testInsert() {
int result = userDAO.insert(new User(null, "LiLei", 27));
Assert.assertTrue(result > 0);
}
@org.junit.Test
public void testUpdate() {
int result = userDAO.update(new User(2L, "Jerry update", 28));
Assert.assertTrue(result > 0);
}
@org.junit.Test
public void testSelect() {
User result = userDAO.findByName(new User(null, "Jerry", null));
Assert.assertTrue(result.getAge() != null);
}
@org.junit.Test
public void testDelete() {
int result = userDAO.delete("Jerry");
Assert.assertTrue(result > 0);
}
}
测试结果通过!