1. 本地事务
1.1. 在一个JMS客户端, 可以使用本地事务来组合消息的发送和接收。JMS Session接口提供了commit和rollback方法。事务提交意味着生产的所有消息被发送, 消费的所有消息被确认; 事务回滚意味着生产的所有消息被销毁, 消费的所有消息被恢复并重新提交, 除非它们已经过期。
1.2. 事务性的会话总是牵涉到事务处理中, commit或rollback方法一旦被调用, 一个事务就结束了, 而另一个事务被开始。关闭事务性会话将回滚其中的事务。
1.3. 需要注意的还有一个, 消息的生产和消费不能包含在同一个事务中。
2. 创建事务会话
2.1. 创建事务会话的时候transacted参数要设置为true, acknowledgeMode参数要设置为Session.SESSION_TRANSACTED。
// 1. 创建一个连接工厂
ConnectionFactory cf = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER, ActiveMQConnection.DEFAULT_PASSWORD, ActiveMQConnection.DEFAULT_BROKER_URL);
// 2. 创建连接
Connection conn = cf.createConnection();
// 4. 创建会话
Session session = conn.createSession(true, Session.SESSION_TRANSACTED);
2.2. Session.SESSION_TRANSACTED值是专门为事务会话使用的。
3. 例子
3.1. 创建一个名为JMSTransacted的Java项目, 同时拷入相关jar包
3.2. 编辑MyProducer.java
package com.jmsapp.tra;
import javax.jms.DeliveryMode;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Session;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
public class MyProducer {
// 默认连接用户名
private static final String dftUsr = ActiveMQConnection.DEFAULT_USER;
// 默认用户密码
private static final String dftPwd = ActiveMQConnection.DEFAULT_PASSWORD;
// 默认连接地址
private static final String dftUrl = ActiveMQConnection.DEFAULT_BROKER_URL;
// 队列名称
private static final String queueName = "queueMapMsgTransacted";
public static void main(String[] args) {
// 1. 创建一个连接工厂
QueueConnectionFactory cf = new ActiveMQConnectionFactory(dftUsr, dftPwd, dftUrl);
// 连接对象
QueueConnection conn = null;
// 会话对象
QueueSession session = null;
try {
// 2. 创建连接
conn = cf.createQueueConnection();
// 3. 启动连接
conn.start();
// 4. 创建会话
session = conn.createQueueSession(true, Session.SESSION_TRANSACTED);
// 5. 创建消息目的地。如果是点对点, 那么它的实现是Queue; 如果是订阅模式, 那它的实现是Topic。这里我们创建一个名为queueMapMsgTransacted的消息队列。
Queue queue = session.createQueue(queueName);
// 6. 消息生产者
QueueSender sender = session.createSender(null);
// 7. 创建文本消息和发送消息
MapMessage user = session.createMapMessage();
user.setLong("id", 100000000000L);
user.setString("name", "lisi");
sender.send(queue, user, DeliveryMode.PERSISTENT, 9, 1000 * 60 * 60);
MapMessage userExtend = session.createMapMessage();
userExtend.setChar("sex", '男');
userExtend.setBoolean("married", false);
sender.send(queue, userExtend, DeliveryMode.PERSISTENT, 5, 1000 * 60 * 60);
// 8. 提交事务
session.commit();
} catch (JMSException e) {
if(null != session) {
try {
session.rollback();
} catch (JMSException e1) {
e1.printStackTrace();
}
}
e.printStackTrace();
} finally {
try {
if (session != null) {
session.close();
}
} catch (JMSException e1) {
e1.printStackTrace();
} finally {
if (conn != null) {
try {
conn.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
}
}
3.3. 点对点消息过期, 会把它存放到一个ActiveMQ.DLQ的队列中
3.4. 编辑MyConsumer.java
package com.jmsapp.tra;
import java.util.Enumeration;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueReceiver;
import javax.jms.QueueSession;
import javax.jms.Session;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
public class MyConsumer {
// 默认连接用户名
private static final String dftUsr = ActiveMQConnection.DEFAULT_USER;
// 默认用户密码
private static final String dftPwd = ActiveMQConnection.DEFAULT_PASSWORD;
// 默认连接地址
private static final String dftUrl = ActiveMQConnection.DEFAULT_BROKER_URL;
// 队列名称
private static final String queueName = "queueMapMsgTransacted";
public static void main(String[] args) {
// 1. 创建一个连接工厂
QueueConnectionFactory cf = new ActiveMQConnectionFactory(dftUsr, dftPwd, dftUrl);
// 连接对象
QueueConnection conn = null;
// 会话对象
QueueSession session = null;
try {
// 2. 创建连接
conn = cf.createQueueConnection();
// 3. 启动连接
conn.start();
// 4. 创建会话
session = conn.createQueueSession(true, Session.SESSION_TRANSACTED);
// 5. 创建消息目的地。如果是点对点, 那么它的实现是Queue; 如果是订阅模式, 那它的实现是Topic。这里我们创建一个名为queueMapMsgTransacted的消息队列。
Queue queue = session.createQueue(queueName);
// 6. 消息接收者
QueueReceiver receiver = session.createReceiver(queue);
// 7. 创建文本消息和发送消息
while(true) {
MapMessage msg = (MapMessage) receiver.receive(3000);
if(msg == null) {
break;
}
@SuppressWarnings("unchecked")
Enumeration<String> e = msg.getMapNames();
while(e.hasMoreElements()) {
String name = e.nextElement();
System.out.println(name + " = " + msg.getObject(name));
}
}
// 8. 提交事务
session.commit();
} catch (JMSException e) {
if(null != session) {
try {
session.rollback();
} catch (JMSException e1) {
e1.printStackTrace();
}
}
e.printStackTrace();
} finally {
try {
if (session != null) {
session.close();
}
} catch (JMSException e1) {
e1.printStackTrace();
} finally {
if (conn != null) {
try {
conn.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
}
}
3.5. 再次运行MyProducer.java, 然后运行MyConsumer.java