J'ai une exigence: si un enregistrement est inséré dans une table de base de données, un processus Java doit être exécuté automatiquement). Quel est le moyen le plus simple d'implémenter un écouteur de base de données?
J'ai une solution pour Oracle. Vous n'avez plus besoin de créer le vôtre puisque Oracle a acheté Java il a publié un écouteur pour celui-ci. Pour autant que je sache, cela n'utilise pas la scrutation en interne, mais les notifications sont poussées vers le = Java (probablement basé sur un déclencheur):
public interface Oracle.jdbc.dcn.DatabaseChangeListener
extends Java.util.EventListener {
void onDatabaseChangeNotification(Oracle.jdbc.dcn.DatabaseChangeEvent arg0);
}
Et vous pouvez l'implémenter comme ceci (ceci n'est qu'un exemple):
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();
}
}
}
}
EDIT:
Vous pouvez utiliser la classe suivante pour vous inscrire: Oracle.jdbc.OracleConnectionWrapper
public class Oracle.jdbc.OracleConnectionWrapper implements Oracle.jdbc.OracleConnection {...}
Dites que vous créez une méthode quelque part:
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();
}
Cet exemple n'inclut pas les clauses try-catch ni la gestion des exceptions.
Une réponse similaire ici: Comment créer un auditeur de base de données avec Java?
Vous pouvez le faire avec une file de messages qui prend en charge les transactions et déclenche simplement un message lorsque la transaction est validée ou (connexion fermée) pour les bases de données qui ne prennent pas en charge les notifications. C’est pour la plupart que vous devrez notifier manuellement et garder trace de ce qu’il faut notifier.
Spring fournit un support de transaction automatique pour [~ # ~] amqp [~ # ~] et [~ # ~ ] jms [~ # ~] . Une alternative plus simple que vous pouvez utiliser est AsyncEventBus de Guava mais cela ne fonctionnera que pour une seule machine virtuelle. Pour toutes les options ci-dessous, je vous recommande d'informer le reste de votre plate-forme avec une file d'attente de messages.
Option ORM
Certaines bibliothèques comme Hibernate JPA ont des écouteurs d'entités qui facilitent les choses, mais parce qu'elles supposent qu'elles gèrent tous les fichiers CRUD.
Pour que votre compte soit normal [~ # ~] jdbc [~ # ~] , vous devrez le faire vous-même. C'est après que la connexion soit validée ou fermée que vous envoyez ensuite le message à MQ que quelque chose a été mis à jour.
Analyse JDBC
Une option compliquée pour la comptabilité consiste à envelopper/décorer votre Java.sql.DataSource
Et/ou Java.sql.Connection
Dans une configuration personnalisée telle que le commit()
(et fermer) vous envoyez ensuite un message. Je pense que certains systèmes de mise en cache fédérés le font. Vous pouvez intercepter le SQL exécuté et analyser pour voir si c'est un INSERT ou UPDATE, mais sans analyse très compliquée et métadonnées, vous n'obtiendrez pas d'écoute au niveau de la ligne. Malheureusement, je dois admettre que c’est l’un des avantages qu’un [~ # ~] orm [~ # ~] fournit en ce qu’il sait quelle est votre mise à jour.
Option Dao
La meilleure option si votre pas utilisant un ORM consiste simplement à envoyer manuellement un message dans votre DAO après la clôture de la transaction indiquant qu'une ligne a été mise à jour. Assurez-vous simplement que la transaction est clôturée avant d'envoyer le message.
Suivez en quelque sorte la recommandation @GlenBest.
J'ai deux choses que je ferais différemment. Je voudrais externaliser la minuterie ou faire en sorte qu'un seul serveur exécute la minuterie (c.-à-d. Programmateur). Je voudrais simplement utiliser ScheduledExecutorService
(il est préférable de l’emballer dans ListenerScheduledExecutorService
de Guava) au lieu de Quartz (à mon humble avis, l’utilisation du quartz pour des interrogations excessives).
Pour toutes les tables que vous souhaitez surveiller, vous devez ajouter une colonne "notifiée".
Ensuite, vous faites quelque chose comme:
// 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); }
Avec Postgres NOTIFY
, vous aurez encore besoin d'interroger dans une certaine mesure pour pouvoir effectuer la plupart des opérations ci-dessus, puis envoyer le message au bus.
Une solution générale consisterait probablement à créer un déclencheur sur la table d’intérêt, en avertissant les auditeurs des événements INSERT
. Certaines bases de données ont formalisé les moyens permettant une telle notification entre processus. Par exemple:
DBMS_ALERT
est un moyen simple pour une telle notificationNOTIFY
est un moyen simple pour une telle notificationHypothèses:
Avoir du code portable standard est plus important que l'exécution instantanée en temps réel du programme Java. Vous souhaitez autoriser la portabilité vers d'autres technologies futures (par exemple, éviter les événements de base de données propriétaires, les déclencheurs externes). Java peut s’exécuter légèrement après l’ajout de l’enregistrement à la table (10 secondes plus tard, par exemple), c’est-à-dire que les événements planifiés + interrogation ou déclencheurs/messages/événements en temps réel sont acceptables.
Si plusieurs lignes sont ajoutées à la table en même temps, vous souhaitez exécuter un processus, pas plusieurs. Un déclencheur de base de données lancerait un processus Java pour chaque ligne - inapproprié).
La qualité de service est importante. Même en cas d'erreur fatale matérielle ou logicielle, vous souhaitez que le programme Java soit exécuté à nouveau et traite les données incomplètes.
Vous souhaitez appliquer des normes de sécurité strictes à votre environnement (par exemple, évitez d'avoir Java ou des commandes d'exécution directe du système d'exploitation))
Vous voulez minimiser le code
Core Java Code standard sans dépendance de la fonctionnalité de base de données propriétaire:
Si vous avez une application sur un serveur d'applications: Créez un bean session utilisant le service de minuterie et interrogez à nouveau la table via JDBC Session Bean Timer Service .
Avoir un déclencheur de base de données qui écrit/ajoute dans un fichier. Utilisez Java 7 filewatcher pour déclencher la logique lorsque le fichier est modifié Observateur de fichiers Java 7
Il existe une autre option: utiliser un ESB open source avec une logique de déclenchement d'adaptateur de base de données (par exemple, Fuse ou Mule ou OpenAdapter), mais cela offre des fonctionnalités puissantes allant au-delà de vos exigences spécifiées. Il est fastidieux et complexe à installer et à apprendre.
Exemple de minuterie EJB utilisant @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
}
}
Voir aussi: Voir cette réponse pour envoyer des objets d'événement CDI d'un conteneur d'EJB vers un conteneur Web.
Je ne sais pas dans quelle mesure cette solution répond à vos besoins, mais peut être considérée comme une option. Si vous utilisez Oracle, vous pouvez écrire un programme Java et le compiler en tant que fonction Oracle. Vous pouvez appeler votre programme Java à partir du déclencheur de post-insertion .