如何在Java中实现一个数据库监听器
我有一个要求,如果一个logging插入一个数据库表,然后自动java进程需要执行。什么是最简单的方法来实现一个数据库监听器?
我有一个针对Oracle的解决scheme。 自从现在Oracle收购了Java之后,您就不需要创build自己的Java了。 据我所知,这不使用轮询在内部,而是通知被推送到Java端(可能基于一些触发器):
public interface oracle.jdbc.dcn.DatabaseChangeListener extends java.util.EventListener { void onDatabaseChangeNotification(oracle.jdbc.dcn.DatabaseChangeEvent arg0); }
你可以像这样实现它(这只是一个示例):
public class DBListener implements DatabaseChangeListener { private DbChangeNotification toNotify; public BNSDBListener(DbChangeNotification toNotify) { this.toNotify = toNotify; } @Override public void onDatabaseChangeNotification(oracle.jdbc.dcn.DatabaseChangeEvent e) { synchronized( toNotify ) { try { toNotify.notifyDBChangeEvent(e); //do sth } catch (Exception ex) { Util.logMessage(CLASSNAME, "onDatabaseChangeNotification", "Errors on the notifying object.", true); Util.printStackTrace(ex); Util.systemExit(); } } } }
编辑:
您可以使用以下类来注册: oracle.jdbc.OracleConnectionWrapper
public class oracle.jdbc.OracleConnectionWrapper implements oracle.jdbc.OracleConnection {...}
假设你在某处创build一个方法:
public void registerPushNotification(String sql) { oracle.jdbc.driver.OracleConnection oracleConnection = ...;//connect to db dbProperties.setProperty(OracleConnection.DCN_NOTIFY_ROWIDS, "true"); dbProperties.setProperty(OracleConnection.DCN_QUERY_CHANGE_NOTIFICATION, "true"); //this is what does the actual registering on the db end oracle.jdbc.dcn.DatabaseChangeRegistration dbChangeRegistration= oracleConnection.registerDatabaseChangeNotification(dbProperties); //now you can add the listener created before my EDIT listener = new DBListener(this); dbChangeRegistration.addListener(listener); //now you need to add whatever tables you want to monitor Statement stmt = oracleConnection.createStatement(); //associate the statement with the registration: ((OracleStatement) stmt).setDatabaseChangeRegistration(dbChangeRegistration); //look up the documentation to this method [http://docs.oracle.com/cd/E11882_01/appdev.112/e13995/oracle/jdbc/OracleStatement.html#setDatabaseChangeRegistration_oracle_jdbc_dcn_DatabaseChangeRegistration_] ResultSet rs = stmt.executeQuery(sql); //you have to execute the query to link it to the statement for it to be monitored while (rs.next()) { ...do sth with the results if interested... } //see what tables are being monitored String[] tableNames = dbChangeRegistration.getTables(); for (int i = 0; i < tableNames.length; i++) { System.out.println(tableNames[i] + " has been registered."); } rs.close(); stmt.close(); }
这个例子不包括try-catch子句或任何exception处理。
一般的解决scheme可能包括在感兴趣的表上创build一个触发器,通知任何监听器关于INSERT
事件。 一些数据库已经forms化地处理这种进程间通知。 例如:
甲骨文:
-
DBMS_ALERT
是这种通知的简单方法 - Oracle AQ / Oracle Streams提供更复杂的队列机制
Postgres的:
-
NOTIFY
声明是这种通知的简单方法
其他:
- 其他数据库中可能有类似的通知机制,我不知道。
- 您可以始终通过在事件表中插入事件表来实现您自己的事件通知队列表,该事件表由Java进程消耗/轮询。 但是,获得这个正确和高性能可能是相当棘手的。
一个类似的答案在这里: 如何使用java的数据库监听器?
您可以使用支持事务的消息队列来执行此操作,并且在事务处理完成或closures(连接closures)时仅针对不支持通知的数据库发出消息。 大部分情况下,您将不得不手动通知并跟踪要通知的内容。
Spring为AMQP和JMS提供了一些自动事务支持。 更简单的select是Guava的AsyncEventBus,但是只能用于一个JVM。 对于下面的所有选项,我build议您通过消息队列通知平台的其余部分。
选项 – 非轮询非数据库特定
ORM选项
像Hibernate JPA 这样的库有实体监听器 ,这使得这更容易,但这是因为他们认为他们pipe理所有的CRUD。
对于常规的JDBC,你必须做自己的记账。 这是连接提交或closures后,然后发送消息到MQ的东西已经更新。
JDBCparsing
一个复杂的选项是保存/修改你的java.sql.DataSource
和/或java.sql.Connection
在一个自定义的,这样在commit()
(和closures),然后发送一条消息。 我相信一些联邦caching系统可以做到这一点。 你可以捕获执行的SQL和parsing,看它是一个INSERT还是UPDATE,但是没有非常复杂的parsing和元数据,你将无法获得行级的监听。 可悲的是,我不得不承认这是ORM提供的优势之一,因为它知道你的更新。
道选项
如果你不使用ORM,最好的select就是在事务closures后手动发送消息到你的DAO中,一行已经更新。 在发送消息之前,请确保交易已closures。
选项 – 轮询非数据库特定的
稍微遵循@GlenBestbuild议。
我有两件事情,我会做不同的事情。 我会外部化计时器或使其只有一个服务器运行计时器(即调度程序)。 我只是使用ScheduledExecutorService
(最好包装它在Guava的ListenerScheduledExecutorService
),而不是石英(恕我直言,使用石英轮询超级杀伤力)。
你希望看到的所有表格都应该添加一个“通知”列。
然后你做这样的事情:
// BEGIN Transaction List<String> ids = execute("SELECT id FROM table where notified = 'f'"); //If db not transactional either insert ids in a tmp table or use IN clause execute("update table set notified = 't' where notified = 'f'") // COMMIT Transaction for (String id : ids) { mq.sendMessage(table, id); }
选项 – 数据库的具体
有了Postgres NOTIFY
你仍然需要进行一定程度的调查,这样你才能完成上面的大部分工作,然后把信息发送到公交车上。
假设:
-
拥有标准的可移植代码比实时执行java程序更重要。 您希望允许将可移植性应用于未来的替代技术(例如避免专有数据库事件,外部触发器)。 logging添加到表后,Java进程可以稍微运行(例如10秒钟之后)。 即时间表+轮询或实时触发/消息/事件均可接受。
-
如果一次将多行添加到表中,则希望运行一个进程,而不是多进程。 数据库触发器会为每一行启动一个java进程 – 不合适的。
-
服务质量很重要。 即使存在硬件或软件致命错误,您也希望Java程序再次运行并处理不完整的数据。
-
您希望将强大的安全标准应用于您的环境(例如避免让java或DB直接执行操作系统命令)
-
你想尽量减less代码
-
核心Java标准代码,不依赖专有数据库function:
- 使用ScheduledExecutorService或Quartz调度程序(或unix cron作业或Windows任务调度程序)每分钟运行一个java程序(或者可以每10秒钟执行一次)。 这既是一个调度程序和看门狗,确保程序全天候运行。 Quartz也可以部署在应用服务器上。
- 让你的java程序运行1分钟(或10秒),循环,通过JDBC查询数据库,睡眠几秒钟,最后退出。
-
如果您在应用程序服务器中有应用程序:创build一个使用定时服务的会话Bean,并通过JDBC 会话Bean定时服务再次查询表。
-
有写入/附加到文件的数据库触发器。 使用java 7 filewatcher在文件更改Java 7 File Watcher时触发逻辑
-
还有另外一种select:使用带有DB适配器触发逻辑的开源ESB(例如Fuse或Mule或OpenAdapter),但是这会提供超出您所要求的强大function,而且安装和学习既费时又复杂。
使用@Schedule的EJB计时器示例:
public class ABCRequest { // normal java bean with data from DB } @Singleton public class ABCProcessor { @Resource DataSource myDataSource; @EJB ABCProcessor abcProcessor; // runs every 3 minutes @Schedule(minute="*/3", hour="*") public void processNewDBData() { // run a JDBC prepared statement to see if any new data in table, put data into RequestData try { Connection con = dataSource.getConnection(); PreparedStatement ps = con.prepareStatement("SELECT * FROM ABC_FEED;"); ... ResultSet rs = ps.executeQuery(); ABCRequest abcRequest while (rs.hasNext()) { // population abcRequest } abcProcessor.processABCRequest(abcRequst); } ... } } @Stateless public class class ABCProcessor { public void processABCRequest(ABCRequest abcRequest) { // processing job logic } }
另请参见: 请参阅此答案将CDI事件对象从EJB发送到Web容器。
我不知道这个解决scheme能满足您的需求多远,但可以考虑作为一种select。 如果你使用的是oracle,那么oracle可以编写一个java程序并将其编译为一个oracle函数。 你可以从后插入触发器调用你的Java程序。
在oracle数据库中的Java程序