PostgreSQL
Java Programming
Database Management
Row Change Notification
Bi-directional Communication

PostgreSQL row change notify java program and vice versa

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

PostgreSQL does not push changed rows directly into a Java process by magic, but it does provide a lightweight signaling mechanism through LISTEN and NOTIFY. The usual pattern is to let a trigger send a small notification when a row changes, have Java listen on that channel, and let Java send notifications back with pg_notify when it needs to signal database-connected listeners.

Database to Java: Notify on Row Changes

The clean approach is to keep the notification payload small and send just enough information for the Java side to react. A trigger can publish the table, operation, and row identifier.

sql
1CREATE TABLE tasks (
2    id BIGSERIAL PRIMARY KEY,
3    description TEXT NOT NULL,
4    done BOOLEAN NOT NULL DEFAULT false
5);
6
7CREATE OR REPLACE FUNCTION notify_task_change()
8RETURNS trigger AS $$
9BEGIN
10    PERFORM pg_notify(
11        'task_events',
12        json_build_object(
13            'operation', TG_OP,
14            'id', COALESCE(NEW.id, OLD.id)
15        )::text
16    );
17
18    RETURN COALESCE(NEW, OLD);
19END;
20$$ LANGUAGE plpgsql;
21
22CREATE TRIGGER tasks_notify_trigger
23AFTER INSERT OR UPDATE OR DELETE ON tasks
24FOR EACH ROW EXECUTE FUNCTION notify_task_change();

Now every row-level change produces a small JSON payload on the task_events channel.

Java Listening to PostgreSQL Notifications

The PostgreSQL JDBC driver exposes notifications through PGConnection. A simple listener looks like this:

java
1import java.sql.Connection;
2import java.sql.DriverManager;
3import java.sql.Statement;
4import java.util.Properties;
5import org.postgresql.PGConnection;
6import org.postgresql.PGNotification;
7
8public class PgListener {
9    public static void main(String[] args) throws Exception {
10        String url = "jdbc:postgresql://localhost:5432/appdb";
11        Properties props = new Properties();
12        props.setProperty("user", "appuser");
13        props.setProperty("password", "secret");
14
15        try (Connection conn = DriverManager.getConnection(url, props);
16             Statement stmt = conn.createStatement()) {
17
18            stmt.execute("LISTEN task_events");
19            PGConnection pgConnection = conn.unwrap(PGConnection.class);
20
21            while (true) {
22                PGNotification[] notifications = pgConnection.getNotifications(5000);
23                if (notifications != null) {
24                    for (PGNotification notification : notifications) {
25                        System.out.println(notification.getName() + ": " + notification.getParameter());
26                    }
27                }
28            }
29        }
30    }
31}

In production, the Java listener usually treats the payload as a hint to refresh state, fetch the changed row by id, or wake up another processing step.

Java to PostgreSQL: Send a Notification Back

Java can also publish notifications by executing pg_notify or NOTIFY itself:

java
1import java.sql.Connection;
2import java.sql.DriverManager;
3import java.sql.PreparedStatement;
4
5public class PgNotifier {
6    public static void main(String[] args) throws Exception {
7        try (Connection conn = DriverManager.getConnection(
8                "jdbc:postgresql://localhost:5432/appdb",
9                "appuser",
10                "secret"
11        )) {
12            try (PreparedStatement ps = conn.prepareStatement(
13                    "SELECT pg_notify(?, ?)"
14            )) {
15                ps.setString(1, "task_events");
16                ps.setString(2, "manual refresh requested");
17                ps.execute();
18            }
19        }
20    }
21}

That is what "vice versa" usually means in practice: the Java program can publish on the same channel mechanism, and any PostgreSQL session currently listening can react.

Keep the Payload Small

NOTIFY is a signaling feature, not a bulk data transport layer. A good pattern is:

  • send row ids, operation names, or small JSON payloads
  • let the receiver fetch full row data if needed
  • keep transactional meaning clear by committing promptly

That keeps the notification path cheap and predictable.

Common Pitfalls

  • Expecting PostgreSQL to stream full changed rows automatically without a trigger or follow-up query.
  • Sending huge payloads instead of small identifiers and metadata.
  • Forgetting that notifications are delivered on transaction commit, not in the middle of an open transaction.
  • Polling the database table constantly even though LISTEN and NOTIFY were meant to avoid that pattern.
  • Treating notifications as durable message-queue storage. They are lightweight signals, not a replacement for a real queue.

Summary

  • Use a trigger plus pg_notify to signal row changes from PostgreSQL to Java.
  • Use PGConnection and LISTEN in Java to receive notifications.
  • Java can send notifications back with pg_notify as well.
  • Keep notification payloads small and fetch full row data separately when needed.
  • Think of LISTEN and NOTIFY as signaling, not as durable event storage.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.