写这个 OR Mapping 层的初衷是因为一个朋友租了个虚拟主机,并试图开发一个小系统来扩展自己公司的业务,但因为虚拟主机的限制,不能使用他熟悉的 Hibernate ,他有不想写繁琐的 JDBC ,于是要我帮忙写一个简单的 OR Mapping 层。根据他的具体要求,我将这个小引擎的目标定为:
1. 在 JDBC API 上建立简单的包装。
2. 优化代码,使资源占用( CPU, RAM )尽量少
3. 不支持事务处理
4. 不支持分布式应用
5. 只支持 MySQL 和 ProgressSQL 两种数据库
6. 不需要配置文件
7. 尽量少的第三方包依赖
8. 实体间关系只处理一对一和一对多两种情况,并且只提供延迟加载方式
9. 内置 Connection Pool
经过三天的努力,基本的功能已经实现,再经过测试后,准备三月中旬交给朋友用。整个引擎只需要一个 jar 文件, 100k 左右(不需要其它第三方包,当然,数据库的 JDBC 驱动还是需要的 ^O^ ),使用起来也比较简单。对于简单的实体来说,只需要通过注释指定相对应的表和字段就可以了。下面用一个简单的 Book 对象来说明:
首先需要定义一个简单实体对象,并加入注释(支持 Java 5 以上环境)
@DBTable (name= "Books" )
public class Book {
@DBColumn (name= "id" ,type= "String" ,isKey= true )
private String id ;
@DBColumn (name= "name" ,type= "String" )
private String name ;
@DBColumn (name= "isdn" ,type= "String" )
private String isdn ;
@DBColumn (name= "price" , type= "float" )
private float price ;
public String getId() {
return id ;
}
public void setId(String id) {
this . id = id;
}
...... (其它的 get, set 方法省略)
然后再调用引擎的相关新增,修改,删除方法就来完成数据维护工作,
1. 新增
DALEngine engine = new DALEngine();
Book b = new Book();
b.setId( "123456789" );
b.setName( "Think in Java 3 Edition" );
b.setIsdn( "123-4567-89" );
b.setPrice( 60.5f );
try {
engine.create(b);
} catch (DALException e) {
log.error(e);
}
}
2. 基于主键的修改
try {
engine.update(b);
} catch (DALException e) {
log.error(e);
}
3. 基于条件的修改(忽略主键,按条件进行批量更改)
HashMap params = new HashMap();
params.put( "price" , 20f );
try {
engine.update(b, params, "WHERE price > ?" );
} catch (DALException e) {
log.error(e);
}
4. 基于主键的删除
try {
engine.delete(b);
} catch (DALException e) {
log.error(e);
}
5. 基于条件的删除(忽略主键,按条件进行批量删除)
HashMap params = new HashMap();
params.put( "price" , 20f );
try {
engine.delete(b.getClass(), params, "WHERE price > ?" );
} catch (DALException e) {
log.error(e);
}
6. 基于主键的查询
Book b = null ;
HashMap pks = new HashMap();
pks.put( "id" , "123456789" );
try {
b = engine.get(Book. class , pks);
} catch (DALException e) {
log.error(e);
}
7. 基于指定条件的查询
Collection<Book> books = null ;
HashMap params = new HashMap();
params.put( "price" , 20f );
try {
books = engine.get(Book. class , params, "WHERE price > ?" );
} catch (DALException e) {
log.error(e);
}
处理一对一或一对多的实体关系,需要用到 One2One 或 One2Many 注释,比如处理 Order 和 OrderDetail ,需要这样定义实体
@DBTable (name= "Orders" )
public class Order {
@DBColumn (name= "id" ,type= "String" ,isKey= true )
private String id ;
@ One2Many(type= "test.OrderDetail" column= "orderId" cause= "id" )
private Collection details ;
......( 其它属性 )
pub lic Collection getDetails() {
r eturn details ;
}
public void setDetails(Collection details) {
this . details = details;
}
public String getId() {
return id ;
}
public void setId( String id) {
this . id = id;
}
...... (其它的 get, set 方法省略)
}
@DBTable (name= "OrderDetails" )
public class OrderDetail {
@DBColumn (name= "id" ,type= "String" ,isKey= true )
private String id ;
@ Many2One(name= "orderId" ,type= "test.Order" )
private Order order ;
......( 其它属性 )
public String getId() {
return id ;
}
public void setId(String id) {
this . id = id;
}
public Order getOrder() {
return order ;
}
public void setOrder(Order order) {
this . order = order;
}
...... (其它的 get, set 方法省略)
}
除了以上的根据实体对象来进行操作的方法外,也提供不依赖于实体的简单的 JDBC 包装方法,这时需要用 DataSet 对象来接受查询结果,用 HashMap 来传递参数。例如执行一个查询:
DataSet dataSet = null ;
HashMap params = new HashMap();
params.put( "O.Operator" , "Tom" );
try {
dataSet = engine.select( "SELECT O.ID, O.Operator, O.DeliveryDate, " +
"D.ProductId FROM Orders O, OrderDetails D " +
"WHERE O.id = D.orderId and O.Operator = ?" , params);
} catch (DALException e) {
log.error(e);
}
同样,可以用 DALEngine 的 insert, update, delete 方法直接执行 SQL 语句。
接下来,我主要是要完成进一步的功能测试和性能测试。但上班后时间可能不是太充裕,我希望能在三月中旬完成,我会在这里继续记录我的测试情况。