From 08f61d36c134069ca67e75f492a458b48f99a000 Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Mon, 24 Jul 2017 09:25:44 -0700 Subject: [PATCH 01/29] Checking in servlets for UCP --- java/ucp/WebLogicServer_Servlet/Readme.md | 35 ++++++++++++++++ java/ucp/WebLogicServer_Servlet/build.xml | 51 +++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 java/ucp/WebLogicServer_Servlet/Readme.md create mode 100644 java/ucp/WebLogicServer_Servlet/build.xml diff --git a/java/ucp/WebLogicServer_Servlet/Readme.md b/java/ucp/WebLogicServer_Servlet/Readme.md new file mode 100644 index 00000000..ef28555b --- /dev/null +++ b/java/ucp/WebLogicServer_Servlet/Readme.md @@ -0,0 +1,35 @@ +# WebLogicServer Java Servlet +The Oracle JDBC drivers allow Java applications to connect and process data in the Oracle Database. Oracle WebLogic Server is the application server for building and deploying enterprise Java EE applications. This repository has code samples for a Java Servlet that connects to the Oracle Database using the Oracle JDBC driver. We have furnished `build.xml` to compile the servlet and the `Readme.md` that has instructions to compile and deploy this servlet on WebLogic Server. If you have subscribed to any Oracle Database Service on Cloud such as DBCS, EECS, BMCS etc., follow these instructions to verify the connectivity with WebLogic Server. + +# What you need to install? + +* **Web Logic Server v12.2.1.2**: Download and install the [WebLogic Server v12.2.1.2](http://www.oracle.com/technetwork/middleware/weblogic/downloads/index.html) +* **Apache Ant**: Make sure you have [Apache ANT](http://ant.apache.org/) to compile the source code +* **JDBC driver**: You can choose to use the JDBC driver (ojdbc7.jar) that is shipped with WebLogicServer v 12.2.1.2 or you can download and use the latest [ojdbc8.jar from OTN](http://www.oracle.com/technetwork/database/features/jdbc/jdbc-ucp-122-3110062.html) +* **Oracle Database**: You need to have a working Oracle Database with the credentials to verify the successful connection. Make sure either you have subscribed to Oracle Database Service on Cloud (DBCS, EECS, BMCS, ExaCS) or installed an Oracle Database on premise. + +# Steps to compile the Java Servlet + +* **Update build.xml**: Download the `build.xml` present in this repository. Update WLS_HOME to point to the location where WebLogic Server is installed. +* **Create a Datasource in WebLogicServer**: Create an Oracle Datasource through the admin console. Refer to the blog for more details ["Create and Deploy a Java Servlet using WebLogic Server"](https://blogs.oracle.com/dev2dev/create-and-deploy-a-java-servlet-using-weblogic-server-wls). Let us name this datasource as `orcljdbc_ds` +* **Update JDBCSample_Servlet**: Download the `JDBCSample_Servlet` from this repository and Update the method `getDataSource()` to use the correct Oracle datasource name. E.g.,`orcljdbc_ds` created through admin console. +* **Create the war file**: Go to the location where the `build.xml` is located. Execute the command `ant` that will compile and also create the `JDBCSample.war` file in the `dist` folder. + +# Steps to deploy and run the Java Servlet + +* **Deploy the WAR file**: Start the WebLogic Server and open the admin console. Follow the steps in this [blog](https://blogs.oracle.com/dev2dev/create-and-deploy-a-java-servlet-using-weblogic-server-wls#step8) to deploy `JDBCSample.war` +* **Invoke the Servlet**: Invoke the servlet at `https://localhost:7001/JDBCSample/JDBCSample_Servlet` +* **Check the Results**: Check the results on the page and make sure that it prints driver information retrieved from the Oracle database. + +# Other Resources + +* [Create and Deploy a Java Servlet using WebLogic Server](https://blogs.oracle.com/dev2dev/create-and-deploy-a-java-servlet-using-weblogic-server-wls) +* [How to use the latest ojdbc8.jar in WebLogic Server?](http://www.oracle.com/technetwork/database/application-development/jdbc/jdbc-eecontainers-cloud.html#wls) +* [Using Java Containers with Exadata Express Cloud Service (EECS)](http://www.oracle.com/technetwork/database/application-development/jdbc/jdbc-eecontainers-cloud.html#wls) + + + + + + + diff --git a/java/ucp/WebLogicServer_Servlet/build.xml b/java/ucp/WebLogicServer_Servlet/build.xml new file mode 100644 index 00000000..f3d63b3d --- /dev/null +++ b/java/ucp/WebLogicServer_Servlet/build.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 11938512cd58ddc40eb8190fca27e6cab403e66b Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 06:58:05 -0700 Subject: [PATCH 02/29] Adding basic code samples --- .../BasicSamples/DateTimeStampSample.java | 307 ++++++++ java/jdbc/BasicSamples/JDBCSampleData.sql | 116 +++ java/jdbc/BasicSamples/JDBCUrlSample.java | 378 ++++++++++ java/jdbc/BasicSamples/JSONBasicSample.java | 261 +++++++ java/jdbc/BasicSamples/LobBasicSample.java | 691 ++++++++++++++++++ java/jdbc/BasicSamples/PLSQLSample.java | 548 ++++++++++++++ .../PreparedStatementBindingsSample.java | 417 +++++++++++ .../BasicSamples/PreparedStatementSample.java | 410 +++++++++++ java/jdbc/BasicSamples/SQLXMLSample.java | 280 +++++++ java/jdbc/BasicSamples/StatementSample.java | 409 +++++++++++ java/jdbc/BasicSamples/UCPBasic.java | 257 +++++++ java/jdbc/BasicSamples/UCPHarvesting.java | 369 ++++++++++ java/jdbc/BasicSamples/UCPLabeling.java | 318 ++++++++ java/jdbc/BasicSamples/UCPManager.java | 358 +++++++++ java/jdbc/BasicSamples/UCPManagerMBean.java | 377 ++++++++++ java/jdbc/BasicSamples/UCPMaxConnReuse.java | 317 ++++++++ java/jdbc/BasicSamples/UCPMultiUsers.java | 172 +++++ java/jdbc/BasicSamples/UCPTimeouts.java | 412 +++++++++++ java/jdbc/BasicSamples/books.png | Bin 0 -> 17262 bytes 19 files changed, 6397 insertions(+) create mode 100755 java/jdbc/BasicSamples/DateTimeStampSample.java create mode 100755 java/jdbc/BasicSamples/JDBCSampleData.sql create mode 100755 java/jdbc/BasicSamples/JDBCUrlSample.java create mode 100755 java/jdbc/BasicSamples/JSONBasicSample.java create mode 100755 java/jdbc/BasicSamples/LobBasicSample.java create mode 100755 java/jdbc/BasicSamples/PLSQLSample.java create mode 100755 java/jdbc/BasicSamples/PreparedStatementBindingsSample.java create mode 100755 java/jdbc/BasicSamples/PreparedStatementSample.java create mode 100755 java/jdbc/BasicSamples/SQLXMLSample.java create mode 100755 java/jdbc/BasicSamples/StatementSample.java create mode 100755 java/jdbc/BasicSamples/UCPBasic.java create mode 100755 java/jdbc/BasicSamples/UCPHarvesting.java create mode 100755 java/jdbc/BasicSamples/UCPLabeling.java create mode 100755 java/jdbc/BasicSamples/UCPManager.java create mode 100755 java/jdbc/BasicSamples/UCPManagerMBean.java create mode 100755 java/jdbc/BasicSamples/UCPMaxConnReuse.java create mode 100755 java/jdbc/BasicSamples/UCPMultiUsers.java create mode 100755 java/jdbc/BasicSamples/UCPTimeouts.java create mode 100755 java/jdbc/BasicSamples/books.png diff --git a/java/jdbc/BasicSamples/DateTimeStampSample.java b/java/jdbc/BasicSamples/DateTimeStampSample.java new file mode 100755 index 00000000..1cb405c1 --- /dev/null +++ b/java/jdbc/BasicSamples/DateTimeStampSample.java @@ -0,0 +1,307 @@ +/* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.*/ +/** + * DESCRIPTION + * + * This code sample illustrates the usage of below Oracle column data types - + *

+ * DATE, TIMESTAMP, TIMESTAMP WITH TIME ZONE and TIMESTAMP WITH LOCAL TIME ZONE + *

+ * The code sample creates a simple table with these data types and performs + * insert, update, and retrieval operation on the table. + *

+ * It is required that applications have Oracle JDBC driver jar (ojdbc8.jar) in + * the class-path, and that the database backend supports SQL (this sample uses + * an Oracle Database). + *

+ *

+ * To run the sample, you must enter the DB user's password from the console, + * and optionally specify the DB user and/or connect URL on the command-line. + * You can also modify these values in this file and recompile the code. + *

