借助 BeanKeeper 快速实现对象持久化

红薯 发布于 2010/06/19 16:02
阅读 214
收藏 2

简介: NetMind BeanKeeper 是一个对象/关系映射库,它可以将 Java 对象保存到关系型数据库中,同时提供了强大的查询功能和对事务的支持,可以满足用户在多种应用场合的需要。本文将介绍 BeanKeeper 的基本原理和架构,并将其同 Hibernate、Spring 等其它框架进行比较,总结其主要优点。最后将演示一个案例介绍如何借助 BeanKeeper 快捷地实现将 Java 对象保存到关系数据库中。

引言

NetMind BeanKeeper 是一个开源的 java 对象 / 关系数据库映射框架,它可以帮助用户快速将对象保存到关系数据库中,同时它也支持自定义地查询和事务,可以满足用户在各种应用场景的需求。它最大的特点就 是简单,无需配置。同时,它是纯 Java 的,也支持 HSQLDB 和 MySQL 等多种关系数据库。本文将介绍 BeanKeeper 的基本原理和架构,并将其同 Hibernate、Spring 等其他框架进行比较,总结其主要优点。最后将演示一个案例介绍如何借助 BeanKeeper 快捷地实现将 Java 对象保存到关系数据库中。

BeanKeeper 的架构

对象持久性几乎是所有 Java™ 应用程序(从桌面应用程序到企业级应用程序)中的必备,持久性的缺点是它一直都不太简单。

面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关系数据库是企业级应用环境中永久存放数据的主流数据存储系统。对象和关系数 据是业务实体的两种表现形式,业务实体在内存中表现为对象,在数据库中表现为关系数据。内存中的对象之间存在关联和继承关系,而在数据库中,关系数据无法 直接表达多对多关联和继承关系。因此,需要 ORM(Object Relational Mapping)实现程序对象到关系数据库数据的映射。

由于关系数据库是目前最流行的存储系统,因此要将对象持久化到关系数据库中,我们就要解决 ORM 的问题。目前主流的 ORM 框架有:Spring、Hibernate 等框架。它们都存在一个问题是:太复杂了。要利用这些框架进行对象持久化,开发人员首先要阅读几百页的文档以了解如何使用这些框架,然后又要编写 XML 配置映射文件以告诉框架如果和进行 ORM。而且一旦对象模型发生改变后又要修改映射文件。这些都极大地增加了开发人员的学习曲线和工作量,同时也容易出错。而 Bean keeper 的理念是简单的事情简单做,它尽量简化这些操作。

Bean Keeper 是一个基于 LGPL 协议的开源软件,它具有如下特性:

  1. 使用简单,你只需要学习 3 个借口就可以基本掌握其使用方法 ;
  2. 零配置。除了数据库的连接 URL 外。你不需要其他配置 ;
  3. 可扩展性,这个类库支持分布式操作,可以将您的数据进行多拷贝存储和负载平衡 ;
  4. 100% 地透明地支持 List、Map、Set 等集合 ;
  5. 自动分页大数据集。分页是 100% 透明地,包含百万条记录的数据集可以直接地给表现层,而不用担心内存和数据库负载的问题 ;
  6. 自定义的面向对象的查询语言,用户不需要编写复杂的 SQL 语句 ;
  7. 各种数据库间的可移植性。BeanKeeper 屏蔽了各类数据库之间的差异,比如对 Null 值的处理,空字符串 (Oracle),查询时大小写敏感,保留字等差异。这就意味着你可以更改底层的数据库 ;
  8. 对事务的支持,能够实现事务的提交和回滚。

下载 BeanKeeper

BeanKeeper 的安装过程很简单。首先,访问 BeanKeeper 站点(参见 参 考资料中的链接)下载 Jar 包。目前最新的发布版本是 2.6.0。本文中所有示例也是基于此版本。

BeanKeeper 是基于 LGPL 协议的,你可以在你的商业软件中商业软件通过类库引用 (link) 方式使用它而不需要开源商业软件的代码。但是如果修改它的代码或者衍生,则所有修改的代码,涉及修改部分的额外代码和衍生的代码都必须采用 LGPL 协议。


