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은 AMQP 및 JMS에 대한 일부 자동 트랜잭션 지원을 제공합니다 . 사용할 수있는 더 간단한 대안은 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) 대신 ( ScheduledExecutorService
Guava '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:
- The
DBMS_ALERT
is a simple means for such notification - Oracle AQ / Oracle Streams provide more sophisticated queue mechanisms
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
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.
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.
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.
참고URL : https://stackoverflow.com/questions/12618915/how-to-implement-a-db-listener-in-java
'Development Tip' 카테고리의 다른 글
선택자에 변수를 사용할 수 있습니까? (0) | 2020.11.23 |
---|---|
자바 스크립트 객체에 메소드 추가 (0) | 2020.11.23 |
Atom의 시작 화면을 제거하는 방법 (0) | 2020.11.21 |
MVC의 대안 (0) | 2020.11.21 |
MySQL에서 열과 테이블 이름이 대소 문자를 구분합니까? (0) | 2020.11.21 |