单击此处查看次Blog的英文版本
1.
依赖注入
(dependency injection)
A.依赖注入之@EJB:
@EJB注释,其常用属性如下:
beanName:指定EJB的类名(不带包名),
mappedName:指定Bean实例的JNDI名。
例如:
@EJB(beanName=
”
A
”
)
IA a;
A 就是
IA 的实现类,如果IA 的实现只有一个,那么这里可以不指定。
注意:学习
EJB3.0时,我用的jboss版本是jboss-4.0.5.GA。在做依赖注入的例子时,因为原来的jboss版本@EJB注释是在javax.annotation包中,正好我使用
eclipse选择Libraties时,选的就是原来版本的jboss-ejb3x.jar包,而部署到jboss服务器运行时,jboss就根据我的import javax.annotation.EJB语句去找@EJB,结果根本就找不到,我找了老半天才发现,原来我用的这个jboss版本中,已经把@EJB注释从javax.annotation包移到了javax.ejb中了。后来我把“jboss安装目录
/ server/default/deploy/ejb3.deployer”目录下的
jboss-ejb3x.jar包复制出来放到了我的Libraties下面,就没有问题了.以后一定要注意这个细节问题。
B.依赖注入之@Resource
@Resource注释,注入来自JNDI的任何资源。
例如:
@Resource (mappedName="java:/DefaultMySqlDS")
DataSource myDb;
"java:/DefaultMySqlDS"是数据源DefaultMySqlDS的全局JNDI名。如果JNDI对象在本地(java:comp/env)JNDI目录中,则只需给定它的映射名称即可。
2
.
实体Bean
一个实体Bean 由实体类和persistence.xml 文件组成。persistence.xml 文件在Jar 文件的META-INF 目录。persistence.xml 文件指定实体Bean 使用的数据源及EntityManager 对象的默认行为。persistence.xml文件的配置说明如下:
<persistence>
<persistence-unit name="xxx">
<jta-data-source>java:/ MySqlDS</jta-data-source>
<properties>
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
</properties>
</persistence-unit>
</persistence>
persistence-unit
节点可以有一个或多个,每个
persistence-unit
节点定义了持久化内容名称、使用的数据源名称及
Hibernate
属性。
name
属性用作设置持久化名称。
jta-data-source
节点用作指定实体
Bean
使用的数据源名称,指定数据源名称时
java:/
前缀不能缺少,数据源名称大小写敏感。
properties
节点用作指定
Hibernate
的各项属性,如果
hibernate.hbm2ddl.auto
的值设为
create-drop
,在实体
Bean
发布及卸载时将自动创建及删除相应数据库表。
注意:在编写EJB的实体bean时一定要注意,必须有一个无参的构造函数,当然如果你没写任何构造函数,java会自动给你一个默认的无参的构造函数的。但是当你自己写了有参数的构造函数时,千万别忘记了加上一个无参的构造函数,最好的习惯就是在建类的就把无参构造函数写上,这样就不会出错了。(以前在写Hibernate的bean时其实也有这么一条原则的,只是当时写的那些bean从没写过有参的构造函数,所以一时没有发现这条原则)。
A.写在类声明之前的有:
a.@Entity,以表明此Bean为EntityBean。
b.@Table(name=”TableName”),表示此实体Bean对应的数据库表名。
B.写在getXxx()方法声明之前的有:
a.@Column注释定义了映射到列的所有属性,如列名是否唯一,是否允许为空,是否允许更新等,他的属性介绍如下:
· name: 映射的列名。如:映射Person表的PersonName列,可以在name属性的getName 方法上面加入@Column(name = "PersonName"),如果不指定映射列名,容器将属性名称作为默认的映射列名。
·unique: 是否唯一
·nullable: 是否允许为空
·length: 对于字符型列,length属性指定列的最大字符长度
·insertable: 是否允许插入
·updatable: 是否允许更新
·columnDefinition: 定义建表时创建此列的DDL
·secondaryTable: 从表名。如果此列不建在主表上(默认建在主表),该属性定义该列所在从表的名字。
b.@Id 注释指定personid属性为表的主键,它可以有多种生成方式:
·TABLE:容器指定用底层的数据表确保唯一。
·SEQUENCE:使用数据库的SEQUENCE 列来保证唯一
·IDENTITY:使用数据库的INDENTIT列来保证唯一
·AUTO:由容器挑选一个合适的方式来保证唯一
·NONE:容器不负责主键的生成,由调用程序来完成。
c.@GeneratedValue注释定义标识字段的生成方式。
3
.
复合主键
(Composite Primary Key)
当需要使用多个属性变量(表中的多列)联合起来作为主键时,我们需要使用复合主键。复合主键要求我们编写一个复合主键类( Composite Primary Key Class )。复合主键类需要符合以下一些要求:
·复合主键类必须是public,必须具备一个没有参数的构造函数
·复合主键类的每个属性变量必须有getter/setter,如果没有,每个属性变量则必须是public或者protected
·复合主键类必须实现java.io.Serializable接口
·复合主键类必须重写equals()和hashcode()方法
·复合主键类中的主键属性变量的名字必须和对应的EntityBean中主键属性变量的名字相同
·一旦主键值设定后,不要修改主键属性变量的值
4
.
JBoss
的目录结构
安装JBoss会创建下列目录结构:
目录 描述
bin
启 动 和关闭JBoss的脚本
client
客户端与JBoss通信所需的Java 库(JARs)
docs
配置的样本文件(数据库配置等)
docs/dtd
在JBoss中使用的各种XML文件的DTD。
lib
一些JAR,JBoss启动时加载,且被所有JBoss配置共享。(不要把你的放在这里)
server
各种JBoss配置。每个配置必须放在不同的子目录。子目录的名字表示配置的名字。JBoss包含3 个默认的配置:minimial,default和all,在你安装时可以进行选择。
server/all
JBoss的完全配置,启动所有服务,包括集群和IIOP。(本教程就采用此配置)
server/default
JBoss 的默认配置。在没有在JBoss 命令航中指定配置名称时使用。(本教程没有安装此配置,如果不指定配置名称,启动将会出错)
server/all/conf JBoss的配置文件。
server/all/data
JBoss的数据库文件。比如,嵌入的数据库,或者JBossMQ。
server/all/deploy JBoss的热部署目录。放到这里的任何文件或目录会被JBoss自动部署。EJB、WAR、EAR,甚至服务。
server/all/lib
一些JAR,JBoss在启动特定配置时加载他们。(default和minimial配置也包含这个和下面两个目录。)
server/all/log
JBoss的日志文件
server/all/tmp
JBoss的临时文件。
5
.
持久化实体管理器EntityManager
EntityManager 是由EJB容器自动地管理和配置的,通常声明方式如下:
@PersistenceContext(unitName="
xxx
")
EntityManager em;
unitName
属性的值
xxx
对应
persistence.xml
文件中的<persistence-unit name="xxx">元素的name属性的值。
如果只有一个持久化内容配置,不需要明确指定。
其常用的API如下:
A.
增加数据:persist()方法
B.
删除数据:remove()方法
C.
修改数据:merge()方法
D.
查询数据:find()方法
E.执行EJB3 QL 操作:createQuery()方法
6
.
会话Bean
在类名前声明:
@Stateless 注释定义这是一个无状态会话Bean
@Remote 注释指明Bean的remote 接口
@Local 注释指明Bean的local接口
@Stateful 注释定义这是一个有状态会话Bean
@SuppressWarnings("serial") 注释屏蔽缺少serialVersionUID 定义的警告
注意:
Stateless Session Bean
与
Stateful Session Bean
的区别
这两种Session Bean都可以将系统逻辑放在方法之中执行,不同的是Stateful Session Bean可以记录呼叫者的状态,因此一个使用者会有自己的一个实例。Stateless Session Bean虽然也是逻辑组件,但是他却不负责记录使用者状态,也就是说当使用者呼叫 Stateless Session Bean的时候,EJB 容器并不会寻找特定的Stateless Session Bean的实体来执行这个method。换言之,很可能数个使用者在执行某个Stateless Session Bean的methods时,会是同一个Bean
的实例在执行。从内存方面来看,Stateful Session Bean与Stateless Session Bean比较,Stateful Session Bean会消耗J2EE Server 较多的内存,然而Stateful Session Bean的优势却在于他可以维持使用者的状态。
如何改变
Session Bean
的
JNDI
名称:
默认的JNDI命名规则前面已经介绍过,但有些情况下需要自定义名称。要自定义JNDI名称,可以使用@LocalBinding 和 @RemoteBinding 注释:
@LocalBinding注释指定Session Bean的Local接口的JNDI名称,
@RemoteBinding注释指定Session Bean的Remote接口的JNDI名称
例如,在类声明之前加上:
@Stateless
@Remote ({RemoteOperation.class})
@RemoteBinding (jndiBinding="xxx/RemoteOperation")
@Local ({LocalOperation.class})
@LocalBinding (jndiBinding="yyy/LocalOperation")
Session Bean
的生命周期:
通过使有下面所列的注释,EJB 3.0允许你将任何方法指定为回调方法。EJB 3.0中,bean可以有任意数量,任意名字的回调方法。
·@PostConstruct:当bean对象完成实例化后,使用了这个注释的方法会被立即调用。这个注释同时适用于有状态和无状态的会话bean。
·@PreDestroy:使用这个注释的方法会在容器从它的对象池中销毁一个无用的或者过期的bean 实例之前调用。这个注释同时适用于有状态和无状态的会话bean。
·@PrePassivate:当一个有状态的session bean实例空闲过长的时间,容器将会钝化(passivate)它,并把它的状态保存在缓存当中。使用这个注释的方法会在容器钝化bean实例之前调用。这个注释适用于有状态的会话bean。当钝化后,又经过一段时间该bean 仍然没有被操作,容器将会把它从存储介质中删除。以后,任何针对该bean方法的调用容器都会抛出例外。
·@PostActivate:当客户端再次使用已经被钝化的有状态session bean时,新的实例被创建,状态被恢复。使用此注释的session bean会在bean的激活完成时调用。这个注释只适用于有状态的会话bean。
·@Init:这个注释指定了有状态session bean初始化的方法。它区别于@PostConstruct注释在于:多个@Init注释方法可以同时存在于有状态session bean 中,但每个bean实例只会有一个@Init注释的方法会被调用。这取决于bean是如何创建的(细节请看EJB 3.0规范)。@PostConstruct在@Init之后被调用。
·@Remove: 对于有状态session bean。当应用通过存根对象调用使用了@Remove注释的方法时,容器就知道在该方法执行完毕后,要把bean实例从对象池中移走。
拦截器
(Interceptor):
拦截器可以监听程序的一个或所有方法。拦截器对方法调用流提供了细粒度控制。可以在无状态会话 bean、有状态会话 bean 和消息驱动 bean 上使用它们。拦截器可以是同一 bean 类中的方法或是一个外部类。
@Interceptors 注释指定一个或多个在外部类中定义的拦截器。例如:
@Interceptors({Xxx.class})
public class A implements IA{
…
…
}
在此
Xxx
拦截器对
A
类中的所有方法进行拦截。
在
Xxx
类中,使用
@AroundInvoke
注释指定了要用作拦截器的方法。用
@AroundInvoke
注释指定的方法必须遵守以下格式:
public
Object xxx
(
InvocationContext ctx)
throws
Exception
xxx
代表方法名可以任意。
除了可以在外部定义拦截器之外,还可以将Session Bean 中的一个或多个方法定义为拦截器。只需一个
@AroundInvoke 注释就指定了要用作拦截器的方法。其定义规则如上所述。
7
.
JBoss
数据源的配置
Jboss有一个默认的数据源DefaultDS,它使用Jboss内置的HSQLDB数据库。实际应用中你可能使用不同的数据库,如MySql、MsSqlServer、Oracle等。各种数据库的数据源配置模版你可以在[Jboss安装目录]/docs/examples/jca 目录中找到,默认名称为:数据库名+ -ds.xml 。
不管你使用那种数据库都需要把他的驱动类Jar 包放置在[Jboss 安装目录]/server/all/lib 目录下,放置后需要启动Jboss服务器。
8
.
关系
/
对象映射
A
.一对多:
@OneToMany
指明关联关系为一对多关系,下面是
@OneToMany
注释的属性:
1>targetEntity
Class
类型的属性。定义关系类的类型,默认是该成员属性对应的类类型,所以通常不需要提供定义。
2>mappedBy
String
类型的属性。
定义类之间的双向关系。如果类之间是单向关系,不需要提供定义,如果类和类之间形成双向关系,我们就需要使用这个属性进行定义,否则可能引起数据一致性的问题。
3>cascade
CascadeType[]
类型。
该属性定义类和类之间的级联关系。定义的级联关系将被容器视为对当前类对象及其关联类对象采取相同的操作,而且这种关系是递归调用的。举个例子:
Order
和
OrderItem
有级联关系,那么删除
Order
时将同时删除它所对应的
OrderItem
对象。而如果
OrderItem
还和其他的对象之间有级联关系,那么这样的操作会一直递归执行下去。
cascade
的值只能从
CascadeType.PERSIST
(级联新建)、
CascadeType.REMOVE
(级联删除)、
CascadeType.REFRESH
(级联刷新)、
CascadeType.MERGE
(级联更新)中选择一个或多个。还有一个选择是使用
CascadeType.ALL
,表示选择全部四项。
4>fatch
FetchType
类型的属性。
可选择项包括:
FetchType.EAGER
和
FetchType.LAZY
。前者表示关系类
(
本例是
OrderItem
类
)
在主类
(
本例是
Order
类
)
加载的时候同时加载,后者表示关系类在被访问时才加载。默认值是
FetchType. LAZY
。
@OrderBy(value = "id ASC")
注释指明加载
OrderItem
时按
id
的升序排序。
B
.多对一:
@ManyToOne注释,有四个属性:targetEntity、cascade、fetch 和optional,前三个属性的具体含义和@OneToMany注释的同名属性相同,但@ManyToOne 注释的fetch 属性默认值是FetchType.EAGER。
optional 属性是定义该关联类对是否必须存在,值为false 时,关联类双方都必须存在,如果关系被维护端不存在,查询的结果为null。值为true 时, 关系被维护端可以不存在,查询的结果仍然会返回关系维护端,在关系维护端中指向关系被维护端的属性为null。optional属性的默认值是true。举个例:某项订单(Order)中没有订
单项(OrderItem),如果optional 属性设置为false,获取该项订单(Order)时,得到的结果为null,如果optional属性设置为true,仍然可以获取该项订单,但订单中指向订单项的属性为null。实际上在解释Order 与OrderItem的关系成SQL时,optional属性指定了他们的联接关系
optional=false 联接关系为inner join,
optional=true 联接关系为left join。
@JoinColumn(name = "order_id")
注释指定
OrderItem
映射表的
order_id
列作为外键与
Order
映射表的主键列关联。
C.一对一:
@OneToOne 注释,有五个属性:targetEntity、cascade、fetch、optional 和mappedBy, 前四个属性的具体含义与@ManyToOne 注释的同名属性一一对应,
fetch 属性默认值是FetchType.EAGER。mappedBy属性的具体含义与@OneToMany 注释的同名属性相同。
如果optional = true 设置表明此属性可以为null,例如在身份证的处理时可以这样设置,因为未成年人就是没有身份证的。
D.多对多:
@ManyToMany 注释:表示此类是多对多关系的一边,mappedBy 属性定义了此类为双向关系的维护端,
注意:mappedBy 属性的值为此关系的另一端的属性名。例如,在
Student类中有如下方法:
@ManyToMany(mappedBy = "students")
public Set<Teacher> getTeachers() {
return teachers;
}
那么这里的“students”就是Teachers的一个属性,通常应该是这样的:
Set<Student> students;
另一端的
getStudents
方法如下所示:
@ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
@JoinTable(name = "Teacher_Student",
joinColumns = {@JoinColumn(name = "Teacher_ID", referencedColumnName = "teacherid")},
inverseJoinColumns = {@JoinColumn(name = "Student_ID", referencedColumnName ="studentid")})
public Set<Student> getStudents() {
return students;
}
@ManyToMany 注释表示Teacher 是多对多关系的一端。@JoinTable 描述了多对多关系的数据表关系。name 属性指定中间表名称,joinColumns 定义中间表与Teacher 表的外键关系。上面的代码中,中间表Teacher_Student的Teacher_ID 列是Teacher 表的主键列对应的外键列,inverseJoinColumns 属性定义了中间表与另外一端(Student)的外键关系。
9
.
查询
A. 位置参数查询
,格式为:“?+位置编号”,例如:
Query query = em.createQuery("select p from Person p where p.personid=?1");
query.setParameter(1,new Integer(1));
B
. 命令参数查询,格式为:“: +参数名”,例如:
Query query = em.createQuery("select p from Person p where p.personid=:Id");
query.setParameter("Id",new Integer(1));
C
.排序(order by)
"ASC"和"DESC"分别为升序和降序,如果不显式注明,EJB3 QL中默认为asc升序。
D.查询中使用构造器(Constructor)
EJB3 QL支持将查询的属性结果直接作为一个java class的构造器参数,并产生实体作为结果返回。例如:
Query query = em.createQuery("select new com.foshanshop.ejb3.bean.SimplePerson(p.name,p.sex) from Person p order by p.personid desc");
//
集合中的元素是
SimplePerson
对象
List result = query.getResultList();
E
.EJB3 QL也支持查询中的聚合函数。目前EJB3 QL支持的聚合函数包括:
1. AVG()
2. SUM()
3. COUNT()
4. MAX()
5. MIN()
用法与SQL里面的一样。
F.
group by,having,where等关键字的用法。和SQL语法一样。
G.关联(join)
在EJB3 QL中,仍然支持和SQL中类似的关联语法:
a.left out join/left join
b.inner join
c.left join/inner join fetch
left out join/left join等,都是允许符合条件的右边表达式中的Entities 为空。需要显式使用left join/left outer join的情况会比较少。
inner join要求右边的表达式必须返回Entities。
left/left out/inner join fetch提供了一种灵活的查询加载方式来提高查询的性能。在默认的查询中,Entity中的集合属性默认不会被关联,集合属性默认是缓加载( lazy-load )
H
.比较Entity:
在查询中使用参数查询时,参数类型除了String, 原始数据类型( int, double等)和它们的对象类型( Integer, Double等),也可以是Entity的实例。例如:
Query query = em.createQuery("select o from Order o where o.ower =?1 order by o.orderid");
Person person = new Person();
person.setPersonid(new Integer(1));
//
设置查询中的参数
query.setParameter(1,person);
I. not in,between,in,like,exists,is null,is empty等关键字的用法。
J
.字符串函数
EJB3 QL定义了内置函数方便使用。这些函数的使用方法和SQL中相应的函数方法类似。EJB3 QL中定义的字符串函数包括:
a. CONCAT
字符串拼接
b. SUBSTRING
字符串截取
c. TRIM
去掉空格
d. LOWER
转换成小写
e. UPPER
转换成大写
f. LENGTH
字符串长度
g. LOCATE
字符串定位
K.
计算函数
EJB3 QL
中定义的计算函数包括:
ABS
绝对值
SQRT
平方根
MOD
取余数
SIZE
取集合的数量
注意:size的用法,例如:
Query query = em.createQuery("select o.orderid, size(o.orderItems) from Order as o group by o.orderid");
10
.
事务管理服务
最有用的容器服务可能就是事务管理服务,当应用出现失败或异常时,它保证了数据库的完整性。你可以简单地将为一个POJO 方法申明它的事务属性。这样容器就可以在合适的上下文中运行这个方法。最常见的事务是定义在session bean的方法上,方法中所有的数据库操作只有在方法正常退出时才会提交,如果方法抛出未捕获的异常,事务管理将回滚所有的变更。
@TransactionAttribute 注释用作定义一个需要事务的方法。例如:
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void insertProduct(String name, Float price, boolean error) {
…
…
}
它可以有以下参数:
1.REQUIRED:方法在一个事务中执行,如果调用的方法已经在一个事务中,则使用该事务,否则将创建一个新的事务。
2.MANDATORY:方法必须在一个事务中执行,也就是说调用的方法必须已经有一个事务,否则新抛出一个错误(ERROR)。
3.REQUIRESNEW:方法将在一个新的事务中执行,如果调用的方法已经在一个事务中,则暂停旧的事务。
4.SUPPORTS:如果方法在一个事务中被调用,则使用该事务,否则不使用事务。
5.NOT_SUPPORTED:如果方法在一个事务中被调用,将抛出一个错误(ERROR)
如果没有指定参数,@TransactionAttribute 注释使用REQUIRED 作为默认参数。
11
.
Entity
的生命周期和状态
在
EJB3
中定义了四种
Entity
的状态
:
1.
新实体
(new)
。
Entity
由应用产生,和
EJB3 Persistence
运行环境没有联系,也没有唯一的标示符
(Identity)
。
2.
持久化实体
(managed)
。新实体和
EJB3 Persistence
运行环境产生关联(通过
persist(), merge()
等方法
)
,在
EJB3 Persistence
运行环境中存在和被管理,标志是在
EJB3 Persistence
运行环境中有一个唯一的标示
(Identity)
。
3.
分离的实体
(detached)
。
Entity
有唯一标示符,但它的标示符不被
EJB3 Persistence
运行环境管理
,
同样的该
Entity
也不被
EJB3 Persistence
运行环境管理。
4.
删除的实体
(removed)
。
Entity
被
remove()
方法删除,对应的纪录将会在当前事务提交的时候从数据库中删除。