图 1. BeanKeeper 目前版本
图 1. BeanKeeper 目前版本

此外 BeanKeeper 还依赖于下列类库::

  • commons-lang-2.4.jar
  • commons-io-1.4.jar
  • commons-logging.jar
  • commons-collections-3.2.1.jar
  • commons-configuration-1.6.jar
  • log4j-1.2.15.jar

在本例中,我们使用的是 MySQL 数据库来持久化数据,所以我们还需要下载 MySQL 的 JDBC 驱动:

  • mysql-connector-java-5.0.8-bin.jar

保存对象

在本章中,我们将演示一个案例介绍如何利用 BeanKeeper 将员工 (Employee) 信息持久化到 MySQL 数据库中。首先从 CSV 文件中一条条读出员工信息,然后将这些信息生成一个 EmployeeBean 对象,然后将 EmployeeBean 对象持久化。

首先我们需要定义一个“类似 JavaBean”类,那么“类似 JavaBean”是什么意思呢?真正的 JavaBeans 是一些可视组件,可以在开发环境中配置它们以便在 GUI 布局中使用。一些用真正的 JavaBeans 开始的惯例在 Java 社区中已经变得非常普及,尤其是对于数据类。如果一个类遵守下列惯例,我就称其为“类似 JavaBean”:

  • 该类是公共的
  • 它定义了一个公共的缺省(无参数)构造函数
  • 它定义了公共的 getX 和 setX 方法用来访问属性(数据)值

既然技术上的定义不成问题,在谈及这些“类似 JavaBean”类的其中一个时,我将跳过所有这些,并只称呼它为“bean”类。

如下表所示,我们定义了一个 EmployeeBean 类,类中包含员工 id,姓名,年龄,入职年月信息。


清单 1. EmployeeBean 类

				
package cn.ac.iscas.beankeeper.sample;

import java.util.Date;

public class EmployeeBean {
String id;
String name;
int age;
Date onBoardTime;

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public Date getOnBoardTime() {
return onBoardTime;
}

public void setOnBoardTime(Date onBoardTime) {
this.onBoardTime = onBoardTime;
}

}

 

接下来存储 EmployeeBean 对象到数据库中。首先从 CSV 文件中一条条读出员工信息,然后将这些信息生成一个 EmployeeBean 对象,最后将 EmployeeBean 对象持久化到数据库。


清单 2. 保存 EmployeeBean 对象代码

				
package cn.ac.iscas.beankeeper.sample;

