持久化是企业级应用的关键组件。当然,Spring和EJB对于持久化都提供了强大的支持。Spring的设计策略是在产品中集成主流的持久化框架如JDBC、Hibernate、JDO、iBatis等。而在最新的EJB3.0规范中,实体bean被JPA(JAVA持久化API)取代;相应地在Spring 2.0中,也增加了对于JPA的支持。JPA旨在提供一个轻量级的对象-关系映射框架。在EJB 3.0中专门定义了操作持久化服务的接口和映射实体对象到关系数据库的方法。
持久化——功能对比
图一. 航班预订模型。模型涉及了对象-关系映射的诸多概念:组合,双向和三向关联等。
如下的单元测试用来验证机票对象生成、与航班关联、分配座位、最后结果存入数据库。
public void testSave()

...{
Ticket ticket = TicketMother.create();
Flight outboundFlight = flightDAO.findById(1);
Flight returnFlight = flightDAO.findById(2);
ticket.setOutboundFlight(outboundFlight);
ticket.setReturnFlight(returnFlight);
ticket.getPassenger().setFlightDetails(outboundFlight,
new PassengerFlightDetails(getSeatForFlight(outboundFlight, "2A")));
ticket.getPassenger().setFlightDetails(returnFlight,
new PassengerFlightDetails(getSeatForFlight(returnFlight, "2B")));
ticketDAO.save(ticket);
ticket = ticketDAO.findById(ticket.getId());
assertEquals("John", ticket.getPassenger().getFirstName());
assertEquals("2A", ticket.getPassenger().getDetailsForFlight
(ticket.getOutboundFlight())
.getSeat().getNumber());
}

以上测试在Spring and EJB 3.0环境下都成功通过。这说明Spring 和 EJB 3.0在基本的ORM持久化方面功能相当,并且其实现方式非常相似。Hibernate是最流行的对象关系映射工具,以下是Spring/Hibernate组合的TicketDAO类的实现:
public class TicketSpringDAO extends HibernateDaoSupport implements TicketDAO

...{
public Ticket save(Ticket ticket)

...{
getHibernateTemplate().saveOrUpdate(ticket);
return ticket;
}
...
}
EJB 3.0的实现如下:
@Stateless
public class TicketEJBDAO implements TicketDAO

...{
@PersistenceContext(unitName = "flightdb")
private EntityManager em;
public Ticket save(Ticket ticket)

...{
em.persist(ticket);
return ticket;
}
...
}
显然,从实现的角度来看,两个产品非常相似。Spring设计了HibernateDaoSupport类,将Spring与Hibernate间的交互操作抽象出来,通过HibernateDaoSupport类可访问Hibernate的诸多特性,以及Spring的Hibernate模版。而在EJB 3.0中,则是凭借容器来注入实体对象管理器。
Hibernate的Session和JPA的实体对象管理器功用相当,但其间细微的差别值得重视。Hibernate的Session是实体对象的缓存同时也是对象-关系映射引擎的接口。而在JPA中,持久化场景承担了缓存的角色,实体管理器则承担接口的角色。
在映射方式方,基于Spring/Hibernate的应用以XML映射文件的方式实现。
<hibernate-mapping package="org.jug.flight.domain" default-lazy="false">
<class name="Ticket">
<id name="id" column="id">
<generator class="native" />
</id>
<property name="status" type="Status" />
<many-to-one name="outboundFlight" column="outbound_flight" />
<many-to-one name="returnFlight" column="return_flight" />
<many-to-one name="passenger" column="passenger_id" cascade="all" />
</class>
</hibernate-mapping>

基于JPA的应用通常采用注释的方式,好处是减少了配置以及映射数据毗邻相关实体对象的工作量,从而提高了可视性。
Spring/Hibernate 和 EJB 3.0/JPA在通过ORM实现持久化方面功能相似。
@Entity
public class Ticket implements Serializable

...{
@Id
@GeneratedValue
private long id;
private Status status;
private double price;
@ManyToOne(cascade = CascadeType.PERSIST)
private Passenger passenger;
private Flight outboundFlight;
private Flight returnFlight;
...
}