Come implementare un listener db in Java

Ho un requisito in cui se un record è inserito in una tabella db, allora automaticamente deve essere eseguito un processo java. Qual è il modo più semplice per implementare un listener di db?

Ho una soluzione per Oracle. Non hai bisogno di crearne di tuoi da quando Oracle ha acquistato Java ha rilasciato un listener per questo. Per quanto ne so, questo non usa il polling internamente, ma le notifiche vengono spinte sul lato Java (probabilmente basato su qualche trigger):

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

E puoi implementarlo in questo modo (questo è solo un esempio):

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

MODIFICARE:
È ansible utilizzare la seguente class per registrarsi: oracle.jdbc.OracleConnectionWrapper

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

Supponiamo che tu crei un metodo da qualche parte:

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

Questo esempio non include clausole try-catch o alcuna gestione delle eccezioni.

Una risposta simile qui: come fare un listener di database con java?

È ansible eseguire questa operazione con una coda di messaggi che supporta le transazioni e triggersre un messaggio solo quando viene eseguita la transazione o (connessione chiusa) per i database che non supportano le notifiche. Questo è per la maggior parte necessario per notificare manualmente e tenere traccia di cosa notificare.

Spring fornisce supporto per le transazioni automatiche per AMQP e JMS . Un’alternativa più semplice da utilizzare è AsyncEventBus di Guava, ma funzionerà solo con una JVM. Per tutte le opzioni di seguito ti consiglio di notificare al resto della tua piattaforma una coda di messaggi.

Opzione – Non specifico del database non di polling

Opzione ORM

Alcune librerie come Hibernate JPA hanno listener di quadro che rendono questo più semplice ma questo perché presuppongono di gestire tutto il CRUD.

Per i normali JDBC dovrai fare la tua contabilità personale. Questo è dopo che la connessione è stata confermata o chiusa, quindi invia il messaggio a MQ che qualcosa è stato aggiornato.

Analisi JDBC

Un’opzione complicata per la conservazione dei libri è di racchiudere / decorare java.sql.DataSource e / o java.sql.Connection in uno personalizzato in modo tale che su commit() (e chiudi) si invii un messaggio. Credo che alcuni sistemi di caching federati lo facciano. È ansible intercettare l’SQL eseguito e analizzare per vedere se è un INSERT o UPDATE ma senza parsing e metadati molto complicati non si otterrà l’ascolto a livello di riga. Purtroppo devo ammettere che questo è uno dei vantaggi offerti da un ORM in quanto sa qual è il tuo aggiornamento.

Opzione Dao

L’opzione migliore se non si utilizza un ORM è solo per inviare manualmente un messaggio nel DAO dopo che la transazione è stata chiusa che una riga è stata aggiornata. Assicurati solo che la transazione sia chiusa prima di inviare il messaggio.

Opzione – Polling non specifico del database

Piuttosto seguire la raccomandazione di @GlenBest.

Ho un paio di cose che farei diversamente. Vorrei esternalizzare il timer o farlo in modo che solo un server esegua il timer (cioè lo scheduler). Vorrei semplicemente usare ScheduledExecutorService (preferibilmente avvolgendolo in ListenerScheduledExecutorService di Guava) invece di Quartz (IMHO che usa il quarzo per il polling super overkill).

Tutti i tuoi tavoli che desideri guardare dovrebbero aggiungere una colonna “notificata”.

Quindi fai qualcosa come:

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

Opzione – db specifico

Con Postgres NOTIFY dovrai comunque eseguire il polling in una certa misura, così farai la maggior parte di quanto sopra e poi manderai il messaggio sul bus.

Una soluzione generale consisterebbe probabilmente nel creare un trigger sul tavolo di interesse, notificando eventuali listener sugli eventi INSERT . Alcuni database hanno mezzi formalizzati per tale notifica tra processi. Per esempio:

Oracolo:

  • Il DBMS_ALERT è un mezzo semplice per tale notifica
  • Gli stream Oracle AQ / Oracle forniscono meccanismi di coda più sofisticati

Postgres:

  • L’istruzione NOTIFY è un mezzo semplice per tale notifica

Altri:

  • Potrebbero esserci meccanismi di notifica simili in altri database, di cui non sono a conoscenza.
  • È sempre ansible implementare le proprie code delle code di notifica degli eventi inserendo un evento in una tabella degli eventi, che viene utilizzata / sottoposta a polling da un processo Java. Ottenere questo giusto e performante può essere piuttosto difficile, però.

ipotesi:

  • Avere un codice portatile standard è più importante dell’esecuzione istantanea in tempo reale del programma java. Si desidera consentire la portabilità a tecnologie alternative alternative (ad es. Evitare eventi DB proprietari, trigger esterni). Il processo Java può essere eseguito leggermente dopo che il record è stato aggiunto alla tabella (ad esempio 10 secondi dopo). cioè Sia la pianificazione + polling o trigger in tempo reale / messaggio / evento sono entrambi accettabili.

  • Se più righe vengono aggiunte alla tabella tutte in una volta, si desidera eseguire un processo, non molti. Un trigger DB avviava un processo Java per ogni riga: inappropriato.

  • La qualità del servizio è importante. Anche se si verifica un errore irreversibile hardware o software, si desidera eseguire nuovamente il programma java ed elaborare i dati incompleti.

  • Vuoi applicare forti standard di sicurezza al tuo ambiente (ad es. Evitare che Java esegua direttamente comandi OS)

  • Vuoi minimizzare il codice

    1. Core Java Standard Code senza dipendenza dalla funzionalità di DB proprietaria:

      • Utilizzare ScheduledExecutorService o Quartz scheduler (o unix cron job o windows task scheduler) per eseguire un programma java ogni minuto (o potrebbe farlo ogni 10 secondi). Questo funge sia da programmatore che da cane da guardia, garantendo che il programma funzioni tutto il giorno. Il quarzo può anche essere distribuito nel server delle app.
      • Far funzionare il programma java per solo 1 minuto (o 10 secondi), eseguire il ciclo, interrogare DB tramite JDBC e dormire per alcuni secondi, quindi uscire definitivamente.
    2. Se si dispone di un’app in un appserver: Creare un bean di sessione che utilizza il servizio timer e interrogare nuovamente la tabella tramite il servizio timer bean sessione JDBC.

    3. Avere un trigger DB che scrive / aggiunge a un file. Usa java 7 filewatcher per triggersre la logica quando il file cambia Java 7 File Watcher

C’è un’altra opzione: usare un ESB open source con una logica di triggerszione dell’adattatore DB (ad es. Fuse o Mule o OpenAdapter), ma offre funzionalità potenti oltre ai requisiti dichiarati, e richiede molto tempo e complessità per l’installazione e l’apprendimento.

Esempio di timer EJB con @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 } } 

Vedere anche: Vedere questa risposta per l’invio di oggetti evento CDI dal bean EJ al contenitore Web.

Non sono sicuro di quanto questa soluzione soddisfi le tue necessità, ma può essere considerata un’opzione. Se si utilizza oracle, allora oracle è ansible scrivere un programma java e compilarlo come funzione oracle. puoi chiamare il tuo programma java dal trigger di inserimento post.

Programma Java nel DB oracle