本教程演示Karaf中的OSGi应用如何访问数据库,如何通过将DataSource安装为OSGi服务来抽象DB产品。一些新的Karaf命令行用于数据库操作。最后JDBC和JPA示例演示如何从用户代码角度使用DataSource。
前提条件
你需要安装karaf 3.0.3。
示例源代码
示例工程位于github:Karaf-Tutorial/db.
Driver和DataSource
在普通Java中,使用DriverManager来创建数据库连接是相当普遍的。在OSGi中, DriverManager就无法工作了,因为bundle的类加载器看不到数据库驱动。所以在OSGi中,最佳实践是在某个知道驱动的地方创建DataSource,并将其发布为OSGi服务。用户bundle应该只使用DataSource,而无需知道驱动细节。这非常类似于应用服务器的最佳实践,那里DataSource是由服务器管理,并发布到jndi。
所以首先我们需要学习如何创建和使用DataSource。
DataSourceFactory服务
为了在OSGi中更容易地创建DataSource,规范定义了DataSourceFactory接口。它允许使用属性指定的驱动创建DataSource。每一个数据库驱动都应该实现这个接口,并用驱动类名和驱动名属性发布为OSGi服务。
pax-jdbc介绍
pax-jdbc工程的目标就是在OSGi环境中使用数据库变得更加容易。它做了以下几件事情:
- 实现数据库的DataSourceFactory服务,而不直接创建服务。
- 实现XADataSource的池子和包装(在the pax jdbc docs中描述)
- 提供了从config admin配置创建DataSource服务的便利。
- 除了上面额外的功能,还提供了许多数据库的karaf特性。
所以它覆盖了你创建产品级的DataSource需要的驱动安装的方方面面。
安装数据库驱动
第一步为karaf中的数据库系统安装驱动bundle。大多数的驱动都已经是合法的bundle,并且可以在maven仓库找到。
对于一些数据库,pax-jdbc已经提供了karafdf特性,来安装当前版本的数据库驱动。
对于H2,下面的命令是有效的:
feature:repo-add mvn:org.ops4j.pax.jdbc/pax-jdbc-features/0.8.0/xml/features feature:install transaction jndi pax-jdbc-h2 pax-jdbc-pool-dbcp2 pax-jdbc-config service:list DataSourceFactory |
严格地讲,我们只需要pax-jdbc-h2特性,但是我们依然需要安装后续步骤会用到的其它特性。
这些命令会安装pax-jdbc特性仓库和h2数据库驱动。H2数据库驱动已经实现了DataSourceFactory,所以最后一个命令将会显示这个服务。
DataSourceFactory
[org.osgi.service.jdbc.DataSourceFactory] ----------------------------------------- osgi.jdbc.driver.class = org.h2.Driver osgi.jdbc.driver.name = H2 osgi.jdbc.driver.version = 1.3.172 service.id = 691 Provided by : H2 Database Engine (68) |
pax-jdbc-pool-dbcp2特性包装了DataSourceFactory,以提供数据库连接池和XA支持。
支持数据库池和 XA的DataSourceFactory
[org.osgi.service.jdbc.DataSourceFactory] ----------------------------------------- osgi.jdbc.driver.class = org.h2.Driver osgi.jdbc.driver.name = H2-pool-xa osgi.jdbc.driver.version = 1.3.172 pooled = true service.id = 694 xa = true Provided by : OPS4J Pax JDBC Pooling support using Commons-DBCP2 (73) |
从技术上说,这个DataSourceFactory也创建DataSource对象,但是它内部会管理XA支持和数据库连接池。所以我们想要在后面的代码示例中使用这个。
创建DataSource
现在我们只需要创建一个带有正确的工程pid的配置来创建DataSource服务。
所以创建了文件etc/org.ops4j.datasource-tasklist.cfg,内容为:
DataSource配置
osgi.jdbc.driver.name=H2-pool-xa url=jdbc:h2:mem:person dataSourceName=person |
这个配置会自动触发pax-jdbc-config模块创建DataSource。
- 名字osgi.jdbc.driver=H2-pool-xa会选择之前安装的H2的支持数据库连接池和XA的DataSourceFactory。
- url配置了H2创建一个简单的内存数据库,名字为test。
- dataSourceName将会反映到DataSource的服务属性里,稍后我们会找到它。
- 你也可以配置数据库池子,但是我们这里只使用默认配置。
DataSource
karaf@root()> service:list DataSource [javax.sql.DataSource] ---------------------- dataSourceName = person osgi.jdbc.driver.name = H2-pool-xa osgi.jndi.service.name = person service.factoryPid = org.ops4j.datasource service.id = 696 service.pid = org.ops4j.datasource.83139141-24c6-4eb3-a6f4-82325942d36a url = jdbc:h2:mem:person Provided by : OPS4J Pax JDBC Config (69) |
所以当我们搜索实现了DataSource接口的服务时,我们找到了我们刚刚创建的那个DataSource实例person。
当我们安装上面的特性时,我们也安装了aries jndi特性。这个模块将OSGi服务映射到jndi对象上。所以我们也可以使用jndi来获取DataSource,这个DataSource稍后用于jpa的persistence.xml。
DataSource的jndi url
osgi:service/person |
Karaf jdbc命令
Karaf包含一些管理DataSource和查询数据库的命令行。Karaf3.x中的管理DataSource的命令行仍然对使用blueprint创建DataSource的老方法有效。所以我们不会使用这些命令行,但是我们使用list DataSource、list数据库表和执行查询等功能。
jdbc commands
feature:install jdbc jdbc:datasources jdbc:tables person |
我们要首先按照karaf jdbc特性,jdbc特性提供了jdbc命令行。然后我们列出所有的DataSource,显示person DataSource能访问的所有数据表。
jdbc:execute person "create table person (name varchar(100), twittername varchar(100))" jdbc:execute person "insert into person (name, twittername) values ('Christian Schneider', '@schneider_chris')" jdbc:query person "select * from person" |
这些命令创建了一个数据表person,添加了一行记录,并显示数据表。输出应该类似于下面这样:
select * from person
NAME | TWITTERNAME -------------------------------------- Christian Schneider | @schneider_chris |
使用JDBC访问数据库
db/examplejdbc示例工程演示了如何使用我们安装的DataSource服务,在这个工程中安装和执行jdbc命令行。这个例子使用blueprint.xml引用OSGi服务DataSource,并将其注入到类DbExample中。然后test方法被作为init方法调用,演示DataSource上的一些jdbc语句。DbExample类完全独立于OSGi, DbExampleTest很容易地单独测试。这个test方法演示了在OSGi外部如何手动设置DataSource。
构建和安装
使用下面的命令构建:mvn clean install
在Karaf中,我们只需要安装我们自己的bundle,因为我们没有特殊的依赖。
> install -s mvn:net.lr.tutorial.karaf.db/db-examplejdbc/1.0-SNAPSHOT Using datasource H2, URL jdbc:h2:~/test Christian Schneider, @schneider_chris, |
在安装完成之后,这个bundle应该直接打印数据库信息和持久化的person。
使用JPA访问数据库
对于更大的工程,经常使用的是JPA,而不是手动创建SQL。相对于JDBC,使用JPA有两大优势:
- 你维护的SQL代码更少;
- JPA提供了方言,以屏蔽数据库的微妙区别,否则你需要自己编写代码。
在这个例子中,我们使用Hibernate作为JPA的实现。在此基础上,我们增加了 Apache Aries JPA,以提供OSGi JPA服务规范的实现,和JPA集成blueprint。
examplejpa演示了一个实现Person对象管理的PersonService服务的简单工程。Person只是一个用JPA @Entity注解了的JavaBean。
另外,这个工程还实现了两个Karaf命令行:person:add和person:list,这两个命令行允许简单地测试这个工程。
persistence.xml
和通常的JPA工程一样,peristence.xml文件定义了DataSource查找,数据库设置和持久类的列表。DataSource使用jndi名字"osgi:service/person"引用。
OSGi JPA服务规范定:Manifest文件应该包含属性"Meta-Persistence",这个属性指向persistence.xml。所以这个需要在maven bundle plugin中的配置中进行定义。Aries JPA容器将会扫描这些属性,将初始化的EntityMangerFactory注册为OSGi服务供用户bundle使用。
blueprint.xml
我们使用blueprint.xml上下文将EntityManager注入到我们的服务事项中,并提供了自动事务支持。下面的片段是我们感兴趣的部分:
<bean id="personService" class="net.lr.tutorial.karaf.db.examplejpa.impl.PersonServiceImpl"> <jpa:context property="em" unitname="person" /> <tx:transaction method="*" value="Required"/> </bean> |
这个配置会查找适用于持久单元person的EntityManagerFactory OSGi服务 ,并注入线程安全的EnityManager (底层使用ThreadLocal)到PersonServiceImpl。另外,它包装了每一个PersonServiceImpl方法调用,在方法调用之前增加打开事务的代码,并提交,如果抛出了任何异常,则执行回滚。
构建和安装
构建:
mvn clean install |
前提是已经安装了derby datasource。然后我们必须安装hibernate、aries jpa、 transaction、jndi和我们自己的db-examplejpa等bundle。参见ReadMe.txt了解命令行的使用。
测试
person:add 'Christian Schneider' @schneider_chris |
然后列出所有的持久person
karaf@root> person:list Christian Schneider, @schneider_chris |
总结
在这个教程里,我们学习了如何在Karaf使用使用数据库。我们安装了数据库驱动和DataSource服务。我们能够使用jdbc:*命令行检查和操作DataSource。在examplejdbc示例中,我们学习了如何获取datasource,并用普通的jdbc和它一起工作。最后我们使用jpa访问我们的数据库。