EJB3.0入门经典

  1. 云栖社区>
  2. 博客>
  3. 正文

EJB3.0入门经典

benjaminwhx 2016-05-19 15:57:43 浏览3600
展开阅读全文
1、什么是EJB?
Enterprise JavaBeans(EJB)定义了3种企业Bean:会话Bean(Session Bean)、实体Bean(Entity Bean)和消息驱动Bean(Message Driven Bean)


2、支持EJB的容器有哪些?
Jboss(4.2x以上版本)、Glassfish、WebLogic(10以上版本)、Sun Application Server(9.0以上版本)、Oracle Application Server(10g以上版本)和我们国内的Apusic应用服务器。


3、什么是API?
API(application programming interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。


4、什么是JNDI?
JNDI(Java Naming and Directory Interface,Java命名和目录接口)是一组在Java应用中访问命名和目录服务的API。命名服务将名称和对象联系起来,使得我们可以用名称访问对象。目录服务是一种命名服务,在这种服务里,对象不但有名称,还有属性。
JNDI是一个应用程序设计的API,为开发人员提供了查找和访问各种命名和服务的通用、统一的接口,类似JDBC都是构建在抽象层上。


5、Stateless Session Beans(无状态Bean)开发
@Stateless注释指明这是一个无状态的bean
@Remote注释指定这个无状态Bean的romote接口。Bean类可以具有多个remote接口,每个接口之间用逗号分隔。
@Local和@Remote不同是的,@Remote声明接口是远程接口,@Local是本地接口。


6、实例化池
类似于JDBC,对象可以共享数据库访问。这样做可以减少所需的数据库连接数,降低资源的消耗,从而增加了系统的吞吐量。
因为EJB容器不为每个客户端分别维护响应的Bean实例,同时还重复使用这些Bean实例,所以实例池化只能应用于Stateless Session Bean和Message-Driven Bean。对于Stateful Session Bean(有状态Bean),因为EJB容器需要为每个客户端分别维护相应地Bean实例,所以不适用实例池化技术。当然,EJB容器也为Stateful Session Bean提供了激活(activation)这种管理大量Bean实例的机制。


7、Stateful Session Bean(有状态Bean)开发
每个用户都有自己的一个实例。


Stateless Session Bean不负责维护会话状态,Stateless Session Bean一旦实例化就被加进实例池中,各个用户都可以共用。即使用户已经消亡,Stateless Session Bean的生命期也不一定结束,它可能依然存在于实例池中,供其他用户使用。如果它有自己的属性(变量),那么这些变量就会受到所有使用它的用户影响。


@PostConstruct:在一个Bean实例化以后被容器调用
@PreDestroy:定义的方法可以用于处理任何清理工作,比如关闭打开着的资源链接。


8、如何改变Session Bean的JNDI名称
Jboss下使用@LocalBinding和@RemoteBinding。
weblogic10/Sun Application Server/Glassfish下可以使用@Stateless.mappedName()。


9、拦截器应用
在制定类或方法上加入注解@Interceptors({A.class,B.class})。@AroundInvoke注释指定了要用作拦截器的方法。拦截器的方法与被拦截的业务方法执行在同一个Java调用堆栈、同一个事务和安全上下文中。用@AroundInvoke注释指定的方法必须遵守以下格式:
public Object XXX(javax.interceptor.InvocationContext ctx)throws Exception
禁用拦截器:在类或方法上加入@ExcludeDefaultInterceptors注释




10、EJB调用机制
当调用远程或本地接口的方法时,接口使用的是存根(stub)对象。该存根实现了Session Bean的远程或本地接口。它负责将方法调用经过网络发送到远程EJB容器,或将请求路由到位于本地JVM内的EJB容器。存根是在部署期间使用JDK所带的java.lang.reflect.Proxy动态生成。


11、EJB的依赖注入
在传统的开发中,要使用某个类对象,可以通过new object的方式来使用它,但在EJB中不能这样做,因为EJB实例的创建及销毁是由容器管理的。要在Bean中要用其他EJB或资源,必须通过JNDI查找或注入注释。如在InjectionBean中使用HelloBean EJB,需要在InjectionBean中通过JNDI查找HelloBean的引用。
LocalHello helloworld = (LocalHello)ctx.lookup(“java:comp/env/ejb/HelloWorld”) ;
或者在方法上加入注解@EJB(beanName=“HelloBean”)LocalHello helloworld;


什么是ENC?怎样向ENC添加一个注册项?
应用服务器中得EJB容器有一个属于它自己的内部注册表,这个内部注册表被称为Enterprise Naming Context(ENC),EJB容器可以在其中维护某些指向外部环境的引用,我们可以把它想象成是EJB容器的私人地址簿。现在ENC登记资源,然后再从ENC中获取资源的引用。


12、如何引入外部资源
@Resource(mappedName=“java:/DefaultMySqlDS”)DataSource myDb;


13、JBoss数据源的配置
(1)把数据库驱动jar复制到”JBoss安装目录/server/配置名/lib“目录,我采用的配置名为default,因此需要把数据库驱动复制到“Jboss安装目录/server/default/lib”
(2)把以*-ds.xml结尾的文件复制到“JBoss安装目录/server/default/deploy”目录。


14、单表映射实体bean的注解
(1)@javax.persistence.Entity注释指明这是一个实体Bean
(2)@javax.persistence.Table注释指定了实体Bean所要映射的表。
(3)@javax.persistence.Id注释指定实体Bean的主键。每个实体Bean必须拥有一个主键,并且必须是唯一的。
(4)@javax.persistence.GeneratedValue注释指定主键值生成方式,该注释与@Id注释结合使用在主键属性上。
(5)@javax.persistence.Column注释指定实体Bean的成员属性映射到数据表中得哪一个字段和该字段的一些结构信息
@PersistenceContext(unitName=“foshanshop”)protected EntityManager em ;
上面通过@PersistenceContext注释动态注入E你体贴又Manager对象。该注释和@EJB注释一样,会在EJB的JNDI ENC中注册一个指向该资源的引用。


15、成员属性映射
(1)Transient:默认情况下,实体Bean的全部成员属性都会成为持久化字段。如果不希望一些成员属性成为持久化字段,可以使用@Transient注释标注。
(2)@Enumerated注释:如果需要将枚举类型成员属性映射到数据库,可以使用@Enumerated注释进行标注。
(3)@Lob注释:将一些文件或大文本数据存入数据库使用。
(4)@Basic注释:对于加了@Lob注释的大数据类型(有时存放的可能是10MB以上的数据),为了避免每次加载实体时占用大量内存,有必要对该属性进行延时加载。
(5)因为数据表对时间类型有更严格的划分,所以必须使用@Temporal注释指明java.util.Date或java.util.Calendar类型的成员属性映射到数据库date、time和timestamp中得那种类型。


******建议重载实体Bean的equals和hashCode方法和toString方法**********


16、如何处理映射的表明和数据库保留字同名?
1、尽量不要使用同名,如果使用了,在MySql中可以用’’字符把保留字括起来,如果数据库是SQLServer可以用[]括起来。


17、Entity获取find()和getReference()区别和联系。
如果知道实体的主键,可以用find()或getReference()方法来获得实体。方法的第一个参数为实体类。第二个参数为实体OID(OID为对象标识,即标注了@Id的属性)的值。
find()方法返回指定OID的实体。如果这个实体存在于当前的persistence context中,那么返回值是被缓存的对象;否则会创建一个新的实体,并从数据库中加载相关的持久状态。如果数据库不存在指定OID的记录,那么find方法返回null。
getReference()方法与find()相似,不同的是如果缓存中没有指定的实体,EntityManager会创建一个新的实体(实际上是实体的一个代理,Hibernate采用CGLIB工具来生成实体类的代理类)


18、合并merge()
merge()方法用于处理实体的同步,即数据库插入和更新操作。因为实体Bean可以脱离EntityManager管理并被序列化到远程客户端,实体对象可能在远程客户端的本地被更新,然后传回服务器。在服务器端,需要重新将这个游离的实体纳入到EntityManager的管理中,使它的数据能被同步回数据库。


19、createQuery和createNativeQuery操作
JPQL语句可以通过createQuery()方法创建一个Query对象,用法类似于hibernate的Hql语句
而createNativeQuery执行的是SQL语句


20、刷新实体refresh()
如果怀疑当前托管对象存放的并非数据库中最新的数据(如:在获取了实体对象之后,有人非法在数据库中修改了该记录),可以通过refresh()方法刷新实体对象,容器会把数据库中的新值重写进实体对象。如果实体存在关联,并且设置了CascadeType.REFRESH或CascadeType.ALL,刷新一个实体对象,那么与之关联的实体对象也会被刷新,刷新操作不会把实体对象的状态同步到数据库。


21、如何监测实体是否处于托管状态(持久态)
contains()方法,该方法使用一个实体作为参数,如果当前的持久化上下文包含指定的实体对象,返回结果为true,否则false。


22、分离所有正在托管的实体
clear()方法会把所有托管状态的实体从持久上下文中清除变为游离状态。


23、@ManyToMany
@ManyToMany(mappedBy=“students”)表示关系被维护的一端
@ManyToMany(cascade={CascadeType.PERSIST,CascadeType.REFRESH}, fetch=FetchType.LAZY)
@JoinTable(name=“Teacher_Student”, joinColumns={@JoinColumn(name=“Teacher_ID”)},inverseJoinColumns={@JoinColumn(name=“Student_ID”)})表示关系维护端


24、命名查询
可以在实体Bean上定义一个或多个查询语句。例如
@NamedQuery(name=“”,query=“”),如果使用多个则使用
@NamedQueries({@NamedQuery(name=“”,query=“”),@NamedQuery(name=“”,query=“”)})
当命名查询定义好了之后,就可以通过其名称执行查询。
Query query = em.createNamedQuery(“getPerson”) ;
query.setParameter(1,1) ;


25、查询中使用构造器
JPQL支持将查询的结果直接作为一个Java类的构造器参数,并产生对象作为结果返回。
例如:Query query = em.createQuery(“select new com.foshanshop.ejb3.bean.SimplePerson(p.name, p.sex) from Person p order by p.personid desc”) ;


26、批量更新、批量删除
query.executeUpdate();


27、结果集分页
List<Person> result = query.setMaxResults(max).setFirstResult(index).getResultList();


28、事务的选择
事务可以确保一个工作单元中得多项任务要么一起成功,要么一起撤销(回滚)。对于事务,一般有两种事务管理的选择:本地事务和全局事务。JDBC事务就属于本地事务,全局事务是通过JTA实现的。
EJB容器提供了两种使用全局事务的方式:一种是Bean管理事务(bean-managed transaction,BMT),它是编程式事务管理,为了更形象,作者在书中称它为手工管理事务;另一种是容器管理事务(container-managed transaction,CMT),它是声明式事务管理。采用CMT的好处在于;不需要在代码中操作事务,事务的开启/提交或回滚完全由容器负责处理。这样不但可以减少代码量,提高开发效率,而且使业务代码与事务代码之间实现了分离,对事务行为的修改不会影响业务逻辑,避免了像JDBC事务或BMT这样的入侵式的编程模型。没有了事务操作代码,代码也显得简单而优雅,CMT是我们使用全局事务的首选。


29、CMT如何声明事务?
声明事务属性使用@TransactionAttribute注释标注,value()指定事务属性,默认为REQUIRED,枚举类型TransactionAttributeType定义了所有的事务属性:
public enum TransactionAttributeType {
  MANDATORY,
  REQUIRED,
  REQUIRED_NEW,
  SUPPORTS,
  NOT_SUPPORTED,
  NEVER
}
(1)TransactionAttributeType.REQUIRED:业务方法需要在一个事务中运行。如果方法运行时,已经处在一个事务中,那么加入到该事务,否则为自己创建一个新的事务。
(2)TransactionAttributeType.NOT_SUPPORTED:声明方法不需要事务。如果方法没有关联到一个事务,容器不会为它开启事务。如果方法子啊一个事务中被调用,该事务会杯挂起,在方法调用结束后,原先的事务便会恢复执行。
(3)TransactionAttributeType.MANDATORY:该属性指定业务方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果业务方法在没有事务的环境下调用,容器就会抛出EJBTransactionRequiredException例外。
(4)TransactionAttributeType.REQUIRES_NEW:该属性表明不关是否存在事务,业务方法总会为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务会被挂起,新的事务会被创建,直到方法执行结束,新事务才算结束,原先的事务才会恢复执行。
(5)TransactionAttributeType.SUPPORTS:这一事务属性表明,如果业务方法在某个事务范围内被调用,则方法成为该事务的一部分。如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行。
(6)TransactionAttributeType.Never:指定业务方法绝对不能在事务范围内执行。如果业务方法在某个事务中执行,容器会抛出javax.ejb.EJBException


30、事务并发的问题
这些并发现象可以归纳为3中;
脏读:一个事务读取到另一个事务未提交的更新数据。
不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同。换句话说就是,后续读取可以读到另一事务已提交的更新数据。相反,“可重复读”在同一事务中多次读取数据时,能够保证所读数据一样,也就是说,后续读取不能读到另一事务已提交的更新数据。
幻读:一个事务读取到另一事务已提交的insert数据。


31、事务并发的处理:
数据库使用锁来实现事务之间的隔离。
下面是锁的基本原理:
当一个事务访问某个数据库资源时,如果执行的是select语句,必须为资源加上共享锁;如果执行的是insert、update或delete语句,必须为资源加上排他锁,这些锁用于锁定被操作的资源。
下面的例子为id等于12的记录加上了排他锁。
select * from auths where id = 12 for update
下面为id等于12的记录加上了共享锁,该所知道事务结束才会解除。
SQLServer语法:select * from auths where id = 12 holdlock
MySQL语法:select * from auths where id = 12 lock in share mode
数据库一般有4种类型的锁,即共享锁、更新锁、排他锁和快照。是否把快照归在锁一类,很矛盾,因为从功能上来说,它起到了锁的作用,但从锁的意义上来说,它并没有对任何资源加锁。


32、四种隔离级别
Read Uncommited:读未提交数据
Read Commited:读已提交数据
Repeatable Read:可重复读
Serializable:串行化


33、因并发事务引起的更新丢失问题及处理
(1)、使用Serializable隔离级别避免更新丢失
如果希望在各种数据库下都能够让锁机制为select加共享锁,那么必须声明事务的隔离级别为serializable。
这种方案的优点是:实现比较简单,只需设置一下事务的隔离级别。
这种方案的缺点是:由于使用了SERIALIZABLE隔离级别,会导致事务占用资源的时间过长,严重降低了系统的并发性能;此外还会引起频繁的死锁现象。
许多数据库能够自动定期搜索和处理思索问题。当检测到锁定请求环时,系统会提交死锁优先级高的事务,同时撤销死锁优先级低得事务。
(2)、修改代码逻辑来避免更新丢失
造成更新丢失的源头是多个并发事务读取同一资源,然后基于最初读到的值更新该资源。我们可以不进行读取操作,直接通过update语句进行累加或累减操作。
这种方案的缺点是:对代码有所改动,只适合在一些简单地场合使用。如果应用需要先读取数据,然后使用该数据进行复杂的运算,最后才把运算的结果更新到数据库,那么这种方案就无法实现。
(3)、使用悲观锁避免更新丢失
悲观锁不是一种锁,更准确地说是一种悲观的加锁方案。每个人看待事物的态度都不一样,有些人比较悲观,有些人比较乐观。就拿对数据加锁的方案来说吧,有些人总认为别人会修改他读取的数据,所以在读取数据时就立刻加上排他锁,防止别人修改其读取的数据,这种行为就是悲观锁。
悲观锁是通过手工为select加排他锁,对哪些select语句加锁由用户决定。手工为select语句加排他锁的SQL语法如下:
select * from users where id = 1 for update
但是有些程序员王乐对其他事务的select加锁,导致更新丢失仍然出现。
EJB中唯一和锁有关的EntityManager.lock()方法仍然无法得到第一个事务提交后的数据。
我们只能暂且通过SQL语句来实现。
如果应用服务器使用的是TopLink,也可以通过设置Query.setHint(“toplink.pessimistic-lock”,”Lock”)来实现
这种方案的优点是:相对Serializable方案而言,系统的并发性能影响不算大。
这种方案的缺点是:尽管只对部分资源长时间占用,但仍然会降低系统的并发性能。另外需要手工加繁琐,实现起来比较繁琐,而且容器出错,For update语句在一些数据库中不支持。
(4)、使用乐观锁避免更新丢失
和悲观锁一样,乐观锁也不是一种锁。它认为别人不会修改其读取的数据,所以在读取数据时并没有为记录加排他锁。难道它就任由其他并发事务覆盖其所作的更新?不是的,尽管它处事乐观,但也为自己留了一手。
那么乐观锁是如何防止其他并发事务覆盖前面事务所作的更新呢?它要求用户在数据库中增加一个版本字段(其实就是一个数字类型的字段,如long)每次执行update语句时,这个字段都会进行累加,当其他事务以旧版本的值更新记录时,由于前面事务的update语句已经累加了版本字段,所以其他事务会找不到响应记录,这样就避免了更新丢失。
乐观锁唯一需要用户做的就是定义版本成员属性,在实体Bean中使用@javax.persistence.Version注释指定。
在保存User实体时,无须为@Version成员属性赋值,entity manager会自动为它芙初始化值0。每当User实体更新时,版本字段就会累加,这个工作是由entity manager自动完成的。事务开始提交时,entity manager首先会在内存中检查User实例的@Version成员属性是否与数据库中得版本字段相等,如果相等,则@Version成员属性会加1,如果不等,entity manager则会抛出异常,整个事务也会随之回滚。


34、消息服务(Java Message Service)
Java消息服务(JMS)是用于访问企业消息系统的开发商中立的API。企业消息系统可以协助应用软件通过网络进行消息交互。JMS在其中扮演的角色与JDBC很相似,正如JDBC提供了一套用于访问各种不同关系数据库的公共API,JMS也提供了独立于特定厂商的企业消息系统访问方式。
消息的传递模型:
JMS支持两种消息传递模型:点对点(point-to-point,简称PTP)和发布/订阅(publish/subscribe,简称pub/sub)。这两种消息传递模型非常相似,但有以下区别。
PTP消息传递模型规定了一条消息只能传递给一个接收方。
pub/sub消息传递模型允许一条消息传递给多个接收方。


35、点对点模型和发布订阅模型的区别
点对点模型:消息不是自动推送给客户端的,而是要由客户端从队列中请求获得的。
pub/sub模型:允许多个主题订阅者接收同一条消息。JMS一直保留消息,直至所有肢体订阅者都收到消息为止。pub/sub消息传递模型基本上一个推模型。在该模型中,消息会自动广播,消费者无须通过主动请求或轮询主题的方式来获得新的消息。

网友评论

登录后评论
0/500
评论