Oracle 高级队列(AQ) 与JAVA JMS

Oracle 高级队列AQ与JAVA JMS


Oracle 高级队列是什么?

高级队列Advanced Queuing(AQ)。它是oracle原生消息软件,这篇文章提供了一个AQ的高级概览。尤其是我们将看到如何启动一个队列并进行入列--出列操作,还有通过通知创建异步出列。

用这个东西我们能做些什么?(笔者理解还不够深入 , 个人见解)


数据库监控 , 消息从数据库到JAVA应用程序的消息推送 , 基于JMS的多个工作模式 , 支持订阅模式的消息分发 , 即时通讯类的应用程序也可能有它参与其中等.......

本篇文章我们所用到的一些环境


JAVA JDK1.8 , Oracle Database Express Edition 11g(Oracle 11g 便携版) , JAVA JMS , Oracle 的一些Jar 包分别是: 

jmscommon.jar aqapi_g.jar orai18n.jar ojdbc6_g.jar

当然这些jar你可以在你的11G 便携版的安装主目录的 Oracle_HOME /lib/ 中找到 , 具体在哪里我下次补充


数据库的一些准备



1.我们需要一个拥有System 权限的用户 , 然后为你平时所用的Oracle用户赋值权限(主要是建立AQ的权限) 比如我自己的devuser  , 我将为这个用户赋值一些相关的 , 必须的权限 , 当然你必须使用具有System权限的用户登录你的Oracle
包括: 
--给用户赋予aq的相关权限
GRANT EXECUTE ON DBMS_AQ TO devuser;
GRANT EXECUTE ON DBMS_AQADM TO devuser;
BEGIN 
      DBMS_AQADM.grant_system_privilege('ENQUEUE_ANY','devuser',false);
      DBMS_AQADM.grant_system_privilege('DEQUEUE_ANY','devuser',false);
      
END;


2.我们需要创建一个Oracle aq 的负荷类型(payloads) , 也可以说是Oracle 的消息载体(纯属个人理解 , 你可以到Oracle 的官方查阅更为详细的文档说明),格式可以是用户自定义对象或XMLType或ANYDATA。本例中我们创建一个简单的对象类型用于传递消息。
create type demo_queue_payload_type as object (message varchar2(4000));
3.我们来创建一个队列表(这里需要注意的是 , 我们仅仅是创建了一个表),队列表用于存储消息,在入队时自动存入表中,出队时自动删除。使用DBMS_AQADM包进行数据表的创建,只需要写表名,同时设置相应的属性。对于队列需要设置multiple_consumers为false,如果使用发布/订阅模式需要设置为true。本例中我们采用订阅模式 
begin
  dbms_aqadm.create_queue_table(
    queue_table   => 'demo_queue_table',
    queue_payload_type => 'demo_queue_payload_type',
    multiple_consumers => true
  );
end;

4.我们要创建一个队列并且启动它 ,PS : 后续的章节我将写到如何删除和停止队列 , 和创建的过程中相关的对象的查看
--创建队列并启动队列:
DECLARE
   subscriber sys.aq$_agent;
BEGIN
	-- 创建队列
   dbms_aqadm.create_queue (
    queue_name  => 'demo_queue',
    queue_table => 'demo_queue_table'
  );
	--启动队列
  dbms_aqadm.start_queue(
    queue_name  =>  'demo_queue'
  );
	--设置订阅人
   subscriber := sys.aq$_agent('recuser1', NULL, NULL);
   DBMS_AQADM.ADD_SUBSCRIBER(queue_name => 'demo_queue',
                             subscriber => subscriber);
END;
5.我们要尝试使用aq来入队一条消息
-- 入列操作是一个基本的事务操作(表数据的插入),因此我们需要Commit
declare
  r_enqueue_options DBMS_AQ.ENQUEUE_OPTIONS_T;
  r_message_properties DBMS_AQ.MESSAGE_PROPERTIES_T;
  v_message_handle RAW(16);
  o_payload demo_queue_payload_type;
begin
  o_payload := demo_queue_payload_type('Hello JMS ?');

  dbms_aq.enqueue(
    queue_name  => 'demo_queue',
    enqueue_options => r_enqueue_options,
    message_properties => r_message_properties,
    payload => o_payload,
    msgid => v_message_handle
  );

  commit;
