第三步
最后,本文将介绍Spring是如何从Scala的高级概念:特征(traits)中受益的。特征可以将内容丰富的Person领域对象转换为羽翼丰满的OO类,这个类能够实现所有的职责,包括CRUD操作。如下所示:
Person(“Martin Odersky”).save |
第一步:使用Scala、Spring和Hibernate/JPA实现DAO
需求
毫无疑问,DAO在设计上应该有一个泛型DAO和一个针对Person实体的具体DAO。泛型DAO中应该包含基本的CRUD方法,如save、remove、findById和findAll等。由于是泛型,因此它处理的是类型而不是具体的实体实现。总的来说,这个泛型DAO具有如下的接口定义:
|
Person实体类的具体DAO应该增加一个特定于Person实体的finder方法:
|
我们需要考虑如下具体的实现细节以便利用上Scala提供的众多富有成效的特性:
◆关于集合:虽然底层的JPA实现并不知道所谓的Scala集合,但DAO接口返回的却是Scala集合类型(scala.List)而不是Java集合。因为Scala集合要比Java集合强大的多,因此DAO方法的调用者非常希望方法能够返回Scala集合。这样,我们需要将JPA返回的Java集合平滑地转换为Scala集合。
◆关于回调:Spring用于粘合JPA、JMS等框架的大多数胶水代码都是基于模板模式,比如JpaTemplate、JmsTemplate等。虽然这些模板通过一些便捷的方法在一定程度上隐藏了底层框架的复杂性,但很多时候我们还是不可避免地要直接访问底层的实现类,如EntityManager、JmsSession等。在这种情况下,Spring通过JpaCallback等回调类来实现我们的愿望。回调方法doIn…(..)唯一的参数就是指向实现类的引用,比如EntityManager。下面的示例阐述了这种编程模型:
|
上面的代码有两点值得我们注意:首先,匿名内部回调类的实例化需要大量的样板代码。其次,还有一个限制:匿名内部类JpaCallback之外的所有参数都必须是final的。如果从Scala的视角来看待这种回调模式,我们发现里面充斥的全都是某个“函数”的繁琐实现。我们真正想要的只是能够直接访问EntityManager而已,并不需要匿名内部类,而且还得实现里面的doInJpa(…)方法,这有点太小题大作了。换句话说,我们只需要下面这一行足矣:
jpaTemplate.execute((em:EntityManager) => em.createQuery(…)// etc. ); |