JSR 22O规范定义了EJB 3.0的一些规范。该规范的主要目标就是简化实体 bean的创建、管理、以及存储。
为了朝着这个目标前进,Sun公司的 Microsystems和支持此规范的社区开发者们创建了一个新的应用程序可编程接口,能够让你将"老的Java对象"或者POJO作为你的持久化实体使用。新的Java 持久化API 能够是你更加容易的将POJO作为实体bean使用,同时,减少复杂的部署描述和额外的辅助bean,甚至你能将这些API 应用到桌面应用程序。
如果你使用这个新的API的话,你就会发现许多的优点,没有理由不让你去使用它,这里我就只举出一些优点:
1.你不需要创建复杂的DAO。
2.API帮助你管理事务。
3.你所写的基于标准的待么能够和任何关系数据库交互,从特定的数据库商家规范中解脱出来。
4.你可以避免直接使用SQL语句,可以使用你类名和属性方法作为查询语言。
5.你能使用和管理POJO。
6.你同样可以使用Java持久化API作为桌面应用程序的持久化开发。
一、API概述
Java持久化 API是 Java EE 5规范的一部分。你将会在 javax.persistence包中找到你操作实体的所有东西。首先你所需要的就是 EntityManager实例。一个 EntityManager实例提供了开始,结束事务的方法,在 persistence context中持续或者查找实体的方法,并且还是合并,甚至是删除那些实体的方法。另外,一个 EntityManager实例能够创建和执行查询。
一个 persistence context是一组唯一的实体实例,在运行时刻有持续提供者( persistence provider)管理它们。与之类似的一个术语就是 persistence unit,它是一组实体类的集合,不过这些实体类是应用程序可能用到的类。一个 persistence unit定义了映射到一个单独数据库的一组实体。
在此API中,实体有自己的声明周期。知道它们的生命周期有助于你理解 API如何操作这些实体。实体不同的状态如下所述:
创建--一个新的实体就是一个你应用程序的一个新对象。一个新的实体将会存在于你的应用程序中,但是 persistence context不会知道这些实体的存在。
管制--受管制实体是那些已经持久化,或者是说已经存在于数据库的那些实体。这些实体在 persistence context.身份唯一。
脱管--脱管的实体就是有一个持久化的身份,但是在 persistence context中当前不会被激活管制。
删除--被删除的实体存在于 persistence context中,但是已经位于隔离,或者说是删除的行列了。
下面的章节,我将要描述如何让你操作这些实体,并且让你明白在不同生命周期状态下,这些实体状态是如何变化的。
二、实体的创建
既然实体是POJO,你创建它们就像创建任何一个对象一样。你能使用 new关键字创建一个类的实例。你可以创建一个新的 Player实体,同时使用 new关键字调用 Player构造函数。
Player p = new Player();
从这一点上讲,你可以设置 player的名称,号码等相关的信息。同时,你可以在构造函数中设置相应的属性。这个非常有用,并且很方便,特别是当你使用基于字段持久化性声明,而不是基于属性的声明。新的实体创建后,还没有被任何一个 persistence context管制。
三、实体管理器
为了持久化一个实体,你需要有一个 EntityManager的实例。获得一个 EntityManager的实例很容易做到,但是在桌面环境中还是要走一些弯路的。你需要获取 EntityManagerFactory事例来创建一个 EntityManager对象。你需要使用 Persistence类来获取 factory方法。在Java SE环境中, Persistence类作为是一个引导类。
下面这个代码示范了如何在 Java SE桌面环境中创建一个 EntityManager实例。既然 EntityManager实例代表一个单元( persistence unit),那么你必须提供给持久化单元( persistence unit)一个名字。在这个例子中, league持久化单元( persistence unit)的名字。再次说明一下,持久化单元( persistence unit)定义了实体的集合,和单个应用程序相关的的集合,这些实体存贮在单个数据库中。 League名字在 persistence.xml文件中和其它的属性一起声明过了。
// Create the EntityManager
EntityManagerFactory factory = Persistence.createEntityManagerFactory("league");
EntityManager em = factory.createEntityManager();
四、管制实体
现在你有了一个 EntityManager对象,你可以在桌面环境用它创建查询和事务处理。在你存储一个新的 Player 和 Team 实体之前,你应该开始一个事务处理。在事务处理过程中,使用实体管理器( entity manager)的 persist方法管制一个新的实体。当你持久化它们后,它们就称为了管制状态。你必须提交事务,让这些实体出现在数据库中。毫无疑问,你将会使用 begin 和commit 方法来实现这些操作。
下面的代码演示了如何创建和保存 几个Players信息和他们球队的比赛赛程。
public class CreatePlayersAndTeams ...{
/**//** Creates a new instance of CreatePlayersAndTeams */
public CreatePlayersAndTeams() ...{
}
/**//**
* @param args the command line arguments
*/
public static void main(String[] args) ...{
// Create the EntityManager
EntityManagerFactory emf = Persistence.createEntityManagerFactory("league");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
for(Team team: teams) ...{
em.persist(team);
}
for(Player player: players) ...{
player.setTeam(teams[0]);
teams[0].addPlayer(player);
em.persist(player);
}
em.getTransaction().commit();
em.close();
emf.close();
}
private static Player[] players = new Player[] ...{
// name, number, last quoted statement
new Player("Lowe", "Derek", 23, "The sinker's been good to me."),
new Player("Kent", "Jeff", 12, "I wish I could run faster."),
new Player("Garciaparra", "Nomar", 5,
"No, I'm not superstitious at all.")
};
public static Team[] teams = new Team[] ...{
new Team("Los Angeles Dodgers", "National"),
new Team("San Francisco Giants", "National"),
new Team("Anaheim Angels", "American"),
new Team("Boston Red Sox", "American")
};
}
这些代码创建了 player和他们的球队,以及如何让这些球队比赛。一旦你创建了这些实体,你就调用了实体管理器的persist 方法。最后,一旦你创建了这些对象和调用了 persist 方法,你就通过调用 commit 方法完成了事务处理。当你完成了数据库操作,通常所有的事务之后,使用 close 方法关闭 实体管理器(entity manager)和它的 factory。
图一展示了一个 TEAM的表。既然 Team实体已经自动生成了标识符,那么在表中的前四行,从1到4的顺序就是主键的值了。
图一:Team实体变缺省映射为TEAM表
图二显示了 PLAYER表。自动生成的标识符是接着 TEAM表下来的。注意到 player每行都有一个外键值,与其球队对应。在此例子中,每个 player都属于 拥有主键值1的Dodgers队。
图二:player表中每行都有一个外键TEAM_ID
GlassFish应用实现实际上为你创建了 PLAYER 和 TEAM 表。一旦你使用数据库工具创建了 league数据库,如果数据库中没有这两个表的存在的话,上面的示例代码就会创建所需的实体表。当然,这种操作是可以选择的,它由 persistence.xml文件指定提供商属性 toplink.ddl-generation所决定,在下载的示例代码中你可以看到 create-tables属性值。当然,如果表存在的话,代码执行结果就向存在的表格中插入记录,并且生成很多的警告信息,告诉你存在了那些表。当你需要做改变,测试,删除表,改变代码,重复测试的时候,使用 toplink.ddl-generation属性可以自动创建表,列,和主键,缩短快速开发周期,提高生产力 。
如果是需要创建一个大型数据库,仅仅通过简单的提交持久化实体是不好的做法。你或许喜欢使用 SQL语句或者是数据库供应商自己的工具,来生成表格和其它的数据库元素。但是,持久化规范明确的描述了一条规则就是:当你将你的对象实体映射到成它们之间实体关系时,你要使用提供商实现。规范定义了实体、属性、标志符如何在数据库中建立成为表,列,主键,外键。当你在创建数据库表的时候,会用到 Java持久化API,所以你必须记住命名规约。否则,你要使用声明来重写那些缺省的设置。
五、实体查找方法
数据库已经有了几个player和球队比赛任务。你可以使用持久化 API从数据库中检索这些相似的实体,而不用任何复杂的查询。当然,你必须知道这些实体的主键。调用 find方法来检索实体。例子到目前为止,你仅仅是创建了少数几个实体实例, persistence provider将这些实体以一定顺序的标志符插入到表中。下面的代码将会调用具有参数id 的find方法,给出几个的标志符查找,然后将与之对应的Player信息打印出来。
// The EntityManager, em, has already been created at this point.
...
// Retrieve Player entities using their keys.
for(long primaryKey = 1; primaryKey < 10; primaryKey++) ...{
Player player = em.find(Player.class, primaryKey);
if (player != null) ...{
System.out.println(player.toString());
}
}
假定你已经成功创建了本文的数据库和表,你将会在你的控制台上看到如下的信息:
[Jersey Number: 23, Name: Derek Lowe, Team: Los Angeles Dodgers]
[Jersey Number: 12, Name: Jeff Kent, Team: Los Angeles Dodgers]
[Jersey Number: 5, Name: Nomar Garciaparra, Team: Los Angeles Dodgers]
六、实体合并方法
托管实体在数据库中有一个持久化的标识符,但是不在当前的persistence contex中。这种状况是有可能存在的,比如如果你通过先前的串行化文件创建了一个 Player,或者你既没有清楚,也没有关闭实体管理器。那么你可以更新这个实体,或者通过 merge 方法来将它合并到 persistence context中去。
下面的代码演示了如何改变脱管实体 Team的比赛任务。 一旦你调用实体管理器的 clear 方法,Player实体就从 persistence context脱管出去了。同样,当你使用 close方法关闭实体管理器的时候,实体也会被脱管。虽说脱管的实体仍然在数据库中,但是实体管理器已经不在能激活管理它们了。
// The EntityManager, em, already exists for this example.
...
// We just happen to know that '5' is one of the
// player identifiers. You shouldn't normally hard-code this
// into any application.
Player p = em.find(Player.class, 5L);
em.clear();
// p is now detached for the convenience of this example
Team t = new Team("Ventura Surfers", "National");
p.setTeam(t);
em.getTransaction().begin();
Player managedPlayer = em.merge(p);
em.getTransaction().commit();
merge 命令使得被脱管的对象重返受管制的状态。另外,该命令返回了该实体的一个管制的副本。
七、实体移除方法
实体移除使用 remove 方法。 remove 方法需要你的提供一个激活的管制实体,作为一个调用的参数。
// 'em' is an EntityManager instance
em.getTransaction().begin();
// use a hard-coded player id for convenience in this sample only
Player player = em.find(Player.class, 5L);
if (player != null) ...{
System.out.println(player.toString());
em.remove(player);
}
em.getTransaction().commit();
八、其它的方法和选项
一个 EntityManager 实例提供了许多不同的方法来帮助你和不同的实体交互,帮助你持久化性存储。此API包括了许多的方法,用于关闭,清楚实体管理器,冲洗实体使得更好存储,刷新驻入内存中的持久化数据,锁定实体,级联操作等等。