import hu.netmind.persistence.Store;
import java.io.File;
import java.sql.Date;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class EmployeeSave {
private static Log log;
/**
* @param args
*/
public static void main(String[] args) {
System.setProperty("log4j.configuration", "file:log4j.xml");
log = LogFactory.getLog(EmployeeBean.class);
try {
String csvFile = args[0];
// open store, uses MySQL driver
Store store = new Store("org.gjt.mm.mysql.Driver",
"jdbc:mysql://localhost:3306/lhq?user=root&password=sa");
// read csv and generate beans
List historicalData = FileUtils.readLines(new File(csvFile));
// remove first line (header)
historicalData.remove(0);
// store beans to database
for (Object _data : historicalData) {
String[] data = ((String) _data).split(",");
EmployeeBean bean = new EmployeeBean();
bean.setId(data[0]);
bean.setName(data[1]);
bean.setAge(Integer.parseInt(data[2]));
bean.setOnBoardTime(Date.valueOf(data[3]));

// save stock bean to database
store.save(bean);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
system.out.println("Usage: java sample.StockData "
+ "<symbol name> <historical prices file> <query>");
}
}
}

从上面的代码我们可以看出,事实上持久化一个对象的过程非常简单,我们只需要首先实例化一个 Store 对象

Store store = new Store("org.gjt.mm.mysql.Driver", 
"jdbc:mysql://localhost:3306/lhq?user=root&password=sa");

然后直接调用 store 的 save() 方法

store.save(bean); 

这样一个 bean 对象就被存储到数据库中了,具体如何实现 ORM 映射对用户来说是透明的,用户不用关心对象的哪一个属性对应到了数据库表的哪一列。读者要是对 BeanKeeper 的后台实现比较感兴趣,可以查看 MySQL 数据库。

图 2. 数据库中生成的表
图 2. 数据库中生成的表

如图 2 所示,BeanKeeper 在数据中生成了四张表:classes 表,employeebean 表,nodes 表和 tablemap 表。其中,classes 表和 tablemap 表保存了 Java 类到表名的映射信息,


图 3.classes 表
图 3.classes 表

图 4.tablemap 表
图 4.tablemap 表

从图 3 和图 4 我们可以看到,cn.ac.iscas.beankeeper.sample.EmployeeData 类的对象被保存到了 employeebean 表中。

打开 employeebean 表 ( 图 5) 我们可以看到,员工对象的各个属性都已经保存到表中。

图 5.employeebean 表
图 5.employeebean 表

查询对象

接下来我们介绍如何从数据库中查询出对象。例如我们要找出所有小于 30 岁的,2005 年之后入职的员工,同时以工号进行排序。

清单 3. 查询示例代码

String query="find EmployeeBean where age<30 and onBoardTime>='2005-01-01' order by id";
Store store = new Store("org.gjt.mm.mysql.Driver",
"jdbc:mysql://localhost:3306/lhq?user=root&password=sa");
List<EmployeeBean> emps = store.find(query);
for(EmployeeBean emp:emps)
System.out.println(emp.getName());

 

BeanKeeper 查询接口有四个:

  • find(String statement)
  • find(String statement, Object[] parameters)
  • findSingle(String statement)
  • findSingle(String statement, Object[] parameters)

其中 find 返回的是所有满足条件的对象的集合 (List),List 接口的具体实现类是 LazyList,它具有自动分页的功能,所以即使返回的结果中包含上百万条记录,你也不用担心内存和数据库负载的问题,他会自动地处理。除此之外,它还 提供了 size() 方法,可以返回查询结果的总记录条数。

findSingle 方法返回单个对象,当查询到第一个满足条件的记录后,即停止查询,返回结果。

事务控制

事务指的是一系列原子的数据库操作,在我们的上下文中指的是对象操作。这一些操作要么是所有操作都成功完成,事务完成提交;要么是某一操作失 败,数据回滚到事务开始前的状态。

BeanKeeper 目前只支持用户管理的事务界限划分,这就意味着你要指定事务的开始和事务的结束。即使你不显示地定义事务,事务其实也是隐含地存在的,因为你对 store 的每个操作(比如 save() 和 remove() 方法)都会隐式地创建一个事务,如果这个操作的某一环节出现了错误,这个事务将会回滚。

事务跟踪器(Transaction tracker)负责管理应用中的所有事务。如果你想跟踪事务的提交和回滚事件,你可以给事务跟踪器添加监听器,这样当提交和回滚操作进行时你将可以得到 通知。

TransactionListener 接口包括两个方法:

  • void transactionCommited(Transaction transaction);
  • void transactionRolledback(Transaction transaction);

注意:这些方法得到触发这个事件的事务对象时,这些事务已经结束了,所以你不能利用它们去执行操作。同样地,当你从跟踪器中获取到事务对象, 并执行某些数据库相关的操作时,为避免形成死循环,此时跟踪器中的事件将不会再被触发。


清单 4. 添加事务监听器代码

				
TransactionTracker tt= store.getTransactionTracker();
tt.addListener(new TransactionListener(){
@Override
public void transactionCommited(Transaction transaction) {
System.out.println("Transaction Commited");
}

@Override
public void transactionRolledback(Transaction transaction) {
System.out.println("Transaction Rolledback");
}

});

结束语

通过上文介绍,我们可以看出借助 NetMind BeanKeeper,我们不需要任何配置,只需使用 BeanKeeper 的三个接口便可实现将对象保存到关系数据库中,相对于 Hibernate 等框架要简单得多。同时它也支持复杂的查询和事务功能,可满足大部分应用场景的需要。

下载本文代码

加载中
返回顶部
顶部