Development Tip

Java에서 db 리스너를 구현하는 방법

yourdevel 2020. 11. 21. 09:12
반응형

Java에서 db 리스너를 구현하는 방법


레코드가 db 테이블에 삽입되면 자동으로 Java 프로세스를 실행해야하는 요구 사항이 있습니다. db 리스너를 구현하는 가장 쉬운 방법은 무엇입니까?


Oracle에 대한 솔루션이 있습니다. Oracle이 Java를 구입하여 리스너를 출시했기 때문에 직접 만들 필요가 없습니다. 내가 아는 한 이것이 내부적으로 폴링을 사용하지 않고 대신 알림이 Java 측으로 푸시됩니다 (아마도 일부 트리거를 기반으로 함).

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

다음과 같이 구현할 수 있습니다 (이것은 샘플 일뿐입니다).

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

편집 :
다음 클래스를 사용하여 등록 할 수 있습니다.oracle.jdbc.OracleConnectionWrapper

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

어딘가에 메서드를 생성한다고 가정 해보십시오.

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

이 예제에는 try-catch 절이나 예외 처리가 포함되지 않습니다.


비슷한 답변 : Java로 데이터베이스 리스너를 만드는 방법?

트랜잭션을 지원하는 메시지 큐를 사용하여이 작업을 수행하고 트랜잭션이 커밋되거나 알림을 지원하지 않는 데이터베이스의 경우 (연결이 닫 혔을 때) 메시지를 시작하면됩니다. 대부분의 경우 수동으로 통지하고 통지 할 내용을 추적해야합니다.

Spring은 AMQPJMS에 대한 일부 자동 트랜잭션 지원을 제공합니다 . 사용할 수있는 더 간단한 대안은 Guava의 AsyncEventBus 이지만 하나의 JVM에서만 작동합니다. 아래의 모든 옵션에 대해 메시지 대기열로 나머지 플랫폼에 알리는 것이 좋습니다.

옵션-비 폴링 비 데이터베이스 특정

ORM 옵션

Hibernate JPA 와 같은 일부 라이브러리 에는 이 작업을 더 쉽게하는 엔티티 리스너 가 있지만 이는 모든 CRUDing을 관리한다고 가정하기 때문입니다.

일반 JDBC 의 경우 자신의 장부를해야합니다. 즉, 연결이 커밋되거나 닫힌 후 무언가가 업데이트되었다는 메시지를 MQ에 보냅니다.

JDBC 구문 분석

장부 보관을위한 한 가지 복잡한 옵션은 사용자 java.sql.DataSource및 / 또는 java.sql.Connection사용자 지정 항목 을 포장 / 장식 commit()한 다음 메시지를 보내도록하는 것입니다. 일부 연합 된 캐싱 시스템이이 작업을 수행한다고 생각합니다. 실행 된 SQL을 트랩하고 구문 분석하여 INSERT 또는 UPDATE인지 확인할 수 있지만 매우 복잡한 구문 분석 및 메타 데이터가 없으면 행 수준 수신을 얻지 못할 것입니다. 슬프게도 이것이 ORM이 제공 하는 이점 중 하나라는 점을 인정 해야합니다.

Dao 옵션

ORM을 사용 하지 않는 경우 가장 좋은 방법 은 트랜잭션이 종료 된 후 행이 업데이트되었다는 메시지를 DAO에 수동으로 보내는 것입니다. 메시지를 보내기 전에 거래가 종료되었는지 확인하십시오.

옵션-비 데이터베이스 특정 폴링

@GlenBest 권장 사항을 다소 따릅니다.

제가 다르게 할 몇 가지 일이 있습니다. 타이머를 외부화하거나 하나의 서버 만 타이머 (예 : 스케줄러)를 실행하도록 만들 것입니다. 나는 Quartz (수퍼 과잉을 폴링하기 위해 Quartz를 사용하는 IMHO) 대신 ( ScheduledExecutorServiceGuava 's로 감싸는 것이 바람직합니다 ListenerScheduledExecutorService).

보고 싶은 모든 테이블에 "알림"열을 추가해야합니다.

그런 다음 다음과 같이합니다.

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

옵션-db 특정

Postgres NOTIFY사용하면 여전히 어느 정도 폴링을해야 위의 대부분을 수행 한 다음 버스로 메시지를 보낼 수 있습니다.


A general solution would probably consist in creating a trigger on the table of interest, notifying any listeners about INSERT events. Some databases have formalised means for such inter-process notification. For instance:

Oracle:

Postgres:

  • The NOTIFY statement is a simple means for such notification

Others:

  • There might be similar notification mechanisms in other databases, that I'm not aware of.
  • You can always implement your own event notification queue tables by inserting an event in an event table, which is consumed / polled by a Java process. Getting this right and performant may be quite tricky, though.

Assumptions:

  • Having standard portable code is more important than instant realtime execution of the java program. You want to allow portability to alternative future technology (e.g. avoid proprietary DB events, external triggers). Java process can run slightly after record is added to table (e.g. 10 seconds later). i.e. Either schedule + polling or realtime trigger/message/event are both acceptable.

  • If multiple rows are added to the table all at once, you want to run one process, not many. A DB trigger would start a java process for each row - inappropriate.

  • Quality of Service is important. Even if there is a hardware or software fatal error, you want the java program to run again and process the incomplete data.

  • You want to apply strong security standards to your environment (e.g. avoid having java or DB execute OS commands directly)

  • You want to minimise code

    1. Core Java Standard Code without dependency on proprietary DB functionality:

      • Use ScheduledExecutorService or Quartz scheduler (or unix cron job or windows task scheduler) to run a java program every minute (or could do every 10 seconds). This acts as both a scheduler and watchdog, ensuring program runs around the clock. Quartz can also be deployed in app server.
      • Have your java program run for just 1 minute (or 10 seconds), looping, querying DB via JDBC and sleeping for a few seconds, then finally exiting.
    2. If you have app in an appserver: Create a Session Bean that uses the Timer Service and again query the table via JDBC Session Bean Timer Service.

    3. Have a DB trigger that writes/appends to a file. Use java 7 filewatcher to trigger logic whent the file changes Java 7 File Watcher

There is another option: using an open source ESB with a DB adaptor triggering logic (e.g. Fuse or Mule or OpenAdapter), but this gives powerful functionality beyond your stated requirements, and is time-consuming and complex to install and learn.

EJB Timer Example using @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
    }
}

See Also: See This Answer for sending CDI Event Objects from EJB to Web container.


I ma not sure how far this solution satisfy your need but can be considered as an option. If you are using oracle then oracle you can write a java program and compile it as an oracle function. you can call your java program from the post insert trigger.

Java program in oracle DB

참고URL : https://stackoverflow.com/questions/12618915/how-to-implement-a-db-listener-in-java

반응형