Neo4j Community Edition - The world's leading Graph Database with Cypher query language and ACID transactions.
—
Comprehensive event system for monitoring database lifecycle, transaction events, and data changes with support for custom event listeners and transaction data inspection.
Interface for listening to transaction lifecycle events with before/after commit and rollback hooks.
/**
* Listen to transaction lifecycle events
*/
public interface TransactionEventListener<T> {
/**
* Called before a transaction is committed
* @param data Transaction data containing changes
* @param transaction The transaction being committed
* @return State object passed to afterCommit/afterRollback
* @throws Exception to prevent commit and trigger rollback
*/
T beforeCommit(TransactionData data, Transaction transaction) throws Exception;
/**
* Called after a transaction is successfully committed
* @param data Transaction data containing changes
* @param state State object returned from beforeCommit
* @param transaction The committed transaction
*/
void afterCommit(TransactionData data, T state, Transaction transaction);
/**
* Called after a transaction is rolled back
* @param data Transaction data containing changes
* @param state State object returned from beforeCommit (may be null)
* @param transaction The rolled back transaction
*/
void afterRollback(TransactionData data, T state, Transaction transaction);
}Usage Examples:
import org.neo4j.graphdb.event.TransactionEventListener;
import org.neo4j.graphdb.event.TransactionData;
public class AuditTransactionListener implements TransactionEventListener<Long> {
@Override
public Long beforeCommit(TransactionData data, Transaction transaction) throws Exception {
long startTime = System.currentTimeMillis();
// Validate changes before commit
for (Node createdNode : data.createdNodes()) {
if (createdNode.hasLabel(Label.label("User"))) {
if (!createdNode.hasProperty("email")) {
throw new IllegalStateException("User nodes must have email property");
}
}
}
// Log significant changes
if (data.createdNodes().iterator().hasNext() ||
data.deletedNodes().iterator().hasNext()) {
System.out.println("Transaction contains node changes");
}
return startTime;
}
@Override
public void afterCommit(TransactionData data, Long startTime, Transaction transaction) {
long duration = System.currentTimeMillis() - startTime;
// Log successful commit
System.out.println("Transaction committed in " + duration + "ms");
System.out.println(" Nodes created: " + count(data.createdNodes()));
System.out.println(" Nodes deleted: " + count(data.deletedNodes()));
System.out.println(" Relationships created: " + count(data.createdRelationships()));
System.out.println(" Relationships deleted: " + count(data.deletedRelationships()));
// Trigger external systems
notifyExternalSystems(data);
}
@Override
public void afterRollback(TransactionData data, Long startTime, Transaction transaction) {
if (startTime != null) {
long duration = System.currentTimeMillis() - startTime;
System.out.println("Transaction rolled back after " + duration + "ms");
}
}
private int count(Iterable<?> iterable) {
int count = 0;
for (Object ignored : iterable) count++;
return count;
}
private void notifyExternalSystems(TransactionData data) {
// Send notifications to external systems
// e.g., message queues, webhooks, etc.
}
}
// Register the listener
DatabaseManagementService managementService = // ... create service
GraphDatabaseService db = managementService.database("neo4j");
db.registerTransactionEventListener(new AuditTransactionListener());Interface for listening to database lifecycle events including creation, deletion, startup, and shutdown.
/**
* Listen to database lifecycle events
*/
public interface DatabaseEventListener {
/**
* Called when a database is created
* @param eventContext Context information about the event
*/
void databaseCreate(DatabaseEventContext eventContext);
/**
* Called when a database is dropped
* @param eventContext Context information about the event
*/
void databaseDrop(DatabaseEventContext eventContext);
/**
* Called when a database is started
* @param eventContext Context information about the event
*/
void databaseStart(DatabaseEventContext eventContext);
/**
* Called when a database is shut down
* @param eventContext Context information about the event
*/
void databaseShutdown(DatabaseEventContext eventContext);
}Usage Examples:
import org.neo4j.graphdb.event.DatabaseEventListener;
import org.neo4j.graphdb.event.DatabaseEventContext;
public class DatabaseLifecycleListener implements DatabaseEventListener {
@Override
public void databaseCreate(DatabaseEventContext eventContext) {
String dbName = eventContext.getDatabaseName();
System.out.println("Database created: " + dbName);
// Initialize new database with default data
initializeDatabase(dbName);
// Log to audit system
auditLog("DATABASE_CREATED", dbName, eventContext.getEventData());
}
@Override
public void databaseDrop(DatabaseEventContext eventContext) {
String dbName = eventContext.getDatabaseName();
System.out.println("Database dropped: " + dbName);
// Cleanup external resources
cleanupExternalResources(dbName);
// Log to audit system
auditLog("DATABASE_DROPPED", dbName, eventContext.getEventData());
}
@Override
public void databaseStart(DatabaseEventContext eventContext) {
String dbName = eventContext.getDatabaseName();
System.out.println("Database started: " + dbName);
// Perform startup tasks
performStartupTasks(dbName);
// Register for monitoring
registerForMonitoring(dbName);
}
@Override
public void databaseShutdown(DatabaseEventContext eventContext) {
String dbName = eventContext.getDatabaseName();
System.out.println("Database shutting down: " + dbName);
// Perform cleanup tasks
performShutdownTasks(dbName);
// Unregister from monitoring
unregisterFromMonitoring(dbName);
}
private void initializeDatabase(String dbName) {
// Add default constraints, indexes, or seed data
}
private void cleanupExternalResources(String dbName) {
// Clean up external caches, connections, etc.
}
private void auditLog(String event, String dbName, Map<String, Object> data) {
// Log to external audit system
}
}
// Register the listener
DatabaseManagementService managementService = new DatabaseManagementServiceBuilder(dbPath)
.addDatabaseListener(new DatabaseLifecycleListener())
.build();Interface providing access to transaction change data including created, deleted, and modified entities.
/**
* Access transaction change data
*/
public interface TransactionData {
/**
* Get nodes created in this transaction
* @return Iterable of created nodes
*/
Iterable<Node> createdNodes();
/**
* Get nodes deleted in this transaction
* @return Iterable of deleted nodes
*/
Iterable<Node> deletedNodes();
/**
* Get relationships created in this transaction
* @return Iterable of created relationships
*/
Iterable<Relationship> createdRelationships();
/**
* Get relationships deleted in this transaction
* @return Iterable of deleted relationships
*/
Iterable<Relationship> deletedRelationships();
/**
* Check if a node was created in this transaction
* @param node Node to check
* @return true if node was created
*/
boolean isCreated(Node node);
/**
* Check if a node was deleted in this transaction
* @param node Node to check
* @return true if node was deleted
*/
boolean isDeleted(Node node);
/**
* Check if a relationship was created in this transaction
* @param relationship Relationship to check
* @return true if relationship was created
*/
boolean isCreated(Relationship relationship);
/**
* Check if a relationship was deleted in this transaction
* @param relationship Relationship to check
* @return true if relationship was deleted
*/
boolean isDeleted(Relationship relationship);
/**
* Get assigned properties for a node
* @param node Node to get properties for
* @return Property container with assigned properties
*/
PropertyContainer assignedNodeProperties(Node node);
/**
* Get removed properties for a node
* @param node Node to get properties for
* @return Property container with removed properties
*/
PropertyContainer removedNodeProperties(Node node);
/**
* Get assigned properties for a relationship
* @param relationship Relationship to get properties for
* @return Property container with assigned properties
*/
PropertyContainer assignedRelationshipProperties(Relationship relationship);
/**
* Get removed properties for a relationship
* @param relationship Relationship to get properties for
* @return Property container with removed properties
*/
PropertyContainer removedRelationshipProperties(Relationship relationship);
/**
* Get assigned labels for a node
* @param node Node to get labels for
* @return Iterable of assigned labels
*/
Iterable<Label> assignedLabels(Node node);
/**
* Get removed labels for a node
* @param node Node to get labels for
* @return Iterable of removed labels
*/
Iterable<Label> removedLabels(Node node);
}Advanced Usage Examples:
public class ChangeTrackingListener implements TransactionEventListener<Map<String, Object>> {
@Override
public Map<String, Object> beforeCommit(TransactionData data, Transaction transaction) {
Map<String, Object> changesSummary = new HashMap<>();
// Track node changes
Map<String, Integer> nodeChanges = new HashMap<>();
// Count created nodes by label
for (Node node : data.createdNodes()) {
for (Label label : node.getLabels()) {
nodeChanges.merge("created_" + label.name(), 1, Integer::sum);
}
}
// Count deleted nodes by label
for (Node node : data.deletedNodes()) {
for (Label label : node.getLabels()) {
nodeChanges.merge("deleted_" + label.name(), 1, Integer::sum);
}
}
changesSummary.put("nodeChanges", nodeChanges);
// Track relationship changes
Map<String, Integer> relChanges = new HashMap<>();
for (Relationship rel : data.createdRelationships()) {
relChanges.merge("created_" + rel.getType().name(), 1, Integer::sum);
}
for (Relationship rel : data.deletedRelationships()) {
relChanges.merge("deleted_" + rel.getType().name(), 1, Integer::sum);
}
changesSummary.put("relationshipChanges", relChanges);
// Track property changes
int propertyChanges = 0;
// Check nodes for property changes
for (Node node : data.createdNodes()) {
PropertyContainer assigned = data.assignedNodeProperties(node);
for (String key : assigned.getPropertyKeys()) {
propertyChanges++;
}
}
// Check for property updates on existing nodes
// (This requires checking all nodes and comparing with removed properties)
changesSummary.put("totalPropertyChanges", propertyChanges);
return changesSummary;
}
@Override
public void afterCommit(TransactionData data, Map<String, Object> changesSummary,
Transaction transaction) {
// Log detailed changes
System.out.println("Transaction committed with changes:");
System.out.println(" Node changes: " + changesSummary.get("nodeChanges"));
System.out.println(" Relationship changes: " + changesSummary.get("relationshipChanges"));
System.out.println(" Property changes: " + changesSummary.get("totalPropertyChanges"));
// Send to monitoring system
sendToMonitoringSystem(changesSummary);
// Update search indexes or caches
updateExternalSystems(data);
}
@Override
public void afterRollback(TransactionData data, Map<String, Object> changesSummary,
Transaction transaction) {
System.out.println("Transaction rolled back - changes discarded");
if (changesSummary != null) {
System.out.println(" Would have applied: " + changesSummary);
}
}
private void sendToMonitoringSystem(Map<String, Object> changes) {
// Send metrics to monitoring system like Prometheus, DataDog, etc.
}
private void updateExternalSystems(TransactionData data) {
// Update search indexes, caches, message queues, etc.
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-neo4j--neo4j