Como implementar um ouvinte de db em Java

Eu tenho um requisito onde se um registro é inserido em uma tabela de database, em seguida, automaticamente um processo de java precisa ser executado. Qual é a maneira mais fácil de implementar um ouvinte de db?

   

Eu tenho uma solução para o Oracle. Você não precisa criar o seu próprio desde agora que a Oracle comprou o Java lançou um ouvinte para ele. Tanto quanto eu sei que isso não usa polling internamente, em vez disso, as notifications são enviadas para o lado do Java (provavelmente com base em algum trigger):

public interface oracle.jdbc.dcn.DatabaseChangeListener extends java.util.EventListener { void onDatabaseChangeNotification(oracle.jdbc.dcn.DatabaseChangeEvent arg0); } 

E você pode implementá-lo assim (isso é apenas uma amostra):

 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(); } } } } 

EDITAR:
Você pode usar a seguinte class para registrar: oracle.jdbc.OracleConnectionWrapper

 public class oracle.jdbc.OracleConnectionWrapper implements oracle.jdbc.OracleConnection {...} 

Digamos que você crie um método em algum lugar:

 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(); } 

Este exemplo não inclui cláusulas try-catch ou qualquer exception handling.

Uma resposta semelhante aqui: Como fazer um ouvinte de database com java?

Você pode fazer isso com uma fila de mensagens que ofereça suporte a transactions e simplesmente envie uma mensagem quando a transação estiver envolvida ou (conexão fechada) para bancos de dados que não suportam notifications. Isso é para a maior parte você terá que notificar manualmente e acompanhar o que notificar.

Spring fornece algum suporte de transação automática para AMQP e JMS . Uma alternativa mais simples que você poderia usar é o AsyncEventBus da Guava, mas isso só funcionará para uma JVM. Para todas as opções abaixo, recomendo que você notifique o restante da sua plataforma com uma fila de mensagens.

Opção – Não-pesquisa sem database específico

Opção ORM

Algumas bibliotecas como o Hibernate JPA possuem ouvintes de entidades que facilitam isso, mas isso acontece porque eles assumem que eles gerenciam todo o CRUD.

Para JDBC regular, você terá que fazer sua própria contabilidade. Depois que a conexão é confirmada ou fechada, você envia a mensagem para o MQ informando que algo foi atualizado.

Análise de JDBC

Uma opção complicada para a manutenção de livros é envolver / decorar seu java.sql.DataSource e / ou java.sql.Connection em um personalizado, de modo que em commit() (e close) você envie uma mensagem. Eu acredito que alguns sistemas de cache federado fazem isso. Você poderia interceptar o SQL executado e analisar para ver se é um INSERT ou UPDATE, mas sem análise muito complicada e metadados você não obterá a escuta em nível de linha. Infelizmente eu tenho que admitir que esta é uma das vantagens que um ORM fornece em saber que sua atualização.

Opção Dao

A melhor opção se você não estiver usando um ORM é apenas enviar manualmente uma mensagem em seu DAO depois que a transação for fechada que uma linha foi atualizada. Apenas verifique se a transação está fechada antes de enviar a mensagem.

Opção – Polling não específico do database

Siga um pouco a recomendação @GlenBest.

Eu par de coisas que eu faria diferente. Eu externalizaria o timer ou faria com que apenas um servidor executasse o timer (por exemplo, agendador). Gostaria apenas de usar ScheduledExecutorService (preferível envolvê-lo em ListenerScheduledExecutorService de Guava) em vez de Quartz (IMHO usando quartzo para super super polling).

Longe de todas as suas tabelas que você quer assistir, você deve adicionar uma coluna “notificada”.

Então você faz algo como:

 // BEGIN Transaction List 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); } 

Opção – db specific

Com o Postgres NOTIFY você ainda precisará pesquisar até certo ponto, de modo que estará fazendo a maioria dos itens acima e, em seguida, enviará a mensagem para o ônibus.

Uma solução geral provavelmente consistiria em criar um gatilho na tabela de interesse, notificando qualquer ouvinte sobre events INSERT . Algumas bases de dados formalizaram meios para tal notificação entre processos. Por exemplo:

Oráculo:

  • O DBMS_ALERT é um meio simples para essa notificação
  • O Oracle AQ / Oracle Streams fornece mecanismos de fila mais sofisticados

Postgres:

  • A declaração NOTIFY é um meio simples para tal notificação

Outras:

  • Pode haver mecanismos de notificação semelhantes em outros bancos de dados, que eu não conheço.
  • Você sempre pode implementar suas próprias tabelas de fila de notificação de events inserindo um evento em uma tabela de events, que é consumida / pesquisada por um processo Java. Obter este direito e desempenho pode ser bastante complicado, no entanto.

Premissas:

  • Ter código portátil padrão é mais importante do que a execução instantânea em tempo real do programa java. Você deseja permitir a portabilidade para tecnologia alternativa futura (por exemplo, evitar events de database proprietários, gatilhos externos). O processo Java pode rodar um pouco após o registro ser adicionado à tabela (por exemplo, 10 segundos depois). Ou seja, agenda + polling ou trigger / mensagem / evento em tempo real são ambos aceitáveis.

  • Se várias linhas forem adicionadas à tabela de uma só vez, você deseja executar um processo, não muitos. Um gatilho de database iniciaria um processo java para cada linha – inadequado.

  • Qualidade de serviço é importante. Mesmo se houver um erro fatal de hardware ou software, você deseja que o programa java seja executado novamente e processe os dados incompletos.

  • Você deseja aplicar fortes padrões de segurança ao seu ambiente (por exemplo, evitar que o java ou o DB executem comandos do SO diretamente)

  • Você quer minimizar o código

    1. Código Padrão Java Básico sem dependência da funcionalidade de database proprietária:

      • Use ScheduledExecutorService ou Quartz scheduler (ou cron job unix ou Windows Task Scheduler) para executar um programa java a cada minuto (ou poderia fazer a cada 10 segundos). Isso funciona como um agendador e um watchdog, garantindo que o programa rode o tempo todo. O quartzo também pode ser implantado no servidor de aplicativos.
      • Ter seu programa java executado por apenas 1 minuto (ou 10 segundos), looping, consultando DB via JDBC e dormindo por alguns segundos e, finalmente, saindo.
    2. Se você tiver um aplicativo em um servidor de aplicativos: Crie um bean de session que use o serviço de timer e, novamente, consulte a tabela por meio do serviço de timer do bean de session JDBC.

    3. Tem um triggersdor de DB que grava / anexa a um arquivo. Use o filewatcher java 7 para acionar a lógica quando o arquivo for alterado .

Há outra opção: usar um ESB de código aberto com uma lógica de acionamento do adaptador DB (por exemplo, Fuse ou Mule ou OpenAdapter), mas isso oferece uma funcionalidade poderosa além dos requisitos declarados e é demorado e complexo para instalar e aprender.

Exemplo de Temporizador EJB usando @Schedule:

 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 } } 

Consulte também: Consulte esta resposta para enviar objects de evento CDI do EJB para o contêiner da Web.

Não sei ao certo até que ponto essa solução satisfaz sua necessidade, mas pode ser considerada como uma opção. Se você está usando oracle então oracle você pode escrever um programa java e compilá-lo como uma function oracle. você pode chamar seu programa java a partir do acionador de inserção de postagens.

Programa Java no oracle DB