11g에서부터 oracle.jdbc.OracleConnection 클래스에서 startup() 및 shutdown()이라는 새로운 JDBC 메소드를 제공한다. 이 두 메소드는 SQL*Plus에서처럼 데이터베이스를 애플리케이션에서 직접 start up하고 shut down할 수 있는 인터페이스이다.
startup()과 shutdown() 메소드를 사용하기 위해서는 커넥션을 맺을 때 반드시 preliminary 모드로 SYSDBA 혹은 SYSOPER로 커넥션을 맺어야 한다.
Preliminary 모드는 데이터베이스 인스턴스가 구동 중이 아닐 때 한 명의 유저만을 허용하여 데이터베이스 start up 시키는 작업에만 허용하는 모드이다. 커넥션 생성 시 다음과 같이 prelim_auth 프로퍼티를 true로 설정하면 된다.
1 2 3 4 5 |
prop.setProperty(“user”,”sys”); prop.setProperty(“password”,”manager”); prop.setProperty(“internal_logon”,”sysdba”); prop.setProperty(“prelim_auth”,”true”); ds.setConnectionProperties(prop); |
이렇게 커넥션을 맺은 후에 startup() 메소드를 호출해서 데이터베이스를 start up 시킨다. startup() 메소드 호출 시 다음과 같은 데이터베이스 start up 옵션 설정 파라미터를 명시할 수 있다.
– FORCE : 인스턴스 start up 전에 데이터베이스를 abort 모드로 shut down
– RESTRICT : 데이터베이스를 start up 한 후에, CREATE SESSION와 RESTRICTED 권한을 가지고 있는 유저(일반적으로는 DBA)에게만 데이터베이스 접근 허용
– NO_RESTRICTION : RESTRICTION없이 데이터베이스 start up
Startup() 메소드 호출 방법은 다음과 같다.
1 2 |
OracleConnection conn = (OracleConnection)ds.getConnection(); conn.startup(OracleConnection.DatabaseStartupMode.NO_RESTRICTION); |
Startrup() 메소드는 데이터베이스 인스턴스를 start up시키기만 할 뿐 mount하거나 open하는 것은 아니다.
데이터베이스를 mount/open하려면 다음과 같이 한다.
1 2 3 |
Statements stmt = conn.createStatement(); stmt.executeUpdate(“ALTER DATABASE MOUNT”); stmt.executeUpdate(“ALTER DATABASE OPEN”); |
Shutdown() 메소드 호출로 데이터베이스를 shutdown 할 때 설정할 수 있는 옵션은 다음과 같다.
– ABORT
– CONNECT
– FINAL
– IMMEDIATE
– TRANSACTIONAL
1 2 3 4 |
conn.shutdown(OracleConnection.DatabaseShutdownMode.IMMEDIATE); Statement stmt1 = conn.createStatement(); stmt1.excuteUpdate(“ALTER DATABASE CLOSE NORMAL”); stmt1.excuteUpdate(“ALTER DATABASE CLOSE DISMOUNT”); |
예제
Quick Implementation of Database Change Notification
DCN 기능을 이용해서 데이터베이스에 registration을 생성하고 리스너를 등록한 후, Scott 유저로 insert문장을 실행하면 리스너가 실행한 작업에 대한 DCN 이벤트를 받아서 화면에 출력해주는 애플리케이션을 구현해본다.
1.) 본 예제에서는 Scott 유저로 데이터베이스에 접근해서 작업하므로, 먼저 Scott 유저에게 다음과 같이 change notification 권한을 부여한다.
1 |
grant change notification to scott; |
2.) 다음 java 소스를 DBChangeNotification.java라는 파일에 저장한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; import oracle.jdbc.OracleConnection; import oracle.jdbc.OracleDriver; import oracle.jdbc.OracleStatement; import oracle.jdbc.dcn.DatabaseChangeEvent; import oracle.jdbc.dcn.DatabaseChangeListener; import oracle.jdbc.dcn.DatabaseChangeRegistration; public class DBChangeNotification { static final String USERNAME= "scott"; static final String PASSWORD= "tiger"; static String URL; public static void main(String[] argv) { if(argv.length < 1) { System.out.println("Error: You need to provide the URL in the first argument."); System.out.println(" For example: > java -classpath .:ojdbc5.jar DBChangeNotification \"jdbc:oracle:thin: @(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=yourhost.yourdomain.com)(PORT=1521))(CONNECT_DATA= (SERVICE_NAME=yourservicename)))\""); System.exit(1); } URL = argv[0]; DBChangeNotification demo = new DBChangeNotification(); try { demo.run(); } catch(SQLException mainSQLException ) { mainSQLException.printStackTrace(); } } void run() throws SQLException { OracleConnection conn = connect(); // first step: create a registration on the server: Properties prop = new Properties(); // if connected through the VPN, you need to provide the TCP address of the client. // For example: // prop.setProperty(OracleConnection.NTF_LOCAL_HOST,"14.14.13.12"); // Ask the server to send the ROWIDs as part of the DCN events (small performance // cost): prop.setProperty(OracleConnection.DCN_NOTIFY_ROWIDS,"true"); // The following operation does a roundtrip to the database to create a new // registration for DCN. It sends the client address (ip address and port) that // the server will use to connect to the client and send the notification // when necessary. Note that for now the registration is empty (we haven't registered // any table). This also opens a new thread in the drivers. This thread will be // dedicated to DCN (accept connection to the server and dispatch the events to // the listeners). DatabaseChangeRegistration dcr = conn.registerDatabaseChangeNotification(prop); try { // add the listenerr: DCNDemoListener list = new DCNDemoListener(this); dcr.addListener(list); // second step: add objects in the registration: Statement stmt = conn.createStatement(); // associate the statement with the registration: ((OracleStatement)stmt).setDatabaseChangeRegistration(dcr); ResultSet rs = stmt.executeQuery("select * from dept where deptno='45'"); while (rs.next()) {} String[] tableNames = dcr.getTables(); for(int i=0;i<tableNames.length;i++) System.out.println(tableNames[i]+" is part of the registration."); rs.close(); stmt.close(); } catch(SQLException ex) { // if an exception occurs, we need to close the registration in order // to interrupt the thread otherwise it will be hanging around. if(conn != null) conn.unregisterDatabaseChangeNotification(dcr); throw ex; } finally { try { // Note that we close the connection! conn.close(); } catch(Exception innerex){ innerex.printStackTrace(); } } synchronized( this ) { // The following code modifies the dept table and commits: try { OracleConnection conn2 = connect(); conn2.setAutoCommit(false); Statement stmt2 = conn2.createStatement(); stmt2.executeUpdate("insert into dept (deptno,dname) values ('45','cool dept')",Statement.RETURN_GENERATED_KEYS); ResultSet autoGeneratedKey = stmt2.getGeneratedKeys(); if(autoGeneratedKey.next()) System.out.println("inserted one row with ROWID="+autoGeneratedKey.getString(1)); stmt2.executeUpdate("insert into dept (deptno,dname) values ('50','fun dept')",Statement.RETURN_GENERATED_KEYS); autoGeneratedKey = stmt2.getGeneratedKeys(); if(autoGeneratedKey.next()) System.out.println("inserted one row with ROWID="+autoGeneratedKey.getString(1)); stmt2.close(); conn2.commit(); conn2.close(); } catch(SQLException ex) { ex.printStackTrace(); } // wait until we get the event try{ this.wait();} catch( InterruptedException ie ) {} } // At the end: close the registration (comment out these 3 lines in order // to leave the registration open). OracleConnection conn3 = connect(); conn3.unregisterDatabaseChangeNotification(dcr); conn3.close(); } /** * Creates a connection the database. */ OracleConnection connect() throws SQLException { OracleDriver dr = new OracleDriver(); Properties prop = new Properties(); prop.setProperty("user",DBChangeNotification.USERNAME); prop.setProperty("password",DBChangeNotification.PASSWORD); return (OracleConnection)dr.connect(DBChangeNotification.URL,prop); } } /** * DCN listener: it prints out the event details in stdout. */ class DCNDemoListener implements DatabaseChangeListener { DBChangeNotification demo; DCNDemoListener(DBChangeNotification dem) { demo = dem; } public void onDatabaseChangeNotification(DatabaseChangeEvent e) { Thread t = Thread.currentThread(); System.out.println("DCNDemoListener: got an event ("+this+" running on thread "+t+")"); System.out.println(e.toString()); synchronized( demo ){ demo.notify();} } } |
3.) 다음과 같이 위 java 소스를 컴파일한다.
1 2 3 4 5 6 7 8 |
UNIX ---- javac -cp .:<directory>/ojdbc5.jar DBChangeNotification.java Windows ------- javac -cp .;<directory>/ojdbc5.jar DBChangeNotification.java <directory>는 ojdbc5.jar 파일이 들어있는 디렉토리 위치로 바꾼다. 일반적으로는 $ORACLE_HOME/jdbc/lib 위치에 들어있다. |
4.) 위에서 컴파일한 클래스를 아래와 같이 실행해보자. 실행시 인수로 커넥션 URL을 넣어준다.
1 2 3 4 5 6 7 |
UNIX ---- java -cp .:<directory>/ojdbc5.jar DBChangeNotification "jdbc:oracle:thin:@obe11g:1521:ORCL" Windows ------- java -cp .;<directory>/ojdbc5.jar DBChangeNotification "jdbc:oracle:thin:@obe11g:1521:ORCL" <directory>는 ojdbc5.jar 파일이 들어있는 디렉토리 위치로 바꾼다. 일반적으로는 $ORACLE_HOME/jdbc/lib 위치에 들어있다. |
5.) 아래와 같이 리스너가 이벤트를 받아서 아래와 같이 이벤트 내용을 화면에 출력하는 것을 확인할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
SCOTT.DEPT is part of the registration. inserted one row with ROWID=AAAQ+yAAEAAAAANAAA inserted one row with ROWID=AAAQ+yAAEAAAAANAAB DCNDemoListener: got an event (DCNDemoListener@170888e running on thread Thread[Thread-2,5,main]) Connection information : local=obe11g.us.oracle.com/192.168.203.11:47632, remote=obe11g.us.oracle.com/192.168.203.11:36251 Registration ID : 1 Notification version : 1 Event type : OBJCHANGE Database name : orcl Table Change Description (length=1) operation=[INSERT], tableName=SCOTT.DEPT, objectNumber=69554 Row Change Description (length=2): ROW: operation=INSERT, ROWID=AAAQ+yAAEAAAAANAAA ROW: operation=INSERT, ROWID=AAAQ+yAAEAAAAANAAB |
Using JDBC method for Database Startup/Shutdown
데이터베이스에 접근해서 startup(), shutdown() 메소드를 이용해서 데이터베이스를 셧다운/스타트업하는 애플리케이션을 작성해본다.
1.) Starup/shutdown 메소드를 사용하기 위해서는 SYSDBA 혹은 SYSOPER 권한으로 데이터베이스에 접속해야 한다.
오라클 JDBC Thin 드라이버를 통해서 SYSDBA나 SYSOPER로 접속하려면 서버가 패스워드 파일을 사용하도록 설정해야 한다.
커맨드라인에서 다음 명령어를 실행한다.
1 |
orapwd file=$ORACLE_HOME/dbs/orapw password=yourpass entries=5 |
2.) Starup/shutdown 메소드를 사용할 유저는 SYSDBA권한을 가지고 있어야 하므로, SQL*Plus에서 다음을 실행한다.
1 2 |
GRANT SYSDBA TO system; ALTER USER system IDENTIFIED BY manager; |
3.) Init<SID>.ora 파일을 열어서 다음 행을 추가한다.
1 |
REMOTE_LOGIN_PASSWORDFILE=EXCLUSIVE |
4.) 이제 startup/shutdown 메소드를 사용하기위한 설정을 마쳤다. 데이테이스 인스턴스를 재 시작해서 설정내용을 적용한다.
5.) $ORACLE_HOME/network/admin/listener.ora 파일에 다음과 같이 JDBC를 붙으려는 서비스가 static하게 등록되어 있는지 확인하고, 등록되어 있지 않다면 아래 내용을 listener.ora 파일에 추가한다.
데이터베이스를 startup 시키려면 인스턴스가 떠 있지 않은 상태에서 그 서비스에 접속해야 하기 때문에 반드시 JDBC를 통해서 접속할 서비스가 리스너에 static하게 등록되어 있어야 한다.
1 2 3 4 5 6 7 8 |
SID_LIST_LISTENER = (SID_LIST = (SID_DESC = (GLOBAL_DBNAME = orcl.us.oracle.com) (SID_NAME = orcl) (ORACLE_HOME = /u01/app/oracle/product/11.1.0/db_1) ) ) |
6.) 다음은 데이터베이스 startup시키는 java 프로그램이다. DBStartup.java라는 이름으로 다음을 저장한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
import java.sql.Statement; import java.util.Properties; import oracle.jdbc.OracleConnection; import oracle.jdbc.pool.OracleDataSource; /** * In order to be able to logon as sysdba, you need to create a password file for user "sys": * orapwd file=$T_DBS/orapw password=manager entries=300 * and add the following setting in init.ora: * REMOTE_LOGIN_PASSWORDFILE=EXCLUSIVE * then restart the database. */ public class DBStartup { static final String DB_URL = "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=obe11g)(PORT=1521))) " +" (CONNECT_DATA=(SERVICE_NAME=orcl.us.oracle.com)))"; public static void main(String[] argv) { try{ OracleDataSource ds = new OracleDataSource(); Properties prop = new Properties(); prop.setProperty("user","sys"); prop.setProperty("password","oracle"); prop.setProperty("internal_logon","sysdba"); prop.setProperty("prelim_auth","true"); ds.setConnectionProperties(prop); ds.setURL(DB_URL); OracleConnection conn = (OracleConnection)ds.getConnection(); System.out.println(" *** OracleConnection conn = (OracleConnection)ds.getConnection() ****"); conn.startup(OracleConnection.DatabaseStartupMode.NO_RESTRICTION); System.out.println(" *** conn.startup(OracleConnection.DBSTARTUP_NO_RESTRICTION) ****"); conn.close(); OracleDataSource ds1 = new OracleDataSource(); Properties prop1 = new Properties(); prop1.setProperty("user","sys"); prop1.setProperty("password","oracle"); prop1.setProperty("internal_logon","sysdba"); ds1.setConnectionProperties(prop1); ds1.setURL(DB_URL); OracleConnection conn1 = (OracleConnection)ds1.getConnection(); System.out.println(" *** OracleConnection conn1 = (OracleConnection)ds1.getConnection() ****"); Statement stmt = conn1.createStatement(); stmt.executeUpdate("ALTER DATABASE MOUNT"); System.out.println(" *** stmt.executeUpdate(\"ALTER DATABASE MOUNT\"); ****"); stmt.executeUpdate("ALTER DATABASE OPEN"); System.out.println(" *** stmt.executeUpdate(\"ALTER DATABASE OPEN\"); ****"); stmt.close(); conn1.close(); System.out.println(" ******** The End Startup **********"); }catch (Exception e) { e.printStackTrace(); } } } |
7.) 다음은 데이터베이스 shutdown시키는 java 프로그램이다. DBShutdown.java라는 이름으로 다음을 저장한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
import java.sql.Statement; import java.util.Properties; import oracle.jdbc.OracleConnection; import oracle.jdbc.pool.OracleDataSource; public class DBShutdown { static final String DB_URL = "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=obe11g)(PORT=1521))) " +" (CONNECT_DATA=(SERVICE_NAME=orcl.us.oracle.com)))"; public static void main(String[] argv) { try{ OracleDataSource ds = new OracleDataSource(); Properties prop = new Properties(); prop.setProperty("user","sys"); prop.setProperty("password","oracle"); prop.setProperty("internal_logon","sysdba"); ds.setConnectionProperties(prop); ds.setURL(DB_URL); OracleConnection conn = (OracleConnection)ds.getConnection(); System.out.println(" *** OracleConnection conn = (OracleConnection)ds.getConnection() ****"); conn.shutdown(OracleConnection.DatabaseShutdownMode.IMMEDIATE); System.out.println(" *** conn.shutdown(OracleConnection.DBSHUTDOWN_IMMEDIATE) *****"); Statement stmt = conn.createStatement(); stmt.executeUpdate("ALTER DATABASE CLOSE NORMAL"); System.out.println(" *** stmt.executeUpdate(\"ALTER DATABASE CLOSE NORMAL\") ****"); stmt.executeUpdate("ALTER DATABASE DISMOUNT"); System.out.println(" *** stmt.executeUpdate(\"ALTER DATABASE DISMOUNT\") ****"); stmt.close(); conn.shutdown(OracleConnection.DatabaseShutdownMode.FINAL); System.out.println(" *** conn.shutdown(OracleConnection.DBSHUTDOWN_FINAL) ****"); conn.close(); System.out.println(" ******** The End ShutDown **********"); }catch(Exception e) { e.printStackTrace(); } } } |
8.) 다음의 명령어로 위에서 작성한 애플리케이션을 컴파일한다.
1 2 3 4 5 6 7 8 9 10 11 |
UNIX ---- javac -cp .:<directory>/ojdbc5.jar DBStartup.java javac -cp .:<directory>/ojdbc5.jar DBShutdown.java Windows ------- javac -cp .;<directory>/ojdbc5.jar DBStartup.java javac -cp .;<directory>/ojdbc5.jar DBShutdown.java <directory>는 ojdbc5.jar 파일이 들어있는 디렉토리 위치로 바꾼다. 일반적으로는 $ORACLE_HOME/jdbc/lib 위치에 들어있다. |
9.) 컴파일한 클래스를 실행한다.
1 2 3 4 5 6 7 8 9 10 11 |
UNIX ---- java -cp .:<directory>/ojdbc5.jar DBStartup java -cp .:<directory>/ojdbc5.jar DBShutdown Windows ------- java -cp .;<directory>/ojdbc5.jar DBStartup java -cp .;<directory>/ojdbc5.jar DBShutdown <directory>는 ojdbc5.jar 파일이 들어있는 디렉토리 위치로 바꾼다. 일반적으로는 $ORACLE_HOME/jdbc/lib 위치에 들어있다. |
10.) 다음과 같이 데이터베이스가 Starup/Shutdown되는 것을 확인할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$ java DBStartup *** OracleConnection conn = (OracleConnection)ds.getConnection() **** *** conn.startup(OracleConnection.DBSTARTUP_NO_RESTRICTION) **** *** OracleConnection conn1 = (OracleConnection)ds1.getConnection() **** *** stmt.executeUpdate("ALTER DATABASE MOUNT"); **** *** stmt.executeUpdate("ALTER DATABASE OPEN"); **** ******** The End Startup ********** $ java DBShutdown *** OracleConnection conn = (OracleConnection)ds.getConnection() **** *** conn.shutdown(OracleConnection.DBSHUTDOWN_IMMEDIATE) ***** *** stmt.executeUpdate("ALTER DATABASE CLOSE NORMAL") **** *** stmt.executeUpdate("ALTER DATABASE DISMOUNT") **** *** conn.shutdown(OracleConnection.DBSHUTDOWN_FINAL) **** ******** The End ShutDown ********** |
Oracle 11g 부터 오라클 JDBC는 JDBC 4.0의 모든 기능을 지원하고, 좀 더 다양한 데이터베이스 특징들을 JDBC 를 통해 사용할 수 있도록 기능이 추가되어 Java 애플리케이션에서 좀 더 향상된 데이터베이스 컨트롤이 가능해졌다.