end;

6.我们如何出队一条消息
declare
  r_dequeue_options DBMS_AQ.DEQUEUE_OPTIONS_T;
  r_message_properties DBMS_AQ.MESSAGE_PROPERTIES_T;
  v_message_handle RAW(16);
  o_payload demo_queue_payload_type;
begin
  DBMS_AQ.DEQUEUE(
    queue_name => 'demo_queue',
    dequeue_options => r_dequeue_options,
    message_properties => r_message_properties,
    payload => o_payload,
    msgid => v_message_handle
  );
  DBMS_OUTPUT.PUT_LINE(
    '***** Browse message is [' || o_payload.message || ']****'
  );
end;

出队操作我没有尝试 , 很遗憾的告诉大家 , 此段代码来自于 scorpio3k  Click here 

我们如何在JMS中接收AQ入队的消息

首先前提是我文章开始所提到的jar 你安装在了你相关的应用程序上面


直接上代码 , 不懂的同学可以参阅其他博文或者查阅Oracle JMS 的相关文档


package *********;

import java.sql.SQLException;
import java.util.Properties;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;

import oracle.jms.AQjmsAdtMessage;
import oracle.jms.AQjmsConsumer;
import oracle.jms.AQjmsDestination;
import oracle.jms.AQjmsFactory;
import oracle.jms.AQjmsSession;

public class Main {
	
	@SuppressWarnings("unused")
	private TopicConnection getTopicConnection(String jdbcUrl, String username, String password) throws JMSException {
		Properties info = new Properties();
		info.put(username, password);
		TopicConnectionFactory topicConnectionFactory = AQjmsFactory.getTopicConnectionFactory(jdbcUrl, info);
		return topicConnectionFactory.createTopicConnection(username, password);
	}

	public static void main(String[] args) throws InterruptedException {
		JmsConfig config = new JmsConfig();
		try {
			TopicConnection conn = new Main().getTopicConnection(config.jdbcUrl, config.username, config.password);
			AQjmsSession session = (AQjmsSession) conn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
			conn.start();

			AQjmsDestination topic = (AQjmsDestination) session.getTopic(config.username, config.queueName);
			AQjmsConsumer subscriber = (AQjmsConsumer) session.createDurableSubscriber(topic, "recuser1",
					QUEUE_MESSAGE_TYPE_1.getORADataFactory());//注意订阅人

			topic.start(session, false, true);

			//Message msg = subscriber.receive(10000);
			subscriber.setMessageListener(new MessageListener() {
				@Override
				public void onMessage(Message message) {
					try {
						AQjmsAdtMessage adtMessage = (AQjmsAdtMessage) message;
						QUEUE_MESSAGE_TYPE_1 payload = (QUEUE_MESSAGE_TYPE_1) adtMessage.getAdtPayload();
						System.out.println("队列消息 : " + payload.getMessage());
					} catch (JMSException e) {
						e.printStackTrace();
					} catch (SQLException e) {
						e.printStackTrace();
					}
				}
			});
			Thread.sleep(1000000);
		} catch (JMSException e) {
			e.printStackTrace();
		}

	}
}

代码很渣 , 大神别喷我 , 下面是关于数据库配置的一些描述类

package **********;

public class JmsConfig {

	public String username = "devuser";
	public String password = "******";
	public String jdbcUrl = "jdbc:oracle:thin:@127.0.0.1:1521:XE";
	public String queueName = "demo_queue";
}


在下面是关于消息负荷的Oracle Type 到JAVA 类型的转换

package *****************;

import java.sql.Connection;
import java.sql.SQLException;

import oracle.jdbc.OracleTypes;
import oracle.jpub.runtime.MutableStruct;
import oracle.sql.Datum;
import oracle.sql.ORAData;
import oracle.sql.ORADataFactory;
import oracle.sql.STRUCT;

public class QUEUE_MESSAGE_TYPE_1 implements ORAData, ORADataFactory {
	public static final String _SQL_NAME = "devuser.DEMO_QUEUE_PAYLOAD_TYPE";
	public static final int _SQL_TYPECODE = OracleTypes.STRUCT;

	protected MutableStruct _struct;

