TimesTen JMS/XLA 实现 Trigger 功能

本文介绍如何使用TimesTen的JMS/XLA技术监听数据库表的更改,并通过Java程序处理这些更改事件。详细解释了JMS/XLA的基本概念、配置流程、确认机制以及如何在TimesTen中设置XLA监听。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

TimesTen支持存储过程、函数等功能,但是不支持触发器。相应的,你可以使用TimesTen JMS/XLA API来监听本地数据库中指定表的更改,并收到这些更改的实时通知。JMS/XLA的首要目的是作为触发器的高性能、异步的替代方式。

JMS/XLA实现了JMS接口,让TimesTen的事务处理日志(Transaction Log API,即XLA)的功能可以使用在Java应用上。

JMS/XLA 基本概念

Java应用可以使用JMS/XLA来接受TimesTen的事件通知,JMS/XLA使用JMS的发布订阅接口来提供对XLA更新事件的访问。

通过建立一个JMS会话(Session)实例来订阅更新,该实例提供与XLA的连接,并用于创建持久订阅者(TopicSubscriber)。你可以通过这个订阅者同步地接受和处理消息,也可以实现一个监听器(MessageListener)异步地处理更新。

JMS/XLA被设计为用来监听本地数据库,TimesTen和和接收通知的应用程序必须在同一个系统上。

XLA 如何读取事务日志

当应用修改数据库之后,TimesTen会产生一条事务日志记录来描述更改。新的日志记录总是添加到缓存区的末尾,并周期性从内存同步到磁盘文件上。应用程序可以使用XLA读取日志,并进行过滤得到感兴趣的更改记录。

XLA 与物化视图

XLA可以用来跟踪表和物化视图的改变,但是不能跟踪普通视图的改变。物化视图提供单个来源,让你可以从多个细节表中跟踪所选行和列的更改。没有物化视图,应用程序必须监听和过滤来自所有细节表的更新记录,包括应用并不关心的行和列。

一般来说,用于跟踪表的变化或物化视图的XLA机制之间没有操作上的差异。但是对于异步物化视图,要意识到对于异步视图,XLA通知的顺序不必和相关联的细节表一致,也不必和同步视图一致。例如,往一张细节表插入两条数据,在异步物化视图中可能是以相反的顺序插入的。此外,更新操作可能被XLA报告为一个删除操作后跟一个插入操作,多个操作可能被合并为一个单一的操作。依赖精确操作顺序的应用程序不应该使用异步物化视图。

XLA 书签

XLA书签标记着XLA订阅者应用程序从事物日志中读取的位置,书签支持持久订阅,允许应用程序从一个主题断开连接,然后重新连接来继续从它离开的地方接收日志更新。

你可以使用一个持久的主题订阅者(TopicSubscriber)来创建一个XLA消息的消费者,创建订阅者时用的订阅标识符被用做XLA书签名。使用ttXlaSubscribettXlaUnsubscribe内置的存储过程启动和通知对一个表的XLA订阅时,显示的指定了使用的书签名。无论何时收到一个确认消息,书签重置到上一次的读取位置。

通过调用会话(Session)对象的unsubscribe()方法可以移除一个持久订阅,同时也会删除相关联的XLA书签,重新连接时必须创建一个新的订阅。当订阅的书签在使用时不能被修改,要想修改一个订阅,必须先关闭消息消费者,然后取消订阅、重新订阅,最后再打开消息消费者。

注意:Session对象的unsubscribe()方法会会删除书签,但是内置的ttXlaUnsubscribe存储过程不会删除书签。此外,可以通过内置的ttXlaBookmarkCreatettXlaBookmarkDelete来创建和删除书签。unsubscribe()方法相当于调用ttXlaUnsubscribettXlaBookmarkDelete两个存储过程。

XLA在使用时,TimesTen事务日志文件会持有这个书签,防止事务日志文件被清除,直到XLA确认不再需要它们。

JMS/XLA 配置文件和主题

要连接到XLA,需要建立与特定数据库对应的JMS主题对象的连接,JMS/XLA配置文件提供了主题名称与数据库之间的映射关系。JMS/XLA默认配置文件为jmsxla.xml,位于当前工作目录下。如果需要使用其它的名字或位置,需要指定环境变量并配置InitialContext类以及类路径,当按照配置找到多个文件时,采用第一个的配置。

