说起持久层框架, Hibernate和mybatis
(早期版本名为 ibatis)应该不少人都听过或使用过,
虽然无从考证上述两个持久层框架的市场份额, 但在国内应算是最为流行的两个框架.
大名鼎鼎的 Hibernate 可能是很多人接触的第一个持久层框架. 它与Struts、 Spring 组成的SSH组合对于程序猿而言就如偶像团体SHE对于追星族般如雷贯耳……
介绍Hibernate的书籍几乎已经到了泛滥成灾的地步. 本人也曾在多个项目中使用过Hibernate, 其强大的功能确实为程序开发带来了不少便利. 找一本入门的书或网上下载一个入门教程, 跟随教程的步骤相信运行起一个简单的例程估计不是什么大问题, 但随着项目复杂度的增加, 配置和使用也同样变得复杂, 偶尔可能还需要应用一些性能优化方面的"技巧". 面对如此庞大的框架, 不经意间抛出的运行时异常着实让人无从下手.
另一持久层框架:mybatis 同样也拥有不少粉丝, 本人也一度是其拥趸者.
相对于Hibernate,
其拥有另一种风格的配置和使用方式, 但面对那密密麻麻的XML配置文件, 以及事必躬亲的查询配置, 本人开始犯懒了…… 当然, 一些配套的工具或插件可以帮我们干不少繁重的工作, 也许还有一些优秀的辅助工具本人未尝见识, 不敢枉加评论.
说了这么多, 并无贬低上述两个持久层框架的意思, 无论什么工具总有其优缺点, 《人月神话》中有句话叫做“没有银弹”. 这里只是想向大家介绍一个
我认为被国人雪藏的优秀持久层框架: Cayenne.
经过本人在几个小项目中的试用, 个人觉得不错, 初步使用后有些体会, 仅供参考:
(1) 配置文件与Hibernate和mybatis类似, 使用XML文件(Hibernate较新的版本中支持注解方式配置), 里面虽然也是令人头大的密密麻麻的映射关系说明, 但官方提供了可视化的建模工具CayenneModeler, 可自动生成XML配置文件和Java持久层的实体类. 此工具支持数据库反向工程(根据已有的数据库提取表/视图结构和外键关系, 自动生成配置文件
), 同样也支持在建模工具中直接创建数据模型后将表和关系生成到数据库中. 这个工具还是比较强大的, 至少在目前本人实践的几个小项目中没有出现过需要手动去修改XML文档的情况.
(2) 提供多种查询方式:
SelectQuery query =
new
SelectQuery(Artist.class,
qual
);
List artists = ctxt.performQuery(query);
其中条件由参数qual (Expression类型) 带入, qual 则可由Cayenne提供的ExpressionFactory来生成.
ExpressionFactory提供很多静态方法来生成Expression, 例如:
Expression.fromString(
"artistName = $aname"
); // 根据字符串来生成带参数的查询条件
ExpressionFactory.matchExp(
"artistName"
,
"张三"
) // artistName = "张三"
当然, 如果你有很多个Expression 组成的List, 现在需要将它们使用And连接起来, 那可以这样:
Expression qual = ExpressionFactory.joinExp(Expression.AND, list);
总之, ExpressionFactory 还
是相当强大的, 这有点类似mybatis的Criterion, 但似乎更胜一筹. 本人比较懒, 就不再一一列举了, 请各位看官方文档.
2. S
QLTemplate Query
这种方式可以支持设定目标数据库方言(SQL Dialect)
, 也就是使用目标数据库原生的SQL语法来查询. 例如:
SQLTemplate rawSelect =
new
SQLTemplate(Artist.class,
"select * from ARTIST"
);
注意, 大写的ARTIST是数据库中的表名, 而非Java类名. 如果我们要使用数据库提供的一些函数, 那这种方式再合适不过了.
当然, 它还可以相对灵活地配置从数据库查来的数据怎么转化成Java实体对象.
3.
EJBQLQuery
使用EJBQL 语法的查询. 呵呵, 什么是EJBQL语法? 看例子……
EJBQLQuery query =
new
EJBQLQuery(
"select p from Painting p WHERE p.estimatedPrice > 3000");
呵呵, 是不是很像标准的SQL, 只是注意 from 后面的Painting是Java 实体类名. 严格的定义请参阅 JPA 吧……
呵呵, 越来越懒了…… 总之查询方式很灵活. 对于大多数的情况, 其实第1种方式已经足够了.
(3) 一般而言在Cayenne里, 查询总会有一个称为 Root 的东东. 回头看一下前面的代码, 例如:
通过
new
SelectQuery(Artist.class,
qual
); 来
构造SelectQuery时, 参数Artist.class就告诉Cayenne把查询结果封装成Artist类型的实体对象集合.
然后, 我们就可以拎着一个Java 实体对象, 通过它 getter 方法方便地得到与之关联的主表和从表数据, 如果关联表的数据尚未提取到内存Cayenne会自动从数据库中获取. 如果你担心所谓的 "N+1"查询问题, 那可在执行查询之前使用
addPrefetch方法设定预提取的主表或从表.
(4) 对于分页、提取前N行数据、Distinct这样的查询要求, Cayenne也提供了现成方法, 执行查询之前调用一下就行, 它会根据目标数据库自行处理.
(5) 数据缓存支持. 这对查询性能改善不小, Hibernate当然也有这个东东. 这里多说一句, 要维持缓存中的数据与数据库中数据的一致性, 以及保持缓存中同一行数据的唯一性是一个不小的问题. 特别是后者, 如果一个持久层框架不能很好地保证同ID的实体对象在缓存中的唯一性, 那当我们引入MVC后, 再把持久层框架提供的实体对象用作Module你就去哭吧…… PS: 之前使用Hibernate的时候遇到过Hibernate抛出异常提示存在多个同ID实体, 然后…… 就没有然后的情况, 当然我只能说肯定是我代码的问题了, 不然要被Hibernate粉丝的口水淹死, 但这种运行时异常确实难Debug.
(6) 可配置拦截器和监听器. 不晓得什么是
拦截器和监听器? OK, 科普一下…… 它们是设计模式里的东东, 这里的
拦截器可以简单理解为当Cayenne要向数据库发指令之前你可以把指令
截获, 然后做一些判断或处理, 如果你愿意还可以阻止指令的执行. 监听器那就是根据你的配置, Cayenne在向数据库发指令之前或之后会调用你指定的监听函数, 当然监听器不会干扰Cayenee的工作, 人家只是干活之前或之后知会你一声, 并不是要征求你的意见 (这一点不像
拦截器)
. 好了, 科普完了, 听着是不是有点像数据库里的触发器, 呵呵. 那支持这两个东东有什么好处呢? 我们可以干些底层一点的事情, 举个例子: 我们要记录下是谁在什么时候修改了员工信息, 如果有监听机制,我们只需要监听员工这个实体类的Update事件, 然后调用写日志功能就行, 代码只需要写一个地方; 但如果没有监听机制, 那就得在所有可能修改员工信息的地方调用写日志的函数. 再比如…… MVC模型下作为Module层的东东向Contorl层发通知 写在Cayenne监听器里也是个不错的选择.
好吧, 码字真的很累, 头上开始冒小星星了…… 还有一些其它的优点, 各位自己玩吧~ 如果能有个小项目具体实践一下, 相信你会有自已的看法. 谁用谁知道, 呵呵~
相对Hibernate和mybatis而言, Cayenne的中文资料要少得多, 百度一下Cayenne这个单词, 大多出来的结果都是关于
土豪们的至爱 --- 卡宴车的介绍, 本人愚见, 由此多少可以看出这个框架在国内程序猿中的普及程度并不是很高.
近期工作琐事较多, 分身乏术, 暂时还不能抽出时间来写一份入门级的配置使用教程, 因此这里只能先向各位引荐, 待空闲的时候再附上教程. 在此之前, 提供2个学习材料:
[1]
http://cayenne.apache.org/ 官方网站, 里面有Tutorial, Guide 等最权威的文档.
[2] 一个叫做 "Apache Cayenne 3.0 指南中文版" 的chm文档, 是一位网友翻译的官方使用手册. 对英文有恐惧的同学可以参阅一下. 网易博客似乎不可以上传附件, 所以各位就只有找度娘要了.
确实少了点, 老外的论坛里相对资料要多些……
PS: 对于程序猿而言, 英文材料就像美女一样, 如果你有美女恐怖症, 那到手的基本上都是人家挑剩的了, 或者你等美女变性?
…… 这是病, 得治! …… …… 我没药, 呵呵~