您的当前位置:首页正文

EJB资料

2021-01-26 来源:钮旅网
服务器端代码:

HelloWorld.java:

1import javax.ejb.Remote; 2 3@Remote 4public interface HelloWorld { 5 public String sayHello(String s); 6} 7

HelloWorldBean.java

1import javax.ejb.Stateless; 2 3import org.jboss.annotation.ejb.Clustered; 4 5import cn.HelloWorld; 6 7@Stateless 8@Clustered 910 11 public String sayHello(String s) { public class HelloWorldBean implements HelloWorld { 12 System.out.println(s); 13 return s; 14 } 15 16}

上面第8行的@Clustered是必须的,因为我们需要使用Stateless Bean的集群功能。

分别在IP地址为:192.168.1.88和192.168.1.99机器上面安装JBOSS4.0.5GA,JBOSS4.0.5可以去下载文件名为jboss-installer-1.2.0.GA.jar的安装,安装的时候选择ejb3-clustered,再下面有一步时选择Advance,再在Name输入框输入all,其它默认就可以了。

两台机器上面都安装好之后,分别在两台机器上面启动jboss,启动的时候需要加参数:如IP为192.168.1.88的机器启动JBOSS,则为:run -c all -b 192.168.1.88

当两台机器的JBOSS都正常启动后,将上面的Stateless Bean打包成jar包,发布到其中一台%JBOSS_HOME%\\server\\all\\farm目录下面,这样集群中的其它结点将自动发布这个jar包。

当发布完成之后,我们编写客户端:

Client.java