一个主题定义包括:主题名,连接字符串,预取值(指定一次检索多少条更新)。下面的配置文件将主题XlaDemo映射到数据库TestDB,如下:

<xlaconfig>
  <topics>
    <topic name="XlaDemo"
      connectionString="DSN=TestDB"
      xlaPrefetch="100" />
  </topics>
</xlaconfig>

XLA 更新

应用程序接收XLA更新作为JMS MapMessage对象,一个MapMessage对象包含了一组类型名/值对,对应于XLA更新头的字段。通过getter方法获取消息字段,可以跳过名字检索独立的字段值。所有保留的字段名一双下划线开头,如__TYPE

所有的更新都有一个__TYPE字段,用来指示消息包含的更新类型。类型值为为一个Integer,可以使用com.timesten.dataserver.jmsxla.XlaConstants中定义的常量来进行比较。有如下支持的类型:

TypeDescription
INSERTA row has been added
UPDATEA row has been modified
DELETEA row has been removed
COMMIT_ONLYA transaction has been committed
CREATE_TABLEA table has been created
DROP_TABLEA table has been dropped
CREATE_INDEXAn index has been created
DROP_INDEXAn index has been dropped
ADD_COLUMNSNew columns have been added to the table
DROP_COLUMNSColumns have been removed from the table
CREATE_VIEWA materialized view has been created
DROP_VIEWA materialized view has been dropped
CREATE_SEQA sequence has been created
DROP_SEQA sequence has been dropped
CREATE_SYNONYMA synonym has been created
DROP_SYNONYMA synonym has been dropped
TRUNCATEAll rows in the table have been delete

XLA 确认模式

XLA确认机制被设计为用来确保应用程序不仅接收到消息,且正确地处理消息。确认一条更新永远将应用的XLA书签重置为上一条记录的读取位置。避免了以前的消息被重复读取,确保当应用程序重新连接到XLA,重用书签时只接收新的更新记录。JMS/XLA可以自动确认XLA更新消息,应用程序也可以显示地选择消息确认模式。创建会话时,可以指定更新如何被确认。

JMS/XLA支持三种确认模式:

  • AUTO_ACKNOWLEDGE:该模式下,当接收到更新时会自动确认。每一条消息只会被投递一次,重复的消息不会被发送,因此,当程序失败的时候,消息可能会丢失。消息的投递与确认总是相互独立的,所以JMS/XLA不会预读取多条记录,xlaprefetch属性将被忽略。
  • DUPS_OK_ACKNOWLEDGE:该模式下,更新会被自动确认,但是当程序失败的时候,重复的消息会被投递。当上一个预读取块的最后一条记录被读取时,JMS/XLA根据xlaprefetch属性值来预读取记录并发送确认。如果程序在读完所有预取记录之前失败,那么程序重新启动之后,预读取块的所有记录都会存在。
  • CLIENT_ACKNOWLEDGE:该模式下,程序负责通过调用MapMessage实例的acknowledge()方法来确认收到更新消息。JMS/XLA通过xlaprefetch属性值来预读取记录。

下例展示了如何设置确认模式:

Session session = connection.createSession (false, Session.CLIENT_ACKNOWLEDGE);
预读取更新

一次预读取多条更新记录比每次单独地从XLA获取记录更加高效。当使用AUTO_ACKNOWLEDGE时,更新不会被预读取,比其他模式更慢。可能的话,应该将程序设计为可以容忍重复的更新,以便可以使用DUPS_OK_ACKNOWLEDGE,或者显示地确认更新。显示确认更新通常会产生最佳性能,只要可以避免单独确认每条消息。

确认更新

在更新消息上调用acknowledge()可以明确地确认一个XLA更新。确认一条消息会隐式地确认所有以前的消息。通常,你会在确认之间接收并处理多条更新消息。如果使用的是CLIENT_ACKNOWLEDGE模式并且想在将来重用持久的订阅,在退出之前,应该调用acknowledge()将书签重置到上一次读取的位置。

使用 JMS/XLA 模拟触发器功能

TimesTen 端配置

1、授权

