高效使用JDBC
 

2009-10-14 作者:liaoxuefeng 来源:liaoxuefeng.com

 

在JavaEE应用中,使用ORM操作数据库虽然简单快捷(参考“高效使用JavaEE ORM”),但是毕竟是对JDBC的封装,很多时候,ORM还是不能满足我们的需求,主要是两个问题:

1. 速度不如JDBC,毕竟是封装JDBC,有额外的开销;

2. ORM提供的xQL很多时候无法满足需求,还需要数据库相关的SQL,这时,必须使用JDBC。

使用JDBC虽然麻烦点,但是,按照软件设计的思想,一步一步封装必要的代码,还是可以做到性能与开发效率并存。

首先,要坚决避免的就是不断重复编写try... catch... finally...。对于查询、更新、插入和删除操作,每一种操作只允许编写一次try... catch... finally...。如何实现?有两个方法。

第一个办法是找一个现成的封装了这些JDBC操作的框架,最好的方案当然就是Spring的JDBC框架了,顺便可以参考JdbcTemplate的源码,以便提升自己的JavaEE功力。

如果不使用Spring,那就采用第二个办法,自己造轮子,封装一个JDBC框架。

很多人反对自己造轮子,原因不外乎费事。不过很多时候,造轮子并不麻烦,而且可以满足特定的需求。今天要造的轮子就是一个封装JDBC的框架:Express-Persist

Express-Persist是ExpressMe的持久化子项目,目标是封装JDBC并提供简单的数据库操作接口。

为什么不使用Spring JDBC呢?主要原因只有一个:Spring的JDBC目前还是1.4兼容的,不支持1.5的泛型。Express-Persist要提供的接口除了基本的数据库操作外,还要实现:

1. 简单的ORM映射,注意是简单的,没有Hibernate那样完整而强悍,本质上就是把ResultSet的每一条记录变成一个JavaBean,可以参考Spring JDBC的RowMapper,实现非常容易;

2. 充分利用Java 5泛型支持,都是类型安全的参数和返回值,不用做强制转化;

3. 利用Java 5的注解(Annotation)把SQL标记在接口方法上,比如:

1.@Query("select * from User where u.id=:id")
2.
public User get(String id);

4. 最后,最重要的,只编写接口,没有实现类!

没有实现类,那JDBC代码写在哪?当然由Express-Persist框架自动生成了。如何自动生成?运行一个命令自动生成Java类?在“高效使用JavaEE ORM”一文中我们已经对JDO的这种静态增强方式表示了强烈的鄙视和唾弃,因此绝不可重蹈覆辙。Express-Persist会在启动时根据接口动态创建出类,不过我们不采用Hibernate使用的CGLIB库,而是直接通过JDK的动态代理功能实现动态类。

如何绑定SQL参数

DAO接口的方法参数要自动绑定到SQL参数中,由于方法参数的顺序与SQL参数的顺序可能不一致,因此,只能使用命名参数来绑定,即:SQL参数定义为:xxx,对应的方法参数用@Param("xxx")标记。

当SQL参数很多的时候(尤其是INSERT语句),方法参数也非常多,调用起来非常不方便,比如:

1.@Update("insert into User values(:id, :email, :password, :name)")
2.public int create(@Param("id") String id, @Param("email") String email, @Param("password") String password, @Param("name") String name);

而且都是String类型,调用起来容易出错。

因此,Express-Persist允许使用JavaBean绑定,把上述代码变为:

1.@Update("insert into User values(:u.id, :u.email, :u.password, :u.name)")
2.public int create(@Param("u") User u);

这样,调用起来只需要传入一个User对象即可,简单且不易出错。

如何分页查询

绝大多数数据库支持分页查询,但语法各不相同。如果让开发者自己写分页SQL语句,难度较大,而且不易复用。因此,Express-Persist仿照Hibernate的做法,为每一种数据库定义一个Dialect,处理分页,这样,无需考虑数据库的特定分页语法,只需额外添加@FirstResult和@MaxResults这两个注解,以便传入分页参数:

1.@Query("select * from User u order by id")
2.List<User> queryAll(@FirstResult int first, @MaxResults int max);

Express-Persist已经内置HSQLDB、MySQL和Oracle的Dialect支持,也可以编写其他数据库的Dialect,只需实现Dialect接口即可。

如何把ResultSet映射为Java对象

要把ResultSet映射为Java对象,我们采用Spring JDBC使用的RowMapper方案,改进之处在于采用了泛型,并且,提供一个BeanRowMapper,实现ResultSet到JavaBean的转换,因为大部分的转换都是到JavaBean。

利用Java 5的泛型支持,可以非常容易地生成一个BeanRowMapper,而无需编写任何方法:

1.public class UserRowMapper extends BeanRowMapper<User> {}

@MappedBy用于告诉Express-Persist如何映射ResultSet:

1.@MappedBy(UserRowMapper.class)
2.@Query("select * from User u order by id")
3.List<User> queryAll(@FirstResult int first, @MaxResults int max);

如果返回结果仅有一个,例如根据主键查询,则必须加上一个@Unique注解,这样,Express-Persist将自动检查返回的记录数,如果不为1,则抛出异常:

1.@Unique
2.@MappedBy(UserRowMapper.class)
3.@Query("select * from User u where id=:id")
4.User queryById(@Param("id") String id);

如果返回结果允许多个,则返回值应该定义为泛型List,如List<User>。

Batch支持

批量插入或修改时,使用和不使用JDBC Batch,其性能将有数量级的差距。Express-Persist提供Batch支持,通过继承BatchSupport接口:

1.public class UserDao extends BatchSupport {
2.    @Update("update User set name=:name where id=:id")
3.    void updateUserName(@Param("id") String id, @Param("name") String name);
4.}

Batch操作的代码稍微复杂一点,必须用try... finally执行,以便正确释放资源:

01.try {
02.    dao.prepareBatch();
03.    // now the batch prepared:
04.    dao.updateUserName("id-1", "change A's name");
05.    dao.updateUserName("id-2", "change B's name");
06.    dao.updateUserName("id-3", "change C's name");
07.    // execute:
08.    int[] results = dao.executeBatch();
09.}
10.finally {
11.    dao.closeBatch();
12.}

事务控制

Express-Persist仅支持JDBC事务,因此无法远程传播事务。事务代码通常写在Web应用程序的Filter或Interceptor中,只需编写一次:

01.TransactionManager txManager = ...;
02.Transaction tx = txManager.beginTransaction();
03.try {
04.    // TODO: DAO operations here...
05.    tx.commit();
06.}
07.catch (Exception e) {
08.    tx.rollback();
09.}

如果你想体验一下Express-Persist带来的全新Java持久化方案,可以从http://express-me.googlecode.com/files/express-persist.jar下载Jar包(含源代码)。完整的文档请参考http://code.google.com/p/express-me/wiki/ExpressPersist


火龙果软件/UML软件工程组织致力于提高您的软件工程实践能力,我们不断地吸取业界的宝贵经验,向您提供经过数百家企业验证的有效的工程技术实践经验,同时关注最新的理论进展,帮助您“领跑您所在行业的软件世界”。
资源网站: UML软件工程组织