我有一个要求,如果在db表中插入一条记录,则需要自动执行java进程。实现db侦听器的最简单方法是什么?
我有一个要求,如果在db表中插入一条记录,则需要自动执行java进程。实现db侦听器的最简单方法是什么?
Answers:
我有一个针对Oracle的解决方案。自从Oracle购买Java以来,您无需创建自己的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 {...}
假设您在某处创建方法:
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子句或任何异常处理。
一个类似的答案在这里:如何使用Java制作数据库侦听器?
您可以使用支持事务的消息队列来执行此操作,并且在事务启动或不支持通知的数据库(关闭连接)时仅触发一条消息。在大多数情况下,您将必须手动通知并跟踪要通知的内容。
Spring为AMQP和JMS提供了一些自动事务支持。可以使用的一种更简单的替代方法是Guava的AsyncEventBus,但这仅适用于一个JVM。对于以下所有选项,我建议您使用消息队列通知平台的其余部分。
ORM选项
一些库(例如Hibernate JPA) 具有实体侦听器,该实体侦听器使此操作变得更容易,但是那是因为它们假定它们管理所有CRUDing。
对于常规的JDBC,您必须自己记账。即,在落实或关闭连接后,您然后将消息已更新的消息发送给MQ。
JDBC解析
簿记的一个复杂选项是包装java.sql.DataSource
和/或装饰您的和/或java.sql.Connection
自定义的,这样commit()
您就可以在打开(然后关闭)时发送消息。我相信某些联合缓存系统可以做到这一点。您可以捕获执行的SQL并进行分析,以查看它是INSERT还是UPDATE,但是如果没有非常复杂的分析和元数据,您将不会获得行级的侦听。可悲的是,我不得不承认这是ORM提供的优势之一,因为它知道您要进行的更新。
道选项
如果您不使用ORM ,最好的选择是在事务关闭后仅手动在DAO中发送一条消息,即已更新了一行。在发送消息之前,只需确保事务已关闭即可。
遵循@GlenBest建议。
我做了几件我会做不同的事情。我将外部化计时器或将其设置为只有一台服务器运行计时器(即调度程序)。我只会使用ScheduledExecutorService
(最好用Guava包装ListenerScheduledExecutorService
)而不是石英(IMHO使用石英来轮询超级杀伤力)。
您想要观看的所有表中,都应该添加“ notified”列。
然后,您可以执行以下操作:
// 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
您仍然需要进行一定程度的轮询,因此您将完成上述大部分操作,然后将消息发送到总线。
一个通用的解决方案可能是在目标表上创建一个触发器,将INSERT
事件通知给所有侦听器。一些数据库具有用于这种进程间通知的形式化手段。例如:
DBMS_ALERT
是进行此类通知的简单方法NOTIFY
声明是这样的通知的简单方法假设:
具有标准的可移植代码比Java程序的即时实时执行更为重要。您希望允许移植到替代的未来技术(例如,避免专有的数据库事件,外部触发器)。将记录添加到表中之后,Java进程可能会稍微运行(例如10秒后)。即时间表+轮询或实时触发/消息/事件都可以接受。
如果一次将多行全部添加到表中,则您要运行一个进程,而不是很多。DB触发器将为每一行启动一个Java进程-不适当。
服务质量很重要。即使存在硬件或软件致命错误,您也希望Java程序再次运行并处理不完整的数据。
您想对环境应用严格的安全标准(例如,避免让Java或DB直接执行OS命令)
您想最小化代码
不依赖专有数据库功能的核心Java标准代码:
如果您在appserver中有app:创建一个使用Timer Service的Session Bean,然后再次通过JDBC Session Bean Timer Service查询该表。
有一个写入/附加到文件的数据库触发器。当文件更改时,使用Java 7 Filewatcher触发逻辑Java 7 File Watcher
还有另一种选择:将开放源代码ESB与DB适配器触发逻辑一起使用(例如Fuse或Mule或OpenAdapter),但这会提供超出您指定要求的强大功能,并且安装和学习非常耗时且复杂。
使用@Schedule的EJB Timer示例:
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
}
}
我不确定该解决方案可以满足您的需求,但可以考虑作为一种选择。如果使用的是oracle,则可以编写一个java程序并将其编译为oracle函数。您可以从插入后触发器触发Java程序。
DBMS_ALERT
或Oracle AQ
代替,并将任何更改通知常规Java进程...