1import java.util.Properties; 2 3import javax.naming.InitialContext; 4import javax.naming.NamingException; 5 6 7 8 public static void main(String[] args) { public class Client { 9 Properties prop = new Properties(); 10 prop.setProperty(\"java.naming.factory.initial\ 11 \"org.jnp.interfaces.NamingContextFactory\"); 12 prop.setProperty(\"java.naming.factory.url.pkgs\ 13 \"org.jboss.naming:org.jnp.interfaces\"); 14 prop.setProperty(\"java.naming.provider.url\ 15 \"192.168.1.88:1099,192.168.1.99:1099\"); 16 17 try { 18 InitialContext ict = new InitialContext(prop); 19 HelloWorld helloWorld = (HelloWorld) ict 20 .lookup(\"HelloWorldBean/remote\"); 21 for (int i = 0; i < 10; i++) { 22 helloWorld.sayHello(\"HelloWorld\"); 23 } 24 } catch (NamingException e) { 25 e.printStackTrace(); 26 } 27 } 28} 29

上面这个客户端配置了JBOSS服务器IP地址及端口,调用远程接口的HelloWorldBean,并连

续调用10次sayHello方法。在JBOSS控制台上面可以看到打印出HelloWorld。

程序运行结果发现:在192.168.1.88 的JBOSS控制台上面打印出4个HelloWorld,另一个JBOSS控制台上面则打印出6个HelloWorld。加起来正好10个HelloWorld,可以发现两台JBOSS已经实现了负载均衡。

集群中的任何一个结点挂掉之后,程序都可以正常运行,

分布式事务分类:

一个App对应若干DB

一个App对应1个DB,但两个app之间有事务

我们将第一幅图描述的情况姑且叫做1app2db,第二副图的情况叫做2app2db(这些仅仅是一个名称而已,不要误解)

我们下来分别描述一下每一种情况. 1app2db:

获取datasource(容其中定义的),由于对应多个db,故存在多个sessionFactory,多个datasource,etc.下面仅对一个进行描述,其余的配置是类似的. class=\"org.springframework.orm.hibernate3.LocalSessionFactoryBean\">

.......下略

定义mysql数据源的sessionFactory,oracle数据源的类似

定义dao和service,oracle的类似

这样myService就可以使用了,而关键点是调用myService的EJB的方法必须要有事务定义.如:

public double businessAdd(String value) throws EJBException { service.modifyUserName(\"4\service2.modifyUserName(\"4\return 2.0; }

businessAdd方法必须要被定义在ejb-jar.xml中,否则会报错,如下

java.sql.SQLException: SQL operations are not allowed with no global transaction by default for XA drivers. If the XA driver suppo

rts performing SQL operations with no global transaction, explicitly allow it by setting \"SupportsLocalTransaction\" JDBC connectio

n pool property to true. In this case, also remember to complete the local transaction before using the connection again for globa

l transaction, else a XAER_OUTSIDE XAException may result. To complete a local transaction, you can either set auto commit to true

or call Connection.commit() or Connection.rollback(). 2app2db:

这种情况下呢,每一个app负责一个或若干个DB,而两个app之间有ejb调用.

关于dao,service的配置与上面相同,而ejb之间的调用,方法必须在ejb-jar.xml中定义事务,这样容器就能保证事务正确的传播和操作,比如一个ejb调用如下: EJB_A.method(){ pojo.method(); EJB_B.method(); }

EJB_A.method()方法只要抛出异常,则事务会传播到EJB_B中,同样引起EJB_B回滚. 在做这个试验的时候,我采用的是一个weblogic跑两个domain的方法,遇到了一个问题,两个domain之间的ejb调用不能直接进行,必须要在域之间建立\"信任\"关系,具体做法如下: mydomain->security->advanced->将\"Enable Generated Credential\"的勾去掉,同时在下面的框中输入密码,然后在另一个域中做同样的操作,注意两个域的密码要相同.另外域的信任关系是可以传播的,比如A信任b,c,那么b,c互相信任.

在使用容器管理的事务的时候,hibernate的 lazy问题必须解决,解决方法:采用拦截器,在方法开始之前打开session,之后关闭,详见另一片blog\"优雅解决hibernate lazy问题\".

______________________________________________________________________________________

其中配置的部分代码为:

class=\"org.springframework.jndi.JndiObjectFactoryBean\">

class=\"org.springframework.orm.hibernate3.LocalSessionFactoryBean\">

classpath:/com/company/jncz/entity/User.hbm.xml

org.hibernate.dialect.MySQLDialect

true

true 50 2 true 1

org.hibernate.hql.classic.ClassicQueryTranslatorFactory

class=\"org.springframework.jndi.JndiTemplate\" singleton=\"true\" lazy-init=\"default\" autowire=\"default\" dependency-check=\"default\">

weblogic.jndi.WLInitialContextFactory

t3://127.0.0.1:7001

class=\"org.springframework.transaction.jta.WebLogicJtaTransactionManager\">

weblogic/transaction/UserTransaction

javax.transaction.TransactionManager

class=\"org.springframework.transaction.interceptor.TransactionProxyFactoryBean\">

PROPAGATION_REQUIRED,-TestException

PROPAGATION_REQUIRED

EJB3.0实现跨数据库进行存取

环境:MySQL5.1/JBoss5.1

1.加入跨数据库的数据源,由于JBoss没有提供mysql的配置文件,所以要手工加入: mysql-xa-ds.xml:(注意:不是mysql-ds.xml)

MySqlXADS1

com.mysql.jdbc.jdbc2.optional.MysqlXADataSource name=\"Url\">jdbc:mysql://localhost/ejb?useUnicode=true&characterEncoding=UTF-8

root

root root

root

5 20

5000 15

org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter

mySQL

MySqlXADS2

com.mysql.jdbc.jdbc2.optional.MysqlXADataSource name=\"Url\">jdbc:mysql://localhost/ejb2?useUnicode=true&characterEncoding=UTF-8

root

root root root

5 20

5000 15

org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter

mySQL

2.加入数据库驱动jar到JBOSS_HOME/server/default/lib下 mysql-connector-java-5.0.2.jar

注意:不要加入过低的版本,如mysql-connector-java-2.0.14.jar,否则JBoss找不到相关类也不能进行加载

3.persistence.xml

xsi:schemaLocation=\"http://java.sun.com/xml/ns/persistence

http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd\" version=\"1.0\">

java:/MySqlXADS1 com.pojo.User

true

java:/MySqlXADS2 com.pojo.Person

true

4.Bean的实现类:

@Stateless

@Remote(UserManager.class)

public class UserManagerImpl implements UserManager {

@PersistenceContext(unitName=\"JTA_DatabasesPU\") private EntityManager em;

@PersistenceContext(unitName=\"JTA_DatabasesPU2\") private EntityManager em2;

public void addDatas() {

User user = new User(); user.setId(1);

user.setName(\"Chuyang\");

em.persist(user);

Person person = new Person(); person.setId(1);

person.setName(\"WuMeng\");

em2.persist(person);

//测试跨数据库回滚

//throw new RuntimeException(\"测试跨数据库回滚 异常\"); } }

5.然后在建立一个测试类进行测试就可以了。

EJB3.0消息驱动Bean 一、工程环境

Myeclipse6.5.1 GA, jboss-5.1.0.GA

二、消息驱动Bean(MyMDBBean.java)

import javax.ejb.ActivationConfigProperty; import javax.ejb.MessageDriven; import javax.jms.JMSException; import javax.jms.Message;

import javax.jms.MessageListener; import javax.jms.TextMessage;

@MessageDriven(

activationConfig = {

@ActivationConfigProperty(propertyName=\"destinationType\),

@ActivationConfigProperty(propertyName=\"destination\ } )

public class MyMDBBean implements MessageListener {

public void onMessage(Message msg) {

TextMessage textMessage = (TextMessage) msg; try {

System.out.println(\"消息驱动Bean被调用了,【\"+textMessage.getText()+\"】\"); } catch (JMSException e) {

e.printStackTrace(); } } }

将此消息驱动Bean部署到JBOSS上老报错:

Attempting to reconnect

org.jboss.resource.adapter.jms.inflow.JmsActivationSpec@90265b(ra=org.jboss.resource.adapter.jms.JmsResourceAdapter@1280e84 destination=queue/myqueue

destinationType=javax.jms.Queue tx=true durable=false reconnect=10

provider=java:/DefaultJMSProvider user=null maxMessages=1 minSession=1 maxSession=15 keepAlive=60000 useDLQ=true

DLQHandler=org.jboss.resource.adapter.jms.inflow.dlq.GenericDLQHandler DLQJndiName=queue/DLQ DLQUser=null DLQMaxResent=5) Unable to reconnect

org.jboss.resource.adapter.jms.inflow.JmsActivationSpec@90265b(ra=org.jboss.resource.adapter.jms.JmsResourceAdapter@1280e84 destination=queue/myqueue

destinationType=javax.jms.Queue tx=true durable=false reconnect=10

provider=java:/DefaultJMSProvider user=null maxMessages=1 minSession=1 maxSession=15 keepAlive=60000 useDLQ=true

DLQHandler=org.jboss.resource.adapter.jms.inflow.dlq.GenericDLQHandler DLQJndiName=queue/DLQ DLQUser=null DLQMaxResent=5) javax.naming.NameNotFoundException: myqueue not bound 后来到网上google了一下,用以下方法可以解决:

在JBoss5.x根目录\\server\\default\\deploy下的mail-service.xml文件中加入如下:

name=\"jboss.org.destination:server=Queue,name=myqueue\" > queue/myqueue jboss.mq:service=DestinationManager 或者

消息驱动Bean需要使用@MessageDriven进行注释。要注意的是destination属性的值是queue/MDBQueue。JBoss不会自已建立一个Queue对象,因此,需要手工来配置Queue对象。读者可以\\server\\default\\deploy目录中建立一个xxx-service.xml文件,其中xxx可以任意取值,但必须跟“-service”后缀,例如,abc-service.xml。该文件可以放在deploy或其子目录(可以是多层子目录)中。该文件的内容如下: 然后重新启动JBOSS即可解决.

三、客户端如下

import java.util.Properties;

import javax.jms.Queue;

import javax.jms.QueueConnection;

import javax.jms.QueueConnectionFactory; import javax.jms.QueueSender; import javax.jms.QueueSession; import javax.jms.TextMessage; import javax.naming.InitialContext;

public class MyMdbClient {

public static void main(String [] args) throws Exception { Properties props = new Properties();

props.setProperty(\"java.naming.factory.initial\\"org.jnp.interfaces.NamingContextFactory\");

props.setProperty(\"java.naming.provider.url\

props.setProperty(\"java.naming.factory.url.pkgs\ InitialContext init = new InitialContext(props);

//创建ConnectionFactory

QueueConnectionFactory factory = (QueueConnectionFactory) init.lookup(\"ConnectionFactory\");

//创建Connection

QueueConnection connection = factory.createQueueConnection();

//创建session,第一个参数:true标识手动提交,false表示事物自动提交; //第二个参数表示一旦消息被正确送达,将自动发回响应 QueueSession session = connection.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE);

//查找queue(destination)

Queue queue = (Queue) init.lookup(\"queue/myqueue\");

//创建TextMessage

TextMessage msg = session.createTextMessage(\"消息驱动Bean跑起来了\");

QueueSender sender = session.createSender(queue);

//利用QueueSender发送TextMessage sender.send(msg);

session.close();

System.out.println(\"消息已被发送\");

} }

客户端的编写开始没有代码中的红色部分,运行后会报如下错误:

Exception in thread \"main\" javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial

原因:在客户端工程的src下,没有提供jndi.properties

java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost

加上红色部分的代码会正常运行:

后台打印结果:消息驱动Bean被调用了,【消息驱动Bean跑起来了】 客户端会打印:消息已被发送

四、总结

(1)将消息驱动Bean的声明中的destination的名字改变后,在mail-service.xml中的名字要做相应的修改,不然同样会报myMessageDrivenBean not bound的错误。

@MessageDriven(

activationConfig = {

@ActivationConfigProperty(propertyName=\"destinationType\),

@ActivationConfigProperty(propertyName=\"destination\myMessageDrivenBean\") } )

基于ip访问jboss的ejb远程调用

解决办法:

在 jboss4.2及以后的版本中,默认情况下jboss只接收来自localhost或127.0.0.1的请求,也就是只接收本地的访问。为了使 jboss接收来自其他地址的请求,在启动jboss时需要使用-b命令行参数进行设置。如下面的启动命令所示:

run.bat -b 200.200.200.123 run.bat -b 0.0.0.0

上面的第一行命令表示jboss可以接收来自200.200.200.123的请求。第二条命令表示

jboss可以接收来自任意地址的请求。如果不设置-b参数,以非本机方式访问EJB时,JBoss会抛出上面异常

EJB3事务管理服务(2010-05-20 09:57:30)转载▼标签: 杂谈 最有用的容器服务可能就是事务管理服务,当应用出现失败或异常时,它保证了数据库的完整性。最常见的事务是定义在session bean 的方法上,方法中所有的数据库操作只有在方法正常退出时才会提交,如果方法抛出未捕获的异 常,事务管理将回滚所有的变更。

@TransactionAttribute 注释用作定义一个需要事务的方法。它可以有以下参数: 1.REQUIRED:方法在一个事务中执行,如果调用的方法已经在一个事务中,则使用该事务,否则将创建一 个新的事务。

2.MANDATORY:如果运行于事务中的客户调用了该方法,方法在客户的事务中执行。如果客户没有关联到

事务中,容器就会抛出TransactionRequiredException。如果企业bean 方法必须用客户事务则采用Mandatory 属性。

3.REQUIRESNEW:方法将在一个新的事务中执行,如果调用的方法已经在一个事务中,则暂停旧的事务。在

调用结束后恢复旧的事务。

4.SUPPORTS:如果方法在一个事务中被调用,则使用该事务,否则不使用事务。

5.NOT_SUPPORTED:如果方法在一个事务中被调用,容器会在调用之前中止该事务。在调用结束后,容器 会恢复客户事务。如果客户没有关联到一个事务中,容器不会在运行入该方法前启动一个新的事务。用

NotSupported 属性标识不需要事务的方法。因为事务会带来更高的性能支出,所以这个属性可以提高性能。

6.Never:如果在一个事务中调用该方法,容器会抛出RemoteException。如果客户没有关联到一个事务中,

容器不会在运行入该方法前启动一个新的事务。

如果没有指定参数,@TransactionAttribute 注释使用REQUIRED 作为默认参数。 下面的代码展示了事务管理的开发: TransactionDAOBean.java Java 代码

1. package com.zhaosoft.session; 2.

3. import java.util.List; 4.

5. import javax.ejb.Remote; 6. import javax.ejb.Stateless;

7. import javax.ejb.TransactionAttribute; 8. import javax.ejb.TransactionAttributeType; 9. import javax.persistence.EntityManager;

10. import javax.persistence.PersistenceContext; 11. import javax.persistence.Query; 12.

13. import com.zhaosoft.bean.Product;

14. import com.zhaosoft.exception.TransException; 15.

16. @Stateless

17. @Remote( { TransactionDAO.class })

18. public class TransactionDAOBean implements TransactionDAO { 19.

20. @PersistenceContext

21. protected EntityManager em; 22.

23. @TransactionAttribute(TransactionAttributeType.REQUIRED) 24. public void insertProduct(String name, Float price, boolean error) { 25. try {

26. for (int i = 0; i < 3; i++) {

27. Product product = new Product(name + i, price * (i + 1));

28. em.persist(product); 29. }

30. if (error) { 31.

32. new Float(\"kkk\"); // 制造一个例外 33. } 34.

35. } catch (Exception e) {

36. throw new RuntimeException(\"应用抛出运行时例外,为了使事务回滚,外部不要用try/catch包围\"); 37. } 38. } 39.

40. @TransactionAttribute(TransactionAttributeType.REQUIRED) 41. public void ModifyProductName(String newname, boolean error) 42. throws Exception {

43. Query query = em.createQuery(\"select p from Product p\"); 44. List result = query.getResultList(); 45. if (result != null) {

46. for (int i = 0; i < result.size(); i++) {

47. Product product = (Product) result.get(i); 48. product.setName(newname + i); 49. em.merge(product); 50. } 51.

52. if (error && result.size() > 0)

53. throw new TransException(\"抛出应用例外\"); 54. } 55. } 56. }

package com.zhaosoft.session;

import java.util.List;

import javax.ejb.Remote; import javax.ejb.Stateless;

import javax.ejb.TransactionAttribute; import javax.ejb.TransactionAttributeType; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query;

import com.zhaosoft.bean.Product;

import com.zhaosoft.exception.TransException;

@Stateless

@Remote( { TransactionDAO.class })

public class TransactionDAOBean implements TransactionDAO {

@PersistenceContext

protected EntityManager em;

@TransactionAttribute(TransactionAttributeType.REQUIRED) public void insertProduct(String name, Float price, boolean error) { try {

for (int i = 0; i < 3; i++) {

Product product = new Product(name + i, price * (i + 1)); em.persist(product); }

if (error) {

new Float(\"kkk\"); // 制造一个例外 }

} catch (Exception e) {

throw new RuntimeException(\"应用抛出运行时例外,为了使事务回滚,外部不要用try/catch包围\"); }

}

@TransactionAttribute(TransactionAttributeType.REQUIRED) public void ModifyProductName(String newname, boolean error) throws Exception {

Query query = em.createQuery(\"select p from Product p\"); List result = query.getResultList(); if (result != null) {

for (int i = 0; i < result.size(); i++) {

Product product = (Product) result.get(i); product.setName(newname + i); em.merge(product); }

if (error && result.size() > 0)

throw new TransException(\"抛出应用例外\"); } } }

上面定义了两个需要事务的方法,容器在运行这两个方法时将会创建一个事务,方法里的所有数据库操作都在此

事务中运行,当这两个方法正常退出时,事务将会提交,所有更改都会同步到数据库中。如果方法抛出

RuntimeException 例外或ApplicationException 例外,事务将会回滚。方法ModifyProductName 中使用的

TransException 类是一个自定义ApplicationException 例外。代码如下: TransException.java Java 代码

1. package com.zhaosoft.exception;

2. publicclass TransException extends Exception { 3. public TransException(String message) { 4. super(message); 5. } 6. }

7. @ApplicationException 注释定义了在例外抛出时将回滚事务。 8. //下面是 TransactionDAOBean 的接口 9. //TransactionDAO.java

10. package com.zhaosoft.session; 11. publicinterface TransactionDAO {

12. publicvoid insertProduct(String name, Float price, boolean error);

13. publicvoid ModifyProductName(String newname, boolean error) throws Exception ;

14. }

15. //下面是TransactionDAOBean 使用的实体Bean 16. //Product.java

17. package com.zhaosoft.bean; 18. import java.io.Serializable;

19. import javax.persistence.Column; 20. import javax.persistence.Entity;

21. import javax.persistence.GeneratedValue; 22. import javax.persistence.Id; 23. import javax.persistence.Table; 24. @Entity

25. @Table(name = \"Products\")

26. public class Product implements Serializable {

27. private int hashCode = Integer.MIN_VALUE; 28. private Integer productid; 29. private String name; 30. private Float price; 31.

32. public Product() { 33. } 34.

35. public Product(String name, Float price) { 36. this.name = name; 37. this.price = price; 38. } 39.

40. @Id

41. @GeneratedValue

42. public Integer getProductid() { 43. return productid; 44. } 45.

46. public void setProductid(Integer productid) { 47. this.productid = productid; 48. } 49.

50. @Column(name = \"ProductName\ 51. public String getName() { 52. return name; 53. } 54.

55. public void setName(String name) { 56. this.name = name; 57. }

58.

59. @Column(nullable = false) 60. public Float getPrice() { 61. return price; 62. } 63.

64. public void setPrice(Float price) { 65. this.price = price; 66. } 67.

68. public boolean equals(Object obj) { 69. if (null == obj)

70. return false;

71. if (!(obj instanceof Product)) 72. return false; 73. else {

74. Product mObj = (Product) obj;

75. if (null == this.getProductid() || null == mObj.getProductid()) 76. return false; 77. else

78. return (this.getProductid().equals(mObj.getProductid())); 79. } 80. } 81.

82. public int hashCode() {

83. if (Integer.MIN_VALUE == this.hashCode) { 84. if (null == this.getProductid()) 85. return super.hashCode(); 86. else {

87. String hashStr = this.getClass().getName() + \":\" 88. + this.getProductid().hashCode(); 89. this.hashCode = hashStr.hashCode(); 90. } 91. }

92. return this.hashCode; 93. } 94. }

95. //下面是Session Bean 的JSP 客户端代码:

96. <%@ page contentType=\"text/html; charset=GBK\"%>

97. <%@ page import=\"com.zhaosoft.session.TransactionDAO, 98. javax.naming.*, 99. java.util.*\"%> 100. <%

101. Properties props = new Properties();

102. props.setProperty(\"java.naming.factory.initial\ 103. props.setProperty(\"java.naming.provider.url\

104. props.setProperty(\"java.naming.factory.url.pkgs\ 105. try {

106. InitialContext ctx = new InitialContext(props);

107. TransactionDAO transactiondao = (TransactionDAO) 108. ctx.lookup(\"TransactionDAOBean/remote\"); 109.

110. //transactiondao.insertProduct(\"电脑\ 111.

112. transactiondao.ModifyProductName(\"数码相机\ 113. out.println(\"执行完成\"); 114. } catch (Exception e) { 115. out.println(e.getMessage()); 116. }

EJB 3 事务管理探讨

EJB事务的特点:

提供声明式事务与编程式事务

声明式事务:应用程序只需要关心业务逻辑,由容器来负责事务的管理。这是实践中常用的方法。 编程时事务:应用程序编码人员自己写事务代码。 ② EJB事务编程的类型 A、CMT 容器管理事务 B、BMT Bean管理事务

C、Client-MT Client-Controlled Transaction客户端管理事务 实体Bean只能用CMT。 CMT:

由容器实现的远程对象/拦截器,负责调用中间件服务。 优点:在应用程序代码中,不用编写事务服务代码; 缺点:粗粒度,只能在方法级别控制事务。

EJB Bean类中编程方式来使用事务(BMT): 优点:细粒度地控制事务 缺点:事务代理与业务代码纠缠 Client-controlled Transaction: 优点:客户端可以精确控制事务

缺点:可能会因为网络问题引起是事务的回滚。 3、EJB事务边界的划分

事务边界:事务边界是指事务从哪里开始。 CMT的事务特性:

Required:Bean类的方法必须要在事务环境下运行,这是容器默认的事务机制。 事务特性只能使用在CMT。

RequiredNew:Bean类中的方法必须在一个新的事务环境下运行。

Supports:Bean类的方法不需要支持事务。如果客户端有事务,则继续沿用原事务环境。 Mandatory:Bean类中方法必须要在事务环境下运行。客户端不启动事务则报错。 NoSupported:Bean类中方法不支持事务。如果客户端启动了事务,则挂起该事务。 Never:Bean类中的方法不支持事务。如果客户端启动了事务,则报错。

如果没有指定参数,@TransactionAttribute 注释使用REQUIRED 作为默认参数。 三、EJB事务的编程 1、CMT

@TransationManagement 用在类前,标注该EJB事务管理方式为Bean | Container(默认) @TrasactionAttribute 用在方法前,标注事务特性(事务的边界)

@SessionContext.setRollbackOnly() 回滚标识,setRollbackOnly()方法必须在事务环境下运行。 EJB容器对于非受查异常(主要指RuntimeException)会回滚,事务对于受查异常则会提交事务。

2、BMT

UserTransaction: ① interface

② 提供控制事务的方法

③ 由容器实现,可以使用@Resource注入

UserTransaction.begin()| commit()| .rollback() 3、客户端控制事务

调用EJB的方法,要求EJB必须采用CMT形式。 4、事务的隔离性 事务的隔离级别:

A、Read uncommitted:性能最高 B、Read committed:解决脏读问题 C、Repeatable read:解决重复读取问题 D、Serializable:解决幻读问题

EJB本身不提供隔离级别的设置,可以通过直接设置数据库(连接池)的隔离级别。

由于CMT依靠容器开始、提交和回滚事务,所以会限制事务的边界位置。而BMT则允许通过编程的方式来指定事务的开始、提交和回滚的位置。主要使用的是javax.transaction.UserTransaction接口。 例如:

如果使用有状态的Session Bean且需要跨越方法调用维护事务,那么BMT是你唯一的选择,当然BMT这种技术复杂,容易出错,且不能连接已有的事务,当调用BMT方法时,总会暂停已有事务,极大的限制了组件的重用。故优先考虑CMT事务管理。

@Stateless)

@TransactionManagement(TransactionManagementType.BEAN) public class ManagerBean {

@Resource

private UserTransaction userTransaction;

public void placeSnagItOrder(Item item, Customer customer){ try {

userTransaction.begin();

validateCredit(customer); ....

userTransaction.commit(); } catch (CreditValidationException cve) { userTransaction.rollback(); } catch (CreditProcessingException cpe){ userTransaction.rollback(); } ..... } }

@TransactionManagement(TransactionManagementType.BEAN) 指定了事务的类型为BMT,上面没有用到@TransactionAttribute,因为它只适用于CMT,在上面代码中,可以显示的指定事务的开始与提交,因此更加的灵活。上面最关键的地方是注入了UserTransaction资源。其中获得UserTransaction资源的方式有三种,除了用EJB资源的方式注入以外,还有以下两种方式: (1) JNDI查找

Context context = new InitialContext();

UserTransaction userTransaction = (UserTransaction) context.lookup(“java:comp/UserTransaction”); userTransaction.begin(); userTransaction.commit();

如果在EJB之外,则可使用此方法,如在不支持依赖注入的JBoss4.2.2的Web工程或helper class即帮助器类中都可以。 (2)EJBContext

@Resource

private SessionContext context; ...

UserTransaction userTransaction = context.getUserTransaction(); userTransaction.begin(); userTransaction.commit();

getUserTransaction方法只能在BMT中使用。如果在CMT中使用,则会抛出IllegalStateException异常。且在BMT中不能使用EJBContext的getRollbackOnly和setRollbackOnly方法,如果这样使用,也会抛出IllegalStateException异常。

@TransactionAttribute(TransactionAttributeType.REQUIRED) public void insertProduct(String name, Float price, boolean error) { }

其中,@TransactionAttribute(TransactionAttributeType.REQUIRED)表示指定事务的类型。 如果省略,默认为CMT方式。

@TransactionAttribute(TransactionAttributeType.REQUIRED)通知容器如何管理事务,

事务的属性控制了事务的使用范围,因为事务之间的关系非常的复杂,这个属性主要是用来处理事务与事务之间怎样来处理的的问题。

如果产生一个系统异常,容器将自动回滚该事务。 EJBException是RuntimeException的子类,即也是一个系统运行时异常。

如果Bean抛出一个普通的非继承自RutimeException应用异常,事务将不会自动回滚,但可以 通过调用EJBContext的SetRollbackOnly回滚。 EJB上下文还有一个getRollbackOnly方法,通过返回一个boolean值来确定CMT事务是否已被标记为回滚。如果开始非常耗资源的操作前判断此bean的事务已被标记为回滚,则可以节约很多系统资源。 对于上面的异常回滚操作,还有一更加优雅的方式:

@ApplicationException(rollback=true) public class CreditValidationException extends Exception { @ApplicationException(rollback=true) public class CreditProcessingException extends Exception { //系统异常

@ApplicationException(rollback=false)

public class DatabaseException extends RuntimeException { @ApplicationExcepti把JAVA核对与不核对的异常标识为应用程序异常。其rollback默认为false,表示程序不会导致CMT自动回滚。

因篇幅问题不能全部显示,请点此查看更多更全内容