10

我目前有一个网络服务,它使用 Hibernate 在 mysql 数据库中插入信息。其中一些信息需要由另一个“导入”应用程序处理。我不想从 web 服务触发这个应用程序。所以网络服务不依赖网络服务,反之亦然。

有没有办法从“导入”应用程序“监听”数据库中的更改(特别是:插入),然后开始执行操作。我已经查看了触发器,但这些似乎只适用于应用程序的 Hibernate 会话中的更改,而不适用于“外部”更改。

编辑*

简而言之,我想要的答案;是否可以从不更改数据库/表本身的 java 应用程序监视对 mysql 数据库/表(来自任何来源)的更改

赏金更新*

我将把赏金奖励给可以向我解释如何使用 Java 应用程序监视对 MySQL 表/数据库所做的更改的人。监视更改的 Java 应用程序不是应用任何更改的应用程序。改变的来源可以是任何东西。

4

6 回答 6

11

我认为你可以很容易地实现这样的事情,假设你不介意在你的数据库上创建一些额外的表和触发器,并且监控 java 应用程序必须轮询数据库而不是专门接收触发器。

假设您要监控的表是这样的:

CREATE TABLE ToMonitor ( id INTEGER PRIMARY KEY, value TEXT );

然后创建一个表来跟踪更改,并创建一个填充该表的触发器:

CREATE TABLE InsertedRecords( value TEXT );
CREATE TRIGGER trig AFTER INSERT ON account
FOR EACH ROW INSERT INTO InsertedRecords( value ) VALUES ( NEW.value );

这将导致在 ToMonitor 中发生的每个插入都填充 InsertedRecords 表。

然后您只需将您的监控应用程序设置为定期SELECT * from InsertedRecords,采取适当的措施,然后清除其中的记录InsertedRecords

编辑:如果您不介意一点 C/C++ 编码,一个小替代方案是按照此处的说明创建一个自定义 SQL 函数,该函数触发您的监控应用程序开始运行,然后从内部调用该 SQL 函数您创建的触发器。

于 2012-07-14T19:25:25.697 回答
5

您可以阅读mysql 二进制日志。在这里你可以找到一些信息。有一个java 解析器另一个- 但它被标记为未完成)您也可以使用其他语言(例如perl)查找类似的解析器并用 Java 重写它们。
也看看mysql-proxy

于 2012-07-14T19:03:51.910 回答
3

我知道这不是您所要求的(因此,这不是一个正确的答案),但是如果您考虑放弃“让数据库通知应用程序”的想法,您将获得使用 JMS 进行应用程序之间通信的完美案例。

发起更改的应用程序可以将消息发布到 JMS 主题,该主题由第二个应用程序订阅。一旦第一个更改数据库,它就会在主题上放置一条消息。然后第二个看到这个新事件并采取相应的行动。您甚至可以在消息中发布增量,这样第二个应用程序就不需要访问数据库。

我有点反对通过“破解”数据库来做更多的事情而不仅仅是存储数据来处理这个问题,因为它在未来不可避免地会遇到麻烦(就像一切最终都会发生的那样),并且调试它会很困难。想象一下在生态系统中添加第三个应用程序,您现在必须复制您为第二个应用程序所做的任何事情,但现在要复制第三个应用程序。如果您没有记录您的步骤,您可能会迷路。

如果您只是在这两个应用程序之间使用 JMS 服务器,那么您当然可以在未来添加第三个应用程序,它只监听这个主题(并发布一条新消息,以防它对数据库具有写访问权),另一个应用程序甚至不必知道还有一个应用程序。也不是数据库。

于 2012-07-17T08:12:03.980 回答
3

假设我们要监视表 'table1' 中的变化

CREATE TABLE `table1` (
    `id` INT(10) NOT NULL AUTO_INCREMENT,
    `value` VARCHAR(50) NULL DEFAULT NULL,
    PRIMARY KEY (`id`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
AUTO_INCREMENT=10;

上面是创建“table1”的查询,它包含一个“id”列,它是自动递增的

创建另一个表来存储更改。查询如下

CREATE TABLE `changes` (
    `id` INT(10) NOT NULL AUTO_INCREMENT,
    `changes` VARCHAR(200) NULL DEFAULT '0',
    `change_time` TIMESTAMP NULL DEFAULT NULL,
    `tablename` VARCHAR(50) NULL DEFAULT NULL,
    `changed_id` VARCHAR(10) NULL DEFAULT NULL,
    PRIMARY KEY (`id`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
AUTO_INCREMENT=21;

现在在第一个表中创建一个触发器,即.. 'table1' 查询如下所示

delimiter |
create trigger trg_table1 AFTER INSERT ON table1
FOR EACH ROW BEGIN
DECLARE lastid INT DEFAULT 0;
SELECT max(id) INTO lastid from table1;
insert into changes values(null,'insert',now(),'table1',lastid);
end;
|
delimiter ;

现在,如果您尝试在“table1”中插入任何内容,其详细信息将自动插入到更改表中。在更改表中更改表示更改的类型,即插入、更新等 change_time 表示发生更改的时间 tablename 表示发生更改的表 changed_id 表示“table1”中新插入行的 id

现在创建一个不断从“更改”表中读取的 java 程序。“更改”表中的新条目意味着数据库发生了某些事情。从“更改”表中的每条记录中,您可以了解插入操作发生在哪个表中。基于此,您可以执行适当的操作。执行适当的操作后,从“更改”表中删除该行。

您可以为数据库中的每个表创建触发器(就像我上面所做的那样)...从“更改”表的“表名”列中,您可以了解插入发生在哪个表中。

于 2012-07-17T14:33:32.710 回答
2

您可以使用 Q4M 之类的排队解决方案,但在您的情况下它可能有点过分了。但你可以:

在 MySQL 数据库中,将时间戳列添加到要插入的表中。在“导入”应用程序中,要么使用 java.util.timer,要么使用外部调度程序,如 cron。使用其中之一触发读取时间戳列为空的插入表的任务。对这些行采取适当的操作,然后为时间戳列设置一个值。如果没有带有空时间戳的行,则您没有新的插入。很简单,但它有效。

出于性能原因,您可能希望向时间戳列添加索引。

于 2012-07-14T19:37:23.083 回答
1

我想这个答案已经很晚了,但正如@dbf 指出的那样,阅读binlog可能是要走的路。我建议查看Debezium 的 MySQL Connector


Debezium MySQL 连接器读取 binlog 并为行级 INSERT、UPDATE 和 DELETE 操作生成更改事件,并将更改事件记录在 Kafka 主题中。

然后,您的“另一个”应用程序可以读取这些 Kafka 主题。

于 2020-08-26T21:23:51.583 回答