Command> grant create session, create table, XLA to parkinglotuser;

2、新建 Xla 测试表

Command> create table xlatest (id number primary key, name varchar2(20));

3、创建 Xla 书签

Command> call ttXlaBookmarkCreate('bookmark');

4、订阅

Command> call ttXlaSubscribe('xlatest', 'bookmark');

Java 端程序

5、配置 jmsxla.xml

<xlaconfig>
  <topics>
    <!-- topic for xla demo -->
    <topic name="XlaDemo"
           connectionString="DSN=TestDB"
           xlaPrefetch="100" />
  </topics>
</xlaconfig>

6、连接 XLA 、接收并处理表的更新

import com.timesten.dataserver.jmsxla.XlaConstants;
import org.junit.Test;

import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Enumeration;

/**
 * Demo which shows how to use JMS/XLA to process updates.
 */
public class AsyncJMS {

    @Test
    public void run() throws JMSException, NamingException {

        String topicName = "XlaDemo";
        String bookmark = "bookmark";

        Context context = new InitialContext();
        TopicConnectionFactory connFactory = (TopicConnectionFactory) context.lookup("TopicConnectionFactory");
        TopicConnection conn = connFactory.createTopicConnection();
        TopicSession session = conn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
        Topic topic = session.createTopic(topicName);
        TopicSubscriber subscriber = session.createDurableSubscriber(topic, bookmark);
        MyListener listener = new MyListener();

        subscriber.setMessageListener(listener);
        conn.start();

        System.out.println("Starting JMS XLA subscriber");
        System.out.println();
        System.out.println("Waiting for XLA updates on table xlatest ... ");
        System.out.println("- Please use ttIsql or Level1 to modify the xlatest table");

        while (true) {
            try {
                Thread.sleep(60000);
            } catch (InterruptedException e) {
                // unsubscribe (which will delete the XLA bookmark)
                session.unsubscribe(bookmark);
                // close the connection
                conn.close();
                // ignore
                e.printStackTrace();
            }
        }
    }

    /**
     * Class to receive update messages.
     */
    class MyListener implements MessageListener {

        MyListener() {
        }

        /**
         * This method is called when an XLA event occurs.
         *
         * @param msg a MapMessage describing the XLA event.
         */
        public void onMessage(Message msg) {

            MapMessage message = (MapMessage) msg;

            if (msg == null) {
                System.err.println("MyListener: update message is null");
                return;
            }

            try {
                System.out.println();
                System.out.println("onMessage: got a " + message.getJMSType() + " message");

                // Get the type of event (insert, update, delete, drop table, etc.).
                int type = message.getInt(XlaConstants.TYPE_FIELD);
                if (type == XlaConstants.INSERT) {
                    System.out.println("A row was inserted.");
                } else if (type == XlaConstants.UPDATE) {
                    System.out.println("A row was updated.");
                } else if (type == XlaConstants.DELETE) {
                    System.out.println("A row was deleted.");
                } else {
                    // Messages are also received for DDL events such as CREATE TABLE.
                    // This program processes INSERT, UPDATE, and DELETE events,
                    // and ignores the DDL events.
                    return;
                }

                Enumeration enumeration = message.getMapNames();
                while (enumeration.hasMoreElements()) {
                    String name = enumeration.nextElement().toString();
                    System.out.println("Name: " + name + ", Value: " + message.getObject(name));
                }
                System.out.println();

                /*
                 * 1.type == INSERT || type == DELETE
                 * MapMessage的字段包括监听表的字段、及类本身的字段
                 * 使用 message.getXXX(field) 读取插入或删除的字段值
                 * 2.type == UPDATE
                 * MapMessage的字段包括监听表的字段(分为原始值和更新值)、及类本身的字段
                 * 使用 message.getXXX(field) 读取更新值
                 * 使用 message.getXXX(_field) 读取原始值
                 * 
                 * 其中field为监听表的字段名
                 */

                // todo 处理更新逻辑

            } catch (JMSException e) {
                System.err.println("JMSException in " + this.getClass().getName());
                e.printStackTrace();
            } catch (Exception e) {
                System.err.println("Exception in " + this.getClass().getName());
                e.printStackTrace();
            }
        }
    }
}

参考文献

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值