声明以下目录结构摘自红工场:http://www.redsoftfactory.com/chinese/ejb3/docs/zh/ref/index.html
看目录回顾所学的知识:
EJB3 Persistence开发手册
1. EJB3 Persistence API介绍
1. 为什么要引入EJB3
2. EJB 3和EJB 2.1的区别
3. EJB 3中的元数据批注:Annotation
2. 第一个Entity Bean:HelloWorld
3. 解说Entity
1. 定义对Entity中属性的访问
2. 主键和实体标识(Primary Key and Entity Identity)
4. 操作Entity
1. 配置和获得EntityManager
2. Entity的生命周期和状态
3. 持久化Entity
4. 获取Entity
5. 更新Entity
6. 删除Entity
7. 分离/附合(Detach/Merge)
5. EJB3 QL查询
1. Query接口
2. 简单查询
3. 使用参数查询
4. 排序(order by)
5. 查询部分属性
6. 查询中使用构造器(Constructor)
7. 聚合查询(Aggregation)
8. 关联(join)
1 left join/left out join
2 inner join
3 left/inner join fetch
9. 比较Entity
10. 批量更新(Batch Update)
11. 批量删除(Batch Remove)
12. 使用操作符NOT
13. 使用操作符BETWEEN
14. 使用操作符IN
15. 使用操作符LIKE
16. 使用操作符IS NULL
17. 使用操作符IS EMPTY
18. 使用操作符EXISTS
19. 使用操作符ALL/SOME/ANY
20. 字符串函数
21. 计算函数
22. 子查询
6. 原生SQL查询(Native SQL)
7. 关系/对象映射
1. 一对一映射
2. 一对多映射
3. 多对多映射
4. 继承
8. 关系对象映射元数据
1. Table
2. SecondaryTable
3. SecondaryTables
4. UniqueConstraint
5. Column
6. JoinColumn
7. JoinColumns
8. Id
9. IdClass
10. MapKey
11. OrderBy
12. PrimaryKeyJoinColumn
13. PrimaryKeyJoinColumns
14. Transient
15. Version
16. Lob
17. JoinTable
18. TableGenerator
19. SequenceGenerator
20. DiscriminatorColumn
21. DiscriminatorColumn
知识片断:
采用EJB架构的目标在于:
数据对象生命周期的自动管理
分布式能力
集成/声明式的安全/事务管理
EJB 3.0 旨在解决以往EJB 2.0 模型的复杂性和提高灵活性,具体体现在:
消除了不必要的接口Remote, Home, EJB以及回调方法实现
实体Bean采用了POJO模型,一个简单的java bean就可以是一个Entity Bean。无需依赖容器运行和测试
灵活丰富的EJB3查询语言
SQL支持
使用元数据批注代替部署描述符,减少复杂配置和提高可维护性
将常规 Java 类用作 EJB 并将常规业务接口用于 EJB
采用元数据可以很好的解决这些问题:
描述符大量减少。
编译期校验。错误的批注在编译期间就会报错。
元数据被编译成java bytecode,消耗小的多内存,读取也非常迅速,往往比xml配置解析快几个数据量级,利于测试和维护
// 获得默认当前的EntityManagerFactory
final EntityManagerFactory emf = Persistence.createEntityManagerFactory();//搜索当前jar包的persistence.xml配置文件,来初始化EntityManagerFactory
final EntityManager entityManager = emf.createEntityManager();
在EJB3中定义了四种Entity的状态:
新实体(new)。Entity由应用产生,和EJB3 Persistence运行环境没有联系,也没有唯一的标示符(Identity)。
持久化实体(managed)。新实体和EJB3 Persistence运行环境产生关联(通过persist(), merge()等方法),在EJB3 Persistence运行环境中存在和被管理,标志是在EJB3 Persistence运行环境中有一个唯一的标示(Identity)。
分离的实体(detached)。Entity有唯一标示符,但它的标示符不被EJB3 Persistence运行环境管理, 同样的该Entity也不被EJB3 Persistence运行环境管理。
删除的实体(removed)。Entity被remove()方法删除,对应的纪录将会在当前事务提交的时候从数据库中删除。
如果知道Entity的唯一标示符,我们可以用find()方法来获得Entity。
Father father = manager.find( Father.class, new Integer( 1 ) );
// 由于JDK1.5支持自动转型,也可以如下使用
Father father = manager.find( Father.class, 1 );
对Entity的更新必须在事物内完成。和persist中一样,关系元数据的cascade属性对是否集联删除有影响。
transaction.begin();
Father father = manager.find( Father.class, 1 );
// 更新原始数据类型
father.setName( "newName" );
// 更新对象引用
Son newSon = new Son();
father.setSon( newSon );
// 提交事务,刚才的更新同步到数据库
transaction.commit();
对Entity的删除必须在事物内完成。
transaction.begin();
Father father = manager.find( Father.class, 1 );
// 如果father/son的@OneToOne的cascade=CascadeType.ALL,在删除father时候,也会把son删除。
// 把cascade属性设为cascade=CascadeType.REMOVE有同样的效果。
manager.remove( father );
// 提交事务,刚才的更新同步到数据库
transaction.commit();
在三层或者分布式应用中,我们很多时候需要Entity能脱离EntityManager,避免长时间保持EntityManager打开占用资源和可以在不同的JVM之间传递Entity。
EntityManager entityManager = emf.createEntityManager();
// 这时Father还是被EntityManager管理的
Father father = manager.find( Father.class, 1 );
// 当entityManger关闭的时候,当前被entityManager管理的Entity都会自动的脱离EntityManager,状态转变为detached
entityManager.close();
// 脱离EntityManager后,我们仍然可以修改Father的属性
father.setName( "newName" );
// 在稍后的,我们可以将father重新附和到一个新的或者原来的EntityManager中
EntityManager newEntityManager = emf.createEntityManager();
// 附合( merge )需要在事务中进行
newEntityManager.getTransaction().begin();
newEntityManager.merge( father );
// commit后father中的被修改的内容会同步到数据库。
newEntityManager.getTransaction().commit();
需要注意的是在脱离EJB3 Persistence Runtime的管理后,如果对象中有定义为lazy-load的属性将无法访问。
EJB3 QL查询
在程序中使用EJB3 QL可以使用大写(SELECT)或者小写(select),但不要大小写(比如:Select)混合使用。
有时候查询会返回海量的数据。Liberator EJB3运行环境采用了自适应的弱引用POJO管理机制,可以处理海量的数据。在我们的测试中和客户的环境可以处千万级别的数据量。但在处理大数据量的时候,注意关闭对集合结果的缓存。
// 假设返回的结果数量巨大
final Query query = entityManager.createQuery( "select o from Order o");
// 关闭对查询结果的缓存
query.setHint( Constants.QUERY_RESULT_CACHE, "false");
final List result = query.getResultList();
final Iterator iterator = result.iterator();
// 这里我们可以处理海量的数据
while( iterator.hasNext() ){
// 处理Order
}
使用参数查询:
EJB3 QL支持两种方式的参数定义方式: 命名参数和位置参数
命名参数:
final Query query = entityManager.createQuery( "select o from Order o where o.id = :myId");
// 设置查询中的参数
query.setParameter( "myId", 2 );
// 可以使用多个参数
final Query query = entityManager.createQuery( "select o from Order o where o.id = :myId and o.customer = :customerName" );
// 设置查询中的参数
query.setParameter( "myId", 2 );
query.setParameter( "customerName", "foo" );
注意不允许在同一个查询中使用两个相同名字的命名参数。
位置参数:
final Query query = entityManager.createQuery( "select o from Order o where o.id = ?1");
// 设置查询中的参数
query.setParameter( 1, 2 );// 1表示第一个参数,2是参数的值
//或者
final Query query = entityManager.createQuery( "select o from Order o where o.id = ?1").setParameter( 1, 2 );
// 可以使用多个参数
final Query query = entityManager.createQuery( "select o from Order o where o.id = ?1 and o.customer = ?2" );
// 设置查询中的参数
query.setParameter( 1, 2 );
query.setParameter( 2, "foo" );
如果在未来需要在不同的EJB3 运行环境中运行,请使用位置参数,保证应用是可移植的。
查询部分属性:
// 直接查询我们感兴趣的属性(列)
final Query query = entityManager.createQuery( "select o.id, o.customerName, o.address.streetNumber from Order o order by o.id");
// 集合中的不再是Order,而是一个Object[]对象数组
final List result = query.getResultList();
// 第一个行
Object[] row = result.get( 0 );
// 数组中的第一个值是id
int id = Integer.parseInt( row[0].toString() );
String customerName = row[1].toString();
String streetNumber = Integer.parseInt( row[2].toString() );
关联(join):
在EJB3 QL中,大部分的情况下,使用对象属性都隐含了关联(join)。例如在以下查询中:
final Query query = entityManager.createQuery( "select o from Order o
where o.address.streetNumber=2000 order by o.id");
当这个句EJB3 QL编译成以下的SQL时就会自动包含了关联,EJB3 QL编译成SQL时关联默认取左关联(left join)。
select o.id, o.vender, o.partNumber, o.amount, addressTable.id, addressTable.streetNumber
from orderTable as o left join addressTable where addressTable.streetNumber = 2000
但在一些情况下,我们仍然需要对关联做精确的控制。因此EJB3 QL仍然支持和SQL中类似的关联语法:
left out join/left join
inner join
left join/inner join fetch
原生SQL查询:
EJB3 QL对原生SQL做了非常好的支持。采用原生SQL做查询结果不但可以是象SQL中的返回列值,也可以是Entity类,甚至可以是两者的混合。
EJB3 EntityManager接口定义了多个原生SQL的产生方法。
public Query createNativeQuery(String sqlString);
public Query createNativeQuery(String sqlString, Class resultClass);
public Query createNativeQuery(String sqlString, String resultSetMapping);
一个简单的SQL查询:
Query query = entityManager.createNativeQuery("select o.id, o.vender as vender from OrderTable");
// 集合中的每个元素是一个数组,代表一行返回列值,每一个数具有2个元素,分别是id列,vender列
List result = query.getResultList();
Object[] row1 = (Object[])result.get(0);
在原生SQL中使用参数和EJB3 QL中使用参数的方法一致,但只支持位置参数。
Query query = entityManager.createNativeQuery("select o.id, o.vender as vender from OrderTable where o.id = ?1");
query.setParameter( 1, 200 );
// 集合中的每个元素是一个数组,代表一行返回列值,每一个数具有2个元素,分别是id列,vender列
List result = query.getResultList();
Object[] row1 = (Object[])result.get(0);
除了象上面例子中直接返回查询结果的列值,我们可以让EJB3 Persistence运行环境将列值直接填充入一个Entity的实例,并将实例作为结果返回。
// 我们这里将结果定义为Entity类Order的实例
Query query = entityManager.createNativeQuery("select o.id, o.vender, o.partId from OrderTable o order by o.amount desc", Order.class);
// 结果集合中是Order类的实例,但Order实例只有id, vender, partId三列对应的属性变量有值,其他的变量属性为空。
List result = query.getResultList();
Order order1 = (Order)result.get( 0 );
关系对象映射元数据
元数据描述:
@OneToOne(optional = true,cascade = CascadeType.ALL, mappedBy = "country")
optional声明关系是否是必须存在的,即是否允许其中一端为null。
cascade声明级联操作。
@JoinColumn(name = "COUNTRY_ID", referencedColumnName = "id")
name声明外键列的名字,referencedColumnName声明外键指向列的列名。
元数据描述:
@OneToMany(targetEntity = Child.class, cascade = CascadeType.ALL, mappedBy = "father")
targetEntity = Child.class表明关系另一端的实体类型
cascade声明级联操作。
mappedBy声明关系维护端的字段(field)名。
@ManyToOne
@JoinColumn(name = "FATHER_ID", referencedColumnName = "id")
name声明外键列的名字,referencedColumnName声明外键指向列的列名。
元数据描述:
@ManyToMany(targetEntity = Student.class, cascade = CascadeType.PERSIST)
targetEntity = Student.class表明关系另一端的实体类型。cascade声明级联操作。
@JoinTable(table = @Table(name = "M2M_TEACHER_STUDENT"),
joinColumns = @JoinColumn(name = "TEACHER_ID", referencedColumnName = "ID"),
inverseJoinColumns = @JoinColumn(name = "STUDENT_ID", referencedColumnName = "ID"))
JoinTable配置中间表信息,它由3个部分组成:
1) table = @Table(name = "M2M_TEACHER_STUDENT") ,声明中间表的名字
2) joinColumns ,定义中间表与关系维护端的外键关系。
3) inverseJoinColumns,定义中间表与inverse端的外键关系.
基类中元数据描述:
@Inheritance(strategy=InheritanceType.SINGLE_TABLE,
discriminatorType=DiscriminatorType.STRING,discriminatorValue="employee")
strategy=InheritanceType.SINGLE_TABLE表示继承映射采用第一种映射策略。
discriminatorType=DiscriminatorType.STRING表示继承层次中类型识别列类型为String.
discriminatorValue="employee" 表示此类对应的类型识别码为employee.