+ * + * java DateTimeStampDemo -l -u + * + */ +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.sql.Connection; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLType; +import java.sql.Statement; +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.time.ZonedDateTime; + +import oracle.jdbc.OracleType; +import oracle.jdbc.pool.OracleDataSource; + +public class DateTimeStampSample { + + // Either modify user and url values to point your DB or + // provide these values using command line arguments. + private static String user = "myuser"; + private static String password = "mypassword"; + private static String url = "jdbc:oracle:thin:@//myhost:1521/myservice"; + + public static void main(String[] args) throws Exception { + + // These 2 can be either default or from command-line + url = getOptionValue(args, "-l", url); + user = getOptionValue(args, "-u", user); + + // DB user's Password must be entered + readPassword(" Enter Password for " + user + ": "); + + DateTimeStampSample demo = new DateTimeStampSample(); + demo.run(); + } + + void run() throws SQLException { + try (Connection conn = getConnection()) { + + // Truncate the existing table + truncateTable(conn); + + // employee details + int empId = 1001; + Date dateOfBirth = Date.valueOf("1988-09-04"); + LocalDateTime joiningDate = LocalDateTime.now(); + ZonedDateTime dateOfResignation = ZonedDateTime + .parse("2018-05-09T22:22:22-08:00[PST8PDT]"); + Timestamp dateOfLeaving = Timestamp.valueOf(LocalDateTime.now()); + Employee e = new Employee(empId, dateOfBirth, joiningDate, + dateOfResignation, dateOfLeaving); + show("\nInsert employee record into table with id = "+empId); + insertEmployee(e, conn); + + show("\nEmployee details of employee = " + empId); + Employee emp = getEmployeeDetails(1001, conn); + if (emp != null) + emp.print(); + + show("Update the employee details of employee = " + empId); + updateEmployee(empId, conn); + + show("\nUpdated details of employee = " + empId); + Employee emp1 = getEmployeeDetails(1001, conn); + if (emp1 != null) + emp1.print(); + + show("JDBCDateTimeSample demo completes."); + } + + } + + /** + * Inserts employee data into table using given connection. + * + * @param emp + * Employee data + * @param conn + * Connection to be used to insert the employee data. + * @throws SQLException + */ + private void insertEmployee(Employee emp, Connection conn) + throws SQLException { + final String insertQuery = "INSERT INTO EMP_DATE_JDBC_SAMPLE VALUES(?,?,?,?,?)"; + try (PreparedStatement pstmt = conn.prepareStatement(insertQuery)) { + SQLType dataType = null; + + pstmt.setInt(1, emp.getId()); + pstmt.setDate(2, emp.getDateOfBirth()); + dataType = OracleType.TIMESTAMP_WITH_LOCAL_TIME_ZONE; + pstmt.setObject(3, emp.getJoiningDate(), dataType); + dataType = OracleType.TIMESTAMP_WITH_TIME_ZONE; + pstmt.setObject(4, emp.getResignationDate(), dataType); + pstmt.setTimestamp(5, emp.getDateOfLeaving()); + pstmt.executeUpdate(); + show("Employee record inserted successfully."); + } + } + + /** + * Fetches the employee data for given employee id. + * + * @param id + * Employee id. + * @param conn + * Connection to be used to fetch employee data. + * @return + * @throws SQLException + */ + private Employee getEmployeeDetails(int id, Connection conn) + throws SQLException { + final String selectQuery = "SELECT EMP_ID, DATE_OF_BIRTH, DATE_OF_JOINING, " + + "DATE_OF_RESIGNATION, DATE_OF_LEAVING FROM EMP_DATE_JDBC_SAMPLE WHERE EMP_ID = ?"; + try (PreparedStatement pstmt = conn.prepareStatement(selectQuery)) { + pstmt.setInt(1, id); + try (ResultSet rs = pstmt.executeQuery()) { + if (rs.next()) { + int employeeId = rs.getInt(1); + Date datOfBirth = rs.getDate(2); + LocalDateTime dateOfJoining = rs.getObject(3, LocalDateTime.class); + ZonedDateTime dateOfResignation = rs + .getObject(4, ZonedDateTime.class); + Timestamp dateOfLeaving = rs.getTimestamp(5); + return new Employee(employeeId, datOfBirth, dateOfJoining, + dateOfResignation, dateOfLeaving); + } else { + show("Employee record not found in the database."); + return null; + } + } + + } + } + + /** + * Updates the employee record for given employee id. + * + * @param id + * Employee id. + * @param conn + * Connection to be used to update employee data. + * @throws SQLException + */ + private void updateEmployee(int id, Connection conn) throws SQLException { + final String updateQuery = "UPDATE EMP_DATE_JDBC_SAMPLE SET DATE_OF_JOINING=? WHERE EMP_ID =?"; + try (PreparedStatement pstmt = conn.prepareStatement(updateQuery)) { + SQLType dataType = OracleType.TIMESTAMP_WITH_LOCAL_TIME_ZONE; + pstmt.setObject(1, + ZonedDateTime.parse("2015-12-09T22:22:22-08:00[PST8PDT]"), dataType); + pstmt.setInt(2, id); + int updateCount = pstmt.executeUpdate(); + show("Successfully updated employee details."); + } + } + + private void truncateTable(Connection conn) { + final String sql = "TRUNCATE TABLE EMP_DATE_JDBC_SAMPLE"; + try (Statement st = conn.createStatement()) { + st.executeQuery(sql); + show("Table truncated successfully."); + } catch (SQLException e) { + showError("Truncate table operation failed.", e); + } + } + + static Connection getConnection() throws SQLException { + OracleDataSource ods = new OracleDataSource(); + ods.setURL(url); + ods.setUser(user); + ods.setPassword(password); + Connection conn = ods.getConnection(); + return conn; + } + + private static void show(String msg) { + System.out.println(msg); + } + + static void showError(String msg, Throwable exc) { + System.out.println(msg + " hit error: " + exc.getMessage()); + } + + // Get specified option value from command-line, or use default value + static String getOptionValue(String args[], String optionName, + String defaultVal) { + String argValue = ""; + try { + int i = 0; + String arg = ""; + boolean found = false; + while (i < args.length) { + arg = args[i++]; + if (arg.equals(optionName)) { + if (i < args.length) + argValue = args[i++]; + if (argValue.startsWith("-") || argValue.equals("")) { + argValue = defaultVal; + } + found = true; + } + } + + if (!found) { + argValue = defaultVal; + } + } catch (Exception e) { + showError("getOptionValue", e); + } + return argValue; + } + + /** + * Reads the password from console. + * + * @param prompt + * @throws Exception + */ + static void readPassword(String prompt) throws Exception { + if (System.console() != null) { + char[] pchars = System.console().readPassword("\n[%s]", prompt); + if (pchars != null) { + password = new String(pchars); + java.util.Arrays.fill(pchars, ' '); + } + } else { + BufferedReader r = new BufferedReader(new InputStreamReader(System.in)); + show(prompt); + password = r.readLine(); + } + } + + /** + * A simple class to represent the employee table structure. An instance of + * this class represents a row in employee table. + */ + static class Employee { + private int id; + private Date dateOfBirth; + private LocalDateTime joiningDate; + private ZonedDateTime dateOfResignation; + private Timestamp dateOfLeaving; + + Employee(int id, Date dateOfBirth, LocalDateTime joiningDate, + ZonedDateTime dateOfResignation, Timestamp dateOfLeaving) { + this.id = id; + this.dateOfBirth = dateOfBirth; + this.joiningDate = joiningDate; + this.dateOfResignation = dateOfResignation; + this.dateOfLeaving = dateOfLeaving; + } + + int getId() { + return id; + } + + Date getDateOfBirth() { + return this.dateOfBirth; + } + + LocalDateTime getJoiningDate() { + return this.joiningDate; + } + + ZonedDateTime getResignationDate() { + return this.dateOfResignation; + } + + Timestamp getDateOfLeaving() { + return this.dateOfLeaving; + } + + void print() { + show("/----------------------------------------------------------------/"); + show("ID : " + id); + show("Date Of Birth : " + dateOfBirth); + show("Joining Date : " + joiningDate); + show("Resignation Date : " + dateOfResignation); + show("Date of Leaving : " + dateOfLeaving); + show("/----------------------------------------------------------------/\n"); + } + } +} diff --git a/java/jdbc/BasicSamples/JDBCSampleData.sql b/java/jdbc/BasicSamples/JDBCSampleData.sql new file mode 100755 index 00000000..aed55ec4 --- /dev/null +++ b/java/jdbc/BasicSamples/JDBCSampleData.sql @@ -0,0 +1,116 @@ +Rem JDBCSampleData.sql +Rem +Rem Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. +Rem +Rem NAME +Rem JDBCSampleData.sql +Rem +Rem DESCRIPTION +Rem This SQL script is for creating a new database user and a sample schema +Rem that JDBC code samples use. +Rem +Rem MODIFIED (MM/DD/YY) +Rem nbsundar 04/06/18 - Created + +Rem Create a new user "jdbcuser" that will be used in all JDBC code samples +Rem Login as sysadmin before executing the script +CREATE USER jdbcuser IDENTIFIED BY jdbcuser123; + +Rem Grant connect and resource access to the new "jdbcuser" +Rem so that the user can connect and create objects +GRANT CONNECT, RESOURCE TO jdbcuser; + +Rem Grant required access to the new "jdbcuser" +GRANT UNLIMITED TABLESPACE TO jdbcuser; + +Rem Switch the current session to the new jdbcuser session +ALTER SESSION SET CURRENT_SCHEMA=jdbcuser; + +Rem Used in the SQLXMLSample.java code sample +CREATE TABLE SQLXML_JDBC_SAMPLE (DOCUMENT XMLTYPE, ID NUMBER); + +Rem Used in the PLSQLSample.java code sample +CREATE TABLE PLSQL_JDBC_SAMPLE + (NUM NUMBER(4) NOT NULL, + NAME VARCHAR2(20) NOT NULL, + INSERTEDBY VARCHAR2(20)); + +Rem Used in LOBBasic.java code sample +CREATE TABLE LOB_JDBC_SAMPLE + (LOB_ID INT NOT NULL, + BLOB_DATA BLOB, + CLOB_DATA CLOB, + NCLOB_DATA NCLOB); + +Rem Used in DateTimeStampSample.java code sample +CREATE TABLE EMP_DATE_JDBC_SAMPLE +(EMP_ID INTEGER PRIMARY KEY, + DATE_OF_BIRTH DATE, + DATE_OF_JOINING TIMESTAMP WITH LOCAL TIME ZONE, + DATE_OF_RESIGNATION TIMESTAMP WITH TIME ZONE, + DATE_OF_LEAVING TIMESTAMP); + +Rem Used in JSONBasicDemo.java code sample +CREATE TABLE JSON_EMP_JDBC_SAMPLE + (EMP_ID RAW(16) NOT NULL PRIMARY KEY, + DATE_LOADED TIMESTAMP WITH TIME ZONE, + EMPLOYEE_DOCUMENT CLOB CONSTRAINT + ENSURE_JSON CHECK (EMPLOYEE_DOCUMENT IS JSON)); + +Rem Used in JSONBasicDemo.java code sample +INSERT INTO JSON_EMP_JDBC_SAMPLE VALUES (SYS_GUID(), SYSTIMESTAMP, '{"employee_number": 1, "employee_name": "John Doe", "salary": 2000}'); +INSERT INTO JSON_EMP_JDBC_SAMPLE VALUES (SYS_GUID(), SYSTIMESTAMP, '{"employee_number": 2, "employee_name": "Jane Doe", "salary": 2010}'); +INSERT INTO JSON_EMP_JDBC_SAMPLE VALUES (SYS_GUID(), SYSTIMESTAMP, '{"employee_number": 3, "employee_name": "John Smith", "salary": 3000, "sons": [{"name": "Angie"}, {"name": "Linda"}]}'); +INSERT INTO JSON_EMP_JDBC_SAMPLE VALUES (SYS_GUID(), SYSTIMESTAMP, '{"employee_number": 3, "employee_name": "Jane Williams", "salary": 1000, "sons": [{"name": "Rosie"}]}'); + +Rem General DEPT table for other code samples +CREATE TABLE DEPT + (DEPTNO NUMBER(2) CONSTRAINT PK_DEPT PRIMARY KEY, + DNAME VARCHAR2(14) , + LOC VARCHAR2(13) ) ; + +Rem Populate the table DEPT with few records +INSERT INTO DEPT VALUES(10,'ACCOUNTING','NEW YORK'); +INSERT INTO DEPT VALUES(20,'RESEARCH','DALLAS'); +INSERT INTO DEPT VALUES(30,'SALES','CHICAGO'); +INSERT INTO DEPT VALUES(40,'OPERATIONS','BOSTON'); + +Rem General EMP table for other code samples +CREATE TABLE EMP + (EMPNO NUMBER(4) CONSTRAINT PK_EMP PRIMARY KEY, + ENAME VARCHAR2(10), + JOB VARCHAR2(9), + MGR NUMBER(4), + HIREDATE DATE, + SAL NUMBER(7,2), + COMM NUMBER(7,2), + DEPTNO NUMBER(2) CONSTRAINT FK_DEPTNO REFERENCES DEPT); + +Rem Populate the table EMP with few records +INSERT INTO EMP VALUES(7369,'SMITH','CLERK',7902,to_date('17-12-1980','dd-mm-yyyy'),800,NULL,20); +INSERT INTO EMP VALUES(7499,'ALLEN','SALESMAN',7698,to_date('20-2-1981','dd-mm-yyyy'),1600,300,30); +INSERT INTO EMP VALUES(7521,'WARD','SALESMAN',7698,to_date('22-2-1981','dd-mm-yyyy'),1250,500,30); +INSERT INTO EMP VALUES(7566,'JONES','MANAGER',7839,to_date('2-4-1981','dd-mm-yyyy'),2975,NULL,20); +INSERT INTO EMP VALUES(7654,'MARTIN','SALESMAN',7698,to_date('28-9-1981','dd-mm-yyyy'),1250,1400,30); +INSERT INTO EMP VALUES(7698,'BLAKE','MANAGER',7839,to_date('1-5-1981','dd-mm-yyyy'),2850,NULL,30); +INSERT INTO EMP VALUES(7782,'CLARK','MANAGER',7839,to_date('9-6-1981','dd-mm-yyyy'),2450,NULL,10); +INSERT INTO EMP VALUES(7788,'SCOTT','ANALYST',7566,to_date('13-JUL-87')-85,3000,NULL,20); +INSERT INTO EMP VALUES(7839,'KING','PRESIDENT',NULL,to_date('17-11-1981','dd-mm-yyyy'),5000,NULL,10); +INSERT INTO EMP VALUES(7844,'TURNER','SALESMAN',7698,to_date('8-9-1981','dd-mm-yyyy'),1500,0,30); +INSERT INTO EMP VALUES(7876,'ADAMS','CLERK',7788,to_date('13-JUL-87')-51,1100,NULL,20); +INSERT INTO EMP VALUES(7900,'JAMES','CLERK',7698,to_date('3-12-1981','dd-mm-yyyy'),950,NULL,30); +INSERT INTO EMP VALUES(7902,'FORD','ANALYST',7566,to_date('3-12-1981','dd-mm-yyyy'),3000,NULL,20); +INSERT INTO EMP VALUES(7934,'MILLER','CLERK',7782,to_date('23-1-1982','dd-mm-yyyy'),1300,NULL,10); + + +Rem commit the changes to the database +commit; + +Rem remove the tables for any clean up +Rem drop table SQLXML_JDBC_SAMPLE; +Rem drop table PLSQL_JDBC_SAMPLE; +Rem drop table LOB_JDBC_SAMPLE; +Rem drop table JSON_EMP_JDBC_SAMPLE; +Rem drop table EMP_DATE_JDBC_SAMPLE; +Rem drop table EMP; +Rem drop table DEPT; diff --git a/java/jdbc/BasicSamples/JDBCUrlSample.java b/java/jdbc/BasicSamples/JDBCUrlSample.java new file mode 100755 index 00000000..f9cc861f --- /dev/null +++ b/java/jdbc/BasicSamples/JDBCUrlSample.java @@ -0,0 +1,378 @@ +/* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.*/ + +/** + * DESCRIPTION + * + * This class demonstrates how to specify a URL to the Oracle JDBC Driver. + *
+ * The demo can be configured to use the following forms of URLs: + *
    + *
  1. Thin-Style
  2. + *
  3. Oracle Net Connect Descriptor
  4. + *
  5. TNS Alias
  6. + *
+ * + *

Thin-Style URL

+ * Thin-style is the simplest form of URL. It consists of a host, port, and a + * service name or system identifier (SID). To run the demo using a thin-style + * URL, provide command line arguments in either of the following forms: + *
+ * -t {host} {port} {sid} + *
+ * -t {host} {port} /{service_name} + *
+ * Note the '/' character is used to differentiate between an SID and service name. + * + *

Connect Descriptor URL

+ * Connect descriptors offer a syntax for advanced configuration of the + * network connection. To learn about the syntax, see the Oracle Net Database + * Net Services Guide linked to below. This demo will use a minimal + * configuration consisting of a host, port, and service name or SID. To run + * the demo using a connect descriptor URL, provide command line arguments in + * either of the following forms: + *
+ * -c {host} {port} {sid} + *
+ * -c {host} {port} /{service_name} + *
+ * Note the '/' character is used to differentiate between an SID and service name. + * + *

TNS Alias URL

+ * A TNS alias is a reference to an connect descriptor defined in a + * tnsnames.ora file. To learn more about tnsnames.ora, see the Oracle + * Database Net Services Reference linked to below. To run the demo using a + * TNS alias URL, provide command line arguments in either of the following + * forms: + *
+ * -a {alias} {tns_names_dir} + *
+ * Where tns_names_dir is the directory which holds tnsnames.ora. + * + *

Further Reading

+ * + */ + +import java.io.Console; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.Objects; +import java.util.Properties; + +import oracle.jdbc.pool.OracleDataSource; +import oracle.jdbc.OracleConnection; + +public class JDBCUrlSample { + + // Default credentials to use if a console is not available. If you are + // running this demo in an IDE like Eclipse, you may need to define these + // values. + private static final String DEFAULT_USER = ""; + private static final String DEFAULT_PASSWORD = ""; + + /** The URL this demo will use */ + private final String url; + + /** Connection properties this demo will use. */ + private final Properties connectionProperties; + + private JDBCUrlSample(String url){ + this(url, new Properties()); + } + + private JDBCUrlSample(String url, Properties connectionProperties) { + this.url = url; + this.connectionProperties = connectionProperties; + } + + /** + * Returns a new demo object initialized with a thin-style URL using a + * database service name. + *

+ * The URL syntax is: + * jdbc:oracle:thin:@{host_name}:{port_number}/{service_name} + *

+ * @param host The hostname of an Oracle Database. + * @param port The port number of an Oracle Database. + * @param service The service name of an Oracle Database. + * @return A newly instantiated demo object. + */ + public static JDBCUrlSample newThinStyleServiceNameDemo(String host, + int port, String service) { + Objects.requireNonNull(host, "Host cannot be null"); + Objects.requireNonNull(service, "Service name cannot be null"); + + String thinStyleURL = + "jdbc:oracle:thin:@" + host + ":" + port + "/" + service; + return new JDBCUrlSample(thinStyleURL); + } + + /** + * Returns a new demo object initialized with a thin-style URL using + * a database SID. + *

+ * The URL syntax is: + * jdbc:oracle:thin:@{host_name}:{port_number}:{sid} + *

+ * @param host The hostname of an Oracle Database. + * @param port The port number of an Oracle Database. + * @param sid The system identifier of an Oracle Database. + * @return A newly instantiated demo object. + */ + public static JDBCUrlSample newThinStyleSIDDemo(String host, int port, + String sid) { + Objects.requireNonNull(host, "Host cannot be null"); + Objects.requireNonNull(sid, "SID cannot be null"); + + String thinStyleURL = + "jdbc:oracle:thin:@" + host + ":" + port + ":" + sid; + return new JDBCUrlSample(thinStyleURL); + } + + /** + * Returns a new demo object initialized with a connect descriptor URL using + * a service name. + *

+ * The URL syntax is: + * jdbc:oracle:thin:@(DESCRIPTION= + * (ADDRESS=(PROTOCOL=tcp)(HOST={host_name})(PORT={port})) + * (CONNECT_DATA=(SERVICE_NAME={service_name}))) + *

+ * @param host The hostname of an Oracle Database. + * @param port The port number of an Oracle Database. + * @param service The service name of an Oracle Database. + * @return A newly instantiated demo object. + */ + public static JDBCUrlSample newConnectDescriptorServiceNameDemo(String host, + int port, String service) { + Objects.requireNonNull(host, "Host cannot be null"); + Objects.requireNonNull(service, "Service name cannot be null"); + + String connectDescriptorURL = + "jdbc:oracle:thin:@(DESCRIPTION=" + + "(ADDRESS=(PROTOCOL=tcp)(HOST=" + host + ")(PORT=" + port + "))" + + "(CONNECT_DATA=(SERVICE_NAME=" + service + ")))"; + + return new JDBCUrlSample(connectDescriptorURL); + } + + /** + * Returns a new demo object initialized with a connect descriptor URL using + * a database SID. + *

+ * The URL syntax is: + * jdbc:oracle:thin:@(DESCRIPTION= + * (ADDRESS=(PROTOCOL=tcp)(HOST={host_name})(PORT={port})) + * (CONNECT_DATA=(SID={sid}))) + *

+ * @param host The hostname of an Oracle Database. + * @param port The port number of an Oracle Database. + * @param sid The system identifier of an Oracle Database. + * @return A newly instantiated demo object. + */ + public static JDBCUrlSample newConnectDescriptorSIDDemo(String host, int port, + String sid) { + Objects.requireNonNull(host, "Host cannot be null"); + Objects.requireNonNull(sid, "SID cannot be null"); + + String connectDescriptorURL = + "jdbc:oracle:thin:@(DESCRIPTION=" + + "(ADDRESS=(PROTOCOL=tcp)(HOST=" + host + ")(PORT=" + port + "))" + + "(CONNECT_DATA=(SID=" + sid + ")))"; + + return new JDBCUrlSample(connectDescriptorURL); + } + + /** + * Returns a new demo object initialized with a TNS Alias URL. + * The syntax is: + *

+ * jdbc:oracle:thin:@{tns_alias} + *

+ * @param alias A tnsnames.ora alias. + * @param tnsAdmin The directory of a tnsnames.ora file. + * @return A newly instantiated demo object. + */ + public static JDBCUrlSample newTNSAliasDemo(String alias, String tnsAdmin) { + Objects.requireNonNull(alias, "Alias cannot be null"); + Objects.requireNonNull(tnsAdmin, "TNS Admin cannot be null"); + + String tnsAliasURL = "jdbc:oracle:thin:@" + alias; + + // The directory of tnsnames.ora is defined as a connection property. + Properties connectionProperties = new Properties(); + connectionProperties.setProperty( + OracleConnection.CONNECTION_PROPERTY_TNS_ADMIN, tnsAdmin); + + return new JDBCUrlSample(tnsAliasURL, connectionProperties); + } + + /** + * Use this demo's URL to establish a connection. + */ + public void connectWithURL() { + try { + + // oracle.jdbc.pool.OracleDataSource is a factory for Connection + // objects. + OracleDataSource dataSource = new OracleDataSource(); + + // The data source is configured with database user and password. + setCredentials(dataSource); + + // The data source is configured with connection properties. + dataSource.setConnectionProperties(connectionProperties); + + // The data source is configured with a database URL. + dataSource.setURL(url); + + // The data source is used to create a Connection object. + System.out.println("\nConnecting to: " + url); + Connection connection = dataSource.getConnection(); + System.out.println("Connection Established!"); + + // Close the connection when its no longer in use. This will free up + // resources in the Java client and the database host. + connection.close(); + } + catch (SQLException sqlE) { + // The getConnection() call throws a SQLException if a connection could + // not be established. + displayConnectionError(sqlE); + } + } + + private void setCredentials(OracleDataSource dataSource) { + String user; + char[] password; + Console console = System.console(); + + if(console == null) { + System.out.println( + "\nNo console available. Using default user and password."); + user = DEFAULT_USER; + password = DEFAULT_PASSWORD.toCharArray(); + } + else { + user = console.readLine("\nUser: "); + password = console.readPassword(user + "'s password: "); + } + + dataSource.setUser(user); + dataSource.setPassword(new String(password)); + Arrays.fill(password, ' '); + } + + private void displayConnectionError(SQLException sqlE) { + System.out.println( + "Connection establishment failed with the following error:"); + + Throwable cause = sqlE; + do { + System.out.println(cause.getMessage()); + cause = cause.getCause(); + } while(cause != null); + } + + // All code beyond this point is related to command line argument parsing. + private static final String THIN_STYLE_OPTION = "-t"; + private static final String DESCRIPTOR_OPTION = "-c"; + private static final String TNS_ALIAS_OPTION = "-a"; + private static final String USAGE_MESSAGE = + "Please provide command line arguments in one of the following forms:" + + "\n\nThin-Style URL:\n\t" + + THIN_STYLE_OPTION + " {host} {port} {sid}\n\t" + + THIN_STYLE_OPTION + " {host} {port} /{service_name}" + + "\n\nOracle Net Connect Descriptor:\n\t" + + DESCRIPTOR_OPTION + " {host} {port} {sid}\n\t" + + DESCRIPTOR_OPTION + " {host} {port} /{service_name}" + + "\n\nTNS Names Alias:\n\t" + + TNS_ALIAS_OPTION + " {alias} {tnsnames_directory}"; + + private static int ARG_POSITION_HOST = 1; + private static int ARG_POSITION_PORT = 2; + private static int ARG_POSITION_SERVICE = 3; + private static int THIN_STYLE_ARG_COUNT = 4; + private static int DESCRIPTOR_ARG_COUNT = 4; + + private static int ARG_POSITION_ALIAS = 1; + private static int ARG_POSITION_TNS_ADMIN = 2; + private static int TNS_ALIAS_ARG_COUNT = 3; + + public static void main(String[] args) { + + // Read the URL type argument and initialize a demo for it. + JDBCUrlSample demo = null; + String typeOption = args.length > 0 ? args[0] : ""; + switch(typeOption) { + + case THIN_STYLE_OPTION: + if(args.length == THIN_STYLE_ARG_COUNT) { + String host = args[ARG_POSITION_HOST]; + int port = Integer.valueOf(args[ARG_POSITION_PORT]); + String service = args[ARG_POSITION_SERVICE]; + demo = service.startsWith("/") + ? newThinStyleServiceNameDemo(host, port, + service.substring(1)) + : newThinStyleSIDDemo(host, port, service); + + } + break; + + case DESCRIPTOR_OPTION: + if(args.length == DESCRIPTOR_ARG_COUNT) { + String host = args[ARG_POSITION_HOST]; + int port = Integer.valueOf(args[ARG_POSITION_PORT]); + String service = args[ARG_POSITION_SERVICE]; + demo = service.startsWith("/") + ? newConnectDescriptorServiceNameDemo(host, port, + service.substring(1)) + : newConnectDescriptorSIDDemo(host, port, service); + } + break; + + case TNS_ALIAS_OPTION: + if(args.length == TNS_ALIAS_ARG_COUNT) { + String alias = args[ARG_POSITION_ALIAS]; + String tnsAdmin = args[ARG_POSITION_TNS_ADMIN]; + demo = newTNSAliasDemo(alias, tnsAdmin); + } + break; + + default: + demo = null; + } + + if(demo == null) { + System.out.println(USAGE_MESSAGE); + } + else { + demo.connectWithURL(); + } + } +} diff --git a/java/jdbc/BasicSamples/JSONBasicSample.java b/java/jdbc/BasicSamples/JSONBasicSample.java new file mode 100755 index 00000000..1f697929 --- /dev/null +++ b/java/jdbc/BasicSamples/JSONBasicSample.java @@ -0,0 +1,261 @@ +/* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.*/ + +/** + * DESCRIPTION + * + * This code sample objective is to show how to use some of the + * enhancements in JavaScript Object Notation (JSON) support for + * Oracle Database 12c Release 2 (12.2). + * + * This release incorporates significant new features. In this sample + * we are going to expose the following: + * + *
    + *
  • Create tables and constraints on columns for JSON documents + * using ENSURE_JSON and IS JSON + * directives.
  • + *
  • Load tables validating those constraints.
  • + *
  • Use Simple Dot-Notation Access to JSON Data.
  • + *
  • Use Simple SQL/JSON Path Expressions using + * JSON_VALUE.
  • + *
  • Use Complex SQL/JSON Path Expressions using + * JSON_EXISTS.
  • + *
+ * + * It is required that applications have Oracle JDBC driver jar on + * the classpath. This sample is based on Oracle as the database + * backend. + * + * To run the sample, you must enter the DB user's password from the + * console, and optionally specify the DB user and/or connect URL on + * the command-line. You can also modify these values in this file + * and recompile the code. + * java JSONBasicDemo -l -u + */ + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +// From Oracle JDBC driver +import oracle.jdbc.pool.OracleDataSource; + +public class JSONBasicSample { + + final static String DEFAULT_URL = "jdbc:oracle:thin:@//myhost:myport/myservice"; + final static String DEFAULT_USER = "myuser"; + final static String DEFAULT_PASSWORD = "mypassword"; + final static String CONN_FACTORY_CLASS = "oracle.jdbc.pool.OracleDataSource"; + + // You must provide non-default values for ALL 3 to execute the program + static String url = DEFAULT_URL; + static String user = DEFAULT_USER; + static String password = DEFAULT_PASSWORD; + + public static void main(String args[]) throws Exception { + + // The program exits if any of the 3 fields still has default value. + getRealUserPasswordUrl(args); + + // Run sample. + JSONBasicSample sample = new JSONBasicSample(); + sample.run(); + } + + private OracleDataSource dataSource = null; + private Connection connection = null; + private Statement statement = null; + private PreparedStatement getSalaryStatement = null; + + private void run() throws Exception { + + // Set up test: open connection/statement to be used through the demo. + demoSetUp(); + + // Test the constraint with an incorrect JSON document. + // If the SQLException is not caught, show as an error. + try { + demoExecute("INSERT INTO JSON_EMP_JDBC_SAMPLE VALUES (SYS_GUID(), SYSTIMESTAMP, '{\"emp\"loyee_number\": 5, \"employee_name\": \"Jack Johnson\"}')"); + showError("Error!. SQLException was expected to be thrown because of bad formatted JSON.", new SQLException()); + } catch (SQLException sqlException) { + show("Good catch! SQLException was expected to be thrown because of bad formatted JSON."); + } + + // This is a Simple Dot Notation Query on a column with a JSON document. + // The return value for a dot-notation query is always a string (data type VARCHAR2) representing JSON data. + // The content of the string depends on the targeted JSON data, as follows: + // If a single JSON value is targeted, then that value is the string content, whether it is a JSON scalar, object, or array. + // If multiple JSON values are targeted, then the string content is a JSON array whose elements are those values. + demoExecuteAndShow("SELECT em.employee_document.employee_number, em.employee_document.salary FROM JSON_EMP_JDBC_SAMPLE em"); + + // This is a Simple Path Notation Query + // SQL/JSON path expressions are matched by SQL/JSON functions and conditions against JSON data, to select portions of it. + // Path expressions are analogous to XQuery and XPath expression. They can use wild-cards and array ranges. Matching is case-sensitive. + demoExecuteAndShow("SELECT JSON_VALUE(employee_document, '$.employee_number') FROM JSON_EMP_JDBC_SAMPLE where JSON_VALUE(employee_document, '$.salary') > 2000"); + + // This is a Complex Path Notation Query (employees with at least one son named 'Angie'). + // An absolute simple path expression begins with a dollar sign ($), which represents the path-expression context item. + // The dollar sign is followed by zero or more path steps. + // Each step can be an object step or an array step, depending on whether the context item represents a JSON object or a JSON array. + // The last step of a simple path expression can be a single, optional function step. + // In all cases, path-expression matching attempts to match each step of the path expression, in turn. + // If matching any step fails then no attempt is made to match the subsequent steps, and matching of the path expression fails. + // If matching each step succeeds then matching of the path expression succeeds. + demoExecuteAndShow("SELECT JSON_VALUE(employee_document, '$.employee_name') FROM JSON_EMP_JDBC_SAMPLE where JSON_EXISTS(employee_document, '$.sons[*]?(@.name == \"Angie\")')"); + + // Demo using getSalary for an existing number + show("Get salary for Jane Doe (employee number:2), " + + "2010 expected: " + getSalary(2)); + + // Demo using getSalary for a non existing number + show("Get salary for non existing (employee number:5), " + + "negative value expected: " + getSalary(5)); + + // Tear down test: close connections/statements that were used through the demo. + demoTearDown(); + } + + /** + * Return an employee's salary using the employee number. + * If the employee does not exist, a negative value is returned. + * Demo based on a Path Notation Query in a PreparedStatement. + * + * @param employee number. + * @return employee salary, negative value if not found. + */ + private double getSalary(long employeeNumber) throws SQLException { + + // Bind parameter (employee number) to the query. + getSalaryStatement.setLong(1, employeeNumber); + + // Return salary (negative value if not found). + ResultSet employees = getSalaryStatement.executeQuery(); + if (employees.next()) { + return employees.getDouble(1); + } else { + return -1d; + } + } + + private void demoSetUp() throws SQLException { + dataSource = new OracleDataSource(); + dataSource.setURL(url); + dataSource.setUser(user); + dataSource.setPassword(password); + connection = dataSource.getConnection(); + statement = connection.createStatement(); + + // PreparedStatement to return the salary from an employee using the + // employee number (with Path Notation Query over the document). + getSalaryStatement = connection.prepareStatement( + "SELECT JSON_VALUE(employee_document, '$.salary') " + + "FROM JSON_EMP_JDBC_SAMPLE where JSON_VALUE " + + "(employee_document, '$.employee_number') = ?"); + + } + + private ResultSet demoExecute(String sql) throws SQLException { + return statement.executeQuery(sql); + } + + private void demoExecuteAndShow(String sql) throws SQLException { + ResultSet resultSet = demoExecute(sql); + final int columnCount = resultSet.getMetaData().getColumnCount(); + while (resultSet.next()) { + StringBuffer output = new StringBuffer(); + for (int columnIndex = 1; columnIndex <= columnCount; columnIndex++) + output.append(resultSet.getString(columnIndex)).append("|"); + show(output.toString()); + } + } + + private void demoTearDown() throws SQLException { + statement.close(); + getSalaryStatement.close(); + connection.close(); + } + + private static void show(String msg) { + System.out.println(msg); + } + + static void showError(String msg, Throwable exc) { + System.out.println(msg + " hit error: " + exc.getMessage()); + } + + // The program exits if any of the 3 fields still has default value. + static void getRealUserPasswordUrl(String args[]) throws Exception { + // URL can be modified in file, or taken from command-line + url = getOptionValue(args, "-l", DEFAULT_URL); + if (DEFAULT_URL.equals(url)) { + show("\nYou must provide a non-default, working connect URL. Exit."); + System.exit(1); + } + + // DB user can be modified in file, or taken from command-line + user = getOptionValue(args, "-u", DEFAULT_USER); + if (DEFAULT_USER.equals(user)) { + show("\nYou must provide a non-default, working DB user. Exit."); + System.exit(1); + } + + // DB user's password can be modified in file, or explicitly entered + readPassword(" Password for " + user + ": "); + if (DEFAULT_PASSWORD.equals(password)) { + show("\nYou must provide a non-default, working DB password. Exit."); + System.exit(1); + } + } + + // Get specified option value from command-line. + static String getOptionValue( + String args[], String optionName, String defaultVal) { + String argValue = ""; + try { + int i = 0; + String arg = ""; + boolean found = false; + + while (i < args.length) { + arg = args[i++]; + + if (arg.equals(optionName)) { + if (i < args.length) + argValue = args[i++]; + if (argValue.startsWith("-") || argValue.equals("")) { + show("No value specified for Option " + optionName); + argValue = defaultVal; + } + found = true; + } + } + + if (!found) { + show("No Option " + optionName + " specified"); + argValue = defaultVal; + } + } catch (Exception e) { + showError("getOptionValue", e); + } + + return argValue; + } + + static void readPassword(String prompt) throws Exception { + if (System.console() != null) { + char[] pchars = System.console().readPassword("\n[%s]", prompt); + if (pchars != null) { + password = new String(pchars); + java.util.Arrays.fill(pchars, ' '); + } + } else { + BufferedReader r = new BufferedReader(new InputStreamReader(System.in)); + show(prompt); + password = r.readLine(); + } + } +} diff --git a/java/jdbc/BasicSamples/LobBasicSample.java b/java/jdbc/BasicSamples/LobBasicSample.java new file mode 100755 index 00000000..5ae165b3 --- /dev/null +++ b/java/jdbc/BasicSamples/LobBasicSample.java @@ -0,0 +1,691 @@ +/* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.*/ + +/* + DESCRIPTION + + Various lob operations sample. + To run the sample, you must enter the DB user's password from the + console, and optionally specify the DB user and/or connect URL on + the command-line. You can also modify these values in this file + and recompile the code. + java LobBasic -l -u + + NOTES + Sample uses books.txt and books.png from current directory. + + */ + +import java.sql.Connection; +import java.sql.NClob; +import java.sql.Statement; +import java.sql.Types; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Blob; +import java.sql.Clob; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.text.NumberFormat; + +import oracle.jdbc.internal.OracleStatement; +import oracle.jdbc.pool.OracleDataSource; +import oracle.sql.CLOB; + +/** + * + * Shows dealing with the various LOB data types. Shows how to add a row to a + * table that has a LOB column. Shows the LOB 2 LONG code path with + * defineColumnType, LOB prefetch (tune the size). Shows how to create a temp + * lob. + * + * @author igarish + * + */ +public class LobBasicSample { + final static String DEFAULT_URL = "jdbc:oracle:thin:@//myhost:myport/myservice"; + final static String DEFAULT_USER = "myuser"; + final static String DEFAULT_PASSWORD = "mypassword"; + + // You must provide non-default values for ALL 3 to execute the program + static String url = DEFAULT_URL; + static String user = DEFAULT_USER; + static String password = DEFAULT_PASSWORD; + + // Table name used in this sample + // Sample truncates the table and show lob operations + private final String TABLE_NAME = "LOB_JDBC_SAMPLE"; + + // Lob read/write chunk buffer size + private final int CHUNK_BUFFER_SIZE = 1024; + + // Connection object for various lob operations. + // Sample uses only one connection for all lob + // operations in this demo program. + private Connection conn; + + /** + * Entry point of the sample. + * + * @param args + * Command line arguments. Supported command line options: -l + * -u + * @throws Exception + */ + public static void main(String args[]) throws Exception { + LobBasicSample lobBasic = new LobBasicSample(); + + getRealUserPasswordUrl(args); + + // Get connection and initialize schema. + lobBasic.setup(); + + // Shows clob operations + lobBasic.clobSample(); + + // Shows clob operations with an empty clob + lobBasic.clobSampleWithEmptyClob(); + + // Shows blob operations + lobBasic.blobSample(); + + // Shows blob operations with an empty blob + lobBasic.blobSampleWithEmptyBlob(); + + // Shows nclob operations + lobBasic.nclobSample(); + + // Shows temporary clob operations + lobBasic.temporaryClobSample(); + + // Fetch a CLOB as a LONG + lobBasic.clobAsLongSample(); + + // Shows how to specify lob prefetch size to fine tune + // clob performance. + lobBasic.clobSampleWithLobPrefetchSize(); + + // Drop table and disconnect from the database. + lobBasic.cleanup(); + } + + // Gets connection to the database and truncate the table + void setup() throws SQLException { + conn = getConnection(); + truncateTable(); + conn.setAutoCommit(false); + } + + // Truncates the table and disconnect from the database + void cleanup() throws SQLException { + if (conn != null) { + truncateTable(); + conn.close(); + conn = null; + } + } + + // Shows how to create a Clob, insert data in the Clob, + // retrieves data from the Clob. + void clobSample() throws Exception { + show("======== Clob Sample ========"); + try (PreparedStatement pstmt = conn.prepareStatement( + "INSERT INTO " + TABLE_NAME + " (LOB_ID, CLOB_DATA) VALUES (1, ?)")) { + // Creates and fill data in the clob + Clob clob = conn.createClob(); + clob.setString(1, "Book Title - Java for Dummies"); + + // Insert clob data in the column of a table. + pstmt.setClob(1, clob); + pstmt.execute(); + conn.commit(); + + // Get data from the clob column. + executeClobQuery(1); + } + } + + // Shows how to create an empty Clob, insert data in the Clob, + // retrieves data from the Clob. + void clobSampleWithEmptyClob() throws Exception { + show("======== Clob Sample with an empty clob ========"); + // Creates an empty clob in a table then update it with actual data. + try (PreparedStatement pstmt = conn.prepareStatement( + "INSERT INTO " + TABLE_NAME + " (LOB_ID, CLOB_DATA) VALUES (2, empty_clob())")) { + pstmt.execute(); + + try (ResultSet rset = pstmt.executeQuery( + "SELECT CLOB_DATA FROM " + TABLE_NAME + " WHERE LOB_ID=2 FOR UPDATE")) { + while (rset.next()) { + Clob c = rset.getClob(1); + + // Fill clob data from a file and update it in the table. + readFileAndUpdateClobData(c, "books.txt"); + } + } + + conn.commit(); + + // Get data from the clob column + executeClobQuery(2); + } + } + + // Shows how to insert binary stream data in the Blob, + // retrieves data from the Blob. + void blobSample() throws Exception { + show("======== Blob Sample ========"); + + try (PreparedStatement pstmt = conn.prepareStatement( + "INSERT INTO " + TABLE_NAME + " (LOB_ID, BLOB_DATA) VALUES (3, ?)")) { + byte[] data = { 1, 2, 3, 77, 80, 4, 5 }; + + // Insert binary input stream data in the Blob + pstmt.setBlob(1, new ByteArrayInputStream(data)); + pstmt.execute(); + conn.commit(); + + // Get data from the blob column. + executeBlobQuery(3); + } + } + + // Shows how to create an empty Blob, insert data in the Blob, + // retrieves data from the Blob. + void blobSampleWithEmptyBlob() throws Exception { + show("======== Blob Sample with an empty blob ========"); + + // Creates an empty blob in a table then update it with actual data. + try (PreparedStatement pstmt = conn.prepareStatement( + "INSERT INTO " + TABLE_NAME + " (LOB_ID, BLOB_DATA) VALUES (4, empty_blob())")) { + pstmt.execute(); + + try (ResultSet rset = pstmt.executeQuery( + "SELECT BLOB_DATA FROM " + TABLE_NAME + " WHERE LOB_ID=4 FOR UPDATE")) { + while (rset.next()) { + Blob b = rset.getBlob(1); + + // Fill blob data from a file and update it in the table. + readFileAndUpdateBlobData(b, "books.png"); + } + } + + conn.commit(); + + // Get data from the blob column + executeBlobQuery(4); + } + } + + // Shows how to create a NClob, insert data in the NClob, + // retrieves data from the NClob. + void nclobSample() throws Exception { + show("======== Nclob Sample ========"); + + try (PreparedStatement pstmt = conn.prepareStatement( + "INSERT INTO " + TABLE_NAME + " (LOB_ID, NCLOB_DATA) VALUES (5, ?)")) { + // Creates and fill data in the nclob + NClob nclob = conn.createNClob(); + nclob.setString(1, "Book Title - Oracle \u00A9 for Dummies "); + + // Insert nclob data in the column of a table. + pstmt.setNClob(1, nclob); + pstmt.execute(); + conn.commit(); + + // Get data from the nclob column. + executeNClobQuery(5); + } + } + + // You can use temporary LOBs to store transient data. The data is stored in + // temporary + // table space rather than regular table space. You should free temporary LOBs + // after you + // no longer need them. If you do not, then the space the LOB consumes in + // temporary + // table space will not be reclaimed. + // + // Shows how to create a temporary CLOB, fill data in the temporary CLOB, + // insert temporary CLOB data in the table. + void temporaryClobSample() throws Exception { + show("======== Temporary Clob Sample ========"); + + try (PreparedStatement pstmt = conn.prepareStatement( + "INSERT INTO " + TABLE_NAME + " (LOB_ID, CLOB_DATA) VALUES (6, ?)")) { + // Creates and fill data in a temporary clob + Clob tempClob = CLOB.createTemporary(conn, false, CLOB.DURATION_SESSION); + tempClob.setString(1, "Book Title - JDBC for Dummies"); + + // Insert temporary CLOB data in the column of a table. + pstmt.setClob(1, tempClob); + pstmt.execute(); + conn.commit(); + + // Check whether the CLOB is temporary or regular CLOB. + boolean isTempCLOB = CLOB.isTemporary((CLOB) tempClob); + show("CLOB.isTemporary: " + isTempCLOB); + + // Free temporary CLOB + CLOB.freeTemporary((CLOB) tempClob); + + // Get data from the clob column. + executeClobQuery(6); + } + } + + // Fetch a CLOB as a LONG + void clobAsLongSample() throws Exception { + show("======== Clob as Long Sample ========"); + + try (PreparedStatement pstmt = conn.prepareStatement( + "INSERT INTO " + TABLE_NAME + " (LOB_ID, CLOB_DATA) VALUES (7, ?)")) { + // Creates and fill data in the clob + Clob clob = conn.createClob(); + clob.setString(1, "Book Title - JNI for Dummies"); + + // Insert clob data in the column of a table. + pstmt.setClob(1, clob); + pstmt.execute(); + conn.commit(); + + // Get data from the clob column as if it's LONG type. + executeClobAsLongQuery(7); + } + } + + // Shows how to set lob prefetch size, + // while retrieving clob data to reduce round trips to the server. + void clobSampleWithLobPrefetchSize() throws Exception { + show("======== Clob Sample with LobPrefetchSize ========"); + + try (PreparedStatement pstmt = conn.prepareStatement( + "INSERT INTO " + TABLE_NAME + " (LOB_ID, CLOB_DATA) VALUES (8, ?)")) { + // Creates and fill data in the clob + Clob clob = conn.createClob(); + clob.setString(1, "Book Title - Linux for Dummies"); + + // Insert clob data in the column of a table. + pstmt.setClob(1, clob); + pstmt.execute(); + conn.commit(); + + // Sets lob preftech size and gets data from the clob column + executeClobQueryWithLobPrefetchSize(8); + } + } + + // Execute a query to get the clob column data. + // Iterate through the result. + private void executeClobQuery(int id) throws Exception { + try (PreparedStatement pstmt = conn + .prepareStatement("SELECT CLOB_DATA FROM " + TABLE_NAME + " WHERE LOB_ID=?")) { + + pstmt.setInt(1, id); + try (ResultSet rset = pstmt.executeQuery()) { + show("LOB_ID = " + id); + while (rset.next()) { + Clob c = rset.getClob(1); + + getAndDisplayClobData("CLOB_DATA = ", c); + } + } + } + + } + + // Get the clob data as a stream. + private void getAndDisplayClobData(String message, Clob clob) + throws Exception { + // Get a character stream of a clob + try (Reader clobStream = clob.getCharacterStream()) { + // Buffer to read chunk of data + char[] buffer = new char[CHUNK_BUFFER_SIZE]; + int length = 0; + + showln(message); + + // Loop for the reading of clob data in chunks. + while ((length = clobStream.read(buffer)) != -1) + showln(new String(buffer, 0, length)); + + show(""); + } + } + + // Read data from a text file and insert it in to the clob column + private void readFileAndUpdateClobData(Clob clob, String fileName) + throws Exception { + // File reader + File file = new File(fileName); + try (FileInputStream fileInputStream = new FileInputStream(file)) { + try (InputStreamReader inputStreamReader = new InputStreamReader( + fileInputStream)) { + try (BufferedReader bufferedReader = new BufferedReader( + inputStreamReader)) { + // Buffer to read/write chunk of data + char[] buffer = new char[CHUNK_BUFFER_SIZE]; + int charsRead = 0; + + // Get a clob writer + try (Writer writer = clob.setCharacterStream(1L)) { + // Loop for reading of chunk of data and then write into the clob. + while ((charsRead = bufferedReader.read(buffer)) != -1) { + writer.write(buffer, 0, charsRead); + } + } + } + } + } + } + + // Execute a query to get the blob column data. + // Iterate through the result. + private void executeBlobQuery(int id) throws Exception { + try (PreparedStatement pstmt = conn + .prepareStatement("SELECT BLOB_DATA FROM " + TABLE_NAME + " WHERE LOB_ID=?")) { + pstmt.setInt(1, id); + try (ResultSet rset = pstmt.executeQuery()) { + show("LOB_ID = " + id); + while (rset.next()) { + Blob b = rset.getBlob(1); + + getAndDisplayBlobData("BLOB_DATA = ", b); + } + } + } + } + + // Get the blob data as a stream. + private void getAndDisplayBlobData(String message, Blob blob) + throws Exception { + // Get a binary stream of a blob + try (InputStream blobStream = blob.getBinaryStream()) { + // Buffer to read chunk of data + byte[] buffer = new byte[CHUNK_BUFFER_SIZE]; + int length = 0; + long totalLength = 0; + + NumberFormat format = NumberFormat.getInstance(); + format.setMinimumIntegerDigits(2); + format.setGroupingUsed(false); + + // Loop for the reading of blob data in chunks. + while ((length = blobStream.read(buffer)) != -1) { + if (totalLength == 0 && length > 25) + show("First 25 bytes of a Blob column"); + + for (int i = 0; i < length; i++) { + int b = (int) buffer[i] & 0XFF; + if (totalLength == 0 && i < 25) + showln(format.format((long) b) + " "); + else + break; // We are not consuming more than 25 bytes for demo purpose. + } + + totalLength += length; + } + + show(""); + + if (totalLength > 25) + show("Total blob data length:" + totalLength); + } + } + + // Read data from a binary file and insert it in to the blob column + private void readFileAndUpdateBlobData(Blob blob, String fileName) + throws Exception { + // File reader + File file = new File(fileName); + try (FileInputStream fileInputStream = new FileInputStream(file)) { + // Buffer to read/write chunk of data + byte[] buffer = new byte[CHUNK_BUFFER_SIZE]; + int bytesRead = 0; + + // Get a blob output stream + try (OutputStream outstream = blob.setBinaryStream(1L)) { + // Loop for reading of chunk of data and then write into the blob. + while ((bytesRead = fileInputStream.read(buffer)) != -1) { + outstream.write(buffer, 0, bytesRead); + } + } + } + } + + // Execute a query to get the nclob column data. + // Iterate through the result. + private void executeNClobQuery(int id) throws Exception { + try (PreparedStatement pstmt = conn + .prepareStatement("SELECT NCLOB_DATA FROM " + TABLE_NAME + " WHERE LOB_ID=?")) { + pstmt.setInt(1, id); + try (ResultSet rset = pstmt.executeQuery()) { + show("LOB_ID = " + id); + while (rset.next()) { + NClob n = rset.getNClob(1); + + getAndDisplayNClobData("NCLOB_DATA = ", n); + } + } + } + } + + // Get the nclob data as a stream. + private void getAndDisplayNClobData(String message, NClob nclob) + throws Exception { + // Get a character stream of a nclob + try (Reader nclobStream = nclob.getCharacterStream()) { + // Buffer to read chunk of data + char[] buffer = new char[CHUNK_BUFFER_SIZE]; + int length = 0; + + showln(message); + + // Loop for the reading of nclob data in chunks. + while ((length = nclobStream.read(buffer)) != -1) + showln(new String(buffer, 0, length)); + + show(""); + } + } + + // Execute a query to get the clob column data. + // Iterate through the result as LONG type. + private void executeClobAsLongQuery(int id) throws Exception { + try (PreparedStatement pstmt = conn + .prepareStatement("SELECT CLOB_DATA FROM " + TABLE_NAME + " WHERE LOB_ID=?")) { + // Fetch LOB data as LONG. + // LOB data can be read using the same streaming mechanism as for LONG RAW + // and LONG data. + // This produces a direct stream on the data as if it were a LONG RAW or + // LONG column. + // This technique is limited to Oracle Database 10g release 1 (10.1) and + // later. + // The benefit of fetching a CLOB as a LONG (or a BLOB as a LONG_RAW) is + // that the data + // will be inlined in the data row that is fetched which may become handy + // when the locator + // is not needed and you just need to read the data into a stream. + // The downside of it is that you don't get the locator and the rows are + // fetched one by one. + // The LOB prefetch gives better benefits such as being able to fetch + // multiple rows in one single roundtrip, + // getting the length of the LOB immediately and getting the locator. + // Overall relying on LOB prefetch is always preferable compared to the + // LOB to LONG technique. + ((OracleStatement) pstmt).defineColumnType(2, Types.LONGVARBINARY); + + pstmt.setInt(1, id); + try (ResultSet rset = pstmt.executeQuery()) { + show("LOB_ID = " + id); + while (rset.next()) { + String c = rset.getString(1); + + show("CLOB_DATA as LONG = " + c); + } + } + } + } + + // If you select LOB columns into a result set, some or all of the data is + // prefetched to the client, when the locator is fetched. It saves the first + // roundtrip to + // retrieve data by deferring all preceding operations until fetching from the + // locator. + // + // The prefetch size is specified in bytes for BLOBs and in characters for + // CLOBs. It can be + // specified by setting the connection property + // oracle.jdbc.defaultLobPrefetchSize. + // The value of this property can be overridden at statement level by using, + // oracle.jdbc.OracleStatement.setLobPrefetchSize(int) method. + // + // The default prefetch size is 4000. + // + // Execute a query to get the clob column data. + // Iterate through the result. + private void executeClobQueryWithLobPrefetchSize(int id) throws Exception { + try (PreparedStatement pstmt = conn + .prepareStatement("SELECT CLOB_DATA FROM " + TABLE_NAME + " WHERE LOB_ID=?")) { + // Fine tune lob prefetch size to reduce number of round trips. + ((OracleStatement) pstmt).setLobPrefetchSize(5000); + + pstmt.setInt(1, id); + try (ResultSet rset = pstmt.executeQuery()) { + show("LOB_ID = " + id); + while (rset.next()) { + Clob c = rset.getClob(1); + + getAndDisplayClobData("CLOB_DATA = ", c); + } + } + } + } + + // ==============================Utility Methods============================== + + private void truncateTable() throws SQLException { + try (Statement stmt = conn.createStatement()) { + String sql = "TRUNCATE TABLE " + TABLE_NAME; + stmt.execute(sql); + } + } + + private Connection getConnection() throws SQLException { + // Create an OracleDataSource instance and set properties + OracleDataSource ods = new OracleDataSource(); + ods.setUser(user); + ods.setPassword(password); + ods.setURL(url); + + return ods.getConnection(); + } + + static void getRealUserPasswordUrl(String args[]) throws Exception { + // URL can be modified in file, or taken from command-line + url = getOptionValue(args, "-l", DEFAULT_URL); + + // DB user can be modified in file, or taken from command-line + user = getOptionValue(args, "-u", DEFAULT_USER); + + // DB user's password can be modified in file, or explicitly entered + readPassword(" Password for " + user + ": "); + } + + // Get specified option value from command-line, or use default value + static String getOptionValue(String args[], String optionName, + String defaultVal) { + String argValue = ""; + + try { + int i = 0; + String arg = ""; + boolean found = false; + + while (i < args.length) { + arg = args[i++]; + if (arg.equals(optionName)) { + if (i < args.length) + argValue = args[i++]; + if (argValue.startsWith("-") || argValue.equals("")) { + argValue = defaultVal; + } + found = true; + } + } + + if (!found) { + argValue = defaultVal; + } + } catch (Exception e) { + showError("getOptionValue", e); + } + return argValue; + } + + static void readPassword(String prompt) throws Exception { + if (System.console() == null) { + BufferedReader r = new BufferedReader(new InputStreamReader(System.in)); + showln(prompt); + password = r.readLine(); + } else { + char[] pchars = System.console().readPassword("\n[%s]", prompt); + if (pchars != null) { + password = new String(pchars); + java.util.Arrays.fill(pchars, ' '); + } + } + } + + private static void show(String msg) { + System.out.println(msg); + } + + // Show message line without new line + private static void showln(String msg) { + System.out.print(msg); + } + + static void showError(String msg, Throwable exc) { + System.out.println(msg + " hit error: " + exc.getMessage()); + } +} + +/* + * ==================================== Expected Output + * ==================================== + * + * ======== Clob Sample ======== LOB_ID = 1 CLOB_DATA = Book Title - Java for Dummies + * ======== Clob Sample with an empty clob ======== LOB_ID = 2 CLOB_DATA = { "books": [ { + * "isbn": "9781593275846", "title": "Eloquent JavaScript, Second Edition", + * "subtitle": "A Modern Introduction to Programming", "author": + * "Marijn Haverbeke", "published": "2014-12-14T00:00:00.000Z", "publisher": + * "No Starch Press", "pages": 472, "description": + * "JavaScript lies at the heart of almost every modern web application, from social apps to the newest browser-based games. Though simple for beginners to pick up and play with, JavaScript is a flexible, complex language that you can use to build full-scale applications." + * , "website": "http://eloquentjavascript.net/" }, { "isbn": "9781449331818", + * "title": "Learning JavaScript Design Patterns", "subtitle": + * "A JavaScript and jQuery Developer's Guide", "author": "Addy Osmani", + * "published": "2012-07-01T00:00:00.000Z", "publisher": "O'Reilly Media", + * "pages": 254, "description": + * "With Learning JavaScript Design Patterns, you'll learn how to write beautiful, structured, and maintainable JavaScript by applying classical and modern design patterns to the language. If you want to keep your code efficient, more manageable, and up-to-date with the latest best practices, this book is for you." + * , "website": + * "http://www.addyosmani.com/resources/essentialjsdesignpatterns/book/" } ] } + * + * ======== Blob Sample ======== LOB_ID = 3 01 02 03 77 80 04 05 ======== Blob + * Sample with an empty blob ======== LOB_ID = 4 First 25 bytes of a Blob column 137 + * 80 78 71 13 10 26 10 00 00 00 13 73 72 68 82 00 00 00 200 00 00 00 198 08 + * Total blob data length:10422 ======== Nclob Sample ======== LOB_ID = 5 NCLOB_DATA = Book + * Title - Oracle ? for Dummies ======== Temporary Clob Sample ======== + * CLOB.isTemporary: true LOB_ID = 6 CLOB_DATA = Book Title - JDBC for Dummies ======== Clob + * as Long Sample ======== LOB_ID = 7 CLOB_DATA as LONG = Book Title - JNI for Dummies + * ======== Clob Sample with LobPrefetchSize ======== LOB_ID = 8 CLOB_DATA = Book Title - + * Linux for Dummies + * + */ diff --git a/java/jdbc/BasicSamples/PLSQLSample.java b/java/jdbc/BasicSamples/PLSQLSample.java new file mode 100755 index 00000000..d6892441 --- /dev/null +++ b/java/jdbc/BasicSamples/PLSQLSample.java @@ -0,0 +1,548 @@ +/* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.*/ + +/* + * DESCRIPTION + * + * This sample demonstrates the usage of PL/SQL Stored Procedures and Functions in JDBC. + * + * Each unit of this sample demonstrates the following: + * 1. creating a PL/SQL Stored Procedure/Function, + * 2. invoking the Stored Procedure/Function with IN, OUT, IN OUT parameters, + * 3. and the correspondence of IN/OUT parameter with get/set/register methods. + * + * It is required that applications have Oracle JDBC driver jar (ojdbc8.jar) in + * the class-path, and that the database back end supports SQL (this sample uses + * an Oracle Database). + * + * To run the sample, you must provide non-default and working values for ALL 3 + * of user, password, and URL. This can be done by either updating + * this file directly or supplying the 3 values as command-line options + * and user input. The password is read from console or standard input. + * java PLSQL2 -l -u + * If you do not update all the defaults, the program proceeds but + * will hit error when connecting. + */ + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Types; + +import oracle.jdbc.pool.OracleDataSource; + +public class PLSQLSample { + + private final static String DEFAULT_URL = "jdbc:oracle:thin:@//myhost:myport/myservice"; + private final static String DEFAULT_USER = "myuser"; + private final static String DEFAULT_PASSWORD = "mypassword"; + //You must provide non-default values for ALL 3 to execute the program + private static String url = DEFAULT_URL; + private static String user = DEFAULT_USER; + private static String password = DEFAULT_PASSWORD; + private static final String TABLE_NAME = "PLSQL_JDBC_SAMPLE"; + + public static void main(String args[]) throws Exception { + Util.getRealUserPasswordUrl(args); + + PLSQLSample sample = new PLSQLSample(); + sample.run(); + } + + private void run() { + try (Connection conn = getConnection()) { + + // Initialize the table + init(conn); + + // Demonstrate how a no arg PLSQL procedure can be invoked. + demoPlsqlProcedureNoParams(conn); + + // Demonstrate how a PLSQL procedure with IN parameters can be invoked. + demoPlsqlProcedureINParams(conn); + + // Demonstrate how a PLSQL procedure with OUT parameters can be invoked. + demoPlsqlProcedureOUTParams(conn); + + // Demonstrate how a PLSQL procedure with IN OUT parameters can be invoked. + demoPlsqlProcedureINOUTParams(conn); + + // Demonstrate how a no arg PLSQL function can be invoked. + demoPlsqlFunctionNoParams(conn); + + // Demonstrate how a PLSQL function with IN parameters can be invoked. + demoPlsqlFunctionINParams(conn); + + // Demonstrate how a PLSQL function with OUT parameters can be invoked. + demoPlsqlFunctionOUTParams(conn); + + // Demonstrate how a PLSQL function with IN OUT parameters can be invoked. + demoPlsqlFunctionINOUTParams(conn); + + // Cleanup the database after the demo. + truncateTable(conn); + } catch (SQLException sqlEx) { + Util.showError("run", sqlEx); + } + } + + private void init(Connection conn) throws SQLException { + + // Truncate the table. + truncateTable(conn); + + // Load the table with few rows. + loadTable(conn); + } + + private void loadTable(Connection conn) throws SQLException { + String insertDml = "INSERT INTO "+TABLE_NAME+" (NUM, NAME, INSERTEDBY) VALUES (?,?,?)"; + try (PreparedStatement prepStmt = conn.prepareStatement(insertDml)) { + prepStmt.setInt(1, 1); + prepStmt.setString(2, "ONE"); + prepStmt.setString(3, "default"); + prepStmt.addBatch(); + + prepStmt.setInt(1, 2); + prepStmt.setString(2, "TWO"); + prepStmt.setString(3, "default"); + prepStmt.addBatch(); + + prepStmt.setInt(1, 3); + prepStmt.setString(2, "THREE"); + prepStmt.setString(3, "default"); + prepStmt.addBatch(); + + prepStmt.setInt(1, 4); + prepStmt.setString(2, "FOUR"); + prepStmt.setString(3, "default"); + prepStmt.addBatch(); + + prepStmt.executeBatch(); + } + + // Display initial set of rows loaded into the table. + Util.show("Table '"+TABLE_NAME+"' is loaded with: "); + displayRows(conn, "default"); + } + + private void truncateTable(Connection conn) { + String sql = "TRUNCATE TABLE " + TABLE_NAME; + Util.show(sql); + Util.trySql(conn, sql); + } + + private void demoPlsqlProcedureNoParams(Connection conn) throws SQLException { + // Create a PLSQL stored procedure that takes no arguments. + final String PROC_NAME = "ProcNoParams"; + String sql = "CREATE OR REPLACE PROCEDURE "+PROC_NAME+" IS " + + "BEGIN " + + "INSERT INTO "+TABLE_NAME+" VALUES (5, 'FIVE', '"+PROC_NAME+"'); " + + "INSERT INTO "+TABLE_NAME+" VALUES (6, 'SIX', '"+PROC_NAME+"'); " + + "INSERT INTO "+TABLE_NAME+" VALUES (7, 'SEVEN', '"+PROC_NAME+"'); " + + "INSERT INTO "+TABLE_NAME+" VALUES (8, 'EIGHT', '"+PROC_NAME+"'); " + + "END; "; + Util.show(sql); + Util.doSql(conn, sql); + + // Invoke the stored procedure. + sql = "CALL "+PROC_NAME+"()"; + try (CallableStatement callStmt = conn.prepareCall(sql)) { + callStmt.execute(); + + // Display rows inserted by the above stored procedure call. + Util.show("Rows inserted by the stored procedure '"+PROC_NAME+"' are: "); + displayRows(conn, PROC_NAME); + } catch (SQLException sqlEx) { + Util.showError("demoPlsqlProcedureNoArgs", sqlEx); + } finally { + // Drop the procedure when done with it. + Util.doSql(conn, "DROP PROCEDURE "+PROC_NAME); + } + } + + private void demoPlsqlProcedureINParams(Connection conn) throws SQLException { + // Create a PLSQL stored procedure with IN parameters. + final String PROC_NAME = "ProcINParams"; + String sql = "CREATE OR REPLACE PROCEDURE "+PROC_NAME+"(num IN NUMBER, name IN VARCHAR2, insertedBy IN VARCHAR2) IS " + + "BEGIN " + + "INSERT INTO "+TABLE_NAME+" VALUES (num, name, insertedBy); " + + "END; "; + Util.show(sql); + Util.doSql(conn, sql); + + // Invoke the stored procedure. + sql = "CALL "+PROC_NAME+"(?,?,?)"; + try (CallableStatement callStmt = conn.prepareCall(sql)) { + callStmt.setInt(1, 9); + callStmt.setString(2, "NINE"); + callStmt.setString(3, PROC_NAME); + callStmt.addBatch(); + + callStmt.setInt(1, 10); + callStmt.setString(2, "TEN"); + callStmt.setString(3, PROC_NAME); + callStmt.addBatch(); + + callStmt.setInt(1, 11); + callStmt.setString(2, "ELEVEN"); + callStmt.setString(3, PROC_NAME); + callStmt.addBatch(); + + callStmt.setInt(1, 12); + callStmt.setString(2, "TWELVE"); + callStmt.setString(3, PROC_NAME); + callStmt.addBatch(); + + callStmt.executeBatch(); + + // Display rows inserted by the above stored procedure call. + Util.show("Rows inserted by the stored procedure '"+PROC_NAME+"' are: "); + displayRows(conn, PROC_NAME); + } catch (SQLException sqlEx) { + Util.showError("demoPlsqlProcedureINParams", sqlEx); + } finally { + // Drop the procedure when done with it. + Util.doSql(conn, "DROP PROCEDURE "+PROC_NAME); + } + } + + private void demoPlsqlProcedureOUTParams(Connection conn) throws SQLException { + // Create a PLSQL stored procedure with OUT parameters. + final String PROC_NAME = "ProcOUTParams"; + String sql = "CREATE OR REPLACE PROCEDURE "+PROC_NAME+"(num IN NUMBER, name IN VARCHAR2, insertedBy IN VARCHAR2, numInserted OUT NUMBER) IS " + + "BEGIN " + + "INSERT INTO "+TABLE_NAME+" VALUES (num, name, insertedBy); " + + "numInserted := num; " + + "END; "; + Util.show(sql); + Util.doSql(conn, sql); + + // Invoke the stored procedure. + sql = "CALL "+PROC_NAME+"(?,?,?,?)"; + try (CallableStatement callStmt = conn.prepareCall(sql)) { + callStmt.setInt(1, 13); + callStmt.setString(2, "THIRTEEN"); + callStmt.setString(3, PROC_NAME); + callStmt.registerOutParameter(4, Types.INTEGER); + callStmt.execute(); + + // Display rows inserted by the above stored procedure call. + Util.show("Rows inserted by the stored procedure '"+PROC_NAME+"' are: "); + displayRows(conn, PROC_NAME); + + // Show the value of OUT parameter after the stored procedure call. + Util.show("The out parameter value of stored procedure '"+PROC_NAME+"' returned "+callStmt.getInt(4)+"."); + + } catch (SQLException sqlEx) { + Util.showError("demoPlsqlProcedureOUTParams", sqlEx); + } finally { + // Drop the procedure when done with it. + Util.doSql(conn, "DROP PROCEDURE "+PROC_NAME); + } + } + + private void demoPlsqlProcedureINOUTParams(Connection conn) throws SQLException { + // Create a PLSQL stored procedure with IN OUT parameters. + final String PROC_NAME = "ProcINOUTParams"; + String sql = "CREATE OR REPLACE PROCEDURE "+PROC_NAME+"(num IN OUT NUMBER, name IN OUT VARCHAR2, insertedBy IN VARCHAR2) IS " + + "BEGIN " + + "INSERT INTO "+TABLE_NAME+" VALUES (num, name, insertedBy); " + + "num := 0; " + + "name := 'ZERO'; " + + "END; "; + Util.show(sql); + Util.doSql(conn, sql); + + // Invoke the stored procedure. + sql = "CALL "+PROC_NAME+"(?,?,?)"; + try (CallableStatement callStmt = conn.prepareCall(sql)) { + callStmt.setInt(1, 14); + callStmt.registerOutParameter(1, Types.INTEGER); + + callStmt.setString(2, "FOURTEEN"); + callStmt.registerOutParameter(2, Types.VARCHAR); + + callStmt.setString(3, PROC_NAME); + callStmt.execute(); + + // Display rows inserted by the above stored procedure call. + Util.show("Rows inserted by the stored procedure '"+PROC_NAME+"' are: "); + displayRows(conn, PROC_NAME); + + // Show the values of OUT parameters after the stored procedure call. + Util.show("Out parameter values of stored procedure '" + PROC_NAME + "': num = " + callStmt.getInt(1) + + ", name = " + callStmt.getString(2) + "."); + } catch (SQLException sqlEx) { + Util.showError("demoPlsqlProcedureINOUTParams", sqlEx); + } finally { + // Drop the procedure when done with it. + Util.doSql(conn, "DROP PROCEDURE "+PROC_NAME); + } + } + + private void demoPlsqlFunctionNoParams(Connection conn) throws SQLException { + // Create a PLSQL function that takes no arguments. + final String FUNC_NAME = "FuncNoParams"; + String sql = "CREATE OR REPLACE FUNCTION "+FUNC_NAME+" RETURN NUMBER IS " + + "BEGIN " + + "INSERT INTO "+TABLE_NAME+" VALUES (15, 'FIFTEEN', '"+FUNC_NAME+"'); " + + "INSERT INTO "+TABLE_NAME+" VALUES (16, 'SIXTEEN', '"+FUNC_NAME+"'); " + + "INSERT INTO "+TABLE_NAME+" VALUES (17, 'SEVENTEEN', '"+FUNC_NAME+"'); " + + "INSERT INTO "+TABLE_NAME+" VALUES (18, 'EIGHTEEN', '"+FUNC_NAME+"'); " + + "RETURN 4;" // Return number of row inserted into the table. + + "END; "; + Util.show(sql); + Util.doSql(conn, sql); + + // Invoke the PLSQL function. + sql = "BEGIN ? := "+FUNC_NAME+"; end;"; + try (CallableStatement callStmt = conn.prepareCall(sql)) { + callStmt.registerOutParameter (1, Types.INTEGER); + callStmt.execute(); + + // Display rows inserted by the above PLSQL function call. + Util.show("Rows inserted by the PLSQL function '"+FUNC_NAME+"' are: "); + displayRows(conn, FUNC_NAME); + + // Show the value returned by the PLSQL function. + Util.show("The value returned by the PLSQL function '"+FUNC_NAME+"' is "+callStmt.getInt(1)+"."); + } catch (SQLException sqlEx) { + Util.showError("demoPlsqlFunctionNoParams", sqlEx); + } finally { + // Drop the function when done with it. + Util.doSql(conn, "DROP FUNCTION "+FUNC_NAME); + } + } + + private void demoPlsqlFunctionINParams(Connection conn) throws SQLException { + // Create a PLSQL function with IN parameters. + final String FUNC_NAME = "FuncINParams"; + String sql = "CREATE OR REPLACE FUNCTION "+FUNC_NAME+"(num IN NUMBER, name IN VARCHAR2, insertedBy IN VARCHAR2) RETURN NUMBER IS " + + "BEGIN " + + "INSERT INTO "+TABLE_NAME+" VALUES (num, name, insertedBy); " + + "RETURN 1;" // Return number of row inserted into the table. + + "END; "; + Util.show(sql); + Util.doSql(conn, sql); + + // Invoke the PLSQL function. + sql = "BEGIN ? := "+FUNC_NAME+"(?,?,?); end;"; + try (CallableStatement callStmt = conn.prepareCall(sql)) { + callStmt.registerOutParameter (1, Types.INTEGER); + callStmt.setInt(2, 19); + callStmt.setString(3, "NINETEEN"); + callStmt.setString(4, FUNC_NAME); + callStmt.execute(); + + // Display rows inserted by the above PLSQL function call. + Util.show("Rows inserted by the PLSQL function '"+FUNC_NAME+"' are: "); + displayRows(conn, FUNC_NAME); + + // Show the value returned by the PLSQL function. + Util.show("The value returned by the PLSQL function '"+FUNC_NAME+"' is "+callStmt.getInt(1)+"."); + } catch (SQLException sqlEx) { + Util.showError("demoPlsqlFunctionINParams", sqlEx); + } finally { + // Drop the function when done with it. + Util.doSql(conn, "DROP FUNCTION "+FUNC_NAME); + } + } + + private void demoPlsqlFunctionOUTParams(Connection conn) throws SQLException { + // Create a PLSQL function with IN parameters. + final String FUNC_NAME = "FuncOUTParams"; + String sql = "CREATE OR REPLACE FUNCTION "+FUNC_NAME+"(num IN NUMBER, name IN VARCHAR2, insertedBy IN VARCHAR2, numInserted OUT NUMBER) RETURN NUMBER IS " + + "BEGIN " + + "INSERT INTO "+TABLE_NAME+" VALUES (num, name, insertedBy); " + + "numInserted := num; " + + "RETURN 1;" // Return number of row inserted into the table. + + "END; "; + Util.show(sql); + Util.doSql(conn, sql); + + // Invoke the PLSQL function. + sql = "BEGIN ? := "+FUNC_NAME+"(?,?,?,?); end;"; + try (CallableStatement callStmt = conn.prepareCall(sql)) { + callStmt.registerOutParameter (1, Types.INTEGER); + callStmt.setInt(2, 20); + callStmt.setString(3, "TWENTY"); + callStmt.setString(4, FUNC_NAME); + callStmt.registerOutParameter(5, Types.INTEGER); + callStmt.execute(); + + // Display rows inserted by the above PLSQL function call. + Util.show("Rows inserted by the PLSQL function '"+FUNC_NAME+"' are: "); + displayRows(conn, FUNC_NAME); + + // Show the value returned by the PLSQL function. + Util.show("The value returned by the PLSQL function '"+FUNC_NAME+"' is "+callStmt.getInt(1)+"."); + + // Show the values of OUT parameters after the PLSQL function call. + Util.show("Out parameter value of PLSQL function '" + FUNC_NAME + "': num = " + callStmt.getInt(5) + "."); + } catch (SQLException sqlEx) { + Util.showError("demoPlsqlFunctionOUTParams", sqlEx); + } finally { + // Drop the function when done with it. + Util.doSql(conn, "DROP FUNCTION "+FUNC_NAME); + } + } + + private void demoPlsqlFunctionINOUTParams(Connection conn) throws SQLException { + // Create a PLSQL function with IN OUT parameters. + final String FUNC_NAME = "FuncINOUTParams"; + String sql = "CREATE OR REPLACE FUNCTION "+FUNC_NAME+"(num IN OUT NUMBER, name IN OUT VARCHAR2, insertedBy IN VARCHAR2) RETURN NUMBER IS " + + "BEGIN " + + "INSERT INTO "+TABLE_NAME+" VALUES (num, name, insertedBy); " + + "num := 0; " + + "name := 'ZERO'; " + + "RETURN 1;" // Return number of row inserted into the table. + + "END; "; + Util.show(sql); + Util.doSql(conn, sql); + + // Invoke the PLSQL function. + sql = "BEGIN ? := "+FUNC_NAME+"(?,?,?); end;"; + try (CallableStatement callStmt = conn.prepareCall(sql)) { + callStmt.registerOutParameter (1, Types.INTEGER); + + callStmt.registerOutParameter (2, Types.INTEGER); + callStmt.setInt(2, 20); + + callStmt.registerOutParameter (3, Types.VARCHAR); + callStmt.setString(3, "TWENTY"); + + callStmt.setString(4, FUNC_NAME); + callStmt.execute(); + + // Display rows inserted by the above PLSQL function call. + Util.show("Rows inserted by the PLSQL function '"+FUNC_NAME+"' are: "); + displayRows(conn, FUNC_NAME); + + // Show the value returned by the PLSQL function. + Util.show("The value returned by the PLSQL function '"+FUNC_NAME+"' is "+callStmt.getInt(1)+"."); + + // Show the values of OUT parameters after the PLSQL function call. + Util.show("Out parameter values of PLSQL function '" + FUNC_NAME + "': num = " + callStmt.getInt(2) + + ", name = " + callStmt.getString(3) + "."); + } catch (SQLException sqlEx) { + Util.showError("demoPlsqlFunctionINOUTParams", sqlEx); + } finally { + // Drop the function when done with it. + Util.doSql(conn, "DROP FUNCTION "+FUNC_NAME); + } + } + + private void displayRows(Connection conn, String insertedByBind) throws SQLException { + + String sql = "SELECT * FROM "+TABLE_NAME+" WHERE insertedBy = ?"; + try (PreparedStatement prepStmt = conn.prepareStatement(sql)) { + prepStmt.setString(1, insertedByBind); + + ResultSet rs = prepStmt.executeQuery(); + while (rs.next()) { + Util.show(rs.getInt(1)+"\t"+rs.getString(2)+"\t"+rs.getString(3)); + } + } + } + + // Get a connection using the driver data source. + private Connection getConnection() throws SQLException { + OracleDataSource ods = new OracleDataSource(); + ods.setURL(url); + ods.setUser(user); + ods.setPassword(password); + + // Creates a physical connection to the database. + return ods.getConnection(); + } + + // Utility methods. + private static class Util { + + static void getRealUserPasswordUrl(String args[]) throws Exception { + // URL can be modified in file, or taken from command-line + url = getOptionValue(args, "-l", DEFAULT_URL); + + // DB user can be modified in file, or taken from command-line + user = getOptionValue(args, "-u", DEFAULT_USER); + + // DB user's password can be modified in file, or explicitly entered + readPassword("Password for " + user + ": "); + } + + public static void show(String msg) { + System.out.println(msg); + } + + public static void showError(String msg, Throwable exc) { + System.err.println(msg + " hit error: " + exc.getMessage()); + } + + // Get specified option value from command-line. + static String getOptionValue(String args[], String optionName, String defaultVal) { + String argValue = ""; + try { + int i = 0; + String arg = ""; + boolean found = false; + + while (i < args.length) { + arg = args[i++]; + + if (arg.equals(optionName)) { + if (i < args.length) + argValue = args[i++]; + if (argValue.startsWith("-") || argValue.equals("")) { + argValue = defaultVal; + } + found = true; + } + } + + if (!found) { + argValue = defaultVal; + } + } catch (Exception e) { + showError("getOptionValue", e); + } + + return argValue; + } + + static void readPassword(String prompt) throws Exception { + if (System.console() != null) { + char[] pchars = System.console().readPassword("\n[%s]", prompt); + if (pchars != null) { + password = new String(pchars); + java.util.Arrays.fill(pchars, ' '); + } + } else { + BufferedReader r = new BufferedReader(new InputStreamReader(System.in)); + show(prompt); + password = r.readLine(); + } + } + + public static void trySql(Connection conn, String sql) { + try (Statement stmt = conn.createStatement()) { + stmt.execute(sql); + } catch (SQLException e) { + // Ignore the exception. + } + } + + public static void doSql(Connection conn, String sql) throws SQLException { + try (Statement stmt = conn.createStatement()) { + stmt.execute(sql); + } + } + + } + +} + diff --git a/java/jdbc/BasicSamples/PreparedStatementBindingsSample.java b/java/jdbc/BasicSamples/PreparedStatementBindingsSample.java new file mode 100755 index 00000000..ef95c538 --- /dev/null +++ b/java/jdbc/BasicSamples/PreparedStatementBindingsSample.java @@ -0,0 +1,417 @@ +/* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.*/ + +/** + * DESCRIPTION + * + * A simple illustration of CRUD operation using the PreparedStatement with named bindings. + */ + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.time.LocalDate; +import java.time.format.DateTimeParseException; +import java.util.Scanner; + +import oracle.jdbc.OracleConnection; +import oracle.jdbc.OraclePreparedStatement; +import oracle.jdbc.pool.OracleDataSource; + + +public class PreparedStatementBindingsSample { + private static final int USER_OPTION_SELECTALL = 1; + private static final int USER_OPTION_SELECTONE = 2; + private static final int USER_OPTION_INSERT = 3; + private static final int USER_OPTION_UPDATE = 4; + private static final int USER_OPTION_DELETE = 5; + private static final int USER_OPTION_EXIT = 0; + + + private static final String DEFAULT_USER = "myuser"; + private static final String DEFAULT_URL + = "jdbc:oracle:thin:@//myhost:1521/myservice"; + + private final String user; + private final String password; + private final String url; + + private static final String SQL_INSERT = "INSERT INTO EMP (EMPNO, ENAME, JOB, HIREDATE, SAL) VALUES(:empid, :empname, " + + ":desgn, :joiningdate, :salary)"; + private static final String SQL_UPDATE = "UPDATE EMP SET ENAME = :empname, " + + "JOB = :desgn, HIREDATE = :joiningdate, " + + "SAL = :salary WHERE EMPNO = :empid"; + private static final String SQL_DELETE = "DELETE FROM EMP WHERE EMPNO = :empid"; + private static final String SQL_SELECT_ALL = "SELECT * FROM EMP"; + private static final String SQL_SELECT_ONE = "SELECT * FROM EMP WHERE EMPNO = :empid"; + + + /** + * Creates an PreparedStatementDemo instance with the given details. + * @param user + * @param pwd + * @param url + */ + private PreparedStatementBindingsSample(String user, String pwd, String url) { + this.user = user; + this.password = pwd; + this.url = url; + } + + /** + * Get a connection from the Oracle Database. + * and performs CRUD operation based on the user input. + * @throws SQLException + */ + private void startDemo() throws SQLException { + OracleConnection connection = getConnection(); + try { + while (true) { + int userOption = getOption(); + switch(userOption) { + case USER_OPTION_SELECTONE : + selectOne(connection); + break; + case USER_OPTION_SELECTALL : + selectAll(connection); + break; + case USER_OPTION_INSERT : + insert(connection); + break; + case USER_OPTION_UPDATE : + update(connection); + break; + case USER_OPTION_DELETE : + delete(connection); + break; + case USER_OPTION_EXIT : + show("Bye !!"); + return; + default : + show("Invalid option : " + userOption); + } + } + } + finally { + connection.close(); + } + } + + + /** + * Creates an OracleConnection instance and return it. + * @return oracleConnection + * @throws SQLException + */ + private OracleConnection getConnection() throws SQLException { + OracleDataSource ods = new OracleDataSource(); + ods.setUser(user); + ods.setPassword(password); + ods.setURL(url); + return (OracleConnection)ods.getConnection(); + } + + /** + * Gets employee details from the user and insert into + * the Employee table. + * @param connection + */ + private void insert(OracleConnection connection) { + try(OraclePreparedStatement pstmt = + (OraclePreparedStatement)connection.prepareStatement(SQL_INSERT)) { + Employee employee = getEmployeeFromConsole(); + if(employee == null) { + showError("Unable to get employee details."); + return; + } + pstmt.setIntAtName("empid", employee.getId()); + pstmt.setStringAtName("empname", employee.getName()); + pstmt.setStringAtName("desgn", employee.getDesignation()); + pstmt.setObjectAtName("joiningdate", employee.getJoiningDate()); + pstmt.setDoubleAtName("salary", employee.getSalary()); + pstmt.execute(); + show("Insert successfull !!"); + } + catch(SQLException sqle) { + sqle.printStackTrace(); + } + } + + /** + * Gets employee details from the user and update row in + * the Employee table with the new details. + * @param connection + */ + private void update(OracleConnection connection) { + try(OraclePreparedStatement pstmt = + (OraclePreparedStatement)connection.prepareStatement(SQL_UPDATE)) { + Employee employee = getEmployeeFromConsole(); + if(employee == null) { + showError("Unable to get employee details."); + return; + } + pstmt.setIntAtName("empid", employee.getId()); + pstmt.setStringAtName("empname", employee.getName()); + pstmt.setStringAtName("desgn", employee.getDesignation()); + pstmt.setObjectAtName("joiningdate", employee.getJoiningDate()); + pstmt.setDoubleAtName("salary", employee.getSalary()); + final int numberOfRecordUpdated = pstmt.executeUpdate(); + show("Number of records updated : " + numberOfRecordUpdated); + } + catch(SQLException sqle) { + sqle.printStackTrace(); + } + } + + /** + * Gets the employee id from the user and deletes the employee + * row from the employee table. + * @param connection + */ + private void delete(OracleConnection connection) { + try(OraclePreparedStatement pstmt = + (OraclePreparedStatement)connection.prepareStatement(SQL_DELETE)) { + int employeeId = getEmployeeIDFromConsole(); + pstmt.setIntAtName("empid", employeeId); + final int numberOfRecordDeleted = pstmt.executeUpdate(); + show("Number of records deleted : " + numberOfRecordDeleted); + } + catch(SQLException sqle) { + sqle.printStackTrace(); + } + } + + /** + * Gets the employee id from the user and retrieve the specific + * employee details from the employee table. + * @param connection + */ + private void selectOne(OracleConnection connection) { + int empId = getEmployeeIDFromConsole(); + try(OraclePreparedStatement pstmt = + (OraclePreparedStatement)connection.prepareStatement(SQL_SELECT_ONE)) { + pstmt.setIntAtName("empid", empId); + ResultSet rs = pstmt.executeQuery(); + if(rs.next()) { + Employee emp = new Employee(rs.getInt("EMPNO"), + rs.getString("ENAME"), rs.getString("JOB"), + LocalDate.parse(rs.getString("HIREDATE").substring(0, 10)), rs.getDouble("SAL")); + emp.print(); + } + else { + show("No records found for the employee id : " + empId); + } + } + catch(SQLException sqle) { + sqle.printStackTrace(); + } + } + + /** + * Selects all the rows from the employee table. + * @param connection + */ + private void selectAll(OracleConnection connection) { + try(OraclePreparedStatement pstmt = + (OraclePreparedStatement)connection.prepareStatement(SQL_SELECT_ALL)) { + ResultSet rs = pstmt.executeQuery(); + while(rs.next()) { + Employee emp = new Employee(rs.getInt("EMPNO"), + rs.getString("ENAME"), rs.getString("JOB"), + LocalDate.parse(rs.getString("HIREDATE").substring(0, 10)), rs.getDouble("SAL")); + emp.print(); + } + } + catch(SQLException sqle) { + sqle.printStackTrace(); + } + } + + // Start the main with the command "java PreparedStatementDemo -u "" -l "" + public static void main(String args[]) throws SQLException, IOException { + // Gets the URL and USER value from command line arguments + String url = getCmdOptionValue(args, "-l", DEFAULT_URL); + String user = getCmdOptionValue(args, "-u", DEFAULT_USER); + + // DB user's Password must be entered + String pwd = readPassword(" Password for " + user + ": "); + + PreparedStatementBindingsSample demo = new PreparedStatementBindingsSample(user, pwd, url); + demo.startDemo(); + } + + private static String readPassword(String prompt) throws IOException { + if (System.console() == null) { + BufferedReader r = new BufferedReader(new InputStreamReader(System.in)); + System.out.print(prompt); + return r.readLine(); + } + else { + return new String(System.console().readPassword(prompt)); + } + } + + // Get specified option value from command-line, or use default value + private static String getCmdOptionValue(String args[], String optionName, + String defaultVal) { + String argValue = ""; + try { + int i = 0; + String arg = ""; + boolean found = false; + while (i < args.length) { + arg = args[i++]; + if (arg.equals(optionName)) { + if (i < args.length) + argValue = args[i++]; + if (argValue.startsWith("-") || argValue.equals("")) { + show("No value for Option " + optionName + ", use default."); + argValue = defaultVal; + } + found = true; + } + } + + if (!found) { + show("No Option " + optionName + " specified, use default."); + argValue = defaultVal; + } + } + catch (Exception e) { + showError("getOptionValue" + e.getMessage()); + } + return argValue; + } + + /** + * Get the user option to perform the table operation. + * + * @return + */ + private static int getOption() { + int userOption = -1; + try { + Scanner scanner = new Scanner(System.in); + System.out.println("1 - Select All, 2 - Select One, 3 - Insert, 4 - Update, 5 - Delete, 0 - Exit"); + System.out.println("Enter Option :"); + userOption = Integer.parseInt(scanner.nextLine()); + } + catch(Exception e) { + /* Ignore exception */ + } + return userOption; + } + + /** + * An utility method to get the employee details from the user. + * + * @return employeeObj + */ + private static Employee getEmployeeFromConsole() { + Employee empObj = null;; + try { + Scanner scanner = new Scanner(System.in); + System.out.println("Enter Employee Details"); + System.out.println("ID : "); + int id = Integer.parseInt(scanner.nextLine()); + System.out.println("Name : "); + String name = scanner.nextLine(); + System.out.println("Designation : "); + String designation = scanner.nextLine(); + System.out.println("Joining Date(yyyy-mm-dd) : "); + LocalDate joiningDate = LocalDate.parse(scanner.nextLine()); + System.out.println("Salary : "); + double salary = Double.parseDouble(scanner.nextLine()); + empObj = new Employee(id, name, designation, joiningDate, salary); + } + catch(DateTimeParseException dte) { + showError("Invalid Date format !!"); + } + catch(Exception e) { + /* Ignore exception */ + e.printStackTrace(); + } + return empObj; + } + + /** + * An utility method to get the employee id from the user. + * + * @return employeeID + */ + private static int getEmployeeIDFromConsole() { + int empId = -1; + try { + Scanner scanner = new Scanner(System.in); + System.out.println("Enter Employee ID :"); + empId = Integer.parseInt(scanner.nextLine()); + } + catch(Exception e) { + /* Ignore exception */ + } + return empId; + } + + static void show(String msg) { + System.out.println(msg); + } + + static void showError(String msg) { + System.out.println("Error : " + msg); + } + + /** + * A simple class to represent the employee table structure + * An instance of this represents a row in employee table. + */ + static class Employee { + private int id; + private String name; + private String designation; + private LocalDate joiningDate; + private double salary; + + Employee(int id, String name, String designation, LocalDate joiningDate, + double salary) { + super(); + this.id = id; + this.name = name; + this.designation = designation; + this.joiningDate = joiningDate; + this.salary = salary; + } + + int getId() { + return id; + } + + String getName() { + return name; + } + + String getDesignation() { + return designation; + } + + LocalDate getJoiningDate() { + return joiningDate; + } + + double getSalary() { + return salary; + } + + void print() { + show("/----------------------------------------------------------------/"); + show("ID : " + id); + show("NAME : " + name); + show("Designation : " + designation); + show("Joining Date: " + joiningDate); + show("Salary : " + salary); + show("/----------------------------------------------------------------/"); + } + + } +} diff --git a/java/jdbc/BasicSamples/PreparedStatementSample.java b/java/jdbc/BasicSamples/PreparedStatementSample.java new file mode 100755 index 00000000..005bbd08 --- /dev/null +++ b/java/jdbc/BasicSamples/PreparedStatementSample.java @@ -0,0 +1,410 @@ +/* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.*/ + +/** + * DESCRIPTION + * + * A simple illustration of CRUD operation using the PreparedStatement object. + */ + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.time.LocalDate; +import java.time.format.DateTimeParseException; +import java.util.Scanner; + +import oracle.jdbc.OracleConnection; +import oracle.jdbc.pool.OracleDataSource; + + +public class PreparedStatementSample { + private static final int USER_OPTION_SELECTALL = 1; + private static final int USER_OPTION_SELECTONE = 2; + private static final int USER_OPTION_INSERT = 3; + private static final int USER_OPTION_UPDATE = 4; + private static final int USER_OPTION_DELETE = 5; + private static final int USER_OPTION_EXIT = 0; + + private static final String DEFAULT_USER = "myuser"; + private static final String DEFAULT_URL + = "jdbc:oracle:thin:@//myhost:1521/myservice"; + + private final String user; + private final String password; + private final String url; + + private static final String SQL_INSERT = "INSERT INTO EMP (EMPNO, ENAME, JOB, HIREDATE, SAL) VALUES(?, ?, ?, ?, ?)"; + private static final String SQL_UPDATE = "UPDATE EMP SET ENAME = ?, JOB = ?, HIREDATE = ?, SAL = ? WHERE EMPNO = ?"; + private static final String SQL_DELETE = "DELETE FROM EMP WHERE EMPNO = ?"; + private static final String SQL_SELECT_ALL = "SELECT * FROM EMP"; + private static final String SQL_SELECT_ONE = "SELECT * FROM EMP WHERE EMPNO = ?"; + + + /** + * Creates an PreparedStatementDemo instance with the given details. + * @param user + * @param pwd + * @param url + */ + private PreparedStatementSample(String user, String pwd, String url) { + this.user = user; + this.password = pwd; + this.url = url; + } + + /** + * Get a connection from the Oracle Database. + * and performs CRUD operation based on the user input. + * @throws SQLException + */ + private void startDemo() throws SQLException { + OracleConnection connection = getConnection(); + try { + while (true) { + int userOption = getOption(); + switch(userOption) { + case USER_OPTION_SELECTONE : + selectOne(connection); + break; + case USER_OPTION_SELECTALL : + selectAll(connection); + break; + case USER_OPTION_INSERT : + insert(connection); + break; + case USER_OPTION_UPDATE : + update(connection); + break; + case USER_OPTION_DELETE : + delete(connection); + break; + case USER_OPTION_EXIT : + show("Bye !!"); + return; + default : + show("Invalid option : " + userOption); + } + } + } + finally { + connection.close(); + } + } + + + /** + * Creates an OracleConnection instance and return it. + * @return oracleConnection + * @throws SQLException + */ + private OracleConnection getConnection() throws SQLException { + OracleDataSource ods = new OracleDataSource(); + ods.setUser(user); + ods.setPassword(password); + ods.setURL(url); + return (OracleConnection)ods.getConnection(); + } + + /** + * Gets employee details from the user and insert into + * the Employee table. + * @param connection + */ + private void insert(OracleConnection connection) { + try(PreparedStatement pstmt = connection.prepareStatement(SQL_INSERT)) { + Employee employee = getEmployeeFromConsole(); + if(employee == null) { + showError("Unable to get employee details."); + return; + } + pstmt.setInt(1, employee.getId()); + pstmt.setString(2, employee.getName()); + pstmt.setString(3, employee.getDesignation()); + pstmt.setObject(4, employee.getJoiningDate()); + pstmt.setDouble(5, employee.getSalary()); + pstmt.execute(); + show("Insert successfull !!"); + } + catch(SQLException sqle) { + //sqle.printStackTrace(); + } + } + + /** + * Gets employee details from the user and update row in + * the Employee table with the new details. + * @param connection + */ + private void update(OracleConnection connection) { + try(PreparedStatement pstmt = connection.prepareStatement(SQL_UPDATE)) { + Employee employee = getEmployeeFromConsole(); + if(employee == null) { + showError("Unable to get employee details."); + return; + } + pstmt.setString(1, employee.getName()); + pstmt.setString(2, employee.getDesignation()); + pstmt.setObject(3, employee.getJoiningDate()); + pstmt.setDouble(4, employee.getSalary()); + pstmt.setInt(5, employee.getId()); + final int numberOfRecordUpdated = pstmt.executeUpdate(); + show("Number of records updated : " + numberOfRecordUpdated); + } + catch(SQLException sqle) { + sqle.printStackTrace(); + } + } + + /** + * Gets the employee id from the user and deletes the employee + * row from the employee table. + * @param connection + */ + private void delete(OracleConnection connection) { + try(PreparedStatement pstmt = connection.prepareStatement(SQL_DELETE)) { + int employeeId = getEmployeeIDFromConsole(); + pstmt.setInt(1, employeeId); + final int numberOfRecordDeleted = pstmt.executeUpdate(); + show("Number of records deleted : " + numberOfRecordDeleted); + } + catch(SQLException sqle) { + //sqle.printStackTrace(); + } + } + + /** + * Gets the employee id from the user and retrieve the specific + * employee details from the employee table. + * @param connection + */ + private void selectOne(OracleConnection connection) { + int empId = getEmployeeIDFromConsole(); + try(PreparedStatement pstmt = connection.prepareStatement(SQL_SELECT_ONE)) { + pstmt.setInt(1, empId); + ResultSet rs = pstmt.executeQuery(); + if(rs.next()) { + Employee emp = new Employee(rs.getInt("EMPNO"), + rs.getString("ENAME"), rs.getString("JOB"), + LocalDate.parse(rs.getString("HIREDATE").substring(0, 10)), rs.getDouble("SAL")); + emp.print(); + } + else { + show("No records found for the employee id : " + empId); + } + } + catch(SQLException sqle) { + //sqle.printStackTrace(); + } + } + + /** + * Selects all the rows from the employee table. + * @param connection + */ + private void selectAll(OracleConnection connection) { + try(PreparedStatement pstmt = connection.prepareStatement(SQL_SELECT_ALL)) { + ResultSet rs = pstmt.executeQuery(); + while(rs.next()) { + Employee emp = new Employee(rs.getInt("EMPNO"), + rs.getString("ENAME"), rs.getString("JOB"), + LocalDate.parse(rs.getString("HIREDATE").substring(0, 10)), rs.getDouble("SAL")); + emp.print(); + } + } + catch(SQLException sqle) { + //sqle.printStackTrace(); + } + } + + // Start the main with the command "java PreparedStatementDemo -u "" -l "" + public static void main(String args[]) throws SQLException, IOException { + // Gets the URL and USER value from command line arguments + String url = getCmdOptionValue(args, "-l", DEFAULT_URL); + String user = getCmdOptionValue(args, "-u", DEFAULT_USER); + + // DB user's Password must be entered + String pwd = readPassword(" Password for " + user + ": "); + + PreparedStatementSample demo = new PreparedStatementSample(user, pwd, url); + demo.startDemo(); + } + + private static String readPassword(String prompt) throws IOException { + if (System.console() == null) { + BufferedReader r = new BufferedReader(new InputStreamReader(System.in)); + System.out.print(prompt); + return r.readLine(); + } + else { + return new String(System.console().readPassword(prompt)); + } + } + + // Get specified option value from command-line, or use default value + private static String getCmdOptionValue(String args[], String optionName, + String defaultVal) { + String argValue = ""; + try { + int i = 0; + String arg = ""; + boolean found = false; + while (i < args.length) { + arg = args[i++]; + if (arg.equals(optionName)) { + if (i < args.length) + argValue = args[i++]; + if (argValue.startsWith("-") || argValue.equals("")) { + show("No value for Option " + optionName + ", use default."); + argValue = defaultVal; + } + found = true; + } + } + + if (!found) { + show("No Option " + optionName + " specified, use default."); + argValue = defaultVal; + } + } + catch (Exception e) { + showError("getOptionValue" + e.getMessage()); + } + return argValue; + } + + /** + * Get the user option to perform the table operation. + * + * @return + */ + private static int getOption() { + int userOption = -1; + try { + Scanner scanner = new Scanner(System.in); + System.out.println("1 - Select All, 2 - Select One, 3 - Insert, 4 - Update, 5 - Delete, 0 - Exit"); + System.out.println("Enter Option :"); + userOption = Integer.parseInt(scanner.nextLine()); + } + catch(Exception e) { + /* Ignore exception */ + } + return userOption; + } + + /** + * An utility method to get the employee details from the user. + * + * @return employeeObj + */ + private static Employee getEmployeeFromConsole() { + Employee empObj = null;; + try { + Scanner scanner = new Scanner(System.in); + System.out.println("Enter Employee Details"); + System.out.println("ID : "); + int id = Integer.parseInt(scanner.nextLine()); + System.out.println("Name : "); + String name = scanner.nextLine(); + System.out.println("Designation : "); + String designation = scanner.nextLine(); + System.out.println("Joining Date(yyyy-mm-dd) : "); + LocalDate joiningDate = LocalDate.parse(scanner.nextLine()); + System.out.println("Salary : "); + double salary = Double.parseDouble(scanner.nextLine()); + empObj = new Employee(id, name, designation, joiningDate, salary); + } + catch(DateTimeParseException dte) { + showError("Invalid Date format !!"); + } + catch(Exception e) { + /* Ignore exception */ + e.printStackTrace(); + } + return empObj; + } + + /** + * An utility method to get the employee id from the user. + * + * @return employeeID + */ + private static int getEmployeeIDFromConsole() { + int empId = -1; + try { + Scanner scanner = new Scanner(System.in); + System.out.println("Enter Employee ID :"); + empId = Integer.parseInt(scanner.nextLine()); + } + catch(Exception e) { + /* Ignore exception */ + } + return empId; + } + + static void show(String msg) { + System.out.println(msg); + } + + static void showError(String msg) { + System.out.println("Error : " + msg); + } + + /** + * A simple class to represent the employee table structure + * An instance of this represents a row in employee table. + */ + static class Employee { + private int id; + private String name; + private String designation; + private LocalDate joiningDate; + private double salary; + + Employee(int id, String name, String designation, LocalDate joiningDate, + double salary) { + super(); + this.id = id; + this.name = name; + this.designation = designation; + this.joiningDate = joiningDate; + this.salary = salary; + } + + int getId() { + return id; + } + + String getName() { + return name; + } + + String getDesignation() { + return designation; + } + + LocalDate getJoiningDate() { + return joiningDate; + } + + double getSalary() { + return salary; + } + + void print() { + show("/----------------------------------------------------------------/"); + show("ID : " + id); + show("NAME : " + name); + show("Designation : " + designation); + show("Joining Date: " + joiningDate); + show("Salary : " + salary); + show("/----------------------------------------------------------------/"); + } + + } + + +} diff --git a/java/jdbc/BasicSamples/SQLXMLSample.java b/java/jdbc/BasicSamples/SQLXMLSample.java new file mode 100755 index 00000000..462469da --- /dev/null +++ b/java/jdbc/BasicSamples/SQLXMLSample.java @@ -0,0 +1,280 @@ +/* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.*/ + +/** + * DESCRIPTION + * + * This is a simple example of how to create, insert, and query SQLXML values. For + * more info see {@link https://docs.oracle.com/javase/tutorial/jdbc/basics/sqlxml.html}, + * and {@link https://docs.oracle.com/en/database/oracle/oracle-database/12.2/adxdb/intro-to-XML-DB.html}. + * + * To run the sample, you must provide non-default and working values + * for ALL 3 of user, password, and URL. This can be done by either updating + * this file directly or supplying the 3 values as command-line options + * and user input. The password is read from console or standard input. + * java SQLXMLSample -l -u + * If you do not update all the defaults, the program proceeds but + * will hit error when connecting. + */ + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.JDBCType; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLXML; +import java.sql.Statement; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.w3c.dom.Document; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + + +public class SQLXMLSample { + final static String DEFAULT_URL = "jdbc:oracle:thin:@//myhost:myport/myservice"; + final static String DEFAULT_USER = "myuser"; + final static String DEFAULT_PASSWORD = "mypassword"; + + // You must provide non-default values for ALL 3 to execute the program + static String url = DEFAULT_URL; + static String user = DEFAULT_USER; + static String password = DEFAULT_PASSWORD; + + /** + * Create an instance, get the database user password, and run the sample code. + * + * @param args command line args + * @throws Exception if an error occurs + */ + public static void main(String args[]) throws Exception { + SQLXMLSample sample = new SQLXMLSample(); + + getRealUserPasswordUrl(args); + sample.run(); + } + + /** + * Demonstrate the sample code. + *
    + *
  1. drop the sample table to insure the table can be created properly
  2. + *
  3. create the sample table
  4. + *
  5. insert SQLXML values into the table
  6. + *
  7. read the SQLXML values from the table
  8. + *
  9. clean up
  10. + *
+ * + * @throws Exception + */ + private void run() throws Exception { + try (Connection conn = DriverManager.getConnection(url, user, password)) { + truncateTable(conn); + loadTable(conn); + queryTable(conn); + truncateTable(conn); + } + } + + /** + * Clear the sample table with two columns. + * + * @param conn a database Connection + * @throws SQLException + */ + private void truncateTable(Connection conn) throws SQLException { + String sql = "TRUNCATE TABLE SQLXML_JDBC_SAMPLE"; + show(sql); + doSql(conn, sql); + } + + /** + * Create SQLXML values and insert them into the sample table. Demonstrates + * two possible ways to create a SQLXML value. There are others. Uses the + * generic setObject(int, Object, SQLType) method to set the parameters. + * + * @param conn + * @throws SQLException + */ + private void loadTable(Connection conn) throws SQLException { + String insertDml = "INSERT INTO SQLXML_JDBC_SAMPLE (DOCUMENT, ID) VALUES (?, ?)"; + try (PreparedStatement prepStmt = conn.prepareStatement(insertDml)) { + + SQLXML xml = conn.createSQLXML(); + xml.setString("\n" + + " \n" + + " 221\n" + + " John\n" + + " "); + + prepStmt.setObject(1, xml, JDBCType.SQLXML); + prepStmt.setObject(2, 221, JDBCType.NUMERIC); + prepStmt.executeUpdate(); + + xml = conn.createSQLXML(); + Writer w = xml.setCharacterStream(); + w.write("\n"); + w.write(" \n"); + w.write(" 222\n"); + w.write(" Mary\n"); + w.write(" \n"); + w.close(); + + prepStmt.setObject(1, xml, JDBCType.SQLXML); + prepStmt.setObject(2, 222, JDBCType.NUMERIC); + prepStmt.executeUpdate(); + + } + catch (IOException ex) { + throw new SQLException(ex); + } + } + + /** + * Query the sample table, retrive the SQLXML values and print their contents + * to stdout. Uses the generic getObject(int, Class) method. + * + * @param conn + * @throws SQLException + */ + private void queryTable(Connection conn) throws SQLException { + String query = "SELECT DOCUMENT, ID FROM SQLXML_JDBC_SAMPLE ORDER BY ID"; + try (PreparedStatement pstmt = conn.prepareStatement(query)) { + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + SQLXML sqlxml = rs.getObject(1, SQLXML.class); + InputStream binaryStream = sqlxml.getBinaryStream(); + DocumentBuilder parser + = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Document result = parser.parse(binaryStream); + printDocument(result, System.out); + System.out.println(); + } + } + catch (IOException | TransformerException | SAXException | ParserConfigurationException ex) { + throw new SQLException(ex); + } + } + + + // + // Utility methods + // + + /** + * Simple code to print an XML Documint to an OutputStream. + * + * @param doc an XML document to print + * @param the stream to print to + * @throws IOException if an error occurs is writing the output + * @throws TransformerException if an error occurs in generating the output + */ + static void printDocument(Document doc, OutputStream out) + throws IOException, TransformerException { + TransformerFactory factory = TransformerFactory.newInstance(); + Transformer transformer = factory.newTransformer(); + transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); + transformer.setOutputProperty(OutputKeys.METHOD, "xml"); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); + + transformer.transform(new DOMSource(doc), + new StreamResult(new OutputStreamWriter(out, "UTF-8"))); + } + + static void doSql(Connection conn, String sql) throws SQLException { + try (Statement stmt = conn.createStatement()) { + stmt.execute(sql); + } + } + + static void trySql(Connection conn, String sql) { + try { + doSql(conn, sql); + } + catch (SQLException ex) { + // ignore + } + } + + static void show(String msg) { + System.out.println(msg); + } + + static void showError(String msg, Throwable exc) { + System.err.println(msg + " hit error: " + exc.getMessage()); + } + + static void getRealUserPasswordUrl(String args[]) throws Exception { + // URL can be modified in file, or taken from command-line + url = getOptionValue(args, "-l", DEFAULT_URL); + + // DB user can be modified in file, or taken from command-line + user = getOptionValue(args, "-u", DEFAULT_USER); + + // DB user's password can be modified in file, or explicitly entered + readPassword(" Password for " + user + ": "); + } + + // Get specified option value from command-line. + static String getOptionValue( + String args[], String optionName, String defaultVal) { + String argValue = ""; + try { + int i = 0; + String arg = ""; + boolean found = false; + + while (i < args.length) { + arg = args[i++]; + + if (arg.equals(optionName)) { + if (i < args.length) + argValue = args[i++]; + if (argValue.startsWith("-") || argValue.equals("")) { + argValue = defaultVal; + } + found = true; + } + } + + if (!found) { + argValue = defaultVal; + } + } catch (Exception e) { + showError("getOptionValue", e); + } + + return argValue; + } + + static void readPassword(String prompt) throws Exception { + if (System.console() != null) { + char[] pchars = System.console().readPassword("\n[%s]", prompt); + if (pchars != null) { + password = new String(pchars); + java.util.Arrays.fill(pchars, ' '); + } + } else { + BufferedReader r = new BufferedReader(new InputStreamReader(System.in)); + show(prompt); + password = r.readLine(); + } + } +} diff --git a/java/jdbc/BasicSamples/StatementSample.java b/java/jdbc/BasicSamples/StatementSample.java new file mode 100755 index 00000000..444bebf8 --- /dev/null +++ b/java/jdbc/BasicSamples/StatementSample.java @@ -0,0 +1,409 @@ +/* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.*/ + +/** + * DESCRIPTION + * + * A simple illustration of CRUD operation using the Statement object. + */ +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Scanner; + +import oracle.jdbc.OracleConnection; +import oracle.jdbc.pool.OracleDataSource; + + +public class StatementSample { + + private static final int USER_OPTION_SELECTALL = 1; + private static final int USER_OPTION_SELECTONE = 2; + private static final int USER_OPTION_INSERT = 3; + private static final int USER_OPTION_UPDATE = 4; + private static final int USER_OPTION_DELETE = 5; + private static final int USER_OPTION_EXIT = 0; + + private static final String SQL_INSERT = "INSERT INTO EMP (EMPNO, ENAME, JOB, HIREDATE, SAL) VALUES(%d, '%s', '%s', TO_DATE('%s', 'yyyy/mm/dd'), %f)"; + + private static final String SQL_UPDATE + = "UPDATE EMP SET ENAME = '%s', JOB = '%s', HIREDATE = " + + "TO_DATE('%s', 'yyyy/mm/dd'), SAL = %f WHERE EMPNO = %d"; + + private static final String SQL_DELETE = "DELETE FROM EMP WHERE EMPNO = %d"; + private static final String SQL_SELECT_ALL = "SELECT * FROM EMP"; + private static final String SQL_SELECT_ONE = "SELECT * FROM EMP WHERE EMPNO = %d"; + + + + + private static final String DEFAULT_USER = "myuser"; + private static final String DEFAULT_URL + = "jdbc:oracle:thin:@//myhost:1521/myservice"; + + private final String user; + private final String password; + private final String url; + + /** + * Creates an StatementDemo instance with the given details. + * @param user + * @param pwd + * @param url + */ + private StatementSample(String user, String pwd, String url) { + this.user = user; + this.password = pwd; + this.url = url; + } + + /** + * Get a connection from the Oracle Database. + * and performs CRUD operation based on the user input. + * @throws SQLException + */ + private void startDemo() throws SQLException { + OracleConnection connection = getConnection(); + try { + // loops forever until the user choose to exit. + while (true) { + int userOption = getUserOption(); + switch(userOption) { + case USER_OPTION_SELECTONE : + selectOne(connection); + break; + case USER_OPTION_SELECTALL : + selectAll(connection); + break; + case USER_OPTION_INSERT : + insert(connection); + break; + case USER_OPTION_UPDATE : + update(connection); + break; + case USER_OPTION_DELETE : + delete(connection); + break; + case USER_OPTION_EXIT : + show("Bye !!"); + return; + default : + show("Invalid option : " + userOption); + } + } + } + finally { + connection.close(); + } + } + + /** + * Creates an OracleConnection instance and return it. + * @return oracleConnection + * @throws SQLException + */ + private OracleConnection getConnection() throws SQLException { + OracleDataSource ods = new OracleDataSource(); + ods.setUser(user); + ods.setPassword(password); + ods.setURL(url); + return (OracleConnection)ods.getConnection(); + } + + /** + * Gets employee details from the user and insert into + * the Employee table. + * @param connection + */ + private void insert(OracleConnection connection) { + try(Statement stmt = connection.createStatement()) { + Employee employee = getEmployeeFromConsole(); + if(employee == null) { + showError("Unable to get employee details."); + return; + } + String insertSQL = String.format(SQL_INSERT, employee.getId(), + employee.getName(), employee.getDesignation(), + employee.getJoiningDate(), employee.getSalary()); + boolean status = stmt.execute(insertSQL); + show("Insert successfull !!"); + } + catch(SQLException sqle) { + showError(sqle.getMessage()); + } + } + + /** + * Gets employee details from the user and update row in + * the Employee table with the new details. + * @param connection + */ + private void update(OracleConnection connection) { + try(Statement stmt = connection.createStatement()) { + Employee employee = getEmployeeFromConsole(); + if(employee == null) { + showError("Unable to get employee details."); + return; + } + String updateSQL = String.format(SQL_UPDATE, + employee.getName(), employee.getDesignation(), + employee.getJoiningDate(), employee.getSalary(), + employee.getId()); + int noOfRecordsUpdated = stmt.executeUpdate(updateSQL); + show("Number of records updated : " + noOfRecordsUpdated); + } + catch(SQLException sqle) { + showError(sqle.getMessage()); + } + } + + /** + * Gets the employee id from the user and deletes the employee + * row from the employee table. + * @param connection + */ + private void delete(OracleConnection connection) { + try(Statement stmt = connection.createStatement()) { + int employeeID = getEmployeeIDFromConsole(); + String deleteSQL = String.format(SQL_DELETE,employeeID); + int noOfRecordsDeleted = stmt.executeUpdate(deleteSQL); + show("Number of records deleted : " + noOfRecordsDeleted); + } + catch(SQLException sqle) { + showError(sqle.getMessage()); + } + } + + /** + * Gets the employee id from the user and retrieve the specific + * employee details from the employee table. + * @param connection + */ + private void selectOne(OracleConnection connection) { + int empId = getEmployeeIDFromConsole(); + try(Statement stmt = connection.createStatement()) { + final String selectSQL = String.format(SQL_SELECT_ONE, empId); + ResultSet rs = stmt.executeQuery(selectSQL); + if(rs.next()) { + Employee emp = new Employee(rs.getInt("EMPNO"), + rs.getString("ENAME"), rs.getString("JOB"), + rs.getString("HIREDATE"), rs.getDouble("SAL")); + emp.print(); + } + else { + show("No records found for the employee id : " + empId); + } + } + catch(SQLException sqle) { + showError(sqle.getMessage()); + } + } + + /** + * Selects all the rows from the employee table. + * @param connection + */ + private void selectAll(OracleConnection connection) { + try(Statement stmt = connection.createStatement()) { + ResultSet rs = stmt.executeQuery(SQL_SELECT_ALL); + while(rs.next()) { + Employee emp = new Employee(rs.getInt("EMPNO"), + rs.getString("ENAME"), rs.getString("JOB"), + rs.getString("HIREDATE"), rs.getDouble("SAL")); + emp.print(); + } + } + catch(SQLException sqle) { + showError(sqle.getMessage()); + } + } + + // Start the main with the command "java StatementDemo -u "" -l "" + public static void main(String args[]) throws SQLException, IOException { + // Gets the URL and USER value from command line arguments + String url = getCmdOptionValue(args, "-l", DEFAULT_URL); + String user = getCmdOptionValue(args, "-u", DEFAULT_USER); + + // DB user's Password must be entered + String pwd = readPassword(" Password for " + user + ": "); + + StatementSample demo = new StatementSample(user, pwd, url); + demo.startDemo(); + } + + private static String readPassword(String prompt) throws IOException { + if (System.console() == null) { + BufferedReader r = new BufferedReader(new InputStreamReader(System.in)); + System.out.print(prompt); + return r.readLine(); + } + else { + return new String(System.console().readPassword(prompt)); + } + } + + // Get specified option value from command-line, or use default value + private static String getCmdOptionValue(String args[], String optionName, + String defaultVal) { + String argValue = ""; + try { + int i = 0; + String arg = ""; + boolean found = false; + while (i < args.length) { + arg = args[i++]; + if (arg.equals(optionName)) { + if (i < args.length) + argValue = args[i++]; + if (argValue.startsWith("-") || argValue.equals("")) { + show("No value for Option " + optionName + ", use default."); + argValue = defaultVal; + } + found = true; + } + } + + if (!found) { + show("No Option " + optionName + " specified, use default."); + argValue = defaultVal; + } + } + catch (Exception e) { + showError("getOptionValue" + e.getMessage()); + } + return argValue; + } + + /** + * Get the user option to perform the table operation. + * + * @return + */ + private static int getUserOption() { + int userOption = -1; + try { + Scanner scanner = new Scanner(System.in); + System.out.println( + "1 - Select All, 2 - Select One, 3 - Insert, 4 - Update, 5 - Delete, 0 - Exit"); + System.out.println("Enter Option :"); + userOption = Integer.parseInt(scanner.nextLine()); + } + catch (Exception e) { + /* Ignore exception */ + } + return userOption; + } + + /** + * An utility method to get the employee details from the user. + * + * @return employeeObj + */ + private static Employee getEmployeeFromConsole() { + Employee empObj = null; + ; + try { + Scanner scanner = new Scanner(System.in); + System.out.println("Enter Employee Details"); + System.out.println("ID : "); + int id = Integer.parseInt(scanner.nextLine()); + System.out.println("Name : "); + String name = scanner.nextLine(); + System.out.println("Designation : "); + String designation = scanner.nextLine(); + System.out.println("Joining Date(yyyy/mm/dd) : "); + String joiningDate = scanner.nextLine(); + System.out.println("Salary : "); + double salary = Double.parseDouble(scanner.nextLine()); + empObj = new Employee(id, name, designation, joiningDate, salary); + } + catch (Exception e) { + /* Ignore exception */ + } + return empObj; + } + + /** + * An utility method to get the employee id from the user. + * + * @return employeeID + */ + private static int getEmployeeIDFromConsole() { + int empId = -1; + try { + Scanner scanner = new Scanner(System.in); + System.out.println("Enter Employee ID :"); + empId = Integer.parseInt(scanner.nextLine()); + } + catch (Exception e) { + /* Ignore exception */ + } + return empId; + } + + private static void show(String msg) { + System.out.println(msg); + } + + private static void showError(String msg) { + System.out.println("Error : " + msg); + } + + /** + * A simple class to represent the employee table structure + * An instance of this represents a row in employee table. + */ + private static class Employee { + private int id; + private String name; + private String designation; + private String joiningDate; + private double salary; + + Employee(int id, String name, String designation, String joiningDate, + double salary) { + super(); + this.id = id; + this.name = name; + this.designation = designation; + this.joiningDate = joiningDate; + this.salary = salary; + } + + int getId() { + return id; + } + + String getName() { + return name; + } + + String getDesignation() { + return designation; + } + + String getJoiningDate() { + return joiningDate; + } + + double getSalary() { + return salary; + } + + void print() { + show( + "/----------------------------------------------------------------/"); + show("ID : " + id); + show("NAME : " + name); + show("Designation : " + designation); + show("Joining Date: " + joiningDate); + show("Salary : " + salary); + show( + "/----------------------------------------------------------------/"); + } + + } + +} diff --git a/java/jdbc/BasicSamples/UCPBasic.java b/java/jdbc/BasicSamples/UCPBasic.java new file mode 100755 index 00000000..0786db42 --- /dev/null +++ b/java/jdbc/BasicSamples/UCPBasic.java @@ -0,0 +1,257 @@ +/* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.*/ + +/* + * DESCRIPTION + * + * This code sample illustrates the simple steps of how JDBC applications + * use the Oracle Universal Connection Pool (UCP). + * + * JDBC applications typically interact with UCP via a pool-enabled + * data source. The embedded connection pool is implicitly created + * at the first connection borrow (or checkout) from the pool. + * + * The basic steps include first creating a pool-enabled data source, + * configuring properties essential to establishing JDBC connections, + * and then invoking JDBC APIs to get connections from the data source + * and the embedded connection pool. + * + * For comparison, this sample also illustrates how applications do + * regular JDBC connect using a JDBC driver data source. It is very + * simple to migrate JDBC applications to using UCP. + * + * It is required that applications have both ucp.jar and Oracle JDBC + * driver jar(s) (such as ojdbc8.jar or ojdbc7.jar) on the classpath, + * and that the database backend supports SQL (this sample uses an + * Oracle Database). + * + * To run the sample, you must provide non-default and working values + * for ALL 3 of user, password, and URL. This can be done by either updating + * this file directly or supplying the 3 values as command-line options + * and user input. The password is read from console or standard input. + * java UCPBasic -url -user + * If you do not update all the defaults, the program proceeds but + * will hit error when connecting. + */ + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +// From Oracle JDBC driver +import oracle.jdbc.pool.OracleDataSource; + +import oracle.ucp.jdbc.PoolDataSourceFactory; +import oracle.ucp.jdbc.PoolDataSource; + + +public class UCPBasic { + final static String DEFAULT_URL = "jdbc:oracle:thin:@//myhost:myport/myservice"; + final static String DEFAULT_USER = "myuser"; + final static String DEFAULT_PASSWORD = "mypassword"; + final static String CONN_FACTORY_CLASS = "oracle.jdbc.pool.OracleDataSource"; + + // You must provide non-default values for ALL 3 to execute the program + static String url = DEFAULT_URL; + static String user = DEFAULT_USER; + static String password = DEFAULT_PASSWORD; + + public static void main(String args[]) throws Exception { + UCPBasic sample = new UCPBasic(); + + getRealUserPasswordUrl(args); + sample.run(); + } + + private void run() throws Exception { + demoRegularJDBCConnect(); + demoUCPConnect(); + } + + // Illustrates how to get a connection using a driver data source. + private void demoRegularJDBCConnect() { + show("\ndemoRegularJDBCConnect starts"); + + try { + OracleDataSource ods = new OracleDataSource(); + ods.setURL(url); + ods.setUser(user); + ods.setPassword(password); + + // This creates a physical connection to the database. + Connection conn = ods.getConnection(); + show("Created a physical connection: " + conn); + + // This closes the physical connection. + conn.close(); + show("Closed physical connection: " + conn); + } catch (Throwable e) { + showError("demoRegularJDBCConnect", e); + } + + show("demoRegularJDBCConnect completes"); + } + + // Illustrates how to use a UCP-enabled data source. + private void demoUCPConnect() { + show("\nUCPBasic starts"); + + try { + /* + * Step 1 - creates a pool-enabled data source instance + */ + PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); + + /* + * Step 2 - configures pool properties for establishing connections. + * These include required and optional properties. + */ + + /* Required pool properties */ + + // UCP uses a connection factory to create physical connections. + // This is typically a JDBC driver javax.sql.DataSource or + // java.sql.Driver implementation class. + pds.setConnectionFactoryClassName(CONN_FACTORY_CLASS); + pds.setURL(url); + pds.setUser(user); + pds.setPassword(password); + + /* Optional pool properties */ + + // Pool name should be unique within the same JVM instance. + // It is useful for administrative tasks, such as starting, + // stopping, refreshing a pool. Setting a pool name is optional + // but recommended. If user does not set a pool name, UCP will + // automatically generate one. + pds.setConnectionPoolName("UCPBasic_pool"); + + // The default is 0. + pds.setInitialPoolSize(5); + + // The default is 0. + pds.setMinPoolSize(5); + + // The default is Integer.MAX_VALUE. + pds.setMaxPoolSize(10); + + show("Connection pool configured"); + + /* + * Step 3 - borrow connections from and return connections to + * the connection pool. + */ + + // Borrow a connection from UCP. The connection object is a proxy + // of a physical connection. The physical connection is returned + // to the pool when Connection.close() is called on the proxy. + try (Connection conn1 = pds.getConnection()) { + showPoolStatistics("After checkout", pds); + + makeJDBCCalls(conn1); + } catch (SQLException exc) { + showError("1st checkout", exc); + } + + showPoolStatistics("After checkin", pds); + + // Another round of borrow/return. + try (Connection conn2 = pds.getConnection()) { + showPoolStatistics("After 2nd checkout", pds); + + makeJDBCCalls(conn2); + } catch (SQLException exc) { + showError("2nd checkout", exc); + } + + showPoolStatistics("After 2nd checkin", pds); + } catch (Throwable e) { + showError("demoUCPConnect", e); + } + + show("UCPBasic completes"); + } + + // Simple query + private void makeJDBCCalls(Connection conn) { + try (Statement statement = conn.createStatement()) { + statement.execute("SELECT 1 FROM DUAL"); + } catch (SQLException exc) { + showError("JDBC operation", exc); + } + } + + private void showPoolStatistics(String prompt, PoolDataSource pds) + throws SQLException { + show(prompt + " -"); + show(" Available connections: " + pds.getAvailableConnectionsCount()); + show(" Borrowed connections: " + pds.getBorrowedConnectionsCount()); + } + + static void show(String msg) { + System.out.println(msg); + } + + static void showError(String msg, Throwable exc) { + System.err.println(msg + " hit error: " + exc.getMessage()); + } + + static void getRealUserPasswordUrl(String args[]) throws Exception { + // URL can be modified in file, or taken from command-line + url = getOptionValue(args, "-url", DEFAULT_URL); + + // DB user can be modified in file, or taken from command-line + user = getOptionValue(args, "-user", DEFAULT_USER); + + // DB user's password can be modified in file, or explicitly entered + readPassword(" Password for " + user + ": "); + } + + // Get specified option value from command-line. + static String getOptionValue( + String args[], String optionName, String defaultVal) { + String argValue = ""; + try { + int i = 0; + String arg = ""; + boolean found = false; + + while (i < args.length) { + arg = args[i++]; + + if (arg.equals(optionName)) { + if (i < args.length) + argValue = args[i++]; + if (argValue.startsWith("-") || argValue.equals("")) { + argValue = defaultVal; + } + found = true; + } + } + + if (!found) { + argValue = defaultVal; + } + } catch (Exception e) { + showError("getOptionValue", e); + } + + return argValue; + } + + static void readPassword(String prompt) throws Exception { + if (System.console() != null) { + char[] pchars = System.console().readPassword("\n[%s]", prompt); + if (pchars != null) { + password = new String(pchars); + java.util.Arrays.fill(pchars, ' '); + } + } else { + BufferedReader r = new BufferedReader(new InputStreamReader(System.in)); + show(prompt); + password = r.readLine(); + } + } +} + diff --git a/java/jdbc/BasicSamples/UCPHarvesting.java b/java/jdbc/BasicSamples/UCPHarvesting.java new file mode 100755 index 00000000..b8f8bbbc --- /dev/null +++ b/java/jdbc/BasicSamples/UCPHarvesting.java @@ -0,0 +1,369 @@ +/* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.*/ + +/* + * DESCRIPTION + * + * This code sample illustrates how applications use the connection harvesting + * feature of Oracle Universal Connection Pool (UCP). + * + * Connection Harvesting allows a specified number of borrowed connections + * to be reclaimed when the connection pool reaches a specified number of + * available connections. Least recently used connections are harvested first. + * + * This feature helps to ensure that a certain number of connections are + * always available in the pool to maximize performance. + * + * UCP gives applications control over which borrowed connections can be + * harvested. By default, all connections are harvestable. Applications + * can use the HarvestableConnection interface to explicitly specify + * whether a connection is harvestable. + * + * For harvestable connections, UCP also provides ConnectionHarvestingCallback + * that allows applications to perform customized cleanup tasks when + * connections are harvested by the pool. + * + * It is required that applications have both ucp.jar and Oracle JDBC + * driver jar(s) (such as ojdbc8.jar or ojdbc7.jar) on the classpath, + * and that the database backend supports SQL (this sample uses an + * Oracle Database and the default HR schema). + * + * To run the sample, you must provide non-default and working values + * for ALL 3 of user, password, and URL. This can be done by either updating + * this file directly or supplying the 3 values as command-line options + * and user input. The password is read from console or standard input. + * java UCPHarvesting -url -user + * If you do not update all the defaults, the program proceeds but + * will hit error when connecting. + */ + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +import oracle.ucp.ConnectionHarvestingCallback; +import oracle.ucp.admin.UniversalConnectionPoolManagerImpl; +import oracle.ucp.jdbc.HarvestableConnection; +import oracle.ucp.jdbc.PoolDataSource; +import oracle.ucp.jdbc.PoolDataSourceFactory; + + +public class UCPHarvesting { + final static String DEFAULT_URL = "jdbc:oracle:thin:@//myhost:myport/myservice"; + final static String DEFAULT_USER = "myuser"; + final static String DEFAULT_PASSWORD = "mypassword"; + final static String CONN_FACTORY_CLASS = "oracle.jdbc.pool.OracleDataSource"; + + // You must provide non-default values for ALL 3 to execute the program + static String url = DEFAULT_URL; + static String user = DEFAULT_USER; + static String password = DEFAULT_PASSWORD; + + public static void main(String args[]) throws Exception { + UCPHarvesting sample = new UCPHarvesting(); + + getRealUserPasswordUrl(args); + sample.run(); + } + + void run() throws Exception { + runDefaultAllConnectionsHarvestable(); + runWithNonHarvestableConnections(); + } + + void runDefaultAllConnectionsHarvestable() throws Exception { + show("\n*** Run with default: all connections are harvestable ***"); + + try { + int INITIAL_POOL_SIZE = 10; + final String POOL_NAME = "UCPHarvesting_pool1"; + Connection conns[] = new Connection[INITIAL_POOL_SIZE]; + + PoolDataSource pds = createPoolDataSource(POOL_NAME); + /* + * ConnectionHarvestTriggerCount specifies the available connections + * threshold that triggers connection harvesting. For example, if the + * connection harvest trigger count is set to 5, connection harvesting + * is triggered when the number of available connections in the pool + * drops to 5. + * + * A value of Integer.MAX_VALUE disables connection harvesting. + * By default, connection harvesting is disabled. + */ + pds.setConnectionHarvestTriggerCount(5); + /* + * ConnectionHarvestMaxumCount specifies the maximum number of + * borrowed connections that can be returned to the pool, once + * connection harvesting is triggered. The default is 1. + */ + pds.setConnectionHarvestMaxCount(2); + + show("Connection pool " + POOL_NAME + " configured"); + show("Initial pool size: " + pds.getInitialPoolSize()); + + show("\nBorrowing 4 connections, conns[0] and conns[1] are LRU"); + + TestHarvestingCallback[] cbks = new TestHarvestingCallback[5]; + for (int i = 0; i < 4; i++) { + conns[i] = pds.getConnection(); + // Register harvesting callbacks to cleanup reclaimed connections. + cbks[i] = new TestHarvestingCallback(conns[i]); + ((HarvestableConnection) conns[i]).registerConnectionHarvestingCallback(cbks[i]); + makeJDBCCalls(conns[i]); + } + + showPoolStatistics("\nAfter borrowing 4 connections", pds); + + // Borrowing the 5th connection to trigger harvesting + show("\nBorrowing 5th connection to trigger harvesting ..."); + conns[4] = pds.getConnection(); + cbks[4] = new TestHarvestingCallback(conns[4]); + ((HarvestableConnection) conns[4]).registerConnectionHarvestingCallback(cbks[4]); + + // Harvesting should happen + Thread.sleep(15000); + + // After harvesting, there will be 7 available connections and + // 3 borrowed connections in the pool. + showPoolStatistics("\nAfter harvesting", pds); + + // conns[0] and [1]'s physical connections should be "harvested" + // by the pool and these two logical connections should be closed + show("\nChecking on the 5 borrowed connections ..."); + show(" conns[0] should be closed -- " + conns[0].isClosed()); + show(" conns[1] should be closed -- " + conns[1].isClosed()); + show(" conns[2] should be open -- " + !conns[2].isClosed()); + show(" conns[3] should be open -- " + !conns[3].isClosed()); + show(" conns[4] should be open -- " + !conns[4].isClosed()); + + // Returning all connections to pool. + for (int i = 2; i < 5; i++) + conns[i].close(); + + destroyConnectionPool(POOL_NAME); + } catch (Throwable e) { + showError("runDefaultAllConnectionsHarvestable", e); + } + + show("\n*** Run with default completes ***"); + } + + void runWithNonHarvestableConnections() throws Exception { + show("\n*** Run with non-harvestable connections ***"); + + try { + int INITIAL_POOL_SIZE = 10; + final String POOL_NAME = "UCPHarvesting_pool2"; + Connection conns[] = new Connection[INITIAL_POOL_SIZE]; + + PoolDataSource pds = createPoolDataSource(POOL_NAME); + /* + * ConnectionHarvestTriggerCount specifies the available connections + * threshold that triggers connection harvesting. For example, if the + * connection harvest trigger count is set to 5, connection harvesting + * is triggered when the number of available connections in the pool + * drops to 5. + * + * A value of Integer.MAX_VALUE disables connection harvesting. + * By default, connection harvesting is disabled. + */ + pds.setConnectionHarvestTriggerCount(5); + /* + * ConnectionHarvestMaxumCount specifies the maximum number of + * borrowed connections that can be returned to the pool, once + * connection harvesting is triggered. The default is 1. + */ + pds.setConnectionHarvestMaxCount(2); + + show("Connection pool " + POOL_NAME + " configured"); + show("Initial pool size: " + pds.getInitialPoolSize()); + + show("\nBorrowing 4 connections, conns[0] and conns[1] are LRU"); + + TestHarvestingCallback[] cbks = new TestHarvestingCallback[5]; + for (int i = 0; i < 4; i++) { + conns[i] = pds.getConnection(); + // Register harvesting callbacks to cleanup reclaimed connections. + cbks[i] = new TestHarvestingCallback(conns[i]); + ((HarvestableConnection) conns[i]).registerConnectionHarvestingCallback(cbks[i]); + makeJDBCCalls(conns[i]); + } + + show("\nMarking conns[0] and conns[1] as non-harvestable"); + // Assuming application is doing critical work on conns[0] and [1] + // and doesn't want those 2 connections to be "harvested". + // Mark conns[0] and [1] as non-harvestable connections. + ((HarvestableConnection) conns[0]).setConnectionHarvestable(false); + ((HarvestableConnection) conns[1]).setConnectionHarvestable(false); + + showPoolStatistics("\nAfter borrowing 4 connections", pds); + + // Borrowing the 5th connection to trigger harvesting + show("\nBorrowing 5th connection to trigger harvesting ..."); + conns[4] = pds.getConnection(); + cbks[4] = new TestHarvestingCallback(conns[4]); + ((HarvestableConnection) conns[4]).registerConnectionHarvestingCallback(cbks[4]); + + // Harvesting should happen + Thread.sleep(15000); + + // After harvesting, there will be 7 available connections and + // 3 borrowed connections in the pool. + showPoolStatistics("\nAfter harvesting", pds); + + // conns[2] and [3]'s physical connections should be "harvested" + // by the pool and these two logical connections should be closed. + // conns[0] and [1]'s physical connections will not be "harvested". + show("\nChecking on the 5 borrowed connections ..."); + show(" conns[0] should be open -- " + !conns[0].isClosed()); + show(" conns[1] should be open -- " + !conns[1].isClosed()); + show(" conns[2] should be closed -- " + conns[2].isClosed()); + show(" conns[3] should be closed -- " + conns[3].isClosed()); + show(" conns[4] should be open -- " + !conns[4].isClosed()); + + // Returning all connections to pool. + conns[0].close(); + conns[1].close(); + conns[4].close(); + + destroyConnectionPool(POOL_NAME); + } catch (Throwable e) { + showError("runWithNonHarvestableConnections", e); + } + + show("\n*** Run with non-harvestable connections completes ***"); + } + + // See sample UCPBasic.java for basic steps to set up a connection pool. + PoolDataSource createPoolDataSource(String poolName) throws Exception { + PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); + pds.setConnectionFactoryClassName(CONN_FACTORY_CLASS); + pds.setURL(url); + pds.setUser(user); + pds.setPassword(password); + pds.setConnectionPoolName(poolName); + pds.setInitialPoolSize(10); + pds.setMaxPoolSize(10); + pds.setTimeoutCheckInterval(5); + + return pds; + } + + void destroyConnectionPool(String poolName) { + try { + UniversalConnectionPoolManagerImpl.getUniversalConnectionPoolManager() + .destroyConnectionPool(poolName); + show("\nConnection pool " + poolName + " destroyed"); + } catch (Throwable e) { + showError("destroyConnectinoPool", e); + } + } + + void showPoolStatistics(String prompt, PoolDataSource pds) + throws SQLException { + show(prompt + " -"); + show(" Available connections: " + pds.getAvailableConnectionsCount()); + show(" Borrowed connections: " + pds.getBorrowedConnectionsCount()); + } + + // Simple query + void makeJDBCCalls(Connection conn) { + try (Statement statement = conn.createStatement()) { + statement.execute("SELECT 1 FROM DUAL"); + } catch (SQLException exc) { + showError("makeJDBCCalls", exc); + } + } + + static void show(String msg) { + System.out.println(msg); + } + + static void showError(String msg, Throwable exc) { + System.out.println(msg + " hit error: " + exc.getMessage()); + } + + static void getRealUserPasswordUrl(String args[]) throws Exception { + // URL can be modified in file, or taken from command-line + url = getOptionValue(args, "-url", DEFAULT_URL); + + // DB user can be modified in file, or taken from command-line + user = getOptionValue(args, "-user", DEFAULT_USER); + + // DB user's password can be modified in file, or explicitly entered + readPassword(" Password for " + user + ": "); + } + + // Get specified option value from command-line. + static String getOptionValue( + String args[], String optionName, String defaultVal) { + String argValue = ""; + try { + int i = 0; + String arg = ""; + boolean found = false; + + while (i < args.length) { + arg = args[i++]; + + if (arg.equals(optionName)) { + if (i < args.length) + argValue = args[i++]; + if (argValue.startsWith("-") || argValue.equals("")) { + argValue = defaultVal; + } + found = true; + } + } + + if (!found) { + argValue = defaultVal; + } + } catch (Exception e) { + showError("getOptionValue", e); + } + + return argValue; + } + + static void readPassword(String prompt) throws Exception { + if (System.console() != null) { + char[] pchars = System.console().readPassword("\n[%s]", prompt); + if (pchars != null) { + password = new String(pchars); + java.util.Arrays.fill(pchars, ' '); + } + } else { + BufferedReader r = new BufferedReader(new InputStreamReader(System.in)); + show(prompt); + password = r.readLine(); + } + } + + /* + * Sample connection harvesting callback implementation. + */ + class TestHarvestingCallback implements ConnectionHarvestingCallback { + private Object objForCleanup = null; + + public TestHarvestingCallback(Object objForCleanup) { + this.objForCleanup = objForCleanup; + } + + public boolean cleanup() { + try { + doCleanup(objForCleanup); + } catch (Exception exc) { + return false; + } + + return true; + } + + private void doCleanup(Object obj) throws Exception { + ((Connection) obj).close(); + } + } +} + diff --git a/java/jdbc/BasicSamples/UCPLabeling.java b/java/jdbc/BasicSamples/UCPLabeling.java new file mode 100755 index 00000000..c334b67e --- /dev/null +++ b/java/jdbc/BasicSamples/UCPLabeling.java @@ -0,0 +1,318 @@ +/* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.*/ + +/* + * DESCRIPTION + * + * This code sample illustrates how applications use the connection labeling + * feature of Oracle Universal Connection Pool (UCP). + * + * Connection Labeling allows applications to request pre-configured + * connections identified by labels, in order to minimize connection + * reinitialization cost. + * + * Connection Labeling does not impose any meaning on user-defined keys + * or values; the meaning of user-defined keys and values is defined + * solely by the application. + * + * Connection labeling is application-driven and requires the use of + * two interfaces: + * + * (1) The oracle.ucp.jdbc.LabelableConnection interface is used to + * apply and remove connection labels, as well as retrieving labels + * that have been set on a connection. + * + * (2) The oracle.ucp.ConnectionLabelingCallback interface is used to + * create a labeling callback that determines whether or not + * a connection with a requested label already exists. If no connections + * exist, the interface allows current connections to be configured + * as required. + * + * It is required that applications have both ucp.jar and Oracle JDBC + * driver jar(s) (such as ojdbc8.jar or ojdbc7.jar) on the classpath, + * and that the database backend supports SQL (this sample uses an + * Oracle Database and the default HR schema). + * + * To run the sample, you must provide non-default and working values + * for ALL 3 of user, password, and URL. This can be done by either updating + * this file directly or supplying the 3 values as command-line options + * and user input. The password is read from console or standard input. + * java UCPLabeling -url -user + * If you do not update all the defaults, the program proceeds but + * will hit error when connecting. + */ + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import oracle.ucp.ConnectionLabelingCallback; +import oracle.ucp.admin.UniversalConnectionPoolManagerImpl; +import oracle.ucp.jdbc.LabelableConnection; +import oracle.ucp.jdbc.PoolDataSource; +import oracle.ucp.jdbc.PoolDataSourceFactory; + + +public class UCPLabeling { + final static String DEFAULT_URL = "jdbc:oracle:thin:@//myhost:myport/myservice"; + final static String DEFAULT_USER = "myuser"; + final static String DEFAULT_PASSWORD = "mypassword"; + final static String CONN_FACTORY_CLASS = "oracle.jdbc.pool.OracleDataSource"; + + // You must provide non-default values for ALL 3 to execute the program + static String url = DEFAULT_URL; + static String user = DEFAULT_USER; + static String password = DEFAULT_PASSWORD; + + public static void main(String args[]) throws Exception { + UCPLabeling sample = new UCPLabeling(); + + getRealUserPasswordUrl(args); + sample.run(); + } + + void run() throws Exception { + show("\n*** Demo Connection Labeling ***"); + + try { + final String POOL_NAME = "UCPLabeling_pool1"; + + PoolDataSource pds = createPoolDataSource(POOL_NAME); + pds.setInitialPoolSize(2); + show("\nConnection pool " + POOL_NAME + " configured"); + show("Initial pool size: " + pds.getInitialPoolSize()); + + // Register connection labeling callback + ExampleLabelingCallback cbk = new ExampleLabelingCallback(); + pds.registerConnectionLabelingCallback(cbk); + show("\nLabeling callback registered on the pool"); + + // All initial connections in the pool do not have labels + show("\nBorrowing a regular connection 1 (without labels) from pool ..."); + Connection conn1 = pds.getConnection(); + showPoolStatistics("After borrowing regular connection 1", pds); + + // Change session state and apply corresponding connection label, + // using the LabelableConnection interface method. + conn1.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); + ((LabelableConnection) conn1).applyConnectionLabel("TRANSACTION_ISOLATION", "8"); + show("\nApplied new label on connection 1"); + + show("\nReturning labeled connection 1 to pool ..."); + conn1.close(); + showPoolStatistics("After returning labeled connection 1", pds); + + // Preferred connection label + Properties label = new Properties(); + label.setProperty("TRANSACTION_ISOLATION", "8"); + + // Specify preferred label(s) with the getConnection call + show("\nBorrowing connection 2 with preferred label ..."); + Connection conn2 = pds.getConnection(label); + showPoolStatistics("After borrowing labeled connection 2", pds); + + show("\nReturning labeled connection 2 to pool ..."); + conn2.close(); + showPoolStatistics("After returning labeled connection 2", pds); + + // Different preferred connection label + Properties label2 = new Properties(); + // Connection.TRANSACTION_READ_COMMITTED == 2 + label2.setProperty("TRANSACTION_ISOLATION", "2"); + + // Specify preferred label(s) with the getConnection call + show("\nBorrowing connection 3 with different preferred label ..."); + Connection conn3 = pds.getConnection(label2); + showPoolStatistics("After borrowing labeled connection 3", pds); + + show("\nReturning labeled connection 3 to pool ..."); + conn3.close(); + showPoolStatistics("After returning labeled connection 3", pds); + + destroyConnectionPool(POOL_NAME); + + } catch (Throwable e) { + showError("UCPLabeling", e); + } + + show("\n*** Demo Connection Labeling completes ***"); + } + + // See sample UCPBasic.java for basic steps to set up a connection pool. + PoolDataSource createPoolDataSource(String poolName) throws Exception { + PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); + pds.setConnectionFactoryClassName(CONN_FACTORY_CLASS); + pds.setURL(url); + pds.setUser(user); + pds.setPassword(password); + pds.setConnectionPoolName(poolName); + + return pds; + } + + void destroyConnectionPool(String poolName) { + try { + UniversalConnectionPoolManagerImpl.getUniversalConnectionPoolManager() + .destroyConnectionPool(poolName); + show("\nConnection pool " + poolName + " destroyed"); + } catch (Throwable e) { + showError("destroyConnectinoPool", e); + } + } + + void showPoolStatistics(String prompt, PoolDataSource pds) + throws SQLException { + show(prompt + " -"); + show(" Available connections: " + pds.getAvailableConnectionsCount()); + show(" Borrowed connections: " + pds.getBorrowedConnectionsCount()); + } + + static void show(String msg) { + System.out.println(msg); + } + + static void showError(String msg, Throwable exc) { + System.out.println(msg + " hit error: " + exc.getMessage()); + } + + static void getRealUserPasswordUrl(String args[]) throws Exception { + // URL can be modified in file, or taken from command-line + url = getOptionValue(args, "-url", DEFAULT_URL); + + // DB user can be modified in file, or taken from command-line + user = getOptionValue(args, "-user", DEFAULT_USER); + + // DB user's password can be modified in file, or explicitly entered + readPassword(" Password for " + user + ": "); + } + + // Get specified option value from command-line. + static String getOptionValue( + String args[], String optionName, String defaultVal) { + String argValue = ""; + try { + int i = 0; + String arg = ""; + boolean found = false; + + while (i < args.length) { + arg = args[i++]; + + if (arg.equals(optionName)) { + if (i < args.length) + argValue = args[i++]; + if (argValue.startsWith("-") || argValue.equals("")) { + argValue = defaultVal; + } + found = true; + } + } + + if (!found) { + argValue = defaultVal; + } + } catch (Exception e) { + showError("getOptionValue", e); + } + + return argValue; + } + + static void readPassword(String prompt) throws Exception { + if (System.console() != null) { + char[] pchars = System.console().readPassword("\n[%s]", prompt); + if (pchars != null) { + password = new String(pchars); + java.util.Arrays.fill(pchars, ' '); + } + } else { + BufferedReader r = new BufferedReader(new InputStreamReader(System.in)); + show(prompt); + password = r.readLine(); + } + } + + class ExampleLabelingCallback implements ConnectionLabelingCallback { + private boolean printed1 = false; + private boolean printed2 = false; + private boolean printed3 = false; + + public ExampleLabelingCallback() {} + + // The pool uses this method to select a connection with the least + // reconfiguration cost. 0 means a perfect match; any positive integer + // value indicates mismatch. It is up to the callback implementor + // to decide how to assign the cost values. + public int cost(Properties reqLabels, Properties currentLabels) { + // Case 1: exact match + if (reqLabels.equals(currentLabels)) { + if (!printed1) { + printed1 = true; + show(" FROM callback cost(): ## Exact match found ##"); + } + return 0; + } + + // Case 2: partial match + String iso1 = (String) reqLabels.get("TRANSACTION_ISOLATION"); + String iso2 = (String) currentLabels.get("TRANSACTION_ISOLATION"); + boolean match = + (iso1 != null && iso2 != null && iso1.equalsIgnoreCase(iso2)); + Set rKeys = reqLabels.keySet(); + Set cKeys = currentLabels.keySet(); + if (match && rKeys.containsAll(cKeys)) { + if (!printed2) { + printed2 = true; + show(" FROM callback cost(): ## Partial match found ##"); + } + return 10; + } + + // Case 3: no label matches application's preference. + // Picking this connection incurs the highest reinitialization cost. + if (!printed3) { + printed3 = true; + show(" FROM callback cost(): ## No match found ##"); + } + return Integer.MAX_VALUE; + } + + // In case a connection does not fully match the requested labels + // (and corresponding session state), configures the connection + // to establish the desired labels and state. This is done before + // the connection is returned to applications for a borrow request. + public boolean configure(Properties reqLabels, Object conn) { + try { + show(" Callback configure() is called to reinitialize connection"); + + String isoStr = (String) reqLabels.get("TRANSACTION_ISOLATION"); + // Map label value to isolation level constants on Connection. + ((Connection)conn).setTransactionIsolation(Integer.valueOf(isoStr)); + + LabelableConnection lconn = (LabelableConnection) conn; + + // Find the unmatched labels on this connection + Properties unmatchedLabels = + lconn.getUnmatchedConnectionLabels(reqLabels); + + // Apply each label in unmatchedLabels to connection. + // A real callback should also apply the corresponding state change. + for (Map.Entry label : unmatchedLabels.entrySet()) { + String key = (String) label.getKey(); + String value = (String) label.getValue(); + + lconn.applyConnectionLabel(key, value); + } + } catch (Exception exc) { + return false; + } + + return true; + } + } +} + diff --git a/java/jdbc/BasicSamples/UCPManager.java b/java/jdbc/BasicSamples/UCPManager.java new file mode 100755 index 00000000..313f51a6 --- /dev/null +++ b/java/jdbc/BasicSamples/UCPManager.java @@ -0,0 +1,358 @@ +/* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.*/ + +/* + * DESCRIPTION + * + * This code sample illustrates how applications use UCP manager's + * administration functions. These include: + * + * 1) createConnectionPool + * 2) stopConnectionPool + * 3) startConnectionPool + * 4) refreshConnectionPool + * 5) recycleConnectionPool + * 6) purgeConnectionPool + * 7) getConnectionPool + * 8) getConnectionPoolNames + * 9) destoryConnectionPool + * + * It is required that applications have both ucp.jar and Oracle JDBC + * driver jar(s) (such as ojdbc8.jar or ojdbc7.jar) on the classpath, + * and that the database backend supports SQL (this sample uses an + * Oracle Database and the default HR schema). + * + * To run the sample, you must provide non-default and working values + * for ALL 3 of user, password, and URL. This can be done by either updating + * this file directly or supplying the 3 values as command-line options + * and user input. The password is read from console or standard input. + * java UCPManager -url -user + * If you do not update all the defaults, the program proceeds but + * will hit error when connecting. + */ + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.sql.Connection; +import java.sql.SQLException; + +import oracle.ucp.UniversalConnectionPool; +import oracle.ucp.UniversalConnectionPoolException; +import oracle.ucp.admin.UniversalConnectionPoolManager; +import oracle.ucp.admin.UniversalConnectionPoolManagerImpl; +import oracle.ucp.jdbc.PoolDataSource; +import oracle.ucp.jdbc.PoolDataSourceFactory; +import oracle.ucp.jdbc.PoolDataSourceImpl; + + +public class UCPManager { + final static String DEFAULT_URL = "jdbc:oracle:thin:@//myhost:myport/myservice"; + final static String DEFAULT_USER = "myuser"; + final static String DEFAULT_PASSWORD = "mypassword"; + final static String CONN_FACTORY_CLASS = "oracle.jdbc.pool.OracleDataSource"; + + // You must provide non-default values for ALL 3 to execute the program + static String url = DEFAULT_URL; + static String user = DEFAULT_USER; + static String password = DEFAULT_PASSWORD; + + // Shared by all methods + private PoolDataSource pds = null; + + // Shared by all methods + private static UniversalConnectionPoolManager mgr = null; + + final static String POOL_NAME = "UCPManager_pool"; + + public static void main(String args[]) throws Exception { + UCPManager sample = new UCPManager(); + + getRealUserPasswordUrl(args); + sample.run(); + } + + void run() throws Exception { + mgr = UniversalConnectionPoolManagerImpl.getUniversalConnectionPoolManager(); + pds = createPoolDataSource(POOL_NAME); + + demoCreateConnectionPool(); + demoStartConnectionPool(); + demoStopConnectionPool(); + demoRefreshConnectionPool(); + demoRecycleConnectionPool(); + demoPurgeConnectionPool(); + demoGetConnectionPool(); + demoGetConnectionPoolNames(); + demoDestroyConnectionPool(); + } + + private void demoCreateConnectionPool() { + try { + show("\n-- demoCreateConnectionPool -- "); + + // Creates the embedded connection pool instance in the data source. + mgr.createConnectionPool((PoolDataSourceImpl) pds); + + show("\nConnection pool " + POOL_NAME + " is created from manager"); + } catch (Exception e) { + showError("demoCreateConnectionPool", e); + } + } + + private void demoStartConnectionPool() { + try { + show("\n-- demoStartConnectionPool -- "); + + // Starts the embedded connection pool instance. + mgr.startConnectionPool(POOL_NAME); + + show("\nConnection pool " + POOL_NAME + " is started from manager"); + showPoolStatistics("After pool start", pds); + } catch(Exception e) { + showError("demoStartConnectionPool", e); + } + } + + private void demoStopConnectionPool() { + try { + show("\n-- demoStopConnectionPool -- "); + + // Stops the embedded connection pool instance. + mgr.stopConnectionPool(POOL_NAME); + + show("\nConnection pool " + POOL_NAME + " is stopped from manager"); + showPoolStatistics("After pool stop", pds); + } catch (Exception e) { + showError("demoStopConnectionPool", e); + } + } + + // Refreshing a connection pool replaces every connection in the pool + // with a new connection. Any borrowed connection is marked for removal + // only, and will be refreshed after the connection is returned to the pool. + private void demoRefreshConnectionPool() { + try { + show("\n-- demoRefreshConnectionPool -- "); + + show("\nSets the initial pool size to 10"); + pds.setInitialPoolSize(10); + + show("\nBorrow a connection from the pool"); + Connection con = pds.getConnection(); + // There will be 9 available connections and 1 borrowed connection. + showPoolStatistics("After borrow and before pool refresh", pds); + + // Refreshes the embedded connection pool instance. + mgr.refreshConnectionPool(POOL_NAME); + show("\nConnection pool " + POOL_NAME + " is refreshed from manager"); + + // Only available connections are immediately refreshed, so there + // will still be 9 available connections and 1 borrowed connection. + showPoolStatistics("After pool refresh", pds); + + // This last connection will be refreshed after returned to pool. + show("\nReturn the borrowed connection to the pool"); + con.close(); + // Wait for pool to asynchronously replace connection. + Thread.sleep(20000); + showPoolStatistics("After last return", pds); + } catch (Exception e) { + showError("demoRefreshConnectionPool", e); + } + } + + // Recycling a connection pool replaces only invalid connections in the pool + // with new connections and does not replace borrowed connections. + private void demoRecycleConnectionPool() { + try { + show("\n-- demoRecycleConnectionPool -- "); + + showPoolStatistics("Before any action", pds); + + show("\nBorrow a connection from the pool"); + Connection con = pds.getConnection(); + // There will be 9 available connections and 1 borrowed connection. + showPoolStatistics("After borrow and before pool recycle", pds); + + // Recycles the embedded connection pool instance. + mgr.recycleConnectionPool(POOL_NAME); + show("\nConnection pool " + POOL_NAME + " is recycled from manager"); + + // Only invalid connections are recycled, so there will still be + // 9 available connections and 1 borrowed connection. + showPoolStatistics("After pool recycle", pds); + + // Return last borrowed connection to the pool. + con.close(); + // Wait for pool to asynchronously validate returned connection. + Thread.sleep(20000); + } catch (Exception e) { + showError("demoRecycleConnectionPool", e); + } + } + + // Purging a connection pool removes every connection (available and + // borrowed) from the connection pool and leaves the pool empty. + private void demoPurgeConnectionPool() { + try { + show("\n-- demoPurgeConnectionPool -- "); + + showPoolStatistics("Before any action", pds); + + show("\nBorrow a connection from the pool"); + Connection con = pds.getConnection(); + // There will be 9 available connections and 1 borrowed connection. + showPoolStatistics("After borrow and before pool purge", pds); + + // Purges the embedded connection pool instance. + mgr.purgeConnectionPool(POOL_NAME); + show("\nConnection pool " + POOL_NAME + " is purged from manager"); + + // All connections are removed, so 0 for both available and borrowed. + showPoolStatistics("After pool purge", pds); + } catch (Exception e) { + showError("demoPurgeConnectionPool", e); + } + } + + private void demoGetConnectionPool() { + try { + show("\n-- demoGetConnectionPool -- "); + + UniversalConnectionPool pool = mgr.getConnectionPool(POOL_NAME); + + show("\nObtained UCP pool object for " + POOL_NAME + + ": " + pool); + } catch (Exception e) { + showError("demoGetConnectionPool", e); + } + } + + private void demoGetConnectionPoolNames() { + try { + show("\n-- demoGetConnectionPoolNames -- "); + + String names[] = mgr.getConnectionPoolNames(); + + show("\nObtained all pool names in this UCP manager:"); + for (int i = 0; i < names.length; i++) { + show("Pool [" +i +"] : " + names[i]); + } + } catch (Exception e) { + showError("demoGetConnectionPoolNames", e); + } + } + + private void demoDestroyConnectionPool() { + try { + show("\n-- demoDestroyConnectionPool -- "); + + showPoolStatistics("Before any action", pds); + + show("\nBorrow a connection from the pool"); + Connection con = pds.getConnection(); + showPoolStatistics("After borrow", pds); + + show("\nReturn the connection to pool"); + con.close(); + showPoolStatistics("After return and before pool destroy", pds); + + // Destroys the embedded connection pool instance. + mgr.destroyConnectionPool(POOL_NAME); + show("\nConnection pool " + POOL_NAME + " is destroyed from manager"); + + try { + show("\nTry to borrow another connection from the pool "); + pds.getConnection(); + } catch (Exception e) { + show("\nGot expected error, cannot borrow since pool is destroyed"); + } + } catch (Exception e) { + showError("demoDestroyConnectionPool", e); + } + } + + // See sample UCPBasic.java for basic steps to set up a connection pool. + PoolDataSource createPoolDataSource(String poolName) throws Exception { + PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); + pds.setConnectionFactoryClassName(CONN_FACTORY_CLASS); + pds.setURL(url); + pds.setUser(user); + pds.setPassword(password); + pds.setConnectionPoolName(poolName); + + return pds; + } + + void showPoolStatistics(String prompt, PoolDataSource pds) + throws SQLException { + show(prompt + " -"); + show(" Available connections: " + pds.getAvailableConnectionsCount()); + show(" Borrowed connections: " + pds.getBorrowedConnectionsCount()); + } + + static void show(String msg) { + System.out.println(msg); + } + + static void showError(String msg, Throwable exc) { + System.out.println(msg + " hit error: " + exc.getMessage()); + } + + static void getRealUserPasswordUrl(String args[]) throws Exception { + // URL can be modified in file, or taken from command-line + url = getOptionValue(args, "-url", DEFAULT_URL); + + // DB user can be modified in file, or taken from command-line + user = getOptionValue(args, "-user", DEFAULT_USER); + + // DB user's password can be modified in file, or explicitly entered + readPassword(" Password for " + user + ": "); + } + + // Get specified option value from command-line. + static String getOptionValue( + String args[], String optionName, String defaultVal) { + String argValue = ""; + try { + int i = 0; + String arg = ""; + boolean found = false; + + while (i < args.length) { + arg = args[i++]; + + if (arg.equals(optionName)) { + if (i < args.length) + argValue = args[i++]; + if (argValue.startsWith("-") || argValue.equals("")) { + argValue = defaultVal; + } + found = true; + } + } + + if (!found) { + argValue = defaultVal; + } + } catch (Exception e) { + showError("getOptionValue", e); + } + + return argValue; + } + + static void readPassword(String prompt) throws Exception { + if (System.console() != null) { + char[] pchars = System.console().readPassword("\n[%s]", prompt); + if (pchars != null) { + password = new String(pchars); + java.util.Arrays.fill(pchars, ' '); + } + } else { + BufferedReader r = new BufferedReader(new InputStreamReader(System.in)); + show(prompt); + password = r.readLine(); + } + } +} + diff --git a/java/jdbc/BasicSamples/UCPManagerMBean.java b/java/jdbc/BasicSamples/UCPManagerMBean.java new file mode 100755 index 00000000..b6778658 --- /dev/null +++ b/java/jdbc/BasicSamples/UCPManagerMBean.java @@ -0,0 +1,377 @@ +/* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.*/ + +/* + * DESCRIPTION + * + * This code sample illustrates how applications use UCP manager MBean's + * administration functions. These include: + * + * 1) createConnectionPool + * 2) stopConnectionPool + * 3) startConnectionPool + * 4) refreshConnectionPool + * 5) recycleConnectionPool + * 6) purgeConnectionPool + * 7) destoryConnectionPool + * + * It is required that applications have both ucp.jar and Oracle JDBC + * driver jar(s) (such as ojdbc8.jar or ojdbc7.jar) on the classpath, + * and that the database backend supports SQL (this sample uses an + * Oracle Database and the default HR schema). + * + * To run the sample, you must provide non-default and working values + * for ALL 3 of user, password, and URL. This can be done by either updating + * this file directly or supplying the 3 values as command-line options + * and user input. The password is read from console or standard input. + * java UCPManagerMBean -url -user + * If you do not update all the defaults, the program proceeds but + * will hit error when connecting. + */ + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.sql.Connection; +import java.sql.SQLException; + +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.ObjectName; + +import oracle.ucp.admin.UniversalConnectionPoolManagerMBean; +import oracle.ucp.admin.UniversalConnectionPoolManagerMBeanImpl; +import oracle.ucp.jdbc.PoolDataSource; +import oracle.ucp.jdbc.PoolDataSourceFactory; + + +public class UCPManagerMBean { + final static String DEFAULT_URL = "jdbc:oracle:thin:@//myhost:myport/myservice"; + final static String DEFAULT_USER = "myuser"; + final static String DEFAULT_PASSWORD = "mypassword"; + final static String CONN_FACTORY_CLASS = "oracle.jdbc.pool.OracleDataSource"; + + // You must provide non-default values for ALL 3 to execute the program + static String url = DEFAULT_URL; + static String user = DEFAULT_USER; + static String password = DEFAULT_PASSWORD; + + final static String POOL_NAME = "UCPManagerMBean_pool"; + + // Shared by all methods + private PoolDataSource pds = null; + + private static MBeanServer mbs = null; + + // Shared by all methods + private UniversalConnectionPoolManagerMBean mgrMBean = null; + + // Shared by all methods + private String OBJECT_NAME = null; + + + public static void main(String args[]) throws Exception { + UCPManagerMBean sample = new UCPManagerMBean(); + + getRealUserPasswordUrl(args); + sample.run(); + } + + void run() throws Exception { + // Get the UniversalConnectionPoolManagerMBean instance. + mgrMBean = UniversalConnectionPoolManagerMBeanImpl + .getUniversalConnectionPoolManagerMBean(); + + // Find an existing MBean Server + mbs = (MBeanServer) MBeanServerFactory.findMBeanServer(null).iterator().next(); + pds = createPoolDataSource(POOL_NAME); + + OBJECT_NAME = + "oracle.ucp.admin:name=UniversalConnectionPoolManagerMBean("+ + UniversalConnectionPoolManagerMBeanImpl.class.hashCode()+")"; + + demoCreateConnectionPool(); + demoStartConnectionPool(); + demoStopConnectionPool(); + demoRefreshConnectionPool(); + demoRecycleConnectionPool(); + demoPurgeConnectionPool(); + demoDestroyConnectionPool(); + } + + private void demoCreateConnectionPool() { + try { + show("\n-- demoCreateConnectionPool -- "); + + // Build required parameters to invoke MBean operation. + ObjectName objName = new ObjectName(OBJECT_NAME); + Object[] params = { pds }; + String[] signature = {"oracle.ucp.UniversalConnectionPoolAdapter"}; + + // Create the pool using Manager MBean. + mbs.invoke(objName, "createConnectionPool", params, signature); + + show("\nConnection pool " + POOL_NAME + " is created from MBean"); + } catch (Exception e) { + showError("demoCreateConnectionPool", e); + } + } + + private void demoStartConnectionPool() { + try { + show("\n-- demoStartConnectionPool -- "); + + // Build required parameters to invoke MBean operation. + ObjectName objName = new ObjectName(OBJECT_NAME); + Object[] params = { POOL_NAME }; + String[] signature = { "java.lang.String" }; + + // Start the pool using Manager MBean. + mbs.invoke(objName, "startConnectionPool", params, signature); + + show("\nConnection pool " + POOL_NAME + " is started from MBean"); + showPoolStatistics("After pool start", pds); + } catch(Exception e) { + showError("demoStartConnectionPool", e); + } + } + + private void demoStopConnectionPool() { + try { + show("\n-- demoStopConnectionPool -- "); + + // Build required parameters to invoke MBean operation. + ObjectName objName = new ObjectName(OBJECT_NAME); + Object[] params = { POOL_NAME }; + String[] signature = { "java.lang.String" }; + + // Stop the pool using Manager MBean. + mbs.invoke(objName, "stopConnectionPool", params, signature); + + show("\nConnection pool " + POOL_NAME + " is stopped from MBean"); + showPoolStatistics("After pool stop", pds); + } catch (Exception e) { + showError("demoStopConnectionPool", e); + } + } + + // Refreshing a connection pool replaces every connection in the pool + // with a new connection. Any borrowed connection is marked for removal + // only, and will be refreshed after the connection is returned to the pool. + private void demoRefreshConnectionPool() { + try { + show("\n-- demoRefreshConnectionPool -- "); + + show("\nSets the initial pool size to 10"); + pds.setInitialPoolSize(10); + + show("\nBorrow a connection from the pool"); + Connection con = pds.getConnection(); + // There will be 9 available connections and 1 borrowed connection. + showPoolStatistics("After borrow and before pool refresh", pds); + + // Build required parameters to invoke MBean operation. + ObjectName objName = new ObjectName(OBJECT_NAME); + Object[] params = { POOL_NAME }; + String[] signature = { "java.lang.String" }; + + // Refresh the connection pool using Manager MBean. + mbs.invoke(objName, "refreshConnectionPool", params, signature); + show("\nConnection pool " + POOL_NAME + " is refreshed from MBean"); + + // Only available connections are immediately refreshed, so there + // will still be 9 available connections and 1 borrowed connection. + showPoolStatistics("After pool refresh", pds); + + // This last connection will be refreshed after returned to pool. + show("\nReturn the borrowed connection to the pool"); + con.close(); + // Wait for pool to asynchronously replace connection. + Thread.sleep(20000); + showPoolStatistics("After last return", pds); + } catch (Exception e) { + showError("demoRefreshConnectionPool", e); + } + } + + // Recycling a connection pool replaces only invalid connections in the pool + // with new connections and does not replace borrowed connections. + private void demoRecycleConnectionPool() { + try { + show("\n-- demoRecycleConnectionPool -- "); + + showPoolStatistics("Before any action", pds); + + show("\nBorrow a connection from the pool"); + Connection con = pds.getConnection(); + // There will be 9 available connections and 1 borrowed connection. + showPoolStatistics("After borrow and before pool recycle", pds); + + // Build required parameters to invoke MBean operation. + ObjectName objName = new ObjectName(OBJECT_NAME); + Object[] params = { POOL_NAME }; + String[] signature = { "java.lang.String" }; + + // Recycle the pool using Manager MBean. + mbs.invoke(objName, "recycleConnectionPool", params, signature); + show("\nConnection pool " + POOL_NAME + " is recycled from MBean"); + + // Only invalid connections are recycled, so there will still be + // 9 available connections and 1 borrowed connection. + showPoolStatistics("After pool recycle", pds); + + // Return last borrowed connection to the pool. + con.close(); + // Wait for pool to asynchronously validate returned connection. + Thread.sleep(20000); + } catch (Exception e) { + showError("demoRecycleConnectionPool", e); + } + } + + // Purging a connection pool removes every connection (available and + // borrowed) from the connection pool and leaves the pool empty. + private void demoPurgeConnectionPool() { + try { + show("\n-- demoPurgeConnectionPool -- "); + + showPoolStatistics("Before any action", pds); + + show("\nBorrow a connection from the pool"); + Connection con = pds.getConnection(); + // There will be 9 available connections and 1 borrowed connection. + showPoolStatistics("After borrow and before pool purge", pds); + + // Build required parameters to invoke MBean operation. + ObjectName objName = new ObjectName(OBJECT_NAME); + Object[] params = { POOL_NAME }; + String[] signature = { "java.lang.String" }; + + // Purge the pool using Manager MBean. + mbs.invoke(objName, "purgeConnectionPool", params, signature); + show("\nConnection pool " + POOL_NAME + " is purged from MBean"); + + // All connections are removed, so 0 for both available and borrowed. + showPoolStatistics("After pool purge", pds); + } catch (Exception e) { + showError("demoPurgeConnectionPool", e); + } + } + + private void demoDestroyConnectionPool() { + try { + show("\n-- demoDestroyConnectionPool -- "); + + showPoolStatistics("Before any action", pds); + + show("\nBorrow a connection from the pool"); + Connection con = pds.getConnection(); + showPoolStatistics("After borrow", pds); + + show("\nReturn the connection to pool"); + con.close(); + showPoolStatistics("After return and before pool destroy", pds); + + // Build required parameters to invoke MBean operation. + ObjectName objName = new ObjectName(OBJECT_NAME); + Object[] params = { POOL_NAME }; + String[] signature = { "java.lang.String" }; + + // Destroy the pool using Manager MBean. + mbs.invoke(objName, "destroyConnectionPool", params, signature); + show("\nConnection pool " + POOL_NAME + " is destroyed from MBean"); + + try { + show("\nTry to borrow another connection from the pool "); + pds.getConnection(); + } catch (Exception e) { + show("\nGot expected error, cannot borrow since pool is destroyed"); + } + } catch (Exception e) { + showError("demoDestroyConnectionPool", e); + } + } + + // See sample UCPBasic.java for basic steps to set up a connection pool. + PoolDataSource createPoolDataSource(String poolName) throws Exception { + PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); + pds.setConnectionFactoryClassName(CONN_FACTORY_CLASS); + pds.setURL(url); + pds.setUser(user); + pds.setPassword(password); + pds.setConnectionPoolName(poolName); + + return pds; + } + + void showPoolStatistics(String prompt, PoolDataSource pds) + throws SQLException { + show(prompt + " -"); + show(" Available connections: " + pds.getAvailableConnectionsCount()); + show(" Borrowed connections: " + pds.getBorrowedConnectionsCount()); + } + + static void show(String msg) { + System.out.println(msg); + } + + static void showError(String msg, Throwable exc) { + System.out.println(msg + " hit error: " + exc.getMessage()); + } + + static void getRealUserPasswordUrl(String args[]) throws Exception { + // URL can be modified in file, or taken from command-line + url = getOptionValue(args, "-url", DEFAULT_URL); + + // DB user can be modified in file, or taken from command-line + user = getOptionValue(args, "-user", DEFAULT_USER); + + // DB user's password can be modified in file, or explicitly entered + readPassword(" Password for " + user + ": "); + } + + // Get specified option value from command-line. + static String getOptionValue( + String args[], String optionName, String defaultVal) { + String argValue = ""; + try { + int i = 0; + String arg = ""; + boolean found = false; + + while (i < args.length) { + arg = args[i++]; + + if (arg.equals(optionName)) { + if (i < args.length) + argValue = args[i++]; + if (argValue.startsWith("-") || argValue.equals("")) { + argValue = defaultVal; + } + found = true; + } + } + + if (!found) { + argValue = defaultVal; + } + } catch (Exception e) { + showError("getOptionValue", e); + } + + return argValue; + } + + static void readPassword(String prompt) throws Exception { + if (System.console() != null) { + char[] pchars = System.console().readPassword("\n[%s]", prompt); + if (pchars != null) { + password = new String(pchars); + java.util.Arrays.fill(pchars, ' '); + } + } else { + BufferedReader r = new BufferedReader(new InputStreamReader(System.in)); + show(prompt); + password = r.readLine(); + } + } +} + diff --git a/java/jdbc/BasicSamples/UCPMaxConnReuse.java b/java/jdbc/BasicSamples/UCPMaxConnReuse.java new file mode 100755 index 00000000..fca96b72 --- /dev/null +++ b/java/jdbc/BasicSamples/UCPMaxConnReuse.java @@ -0,0 +1,317 @@ +/* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.*/ + +/* + * DESCRIPTION + * + * This code sample illustrates how applications use the MaxConnectionReuseTime + * and MaxConnectionReuseCount features of Oracle Universal Connection Pool + * (UCP). + * + * The maximum connection reuse time allows connections to be gracefully + * closed and removed from the pool after a connection has been in use for + * a specific amount of time. The timer for this property starts when a + * connection is physically created. Borrowed connections are closed only + * after they are returned to the pool and the reuse time is exceeded. + * + * This feature is typically used when a firewall exists between the pool tier + * and the database tier and is setup to block connections based on time + * restrictions. The blocked connections remain in the pool even though + * they are unusable. In such scenarios, the connection reuse time can be + * set to a smaller value than the firewall timeout policy. + * + * The time is measured in seconds. 0 disables the feature, which is + * the default. + * + * The maximum connection reuse count works similarly, allowing a connection + * to be closed and removed from the pool after it has been borrowed + * a specific number of times. + * + * Value 0 disables the feature, which is the default. + * + * It is required that applications have both ucp.jar and Oracle JDBC + * driver jar(s) (such as ojdbc8.jar or ojdbc7.jar) on the classpath, + * and that the database backend supports SQL (this sample uses an + * Oracle Database and the default HR schema). + * + * To run the sample, you must provide non-default and working values + * for ALL 3 of user, password, and URL. This can be done by either updating + * this file directly or supplying the 3 values as command-line options + * and user input. The password is read from console or standard input. + * java UCPMaxConnReuse -url -user + * If you do not update all the defaults, the program proceeds but + * will hit error when connecting. + */ + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.sql.Connection; +import java.sql.SQLException; + +import oracle.ucp.admin.UniversalConnectionPoolManagerImpl; +import oracle.ucp.jdbc.PoolDataSource; +import oracle.ucp.jdbc.PoolDataSourceFactory; + + +public class UCPMaxConnReuse { + final static String DEFAULT_URL = "jdbc:oracle:thin:@//myhost:myport/myservice"; + final static String DEFAULT_USER = "myuser"; + final static String DEFAULT_PASSWORD = "mypassword"; + final static String CONN_FACTORY_CLASS = "oracle.jdbc.pool.OracleDataSource"; + + // You must provide non-default values for ALL 3 to execute the program + static String url = DEFAULT_URL; + static String user = DEFAULT_USER; + static String password = DEFAULT_PASSWORD; + + public static void main(String args[]) throws Exception { + UCPMaxConnReuse sample = new UCPMaxConnReuse(); + + getRealUserPasswordUrl(args); + sample.run(); + } + + void run() throws Exception { + demoMaxConnectionReuseTime(); + demoMaxConnectionReuseCount(); + } + + public void demoMaxConnectionReuseTime() throws Exception { + show("\n*** Demo MaxConnectionReuseTime ***"); + + try { + final String POOL_NAME = "UCPMaxConnReuse_pool1"; + Connection[] conns = new Connection[3]; + String[] connStrs = new String[3]; + + PoolDataSource pds = createPoolDataSource(POOL_NAME); + + // Each connection in the pool will be reusable for 25 seconds after + // its creation. Then it will be gracefully closed and removed on + // its next return to the pool. + pds.setMaxConnectionReuseTime(25); + + show("Connection pool " + POOL_NAME + " configured"); + + show("\nBorrow conns[0] from pool."); + conns[0] = pds.getConnection(); + connStrs[0] = ((Object)conns[0]).toString(); + + showPoolStatistics("After conns[0] is borrowed", pds); + + show("\nReturn conns[0] to pool."); + conns[0].close(); + + showPoolStatistics("After conns[0] is returned", pds); + + show("\nBorrow conns[1] from pool."); + conns[1] = pds.getConnection(); + connStrs[1] = ((Object)conns[1]).toString(); + + show("conns[0] and conns[1] should be equal : " + + connStrs[0].equals(connStrs[1])); + + show("\nSleep for 30 seconds that exceeds MaxConnnectionReuseTime."); + Thread.sleep(30000); + + show("\nconns[1] is not closed, since it's still borrowed."); + showPoolStatistics("Just before conns[1] is returned to pool", pds); + + show("\nReturn conns[1] to pool."); + show("This will close the physical connection in the pool."); + // Close the second connection. Since this connection has exceeded + // MaxConnectionReuseTime, it will be closed and removed from the pool. + conns[1].close(); + + showPoolStatistics("\nAfter conns[1] is returned", pds); + + // Get the third connection. This should be a new physical connection + // from conns[1]. + show("\nBorrow conns[2] from pool."); + conns[2] = pds.getConnection(); + connStrs[2] = ((Object)conns[2]).toString(); + + show("conns[0] and conns[2] should not be equal : " + + !connStrs[0].equals(connStrs[2])); + + show("conns[1] and conns[2] should not be equal : " + + !connStrs[1].equals(connStrs[2])); + + show("\nReturn conns[2] to pool."); + conns[2].close(); + + destroyConnectionPool(POOL_NAME); + + } catch (Throwable e) { + showError("demoMaxConnectionReuseTime", e); + } + + show("\n*** Demo MaxConnectionReuseTime completes ***"); + } + + public void demoMaxConnectionReuseCount() throws Exception { + show("\n*** Demo MaxConnectionReuseCount ***"); + + try { + final String POOL_NAME = "UCPMaxConnReuse_pool2"; + Connection[] conns = new Connection[3]; + String[] connStrs = new String[3]; + + PoolDataSource pds = createPoolDataSource(POOL_NAME); + + // Each connection in the pool will be reusable for 2 borrow's + // after its creation. Then it will be gracefully closed and + // removed on its next (i.e., 2nd) return to the pool. + pds.setMaxConnectionReuseCount(2); + + show("Connection pool " + POOL_NAME + " configured"); + + show("\nBorrow conns[0] from pool."); + conns[0] = pds.getConnection(); + connStrs[0] = ((Object)conns[0]).toString(); + + showPoolStatistics("After conns[0] is borrowed", pds); + + show("\nReturn conns[0] to pool."); + conns[0].close(); + + showPoolStatistics("After conns[0] is returned", pds); + + show("\nBorrow conns[1] from pool."); + conns[1] = pds.getConnection(); + connStrs[1] = ((Object)conns[1]).toString(); + + show("conns[0] and conns[1] should be equal : " + + connStrs[0].equals(connStrs[1])); + + show("\nconns[1]'s physical connection has reached MaxConnnectionReuseCount."); + + show("It is not closed right away, since it's still borrowed."); + showPoolStatistics("\nJust before conns[1] is returned to pool", pds); + + show("\nReturn conns[1] to pool."); + show("This will close the physical connection in the pool."); + // Close the second connection. Since this connection has exceeded + // MaxConnectionReuseCount, it will be closed and removed from the pool. + conns[1].close(); + + showPoolStatistics("\nAfter conns[1] is returned", pds); + + // Get the third connection. This should be a new physical connection + // from conns[1]. + show("\nBorrow conns[2] from pool."); + conns[2] = pds.getConnection(); + connStrs[2] = ((Object)conns[2]).toString(); + + show("conns[0] and conns[2] should not be equal : " + + !connStrs[0].equals(connStrs[2])); + + show("conns[1] and conns[2] should not be equal : " + + !connStrs[1].equals(connStrs[2])); + + show("\nReturn conns[2] to pool."); + conns[2].close(); + + destroyConnectionPool(POOL_NAME); + + } catch (Throwable e) { + showError("demoMaxConnectionReuseCount", e); + } + + show("\n*** Demo MaxConnectionReuseCount completes ***"); + } + + // See sample UCPBasic.java for basic steps to set up a connection pool. + PoolDataSource createPoolDataSource(String poolName) throws Exception { + PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); + pds.setConnectionFactoryClassName(CONN_FACTORY_CLASS); + pds.setURL(url); + pds.setUser(user); + pds.setPassword(password); + pds.setConnectionPoolName(poolName); + + return pds; + } + + void destroyConnectionPool(String poolName) { + try { + UniversalConnectionPoolManagerImpl.getUniversalConnectionPoolManager() + .destroyConnectionPool(poolName); + show("\nConnection pool " + poolName + " destroyed"); + } catch (Throwable e) { + showError("destroyConnectinoPool", e); + } + } + + void showPoolStatistics(String prompt, PoolDataSource pds) + throws SQLException { + show(prompt + " -"); + show(" Available connections: " + pds.getAvailableConnectionsCount()); + show(" Borrowed connections: " + pds.getBorrowedConnectionsCount()); + } + + static void show(String msg) { + System.out.println(msg); + } + + static void showError(String msg, Throwable exc) { + System.out.println(msg + " hit error: " + exc.getMessage()); + } + + static void getRealUserPasswordUrl(String args[]) throws Exception { + // URL can be modified in file, or taken from command-line + url = getOptionValue(args, "-url", DEFAULT_URL); + + // DB user can be modified in file, or taken from command-line + user = getOptionValue(args, "-user", DEFAULT_USER); + + // DB user's password can be modified in file, or explicitly entered + readPassword(" Password for " + user + ": "); + } + + // Get specified option value from command-line. + static String getOptionValue( + String args[], String optionName, String defaultVal) { + String argValue = ""; + try { + int i = 0; + String arg = ""; + boolean found = false; + + while (i < args.length) { + arg = args[i++]; + + if (arg.equals(optionName)) { + if (i < args.length) + argValue = args[i++]; + if (argValue.startsWith("-") || argValue.equals("")) { + argValue = defaultVal; + } + found = true; + } + } + + if (!found) { + argValue = defaultVal; + } + } catch (Exception e) { + showError("getOptionValue", e); + } + + return argValue; + } + + static void readPassword(String prompt) throws Exception { + if (System.console() != null) { + char[] pchars = System.console().readPassword("\n[%s]", prompt); + if (pchars != null) { + password = new String(pchars); + java.util.Arrays.fill(pchars, ' '); + } + } else { + BufferedReader r = new BufferedReader(new InputStreamReader(System.in)); + show(prompt); + password = r.readLine(); + } + } +} + diff --git a/java/jdbc/BasicSamples/UCPMultiUsers.java b/java/jdbc/BasicSamples/UCPMultiUsers.java new file mode 100755 index 00000000..9401712b --- /dev/null +++ b/java/jdbc/BasicSamples/UCPMultiUsers.java @@ -0,0 +1,172 @@ +/* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.*/ + +/* + * DESCRIPTION + * + * This code sample illustrates how JDBC applications use the Oracle + * Universal Connection Pool (UCP) to pool connections for different + * users. + * + * It is required that applications have both ucp.jar and Oracle JDBC + * driver jar(s) (such as ojdbc8.jar or ojdbc7.jar) on the classpath, + * and that the database backend supports SQL (this sample uses an + * Oracle Database and the default HR schema). + * + * To run the sample, you must provide non-default and working values + * for ALL 3 of user, password, and URL. This can be done by either updating + * this file directly or supplying the 3 values as command-line options + * and user input. The password is read from console or standard input. + * java UCPMultiUsers -url -user + * If you do not update all the defaults, the program proceeds but + * will hit error when connecting. + */ + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +import oracle.ucp.jdbc.PoolDataSourceFactory; +import oracle.ucp.jdbc.PoolDataSource; + + +public class UCPMultiUsers { + final static String DEFAULT_URL = "jdbc:oracle:thin:@//myhost:myport/myservice"; + final static String DEFAULT_USER = "myuser"; + final static String DEFAULT_PASSWORD = "mypassword"; + final static String CONN_FACTORY_CLASS = "oracle.jdbc.pool.OracleDataSource"; + + // You must provide non-default values for ALL 3 to execute the program + static String url = DEFAULT_URL; + static String user = DEFAULT_USER; + static String password = DEFAULT_PASSWORD; + + // Modify these if user "scott" is locked in DB. + final static String USER2 = "scott"; + final static String PASSWORD2 = "tiger"; + + public static void main(String args[]) throws Exception { + getRealUserPasswordUrl(args); + + show("\nUCPMultiUsers starts"); + + // See sample UCPBasic for basic steps to set up a pool. + PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); + pds.setConnectionFactoryClassName(CONN_FACTORY_CLASS); + pds.setURL(url); + pds.setUser(user); + pds.setPassword(password); + pds.setConnectionPoolName("UCPMultiUsers_pool"); + + show("Connection pool configured"); + + try (Connection conn1 = pds.getConnection()) { + showPoolStatistics("\nAfter checkout for "+user, pds); + + makeJDBCCalls(conn1); + } catch (SQLException exc) { + showError("Checkout with "+user, exc); + } + + showPoolStatistics("\nAfter checkin for "+user, pds); + + // Use this version of getConnection with different users. + try (Connection conn2 = pds.getConnection(USER2, PASSWORD2)) { + showPoolStatistics("\nAfter checkout for "+USER2, pds); + + makeJDBCCalls(conn2); + } catch (SQLException exc) { + showError("Checkout with "+USER2, exc); + } + + showPoolStatistics("\nAfter checkin for "+USER2, pds); + + show("\nUCPMultiUsers completes"); + } + + // Simple query + static void makeJDBCCalls(Connection conn) { + try (Statement statement = conn.createStatement()) { + try (java.sql.ResultSet rset = statement.executeQuery("SELECT USER FROM DUAL")) { + rset.next(); + show("\n Current user: " + rset.getString(1)); + } + } catch (SQLException exc) { + showError("JDBC operation", exc); + } + } + + static void showPoolStatistics(String prompt, PoolDataSource pds) + throws SQLException { + show(prompt + " -"); + show(" Available connections: " + pds.getAvailableConnectionsCount()); + show(" Borrowed connections: " + pds.getBorrowedConnectionsCount()); + } + + static void show(String msg) { + System.out.println(msg); + } + + static void showError(String msg, Throwable exc) { + System.err.println(msg + " hit error: " + exc.getMessage()); + } + + static void getRealUserPasswordUrl(String args[]) throws Exception { + // URL can be modified in file, or taken from command-line + url = getOptionValue(args, "-url", DEFAULT_URL); + + // DB user can be modified in file, or taken from command-line + user = getOptionValue(args, "-user", DEFAULT_USER); + + // DB user's password can be modified in file, or explicitly entered + readPassword(" Password for " + user + ": "); + } + + // Get specified option value from command-line. + static String getOptionValue( + String args[], String optionName, String defaultVal) { + String argValue = ""; + try { + int i = 0; + String arg = ""; + boolean found = false; + + while (i < args.length) { + arg = args[i++]; + + if (arg.equals(optionName)) { + if (i < args.length) + argValue = args[i++]; + if (argValue.startsWith("-") || argValue.equals("")) { + argValue = defaultVal; + } + found = true; + } + } + + if (!found) { + argValue = defaultVal; + } + } catch (Exception e) { + showError("getOptionValue", e); + } + + return argValue; + } + + static void readPassword(String prompt) throws Exception { + if (System.console() != null) { + char[] pchars = System.console().readPassword("\n[%s]", prompt); + if (pchars != null) { + password = new String(pchars); + java.util.Arrays.fill(pchars, ' '); + } + } else { + BufferedReader r = new BufferedReader(new InputStreamReader(System.in)); + show(prompt); + password = r.readLine(); + } + } +} + diff --git a/java/jdbc/BasicSamples/UCPTimeouts.java b/java/jdbc/BasicSamples/UCPTimeouts.java new file mode 100755 index 00000000..030902ff --- /dev/null +++ b/java/jdbc/BasicSamples/UCPTimeouts.java @@ -0,0 +1,412 @@ +/* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.*/ + +/* + * DESCRIPTION + * + * This code sample illustrates key connection timeout features of the + * Oracle Universal Connection Pool (UCP). These include: + * 1) ConnectionWaitTimeout + * 2) InactiveConnectionTimeout + * 3) TimeToLiveConnectionTimeout + * 4) AbandonedConnectionTimeout + * + * It is required that applications have both ucp.jar and Oracle JDBC + * driver jar(s) (such as ojdbc8.jar or ojdbc7.jar) on the classpath, + * and that the database backend supports SQL (this sample uses an + * Oracle Database and the default HR schema). + * + * To run the sample, you must provide non-default and working values + * for ALL 3 of user, password, and URL. This can be done by either updating + * this file directly or supplying the 3 values as command-line options + * and user input. The password is read from console or standard input. + * java UCPTimeouts -url -user + * If you do not update all the defaults, the program proceeds but + * will hit error when connecting. + */ + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Timer; +import java.util.TimerTask; + +import oracle.ucp.admin.UniversalConnectionPoolManagerImpl; +import oracle.ucp.jdbc.PoolDataSourceFactory; +import oracle.ucp.jdbc.PoolDataSource; + + +public class UCPTimeouts { + final static String DEFAULT_URL = "jdbc:oracle:thin:@//myhost:myport/myservice"; + final static String DEFAULT_USER = "myuser"; + final static String DEFAULT_PASSWORD = "mypassword"; + final static String CONN_FACTORY_CLASS = "oracle.jdbc.pool.OracleDataSource"; + + // You must provide non-default values for ALL 3 to execute the program + static String url = DEFAULT_URL; + static String user = DEFAULT_USER; + static String password = DEFAULT_PASSWORD; + + public static void main(String args[]) throws Exception { + UCPTimeouts sample = new UCPTimeouts(); + + getRealUserPasswordUrl(args); + sample.run(); + } + + void run() throws Exception { + demoConnectionWaitTimeout(); + demoInactiveConnectionTimeout(); + demoTimeToLiveConnectionTimeout(); + demoAbandonedConnectionTimeout(); + } + + /* + * The connection wait timeout specifies how long, in seconds, application + * requests wait to obtain a connection, if there are no available + * connections inside the pool. The application receives an SQLException + * if the timeout is reached. + * + * A value of 0 disables this feature. The default is 3 seconds. + */ + private void demoConnectionWaitTimeout() { + show("\n*** Demo ConnectionWaitTimeout ***"); + + try { + final int MAX_POOL_SIZE = 5; + final String POOL_NAME = "UCPTimeouts_pool1"; + Connection conns[] = new Connection[MAX_POOL_SIZE]; + + PoolDataSource pds = createPoolDataSource(POOL_NAME); + pds.setMaxPoolSize(MAX_POOL_SIZE); + // Set ConnectionWaitTimeout to 8 seconds + pds.setConnectionWaitTimeout(8); + pds.setTimeoutCheckInterval(5); + + show("Connection pool " + POOL_NAME + " configured"); + show("Max pool size: " + pds.getMaxPoolSize()); + + show("\nBorrow connections to reach max pool size."); + + for (int i = 0; i < MAX_POOL_SIZE; i++) { + conns[i] = pds.getConnection(); + } + + showPoolStatistics("After all connections are borrowed", pds); + + try { + show("\nNow trying to borrow another connection from pool ..."); + + // This request is expected to fail after ConnectionWaitTimeout. + Connection conn = pds.getConnection(); + } catch (Exception e) { + show("\nGetting expected error after ConnectionWaitTimeout"); + } + + show("\nReturn all borrowed connections to pool"); + // Return all borrowed connections to pool. + for (int i = 0; i < MAX_POOL_SIZE; i++) { + conns[i].close(); + conns[i] = null; + } + + destroyConnectionPool(POOL_NAME); + + } catch (Throwable e) { + showError("demoConnectionWaitTimeout", e); + } + + show("\n*** Demo ConnectionWaitTimeout completes ***"); + } + + /* + * The inactive connection timeout specifies how long, in seconds, + * an available connection can remain idle inside the pool, before + * it is closed and removed from the pool. This timeout property is + * only applicable to available connections and does not affect borrowed + * connections. + * + * A value 0 disables this feature. By default, this timeout is disabled. + */ + private void demoInactiveConnectionTimeout() { + show("\n*** Demo InactiveConnectionTimeout ***"); + + try { + final int MIN_POOL_SIZE = 5; + final int MAX_POOL_SIZE = 10; + final String POOL_NAME = "UCPTimeouts_pool2"; + Connection conns[] = new Connection[MAX_POOL_SIZE]; + + PoolDataSource pds = createPoolDataSource(POOL_NAME); + pds.setMinPoolSize(MIN_POOL_SIZE); + pds.setMaxPoolSize(MAX_POOL_SIZE); + // Set InactiveConnectionTimeout to 10 seconds + pds.setInactiveConnectionTimeout(10); + pds.setTimeoutCheckInterval(5); + + show("Connection pool " + POOL_NAME + " configured"); + show("Min pool size: " + pds.getMinPoolSize()); + show("Max pool size: " + pds.getMaxPoolSize()); + + show("\nBorrow connections to reach min pool size."); + + // First borrow all connections in the pool + for (int i = 0; i < MAX_POOL_SIZE; i++) { + conns[i] = pds.getConnection(); + } + + // Return all connections beyond MinPoolSize to pool + for (int i = MIN_POOL_SIZE; i < MAX_POOL_SIZE; i++) { + conns[i].close(); + } + + showPoolStatistics("After borrowing connections", pds); + + show("\nSleep for 15 seconds to trigger InactiveConnectionTimeout."); + show("Available connections beyond MinPoolSize are expected to close"); + + try { + Thread.sleep(15000); + } catch (InterruptedException e) {} + + showPoolStatistics("\nAfter InactiveConnectionTimeout", pds); + + show("\nReturn all borrowed connections to pool"); + // Return all borrowed connections to pool. + for (int i = 0; i < MIN_POOL_SIZE; i++) { + conns[i].close(); + conns[i] = null; + } + + destroyConnectionPool(POOL_NAME); + + } catch (Throwable e) { + showError("demoInactiveConnectionTimeout", e); + } + + show("\n*** Demo InactiveConnectionTimeout completes ***"); + } + + /* + * The time-to-live connection timeout enables borrowed connections to + * remain borrowed for a specific amount of time before the connection + * is reclaimed by the pool. The timeout is in seconds. + * + * A value 0 disables this feature. By default, this timeout is disabled. + */ + private void demoTimeToLiveConnectionTimeout() { + show("\n*** Demo TimeToLiveConnectionTimeout ***"); + + try { + final int MAX_POOL_SIZE = 5; + final String POOL_NAME = "UCPTimeouts_pool3"; + Connection conns[] = new Connection[MAX_POOL_SIZE]; + + PoolDataSource pds = createPoolDataSource(POOL_NAME); + pds.setMaxPoolSize(MAX_POOL_SIZE); + // Set TimeToLiveConnectionTimeout to 10 seconds + pds.setTimeToLiveConnectionTimeout(10); + pds.setTimeoutCheckInterval(5); + + show("Connection pool " + POOL_NAME + " configured"); + show("Max pool size: " + pds.getMaxPoolSize()); + + show("\nBorrow connections to reach max pool size."); + + for (int i = 0; i < MAX_POOL_SIZE; i++) { + conns[i] = pds.getConnection(); + } + + showPoolStatistics("After all connections are borrowed", pds); + + show("\nSleep for 15 seconds to trigger TimeToLiveConnectionTimeout."); + show("All borrowed connections are expected to be returned to pool."); + + try { + Thread.sleep(15000); + } catch (InterruptedException e) {} + + showPoolStatistics("\nAfter TimeToLiveConnectionTimeout", pds); + + destroyConnectionPool(POOL_NAME); + + } catch (Throwable e) { + showError("demoTimeToLiveConnectionTimeout", e); + } + + show("\n*** Demo TimeToLiveConnectionTimeout completes ***"); + } + + /* + * The abandoned connection timeout (ACT) enables a borrowed connection + * to be reclaimed back into the connection pool, after that borrowed + * connection has not been used for a specific amount of time. + * The timeout is in seconds. + * + * A value 0 disables this feature. By default, this timeout is disabled. + */ + private void demoAbandonedConnectionTimeout() { + show("\n*** Demo AbandonedConnectionTimeout ***"); + + try { + final int MAX_POOL_SIZE = 10; + final String POOL_NAME = "UCPTimeouts_pool4"; + Connection conns[] = new Connection[MAX_POOL_SIZE]; + + PoolDataSource pds = createPoolDataSource(POOL_NAME); + pds.setMaxPoolSize(MAX_POOL_SIZE); + // Set AbandonedConnectionTimeout to 10 seconds + pds.setAbandonedConnectionTimeout(10); + pds.setTimeoutCheckInterval(5); + + show("Connection pool " + POOL_NAME + " configured"); + show("Max pool size: " + pds.getMaxPoolSize()); + + show("\nBorrow connections to reach max pool size."); + + for (int i = 0; i < MAX_POOL_SIZE; i++) { + conns[i] = pds.getConnection(); + } + + showPoolStatistics("After all connections are borrowed", pds); + + Timer tm = new Timer(true); + + show("\nDo some work periodically only on 3 borrowed connections ..."); + + for (int i = 0; i < 3; i++) { + tm.schedule(new TestACTTimerTask(conns[i]), 1000, 1000); + } + + show("\nSleep for 15 seconds to trigger AbandonedConnectionTimeout."); + show("All borrowed connections other than the 3 are expected to be returned to pool."); + + try { + Thread.sleep(15000); + } catch (InterruptedException e) {} + + showPoolStatistics("\nAfter AbandonedConnectionTimeout", pds); + + // Cancel all timer tasks on the 3 borrowed connections + tm.cancel(); + + destroyConnectionPool(POOL_NAME); + + } catch (Throwable e) { + showError("demoAbandonedConnectionTimeout", e); + } + + show("\n*** Demo AbandonedConnectionTimeout completes ***"); + } + + // See sample UCPBasic.java for basic steps to set up a connection pool. + PoolDataSource createPoolDataSource(String poolName) throws Exception { + PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); + pds.setConnectionFactoryClassName(CONN_FACTORY_CLASS); + pds.setURL(url); + pds.setUser(user); + pds.setPassword(password); + pds.setConnectionPoolName(poolName); + + return pds; + } + + void destroyConnectionPool(String poolName) { + try { + UniversalConnectionPoolManagerImpl.getUniversalConnectionPoolManager() + .destroyConnectionPool(poolName); + show("\nConnection pool " + poolName + " destroyed"); + } catch (Throwable e) { + showError("destroyConnectinoPool", e); + } + } + + void showPoolStatistics(String prompt, PoolDataSource pds) + throws SQLException { + show(prompt + " -"); + show(" Available connections: " + pds.getAvailableConnectionsCount()); + show(" Borrowed connections: " + pds.getBorrowedConnectionsCount()); + } + + static void show(String msg) { + System.out.println(msg); + } + + static void showError(String msg, Throwable exc) { + System.out.println(msg + " hit error: " + exc.getMessage()); + } + + static void getRealUserPasswordUrl(String args[]) throws Exception { + // URL can be modified in file, or taken from command-line + url = getOptionValue(args, "-url", DEFAULT_URL); + + // DB user can be modified in file, or taken from command-line + user = getOptionValue(args, "-user", DEFAULT_USER); + + // DB user's password can be modified in file, or explicitly entered + readPassword(" Password for " + user + ": "); + } + + // Get specified option value from command-line. + static String getOptionValue( + String args[], String optionName, String defaultVal) { + String argValue = ""; + try { + int i = 0; + String arg = ""; + boolean found = false; + + while (i < args.length) { + arg = args[i++]; + + if (arg.equals(optionName)) { + if (i < args.length) + argValue = args[i++]; + if (argValue.startsWith("-") || argValue.equals("")) { + argValue = defaultVal; + } + found = true; + } + } + + if (!found) { + argValue = defaultVal; + } + } catch (Exception e) { + showError("getOptionValue", e); + } + + return argValue; + } + + static void readPassword(String prompt) throws Exception { + if (System.console() != null) { + char[] pchars = System.console().readPassword("\n[%s]", prompt); + if (pchars != null) { + password = new String(pchars); + java.util.Arrays.fill(pchars, ' '); + } + } else { + BufferedReader r = new BufferedReader(new InputStreamReader(System.in)); + show(prompt); + password = r.readLine(); + } + } + + // Used for AbandonedConnectionTimeout only + class TestACTTimerTask extends TimerTask { + Connection conn = null; + + public TestACTTimerTask(Connection con) { + conn = con; + } + + public void run() { + try (Statement statement = conn.createStatement()) { + statement.execute("select 1 from dual"); + } catch (Exception ucpException) {} + } + } +} + diff --git a/java/jdbc/BasicSamples/books.png b/java/jdbc/BasicSamples/books.png new file mode 100755 index 0000000000000000000000000000000000000000..1db1844124f2b2d804624c7ceef46b4744895123 GIT binary patch literal 17262 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 literal 0 HcmV?d00001 From 15f982d199f19469c1f0e1175c0a2e023664d00b Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 07:00:38 -0700 Subject: [PATCH 03/29] Create Readme.md --- java/jdbc/BasicSamples/Readme.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 java/jdbc/BasicSamples/Readme.md diff --git a/java/jdbc/BasicSamples/Readme.md b/java/jdbc/BasicSamples/Readme.md new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/java/jdbc/BasicSamples/Readme.md @@ -0,0 +1 @@ + From a979f49ea57ec58c58aeafb282975c41c979a778 Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 07:08:47 -0700 Subject: [PATCH 04/29] Update Readme.md --- java/jdbc/BasicSamples/Readme.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/java/jdbc/BasicSamples/Readme.md b/java/jdbc/BasicSamples/Readme.md index 8b137891..1b165d9e 100644 --- a/java/jdbc/BasicSamples/Readme.md +++ b/java/jdbc/BasicSamples/Readme.md @@ -1 +1,15 @@ +# Basic Samples in JDBC + +"Connection Management" code samples explain various ways of connecting to an +Oracle Database and explain use-cases to be considered while choosing the +connection management strategy. The section below provides more details on +specific connection management strategy. + +---- +## DateTimeStampSample.java: +This sample shows illustrates the usage of below Oracle column data types +DATE, TIMESTAMP, TIMESTAMP WITH TIME ZONE and TIMESTAMP WITH LOCAL TIME ZONE. +It uses the table 'EMP_DATE_JDBC_SAMPLE' which has dates as columns for +all the insert, delete, and update operations. + From 9d335eb578c01389c357f65169544735c4602474 Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 07:09:37 -0700 Subject: [PATCH 05/29] Update Readme.md --- java/jdbc/BasicSamples/Readme.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/java/jdbc/BasicSamples/Readme.md b/java/jdbc/BasicSamples/Readme.md index 1b165d9e..99e20db3 100644 --- a/java/jdbc/BasicSamples/Readme.md +++ b/java/jdbc/BasicSamples/Readme.md @@ -1,10 +1,7 @@ # Basic Samples in JDBC -"Connection Management" code samples explain various ways of connecting to an -Oracle Database and explain use-cases to be considered while choosing the -connection management strategy. The section below provides more details on -specific connection management strategy. +"Basic Samples" explain some of the basic operations such as connecting. ---- ## DateTimeStampSample.java: From 2e80e51b33639224eed4615dbf1f0343ff820a52 Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 10:07:26 -0700 Subject: [PATCH 06/29] Update Readme.md --- java/jdbc/BasicSamples/Readme.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/java/jdbc/BasicSamples/Readme.md b/java/jdbc/BasicSamples/Readme.md index 99e20db3..9a30a45a 100644 --- a/java/jdbc/BasicSamples/Readme.md +++ b/java/jdbc/BasicSamples/Readme.md @@ -1,7 +1,26 @@ # Basic Samples in JDBC -"Basic Samples" explain some of the basic operations such as connecting. +"Basic Samples" is the first set of code samples aimed to show case some +some of the basic operations and using some datatypes (LOB, SQLXML, DATE etc.,) +The samples also contain Universal Connection Pool (UCP) functionalities to show +how to harvest connections, label connections, how to use different timeouts, and +how to configure MBean to monitor UCP statistics etc., Read below for the +description of the code samples. + +# Running Code Samples +There are three ways to run the sample. +(a) Run each sample by passing the database URL and database user as the command-line +options. The password is read from console or standard input. + +```java UCPMultiUsers -l -u ``` + +(b) Optionally, each sample has DEFAULT_URL, DEFAULT_USER, and DEFAULT_PASSWORD +in the file. You can choose to update these values with your database credentials +and run the program. + +(c) If you don't update the defaults, then the program proceeds with the defaults +but, will hit error when connecting as these are dummy values. ---- ## DateTimeStampSample.java: From 8333a0cd0a68df2ac849a89a44394ad2abd98754 Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 10:08:05 -0700 Subject: [PATCH 07/29] Update Readme.md --- java/jdbc/BasicSamples/Readme.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/java/jdbc/BasicSamples/Readme.md b/java/jdbc/BasicSamples/Readme.md index 9a30a45a..75a973de 100644 --- a/java/jdbc/BasicSamples/Readme.md +++ b/java/jdbc/BasicSamples/Readme.md @@ -9,7 +9,8 @@ how to configure MBean to monitor UCP statistics etc., Read below for the description of the code samples. # Running Code Samples -There are three ways to run the sample. +There are three ways to run the sample. + (a) Run each sample by passing the database URL and database user as the command-line options. The password is read from console or standard input. From fcd8861e44d87c3ee480aadf21934a64d5171c55 Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 10:12:18 -0700 Subject: [PATCH 08/29] Update Readme.md --- java/jdbc/BasicSamples/Readme.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/java/jdbc/BasicSamples/Readme.md b/java/jdbc/BasicSamples/Readme.md index 75a973de..77f66d2f 100644 --- a/java/jdbc/BasicSamples/Readme.md +++ b/java/jdbc/BasicSamples/Readme.md @@ -9,6 +9,10 @@ how to configure MBean to monitor UCP statistics etc., Read below for the description of the code samples. # Running Code Samples +Before you run the code samples, we want you to create the necessary tables. + +(a) Create a new user(JDBCUSER) and new tables +Connect to [SQLDeveloper] (http://www.oracle.com/technetwork/developer-tools/sql-developer/downloads/sqldev-downloads-42-3802334.html) There are three ways to run the sample. (a) Run each sample by passing the database URL and database user as the command-line From d0c1b0140b106065504212e5abcb9d04c9eef497 Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 10:14:52 -0700 Subject: [PATCH 09/29] Update Readme.md --- java/jdbc/BasicSamples/Readme.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/java/jdbc/BasicSamples/Readme.md b/java/jdbc/BasicSamples/Readme.md index 77f66d2f..1ae23ed7 100644 --- a/java/jdbc/BasicSamples/Readme.md +++ b/java/jdbc/BasicSamples/Readme.md @@ -12,7 +12,9 @@ description of the code samples. Before you run the code samples, we want you to create the necessary tables. (a) Create a new user(JDBCUSER) and new tables -Connect to [SQLDeveloper] (http://www.oracle.com/technetwork/developer-tools/sql-developer/downloads/sqldev-downloads-42-3802334.html) + +Connect to [SQLDeveloper](http://www.oracle.com/technetwork/developer-tools/sql-developer/downloads/sqldev-downloads-42-3802334.html) + There are three ways to run the sample. (a) Run each sample by passing the database URL and database user as the command-line From f1ebc56339f9e3c1410b88dfaf171174433c7549 Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 10:18:24 -0700 Subject: [PATCH 10/29] Update Readme.md --- java/jdbc/BasicSamples/Readme.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/java/jdbc/BasicSamples/Readme.md b/java/jdbc/BasicSamples/Readme.md index 1ae23ed7..b153d9b5 100644 --- a/java/jdbc/BasicSamples/Readme.md +++ b/java/jdbc/BasicSamples/Readme.md @@ -8,13 +8,14 @@ how to harvest connections, label connections, how to use different timeouts, an how to configure MBean to monitor UCP statistics etc., Read below for the description of the code samples. -# Running Code Samples -Before you run the code samples, we want you to create the necessary tables. - -(a) Create a new user(JDBCUSER) and new tables +# Creating DB User and Sample Data +Before you run the code samples, we want you to create a new DB user and the necessary tables. -Connect to [SQLDeveloper](http://www.oracle.com/technetwork/developer-tools/sql-developer/downloads/sqldev-downloads-42-3802334.html) +Download [SQLDeveloper](http://www.oracle.com/technetwork/developer-tools/sql-developer/downloads/sqldev-downloads-42-3802334.html) or you can use SQLPLUS. Connect to your database and login as SYSADMIN. +Execute the script [JDBCSampleData.sql]() that will create the new database user and the +tables necessary for the code samples. +# Running Code Samples There are three ways to run the sample. (a) Run each sample by passing the database URL and database user as the command-line From 87ee1f4f90183f23980029381e82cf4544675a5f Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 10:19:01 -0700 Subject: [PATCH 11/29] Update Readme.md --- java/jdbc/BasicSamples/Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/jdbc/BasicSamples/Readme.md b/java/jdbc/BasicSamples/Readme.md index b153d9b5..75d1f5e3 100644 --- a/java/jdbc/BasicSamples/Readme.md +++ b/java/jdbc/BasicSamples/Readme.md @@ -12,7 +12,7 @@ description of the code samples. Before you run the code samples, we want you to create a new DB user and the necessary tables. Download [SQLDeveloper](http://www.oracle.com/technetwork/developer-tools/sql-developer/downloads/sqldev-downloads-42-3802334.html) or you can use SQLPLUS. Connect to your database and login as SYSADMIN. -Execute the script [JDBCSampleData.sql]() that will create the new database user and the +Execute the script [JDBCSampleData.sql](https://github.com/oracle/oracle-db-examples/blob/basicsamples/java/jdbc/BasicSamples/JDBCSampleData.sql) that will create the new database user and the tables necessary for the code samples. # Running Code Samples From c6ef7a4f9b1e9cf3b07f6fed482523978704dcfd Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 10:43:58 -0700 Subject: [PATCH 12/29] Update Readme.md --- java/jdbc/BasicSamples/Readme.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/java/jdbc/BasicSamples/Readme.md b/java/jdbc/BasicSamples/Readme.md index 75d1f5e3..66af33df 100644 --- a/java/jdbc/BasicSamples/Readme.md +++ b/java/jdbc/BasicSamples/Readme.md @@ -37,3 +37,19 @@ DATE, TIMESTAMP, TIMESTAMP WITH TIME ZONE and TIMESTAMP WITH LOCAL TIME ZONE. It uses the table 'EMP_DATE_JDBC_SAMPLE' which has dates as columns for all the insert, delete, and update operations. +## JDBCUrlSample.java +This sample shows how to use the easy connection URL, connection URL with connection descriptors, +and using TNS alias to connect to the Oracle database. + +## JSONBasicSample.java +This sample shows how to use some of the enhancements in JavaScript Object Notation (JSON) support +for Oracle Database 12c Release 2 (12.2). + +## LobBasicSample.java +This sample shows how to use different types of LOBs (Large Objects). +It shows using BLOB, CLOB, and NLOB as datatypes. + +## + + + From 78eda29c0351541a3162ca2330b24bc2d5dd8c4a Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 10:44:49 -0700 Subject: [PATCH 13/29] Update DateTimeStampSample.java --- java/jdbc/BasicSamples/DateTimeStampSample.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/jdbc/BasicSamples/DateTimeStampSample.java b/java/jdbc/BasicSamples/DateTimeStampSample.java index 1cb405c1..acfa8b00 100755 --- a/java/jdbc/BasicSamples/DateTimeStampSample.java +++ b/java/jdbc/BasicSamples/DateTimeStampSample.java @@ -19,7 +19,7 @@ * You can also modify these values in this file and recompile the code. *

* - * java DateTimeStampDemo -l -u + * java DateTimeStampSample -l -u * */ import java.io.BufferedReader; From 5c278c22a354303e3e4a33eb30367ec7802d13e4 Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 10:45:57 -0700 Subject: [PATCH 14/29] Update JSONBasicSample.java --- java/jdbc/BasicSamples/JSONBasicSample.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/jdbc/BasicSamples/JSONBasicSample.java b/java/jdbc/BasicSamples/JSONBasicSample.java index 1f697929..2b717e77 100755 --- a/java/jdbc/BasicSamples/JSONBasicSample.java +++ b/java/jdbc/BasicSamples/JSONBasicSample.java @@ -30,7 +30,7 @@ * console, and optionally specify the DB user and/or connect URL on * the command-line. You can also modify these values in this file * and recompile the code. - * java JSONBasicDemo -l -u + * java JSONBasicSample -l -u */ import java.io.BufferedReader; From 8bf115bc880b7d68b15fc8d61aaed40685962c53 Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 10:46:24 -0700 Subject: [PATCH 15/29] Update LobBasicSample.java --- java/jdbc/BasicSamples/LobBasicSample.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/jdbc/BasicSamples/LobBasicSample.java b/java/jdbc/BasicSamples/LobBasicSample.java index 5ae165b3..3f6cc87e 100755 --- a/java/jdbc/BasicSamples/LobBasicSample.java +++ b/java/jdbc/BasicSamples/LobBasicSample.java @@ -8,7 +8,7 @@ console, and optionally specify the DB user and/or connect URL on the command-line. You can also modify these values in this file and recompile the code. - java LobBasic -l -u + java LobBasicSample -l -u NOTES Sample uses books.txt and books.png from current directory. From bb79bd3fccbdb093c06a1118e10377b781e324b3 Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 10:46:41 -0700 Subject: [PATCH 16/29] Update PLSQLSample.java --- java/jdbc/BasicSamples/PLSQLSample.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/jdbc/BasicSamples/PLSQLSample.java b/java/jdbc/BasicSamples/PLSQLSample.java index d6892441..17015b65 100755 --- a/java/jdbc/BasicSamples/PLSQLSample.java +++ b/java/jdbc/BasicSamples/PLSQLSample.java @@ -18,7 +18,7 @@ * of user, password, and URL. This can be done by either updating * this file directly or supplying the 3 values as command-line options * and user input. The password is read from console or standard input. - * java PLSQL2 -l -u + * java PLSQLSample -l -u * If you do not update all the defaults, the program proceeds but * will hit error when connecting. */ From 699ff6b609c88f2d3a13071a5c908176849133f5 Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 10:57:05 -0700 Subject: [PATCH 17/29] Update Readme.md --- java/jdbc/BasicSamples/Readme.md | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/java/jdbc/BasicSamples/Readme.md b/java/jdbc/BasicSamples/Readme.md index 66af33df..420565e2 100644 --- a/java/jdbc/BasicSamples/Readme.md +++ b/java/jdbc/BasicSamples/Readme.md @@ -30,7 +30,6 @@ and run the program. (c) If you don't update the defaults, then the program proceeds with the defaults but, will hit error when connecting as these are dummy values. ----- ## DateTimeStampSample.java: This sample shows illustrates the usage of below Oracle column data types DATE, TIMESTAMP, TIMESTAMP WITH TIME ZONE and TIMESTAMP WITH LOCAL TIME ZONE. @@ -49,7 +48,29 @@ for Oracle Database 12c Release 2 (12.2). This sample shows how to use different types of LOBs (Large Objects). It shows using BLOB, CLOB, and NLOB as datatypes. -## +## PLSQLSample.java +This sample demonstrates the usage of PL/SQL Stored Procedures and Functions in JDBC. + +## PreparedStatementBindingsSample.java +This sample shows CRUD operations using the PreparedStatement with named bindings. + +## PreparedStatementSample.java +This simple shows CRUD operations using the PreparedStatement object. + +## SQLXMLSample.java +This sample shows how to create, insert, and query SQLXML values. + + ## StatementSample.java + This sample shows CRUD operations using the Statement object. + + ## UCPBasic.java + This sample shows simple steps of how JDBC applications use the Oracle Universal Connection Pool (UCP). + + ## UCPLabeling.java + This sample shows how applications use the connection labeling feature of Oracle Universal Connection Pool (UCP). + + ## UCPHarvesting.java + From bb2f75f8f93f79ec0be4f1e1a35ec4b640b3b885 Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 10:58:18 -0700 Subject: [PATCH 18/29] Update Readme.md --- java/jdbc/BasicSamples/Readme.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/java/jdbc/BasicSamples/Readme.md b/java/jdbc/BasicSamples/Readme.md index 420565e2..0bc4dbab 100644 --- a/java/jdbc/BasicSamples/Readme.md +++ b/java/jdbc/BasicSamples/Readme.md @@ -66,11 +66,14 @@ This sample shows how to create, insert, and query SQLXML values. ## UCPBasic.java This sample shows simple steps of how JDBC applications use the Oracle Universal Connection Pool (UCP). - ## UCPLabeling.java - This sample shows how applications use the connection labeling feature of Oracle Universal Connection Pool (UCP). - - ## UCPHarvesting.java + ## UCPHarvesting.java + This code sample shows how applications use the connection harvesting feature of UCP. + ## UCPLabeling.java + This sample shows how applications use the connection labeling feature of UCP. + + ## + From f2cf0123bf768d9de20338b54656beb7bd968b38 Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 10:59:27 -0700 Subject: [PATCH 19/29] Update Readme.md --- java/jdbc/BasicSamples/Readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/jdbc/BasicSamples/Readme.md b/java/jdbc/BasicSamples/Readme.md index 0bc4dbab..cb363d92 100644 --- a/java/jdbc/BasicSamples/Readme.md +++ b/java/jdbc/BasicSamples/Readme.md @@ -52,13 +52,13 @@ It shows using BLOB, CLOB, and NLOB as datatypes. This sample demonstrates the usage of PL/SQL Stored Procedures and Functions in JDBC. ## PreparedStatementBindingsSample.java -This sample shows CRUD operations using the PreparedStatement with named bindings. +This sample shows CRUD operations using the ```PreparedStatement``` with named bindings. ## PreparedStatementSample.java -This simple shows CRUD operations using the PreparedStatement object. +This simple shows CRUD operations using the ```PreparedStatement``` object. ## SQLXMLSample.java -This sample shows how to create, insert, and query SQLXML values. +This sample shows how to create, insert, and query ``SQLXML`` values. ## StatementSample.java This sample shows CRUD operations using the Statement object. From 5c76f5eaeeb70c1374684e4e3a2e41d7b91c143e Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 11:05:12 -0700 Subject: [PATCH 20/29] Update Readme.md --- java/jdbc/BasicSamples/Readme.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/java/jdbc/BasicSamples/Readme.md b/java/jdbc/BasicSamples/Readme.md index cb363d92..84b89037 100644 --- a/java/jdbc/BasicSamples/Readme.md +++ b/java/jdbc/BasicSamples/Readme.md @@ -72,7 +72,14 @@ This sample shows how to create, insert, and query ``SQLXML`` values. ## UCPLabeling.java This sample shows how applications use the connection labeling feature of UCP. - ## + ## UCPManager.java + This sample shows how applications use UCP manager's administration functions. + + ## UCPManagerMBean.java + This sample shows how applications use UCP manager MBean's administration functions. + + ## UCPMaxConnReuse.java + This sample shows how applications use the MaxConnectionReuseTime and MaxConnectionReuseCount features of UCP. From 53936869df8998590d919338f184096fe4bee226 Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 11:08:17 -0700 Subject: [PATCH 21/29] Update Readme.md --- java/jdbc/BasicSamples/Readme.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/java/jdbc/BasicSamples/Readme.md b/java/jdbc/BasicSamples/Readme.md index 84b89037..fcbe6f71 100644 --- a/java/jdbc/BasicSamples/Readme.md +++ b/java/jdbc/BasicSamples/Readme.md @@ -80,7 +80,12 @@ This sample shows how to create, insert, and query ``SQLXML`` values. ## UCPMaxConnReuse.java This sample shows how applications use the MaxConnectionReuseTime and MaxConnectionReuseCount features of UCP. - + + ## UCPMultiUsers.java + This sample shows how JDBC applications use UCPP to pool connections for different users. + + ## UCPTimeouts.java +This sample shows key connection timeout features of UCP such as ConnectionWaitTimeout, InactiveConnectionTimeout, TimeToLiveConnectionTimeout, and AbandonedConnectionTimeout. From 868ab0726590f42da9724b739e1ff2821a868b3b Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 11:09:04 -0700 Subject: [PATCH 22/29] Update UCPBasic.java --- java/jdbc/BasicSamples/UCPBasic.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/jdbc/BasicSamples/UCPBasic.java b/java/jdbc/BasicSamples/UCPBasic.java index 0786db42..9c388c2e 100755 --- a/java/jdbc/BasicSamples/UCPBasic.java +++ b/java/jdbc/BasicSamples/UCPBasic.java @@ -28,7 +28,7 @@ * for ALL 3 of user, password, and URL. This can be done by either updating * this file directly or supplying the 3 values as command-line options * and user input. The password is read from console or standard input. - * java UCPBasic -url -user + * java UCPBasic -l -u * If you do not update all the defaults, the program proceeds but * will hit error when connecting. */ @@ -199,10 +199,10 @@ static void showError(String msg, Throwable exc) { static void getRealUserPasswordUrl(String args[]) throws Exception { // URL can be modified in file, or taken from command-line - url = getOptionValue(args, "-url", DEFAULT_URL); + url = getOptionValue(args, "-l", DEFAULT_URL); // DB user can be modified in file, or taken from command-line - user = getOptionValue(args, "-user", DEFAULT_USER); + user = getOptionValue(args, "-u", DEFAULT_USER); // DB user's password can be modified in file, or explicitly entered readPassword(" Password for " + user + ": "); From 926ce3f1bb593d63438482fbc4552665c7776eea Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 11:11:06 -0700 Subject: [PATCH 23/29] Update UCPHarvesting.java --- java/jdbc/BasicSamples/UCPHarvesting.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/jdbc/BasicSamples/UCPHarvesting.java b/java/jdbc/BasicSamples/UCPHarvesting.java index b8f8bbbc..c2d557d4 100755 --- a/java/jdbc/BasicSamples/UCPHarvesting.java +++ b/java/jdbc/BasicSamples/UCPHarvesting.java @@ -31,7 +31,7 @@ * for ALL 3 of user, password, and URL. This can be done by either updating * this file directly or supplying the 3 values as command-line options * and user input. The password is read from console or standard input. - * java UCPHarvesting -url -user + * java UCPHarvesting -l -u * If you do not update all the defaults, the program proceeds but * will hit error when connecting. */ @@ -286,10 +286,10 @@ static void showError(String msg, Throwable exc) { static void getRealUserPasswordUrl(String args[]) throws Exception { // URL can be modified in file, or taken from command-line - url = getOptionValue(args, "-url", DEFAULT_URL); + url = getOptionValue(args, "-l", DEFAULT_URL); // DB user can be modified in file, or taken from command-line - user = getOptionValue(args, "-user", DEFAULT_USER); + user = getOptionValue(args, "-u", DEFAULT_USER); // DB user's password can be modified in file, or explicitly entered readPassword(" Password for " + user + ": "); From 65f9d8c63ece85d17b49ce860afa9137c946210c Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 11:11:43 -0700 Subject: [PATCH 24/29] Update UCPLabeling.java --- java/jdbc/BasicSamples/UCPLabeling.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/jdbc/BasicSamples/UCPLabeling.java b/java/jdbc/BasicSamples/UCPLabeling.java index c334b67e..f1808724 100755 --- a/java/jdbc/BasicSamples/UCPLabeling.java +++ b/java/jdbc/BasicSamples/UCPLabeling.java @@ -36,7 +36,7 @@ * for ALL 3 of user, password, and URL. This can be done by either updating * this file directly or supplying the 3 values as command-line options * and user input. The password is read from console or standard input. - * java UCPLabeling -url -user + * java UCPLabeling -l -u * If you do not update all the defaults, the program proceeds but * will hit error when connecting. */ @@ -181,10 +181,10 @@ static void showError(String msg, Throwable exc) { static void getRealUserPasswordUrl(String args[]) throws Exception { // URL can be modified in file, or taken from command-line - url = getOptionValue(args, "-url", DEFAULT_URL); + url = getOptionValue(args, "-l", DEFAULT_URL); // DB user can be modified in file, or taken from command-line - user = getOptionValue(args, "-user", DEFAULT_USER); + user = getOptionValue(args, "-u", DEFAULT_USER); // DB user's password can be modified in file, or explicitly entered readPassword(" Password for " + user + ": "); From 0c8b165b767c29713e6e8c8aea028502a95a6b8d Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 11:12:17 -0700 Subject: [PATCH 25/29] Update UCPManager.java --- java/jdbc/BasicSamples/UCPManager.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/jdbc/BasicSamples/UCPManager.java b/java/jdbc/BasicSamples/UCPManager.java index 313f51a6..a1eefe6b 100755 --- a/java/jdbc/BasicSamples/UCPManager.java +++ b/java/jdbc/BasicSamples/UCPManager.java @@ -25,7 +25,7 @@ * for ALL 3 of user, password, and URL. This can be done by either updating * this file directly or supplying the 3 values as command-line options * and user input. The password is read from console or standard input. - * java UCPManager -url -user + * java UCPManager -l -u * If you do not update all the defaults, the program proceeds but * will hit error when connecting. */ @@ -300,10 +300,10 @@ static void showError(String msg, Throwable exc) { static void getRealUserPasswordUrl(String args[]) throws Exception { // URL can be modified in file, or taken from command-line - url = getOptionValue(args, "-url", DEFAULT_URL); + url = getOptionValue(args, "-l", DEFAULT_URL); // DB user can be modified in file, or taken from command-line - user = getOptionValue(args, "-user", DEFAULT_USER); + user = getOptionValue(args, "-u", DEFAULT_USER); // DB user's password can be modified in file, or explicitly entered readPassword(" Password for " + user + ": "); From ddcd2426d5c15941cb6e40135afdc765968d34c3 Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 11:12:49 -0700 Subject: [PATCH 26/29] Update UCPManagerMBean.java --- java/jdbc/BasicSamples/UCPManagerMBean.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/jdbc/BasicSamples/UCPManagerMBean.java b/java/jdbc/BasicSamples/UCPManagerMBean.java index b6778658..c1d2a88c 100755 --- a/java/jdbc/BasicSamples/UCPManagerMBean.java +++ b/java/jdbc/BasicSamples/UCPManagerMBean.java @@ -23,7 +23,7 @@ * for ALL 3 of user, password, and URL. This can be done by either updating * this file directly or supplying the 3 values as command-line options * and user input. The password is read from console or standard input. - * java UCPManagerMBean -url -user + * java UCPManagerMBean -l -u * If you do not update all the defaults, the program proceeds but * will hit error when connecting. */ @@ -319,10 +319,10 @@ static void showError(String msg, Throwable exc) { static void getRealUserPasswordUrl(String args[]) throws Exception { // URL can be modified in file, or taken from command-line - url = getOptionValue(args, "-url", DEFAULT_URL); + url = getOptionValue(args, "-l", DEFAULT_URL); // DB user can be modified in file, or taken from command-line - user = getOptionValue(args, "-user", DEFAULT_USER); + user = getOptionValue(args, "-u", DEFAULT_USER); // DB user's password can be modified in file, or explicitly entered readPassword(" Password for " + user + ": "); From 3ac7c68f6df05002b64588a52c90981bca448c19 Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 11:13:25 -0700 Subject: [PATCH 27/29] Update UCPMaxConnReuse.java --- java/jdbc/BasicSamples/UCPMaxConnReuse.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/jdbc/BasicSamples/UCPMaxConnReuse.java b/java/jdbc/BasicSamples/UCPMaxConnReuse.java index fca96b72..faf95c2b 100755 --- a/java/jdbc/BasicSamples/UCPMaxConnReuse.java +++ b/java/jdbc/BasicSamples/UCPMaxConnReuse.java @@ -37,7 +37,7 @@ * for ALL 3 of user, password, and URL. This can be done by either updating * this file directly or supplying the 3 values as command-line options * and user input. The password is read from console or standard input. - * java UCPMaxConnReuse -url -user + * java UCPMaxConnReuse -l -u * If you do not update all the defaults, the program proceeds but * will hit error when connecting. */ @@ -259,10 +259,10 @@ static void showError(String msg, Throwable exc) { static void getRealUserPasswordUrl(String args[]) throws Exception { // URL can be modified in file, or taken from command-line - url = getOptionValue(args, "-url", DEFAULT_URL); + url = getOptionValue(args, "-l", DEFAULT_URL); // DB user can be modified in file, or taken from command-line - user = getOptionValue(args, "-user", DEFAULT_USER); + user = getOptionValue(args, "-u", DEFAULT_USER); // DB user's password can be modified in file, or explicitly entered readPassword(" Password for " + user + ": "); From a62033797ffa78a05d9d342390ea69ab4d416f8d Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 11:13:56 -0700 Subject: [PATCH 28/29] Update UCPMultiUsers.java --- java/jdbc/BasicSamples/UCPMultiUsers.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/jdbc/BasicSamples/UCPMultiUsers.java b/java/jdbc/BasicSamples/UCPMultiUsers.java index 9401712b..c4f72443 100755 --- a/java/jdbc/BasicSamples/UCPMultiUsers.java +++ b/java/jdbc/BasicSamples/UCPMultiUsers.java @@ -16,7 +16,7 @@ * for ALL 3 of user, password, and URL. This can be done by either updating * this file directly or supplying the 3 values as command-line options * and user input. The password is read from console or standard input. - * java UCPMultiUsers -url -user + * java UCPMultiUsers -l -u * If you do not update all the defaults, the program proceeds but * will hit error when connecting. */ @@ -114,10 +114,10 @@ static void showError(String msg, Throwable exc) { static void getRealUserPasswordUrl(String args[]) throws Exception { // URL can be modified in file, or taken from command-line - url = getOptionValue(args, "-url", DEFAULT_URL); + url = getOptionValue(args, "-l", DEFAULT_URL); // DB user can be modified in file, or taken from command-line - user = getOptionValue(args, "-user", DEFAULT_USER); + user = getOptionValue(args, "-u", DEFAULT_USER); // DB user's password can be modified in file, or explicitly entered readPassword(" Password for " + user + ": "); From 93d2ed71eea6fa4ebb8b5d10ee99ab7ba3560ea4 Mon Sep 17 00:00:00 2001 From: Nirmala Sundarappa Date: Fri, 27 Apr 2018 11:14:53 -0700 Subject: [PATCH 29/29] Update UCPTimeouts.java --- java/jdbc/BasicSamples/UCPTimeouts.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/jdbc/BasicSamples/UCPTimeouts.java b/java/jdbc/BasicSamples/UCPTimeouts.java index 030902ff..5bc1cc14 100755 --- a/java/jdbc/BasicSamples/UCPTimeouts.java +++ b/java/jdbc/BasicSamples/UCPTimeouts.java @@ -19,7 +19,7 @@ * for ALL 3 of user, password, and URL. This can be done by either updating * this file directly or supplying the 3 values as command-line options * and user input. The password is read from console or standard input. - * java UCPTimeouts -url -user + * java UCPTimeouts -l -u * If you do not update all the defaults, the program proceeds but * will hit error when connecting. */ @@ -339,10 +339,10 @@ static void showError(String msg, Throwable exc) { static void getRealUserPasswordUrl(String args[]) throws Exception { // URL can be modified in file, or taken from command-line - url = getOptionValue(args, "-url", DEFAULT_URL); + url = getOptionValue(args, "-l", DEFAULT_URL); // DB user can be modified in file, or taken from command-line - user = getOptionValue(args, "-user", DEFAULT_USER); + user = getOptionValue(args, "-u", DEFAULT_USER); // DB user's password can be modified in file, or explicitly entered readPassword(" Password for " + user + ": ");