ЗАНЯТИЕ №5
Изучение интерфейсов DataSource и ConnectionPoolDataSource.
Оба интерфейса входят в пакет javax.sql и должны быть реализованы поставщиками JDBC классов (драйверов). Основное назначение интерфейсов DataSource и ConnectionPoolDataSource состои в предоставлении возможности получения соединения с базой данных абстрагируясь от местоположения сервера СУБД и типа драйвера конкретного производителя. Интерфейсы определяют ряд обязательных для реализации методов, в том числе и метод getConnection(). Объекты, получаемые от реализации данных интерфейсов используются для задания параметров соединения с базой данных и установки соединения в виде объекта типа Connection.
JDBC DataSource объекты используются для получения физического соединения с базой данных и являются альтернативой DriverManager. При этом нет необходимости регистрировать драйвер. Необходимо только установить соответствующие параметры для установки соединения и выполнить метод getConnection(). При создании объекта типа DataSource локально (прямо в приложении) параметры соединения задаются соответствующими методами, предусмотренными поставщиком драйвера. Эти методы не определены интерфейсом DataSource, т.к. параметры для соединения с СУБД разных производителей могут отличаться как по типу, так и по количеству (например, не для всех СУБД необходимо указывать тип драйвера или сетевой протокол). Так при работе с СУБД Oracle для использования соответствующих set и get методов необходимо получить объект типа OracleDataSource, который является экземпляром одноименного класса, реализующего интерфейс DataSource. В следствии этого такой способ создания объектов типа DataSource делает программный код менее мобильным и зависимым от конкретной реализации драйвера. Ниже приведен пример программного кода, иллюстрирующий локальное использование объектов типа DataSource.
import java.sql.*;
import javax.sql.*;
import oracle.jdbc.driver.*;
import oracle.jdbc.pool.*;
public class DataSource {
public static void main(String[] args) {
try {
OracleDataSource ods = new OracleDataSource();
ods.setUser("stud");
ods.setPassword("stud");
ods.setDriverType("thin");
ods.setDatabaseName("orbis");
ods.setServerName("localhost");
ods.setPortNumber(1521);
Connection conn = ods.getConnection();
System.out.println("Connection successful!!!");
} catch (SQLException se) {
se.printStackTrace();
}
System.out.println("Goodbye!");
}
}
Полностью возможности объектов типа DataSource проявляются в сочетании с использованием интерфейса JNDI. Использование службы имен и каталогов позволяет хранить заранее созданные системным администратором объекты типа DataSource с предустановленными параметрами соединения. Ниже приведены некоторые стандартные имена свойств (параметров), разработанные компанией Sun:
Имя свойства Тип данных Java Назначение ------------------------------------------------------------- databaseName String имя базы данных serverName String имя сервера user String имя пользователя (ID) password String пароль пользователя portNumber Int номер порта сервера СУБД
Комбинирование интерфейсов DataSource и JNDI играет ключевую роль в разработке многоуровневых корпоративных приложений, основанных на компонентной технологии J2EE. В случае использования комбинации интерфейсов DataSource и JNDI в программном коде приложения необходимо только затребовать объект типа DataSource у службы имен и каталогов. При этом детали соединения и зависимый от конкретной реализации драйвера программный код оказывается скрытым от приложения в хранящемся в службе имен и каталогов объекте.
Таким образом, совместное использование объектов типа DataSource и JNDI предполагает наличие двух выполняемых независимо друг от друга шагов:
1. Необходимо сохранить именованный объект типа DataSource в службе имен и каталогов, используя метод Context.bind() пакета javax.naming.
2. Затребовать в приложении объект типа DataSource у службы имен и каталогов, использую метод Context.lookup(). После чего можно использовать его метод DataSource.getConnection() для получения соединения с базой данных.
Ниже приведен пример совместного использования интерфейса JNDI и объекта типа OracleDataSource. В качестве службы имен и каталогов используется файловая система и необходимый для этого SPI (fscontext.jar и providerutil.jar).
import java.sql.*;
import javax.sql.DataSource;
import oracle.jdbc.pool.OracleDataSource;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.InitialContext;
import java.util.Hashtable;
public class JndiDataSource{
static Connection conn = null;
static Statement stmt = null;
static ResultSet rs = null;
static Context ctx = null;
static DataSource ds = null;
public static void main (String args []){
// Initialize the Context
String sp = "com.sun.jndi.fscontext.RefFSContextFactory";
String file = "file:JNDI";
String dataSourceName = "myDatabase";
try {
//Create Hashtable to hold environment properties
//then open InitialContext
Hashtable env = new Hashtable();
env.put (Context.INITIAL_CONTEXT_FACTORY, sp);
env.put (Context.PROVIDER_URL, file);
ctx = new InitialContext(env);
//Bind the DataSource object
bindDataSource(ctx, dataSourceName);
//Retrieve the DataSource object
DataSource ds = null;
ds = (DataSource) ctx.lookup(dataSourceName);
//Open a connection, submit query, and print results
Connection conn = ds.getConnection();
Statement stmt = conn.createStatement();
String sql = "SELECT count(*) FROM all_tables";
ResultSet rs = stmt.executeQuery(sql);
System.out.println("The result is: ");
while(rs.next())
System.out.println(rs.getInt(1));
// Close the connections to the data store resources
ctx.close();
rs.close();
stmt.close();
conn.close();
}catch (NamingException ne){
ne.printStackTrace();
}catch (SQLException se){
se.printStackTrace();
//ensure all resources are closed
}finally{
try{
if(ctx!=null)
ctx.close();
}catch (NamingException ne){
ne.printStackTrace();
}finally{
try{
if(conn!=null)
conn.close();
}catch (SQLException se){
se.printStackTrace();
}
}
}
System.out.println("Goodbye!");
}
//Method to bind DataSource object
public static void bindDataSource(Context ctx, String dsn)
throws SQLException, NamingException{
//Create an OracleDataSource instance
OracleDataSource ods = new OracleDataSource();
//Set the connection parameters
ods.setUser("stud");
ods.setPassword("stud");
ods.setDriverType("thin");
ods.setDatabaseName("orbis");
ods.setServerName("localhost");
ods.setPortNumber(1521);
//Bind the DataSource
ctx.rebind (dsn,ods);
}
}Приложение начинается с инициализации объекта типа InitialContext, который открывает соединение со службой имен и каталогов, задает начальный контекст (начальный каталог дерева хранения объектов) и используется для загрузки объекта типа DataSource. Далее используется метод bindDataSource() для сохранения объекта типа DataSource в службе имен и каталогов.
Для демонстрации работы с сохраненным объектом создается переменная ds типа DataSource. Полиморфизм позволяет назначать ей экземпляры любых классов, реализующих интерфейс DataSource, в нашем случае это объект типа OracleDataSource. Метод lookup() запрашивает сохраненный объект типа OracleDataSource и возвращает результат типа Object, по этому его надо преобразовать в тип DataSource. Получив требуемый объект типа DataSource можно использовать его метод getConnection() для получения соединения с базой данных.
Объекты типа ConnectionPoolDataSource используются для получения объектов типа PooledConnection, которые представляют физическое соединение с базой данных и являются поставщиками объектов типа Connection. Сам по себе интерфейс PooledConnection не предусматривает методов для создания объектов типа Statement, по этому необходимо использовать объекты типа Connection.
Объекты типа Connection, получаемые с помощью объектов типа PooledConnection представляют логическое соединение с базой данных. Они выполняют те же функции, что и объекты типа Connection, получаемые с использованием интерфейса DriverManager. Они используюся для создания объектов типа Statement, PrepareStatement или CollableStatement, и управляют уровнем транзакций. Однако, метод close() таких объектов не закрывает соединение с базой данных, а возвращает объект логического соединения обратно в пул для повторного использования его другими клиентами.
Ниже приведен пример использования локального объекта типа ConnectionPoolDataSource, требующего установки параметров соединения в программном коде приложения.
import java.sql.*;
import javax.sql.*;
import oracle.jdbc.pool.*;
public class ConnPool {
//Global variables
static OracleConnectionPoolDataSource ocpds = null;
static PooledConnection pc_1 = null;
static PooledConnection pc_2 = null;
public static void main(String[] args){
String jdbcUrl = "jdbc:oracle:thin:@localhost:1521:orbis";
String user = "stud";
String password = "stud";
try{
ocpds = new OracleConnectionPoolDataSource();
ocpds.setURL(jdbcUrl);
ocpds.setUser(user);
ocpds.setPassword(password);
//Create a pooled connection
pc_1 = ocpds.getPooledConnection();
//Open a Connection and a Statement object
Connection conn_1 = pc_1.getConnection();
Statement stmt = conn_1.createStatement();
//Build query string
String sql = "SELECT count(*) FROM v$session ";
sql = sql + "WHERE username = 'STUD'";
//Execute query and print results
ResultSet rs = stmt.executeQuery(sql);
rs.next();
String msg = "Total connections after ";
System.out.println(msg + "conn_1: " + rs.getString(1));
///Open second logical connection and execute query
Connection conn_2 = pc_1.getConnection();
stmt = conn_2.createStatement();
rs = stmt.executeQuery(sql);
rs.next();
System.out.println(msg + "conn_2: " + rs.getString(1));
//Open second physical connection and execute query.
pc_2 = ocpds.getPooledConnection();
rs = stmt.executeQuery(sql);
rs.next();
System.out.println(msg + "pc_2: " + rs.getString(1));
//Close resources
conn_1.close();
conn_2.close();
//Standard error handling.
}catch(SQLException se){
//Handle errors for JDBC
se.printStackTrace();
}catch(Exception e){
//Handle errors for Class.forName
e.printStackTrace();
}finally{
//Finally clause used to close resources
try{
if(pc_1!=null)
pc_1.close();
}catch(SQLException se){
se.printStackTrace();
}finally{
try{
if(pc_2!=null)
pc_2.close();
}catch(SQLException se){
se.printStackTrace();
}
}
}//end try
System.out.println("Goodbye!");
}
}В примере создается объект pc_1 типа PooledConnection для поставки объектов типа Connection. Далее создаются два логических соединения conn_1 и conn_2. После их создания выводится количество физических соединения. Физическое соединение одно, не смотря на наличие двух открытых логических соединений. При создании второго объекта pc_2 типа PooledConnection физических соединений становится два.
Изучение использования объекта типа ConnectionPoolDataSource с предустановленными параметрами в комбинации с интерфейсом JNDI выносится на самостоятельную проработку.