	protected static int[] _sqlType = {12};
	protected static ORADataFactory[] _factory = new ORADataFactory[3];
	protected static final QUEUE_MESSAGE_TYPE_1 _QUEUE_MESSAGE_TYPE_1Factory = new QUEUE_MESSAGE_TYPE_1();

	public static ORADataFactory getORADataFactory() {
		return _QUEUE_MESSAGE_TYPE_1Factory;
	}

	/* constructors */
	protected void _init_struct(boolean init) {
		if (init)
			_struct = new MutableStruct(new Object[3], _sqlType, _factory);
	}

	public QUEUE_MESSAGE_TYPE_1() {
		_init_struct(true);
	}

	public QUEUE_MESSAGE_TYPE_1(String message) throws SQLException {
		_init_struct(true);
		setMessage(message);
	}

	/* ORAData interface */
	public Datum toDatum(Connection c) throws SQLException {
		return _struct.toDatum(c, _SQL_NAME);
	}

	/* ORADataFactory interface */
	public ORAData create(Datum d, int sqlType) throws SQLException {
		return create(null, d, sqlType);
	}

	protected ORAData create(QUEUE_MESSAGE_TYPE_1 o, Datum d, int sqlType) throws SQLException {
		if (d == null)
			return null;
		if (o == null)
			o = new QUEUE_MESSAGE_TYPE_1();
		o._struct = new MutableStruct((STRUCT) d, _sqlType, _factory);
		return o;
	}

	/* accessor methods */
	public String getMessage() throws SQLException {
		return (String) _struct.getAttribute(0);
	}

	public void setMessage(String message) throws SQLException {
		_struct.setAttribute(0, message);
	}

}

命名不太规范哈 , 还是见谅 , 

一些说明


这篇文章主要是给自己的一个学习笔记 , 代码编写规范 , 详细的文档说明由于我的准备不足很遗憾的告诉大家不太好,花了几天时间研究的这么一个东西 , 参阅了一些博客,查阅了一些Oracle 的官方文献 , 还是略显不够成熟的一个东西 , 但是我提供的代码部署 , 毫无疑问是可以跑起来的,这点放心 , 


一些完善


当然我们可以对相关的代码进行封装 , 比如尝试和spring 进行集成 , 在IOC容器中对其深度管理 ,还可以对Oracle AQ 的入队操作完全封装为存储过程 , 让触发器来调用存储过程,尝试消息的入队操作 , 数据库端也可以有更多的完善 , 等等 


消息载荷的Oracle类型到JAVA类型的转换

前面我们提到如何进行进行类型转换 , 我只是一味的提供了代码 , 这段代码为什么可以这样写 , 如果我的消息载荷里有多个类型呢? 

根据我在Oracle 官方查阅到的资料 , 可能啊 , 只是可能 , 在Oracle 11G 之前Oracle提供了 jpublisher 工具进行类型转换 , 你需要配置一些数据库连接的相关配置 , 然后告诉Oracle你需要对那个"TYPE" 或者说哪个消息载荷进行转换 , 然互jpublisher会输出一段java代码给你使用 , 目前从网络上我了解到的相关资料 , Oracle已经停止了该工具的下载 , 至少我在oracle 官方上没有下载成功 , 提示我需要同意他的一些协议 , 但是事实上并没有相关条款的弹出 , 我也无法同意 , 诚然 , 我也无法下载


但是关于jpublisher 的相关文档Oracle 上面我们还是可以查到 , 想了解或者已经在其他的地方获得了jpublisher jar包的同学可以去阅读一下相关的文档 

后面我还了解到在11g oracle database 的安装中会附送jpublisher 的相关jar 和jplus 的相关的jar,至于如何找到这些东西 , 我了解还不够深入 , 还是去看关于这方面的相关文档

笔者是如何写出上面的类型装换代码的呢? 我是参阅别人的代码 , ,然后改造的自己的代码 , 细心的同学可以去看看这个博文 , 然后你也许能纯手打 , 或者定制自己的类型转换代码 , 都要规则 , 或者说是规律 , 应该很容易学习

http://blog.csdn.net/peigen521/article/details/26993683


关于消息到达的延迟 , 



你可能感兴趣的:(java,oracle,java,数据库,oracle,11g,jms)