自从起草EJB 3.0的规范开始,无论是在客户端还是在服务器端的应用程序里,Java的类就一直有一种单一的、标准的persistence机制。Java 5的Annotations(批注)功能很容易使用。本文将介绍如何使用它。
保持Java类;现在已经有很多方式做到这一点,而适用于所有应用程序的一个标准的出现很可能让所有的开发人员从中受益。但其中的挑战就是弥合Java标准版和企业版之间的差别,形成一个标准的API,从而能够用于运行在托管容器里的企业应用程序,也能用于希望管理自己的、无容器的标准应用程序。现在我们有了JSR-220——Enterprise Java Beans 3.0规范。随着JSR-220的发展,它分成两个部分:EJB3.0 persistence和EJB 3.0 core(以及其他)。
EJB3.0 persistence与先前的EJB persistence不同,它在Java 5.0里加入了Annotations(批注)以及各种POJO persistence开发人员的经验。
在编写EJB3.0 Persistence的时候,它的规范还在“最后的草案”阶段,有些内容还有可能发生改变。尽管如此,现在就是你研究一下规范里很多实现的好机会;参考实现可以在Glassfish里找到,而另外一个是实现Hibernate的Annotation和EntityManager项目。
现在让我们从基本的东西开始:如何让一个类在Java SE里面保持连续性。下面就是一个简单的例子:
public class Address {
private String street;
private String postcode;
public Address() {}
public String getStreet() { return street; }
public void setStreet(String street) { this.street = street; }
public String getPostcode() { return postcode; }
public void setPostcode(String postcode) { this.postcode = postcode; }
}
为了把类放进一个能够被持久保持的实体,我们在一开始就加入了@Entity批注,从而让其能够保持持久,就像下面这样:
import javax.persistence.Entity;
@Entity
public class Address {
…
现在,如果你得第一反应是“嗨,什么是@Something”,那么就让我简单介绍一下批注。从历史上讲,Java一直缺少在代码里嵌入元数据的方式。Javadoc是一种源数据,用于生成文档,使用方法是在注释块里使用前缀为@的标记。这一概念建立在Xdoclet基础之上,人们用它来承载自己的元数据。另外一种替代方式是用于类的匹配XML文件,例如Hibernate除了使用类以外,还利用一个.hbm.xml文件为同名的类承载自己的映射信息。这两种技术都很好用,但是对于Java 5来说,人们更希望在运行时实现看到一种更能够验证和发现的内联和编译器;这就是Annotations出现的原因。在本文里,你需要知道批注是什么,如何添加元数据,如何持久地映射一个类。
@Entity批注的作用是标记用来保持的类。为了便于更容易检索,POJO会给每一个需求配备一个主键。这样做的一种常见方法是引入一个长字段来作为这个键。所以我们会把这个加到类上面:
private Long id;
@Id
@GeneratedValue
public Long getId() { return id; }
public void setId(Long id) { this.id=id; }
@Id批注表示主键字段的获得者是谁,而@GeneratedValue批注会要求persistence层为这个字段生成一个值。这就是我们需要做的所有事情;我们现在就可以开始使用Address(地址)了。
Address address=new Address();
address.setPostcode("ZZ9 99Z");
address.setName("John Doe");
如果要保持Address,我们就需要为处理保持数据所需的每个工作单元取得一个EntityManager。Java EE和Java SE下的persistence在这一点上有很大的不同。在Java EE里,会有很多批注让周围的框架来管理实体。在Java SE里,这一任务就交给了开发人员,由他们来获得EntityManager。我们从EntityManagerFactory取得了一个EntityManager。
EntityManagerFactory emf=null;
…
EntityManager em=emf.getEntityManager();
我们以后再回头讲是从哪里获得EntityManagerFactory的;现在,我们就假设它已经被初始化,我们能够从其中获得EntityManager。当你保持Java SE的时候,你还有责任管理数据库的事务;你必须开始和结束数据库的事务,所以就让我们从获得一个事务开始吧:
EntityTransaction tx=em.getTransaction();
tx.begin();
现在我们可以要EntityManager保持我们的Address。
em.persist(address);
然后执行事务,关闭EntityManager;
tx.commit();
em.close();
完成之后,会有一个id被分配给我们的Person,它将被写入到数据库里。获得和执行事务的这种方式对于任何用来修改对象保持状态的代码来说都是相当常见的。为了保证可靠性,如果出现像下面这样的问题,它应该提示出现异常,并进行回滚。
EntityManager em=emf.getEntityManager();
EntityTransaction tx=em.getTransaction();
try {
tx.begin();
// Do saves or modifications here
tx.commit();
} catch (Exception e) {
if(tx.isActive()) tx.rollback();
System.err.println("Error:"+e);
} finally {
em.close();
}
假设这段代码是放在下面用来修改数据库的例子的周围。如果要检索数据,我们只需要获得EntityManager就行了。
要检索先前保存的Address,我们可以把id用作参考;我们不需要来自EntityManager的事务,所以这个过程就变得很简单了:
Address address2=em.find(Address.class,address.getId());
如果我们想要通过邮政编码找到一个Address,我们可以使用EJB查询语言——EJBQL来定义查询,用EntityManager来创建这一查询。
Query q=em.createQuery("select address from Address as address where postcode=:param");
这个查询会着手查找邮政编码符合要求的地址(Address被保持,返回的结果被作为Address对象),也就是符合“param”参数为“to be set”的地址。现在我们可以设置这个参数:
q.setParameter("param",name);
要获得结果,就要对查询调用getResultList,如果我们希望得到0个或者更多的结果,或者我们只希望获得1个结果的话。
List<Address> l=(List<Address>)q.getResultList();
or
Address addresstochange=(Address)q.getSingleResult();
我们最后要做的事情是更改Address(地址),并把它的改变合并到数据库里;假设我们已经通过先前的查询检索到了一个Address。
addresstochange.setStreet("A Different Street");
…
em.merge(addresstochange);
…