ЗАНЯТИЕ №6

Изучение семейства интерфейсов RowSet:

JdbcRowSet, CachedRowSet, WebRowSet



Объекты типа RowSet являются альтернативой объектам типа ResultSet. Интерфейс RowSet расширяет интерфейс ResultSet и определяет дополнительные методы, которые позволяют работать с компонентной архитектурой JavaBean. Кроме того, объекты данного типа могут использоваться при отсутствии постоянного соединения с базой данных. При этом объект типа RowSet будет автоматически соединяться с базой при необходимости получения из нее данных. Интерфейс входит в пакет javax.sql и должен быть реализован поставщиком JDBC классов (драйверов).

Для использования реализации возможностей данного интерфейса компанией Oracle необходимо импортировать пакет oracle.jdbc.rowset, который находится в архиве ocrs12.jar. После чего можно использовать классы OracleCachedRowSet, OracleJDBCRowSet и OracleWebRowSet, применяющие интерфейс javax.sql.RowSet.

Интерфейсы ResultSet и RowSet имеют два принципиальных отличия:

1. Интерфейс RowSet поддерживает компонентную модель JavaBeans. Это позволяет изменять свойства объектов используя визуальные средства разработки компонентов. Кроме того, объекты типа RowSet могут информировать службы обработки событий, например о событии обновления строк.

2. Объекты типа RowSet могут использоваться как при наличии постоянного соединения с базой данных, так и при отсутствии соединения. В режиме постоянного соединения с базой данных использование объектов типа RowSet аналогично использованию объектов типа ResultSet. В режиме отсутствия постоянного соединения наличие соединения с базой данных необходимо только при заполнении объекта типа RowSet данными из базы. После чего соединение разрывается автоматически и данные для обработки будут выбираться далее из памяти.

Еще одно полезное преимущество интерфейса RowSet состоит в реализации прокручиваемых и обновляемых курсоров в виде надстройки над драйверами, которые сами этого не поддерживают.

Объекты типа RowSet могут создавать события, что позволяет оповещать о их происхождении другие компоненты JavaBeans, применяющие интерфейс RowSetListener, который предусматривает три основных метода для обработки одноименных событий: rowChanged(), rowSetChenged() и cursorMoved(). Первое событие (row changes) происходит при изменении при изменении строки, например, когда выполняется UPDATE SQL-предложение. Следующее событие (rowset changes) происходит, когда клиент изменяет весь набор строк. И последнее событие (cursor movements) происходит при перемещении курсора с одной строки на другую, например при выполнении методов next(), previous() или relative().

Интерфейс RowSet не только расширяет интерфейс ResultSet, но и предусматривает установку некоторый свойств, заменяющих использование интерфейсов Connection, Statement, PreparedStatement и CallableStatement. Выполнение простого запроса с использованием интерфейса RowSet может выглядеть следующим образом:

import java.sql.*;
import javax.sql.*;
import oracle.jdbc.rowset.*;

public class JdbcRS {
    
        
    public static void main(String[] args) {
        try {
            
            OracleJDBCRowSet ojrs = new OracleJDBCRowSet();
            ojrs.setUrl("jdbc:oracle:thin:@localhost:1521:orbis");
            ojrs.setUsername("stud");
            ojrs.setPassword("stud");
            ojrs.setCommand("SELECT count(*) FROM н_люди");
            ojrs.execute();
            while (ojrs.next()) {
                System.out.println("Total count of row is: " + ojrs.getInt(1));
            }
            
        } catch (SQLException se) {
            se.printStackTrace();
        }
        System.out.println("Goodbye!");
    }
    
}

Соединение с базой данных устанавливается в момент выполнения метода execute(). При выполнении SQL-предложения SELECT объект типа RowSet заполняет себя удовлетворяющими условию запроса данными. Результаты выполнения SQL-предложений INSERT, UPDATE, DELETE и любых DML-предложений игнорируются. В случае ошибки возникает исключение SQLException. Если используется параметризованный запрос, то перед выполнением метода execute() необходимо установить все параметры методами setXXX(), как при использовании объектов типа PreparedStatement, например:

OracleCachedRowSet ocrs = new OracleCachedRowSet();
//
ocrs.setUrl("jdbc:oracle:thin:@localhost:1521:orbis");
ocrs.setUsername("stud");
ocrs.setPassword("stud");
String sql = "SELECT * FROM н_люди WHERE ид = ?";
ocrs.setCommand(sql);
ocrs.setInt(1,123456);
ocrs.execute();



Для извлечения данных из объекта типа RowSet можно использовать RowSet.getXXX() методы, XXX-одноименные с типами данных Java тех переменных, в которые эти данные будут сохранены. Перемещение курсора по набору данных объекта типа RowSet выполняется методами, унаследованными от интерфейса ResultSet:


Для задания параметра прокрутки (scrollability) используется метод setType(). Он задает возможность перемещения курсора в разных направления по строкам результата запроса и определяет чувствительность к изменениям данных, которые были изменены в базе после выполнения запроса. Параметр прокрутки может принимать следующие:

Второй параметр устанавливается методом setConcurrency() и отвечает за возможность изменения данных результата запроса, вставки и удаления строк из базы данных. Он может принимать одно из двух значений:

Третий параметр позволяет контролировать уровень транзакций. Он определяет возможность доступа к данным объекта типа RowSet в течении транзакции с использованием данных другого объекта типа RowSet. Этот параметр устанавливается методом setTransactionIsolation() и может принимать следующие значения:

После завершения обработки данных объекта типа RowSet необходимо закрыть объект методом RowSet.close().

Из перечисленного выше семейства самой простой является реализация интерфейса JdbcRowSet. Работа с объектами типа JdbcRowSet требует постоянного соединения с базой данных. Кроме того, они не могут быть преобразованы в последовательную форму (не serializable), что ограничивает возможности их использования в распределенных системах обработки данных.

Реализация интерфейса CachedRowSet позволяет использовать не постоянное соединение и предоставляет возможность преобразования объектов типа CachedRowSet в последовательную форму, т.е. позволяет сохранять объекты. При отключении соединения объекты типа CachedRowSet создают внутри себя виртуальную таблицу, что позволяет работать с ними, как с отдельными независимыми объектами. После заполнения объекта данными можно сохранить его и использовать совместно сразу несколькими клиентами. Это позволяет уменьшить количество постоянных соединений с базой и сократить частоту их создания. Кроме того объект могут получить мобильные клиенты и использовать его отключившись от сетевого соединения. Ниже приведен пример создания, сохранения и использования объекта типа CachedRowSet:

import java.io.*;
import java.sql.SQLException;
import oracle.jdbc.rowset.OracleCachedRowSet;

public class CachedRS {

    //Constant to hold file name used to store the CachedRowSet
    private final static String CRS_FILE_LOC ="cachedrs.crs";

    public static void main(String[] args) throws Exception {
        try {
            //Create serialized CachedRowSet
            writeCachedRowSet();

            //Create CachedRowSet from serialized object
            OracleCachedRowSet crs = readCachedRowSet();

            //Display values
            while(crs.next()){
                System.out.print("Фамилия: " + crs.getString("фамилия"));
                System.out.print(", Имя: " + crs.getString("имя"));
                System.out.print(", Отчество: " + crs.getString("отчество"));
                System.out.print(", Дата рождения: " + crs.getDate("дата_рождения"));
                System.out.println();
            }
            //Close resource
            crs.close();
        }catch (SQLException se){
            se.printStackTrace();
        }catch (Exception ex) {
            ex.printStackTrace();
        }
    }//end main

    public static void writeCachedRowSet() throws Exception{
        //Instantiate a CachedRowSet object, set connection parameters
        OracleCachedRowSet crs = new OracleCachedRowSet();
        crs.setUrl("jdbc:oracle:thin:@localhost:1521:orbis");
        crs.setUsername("stud");
        crs.setPassword("stud");

        //Set and execute the command. Notice the parameter query.
        String sql = "SELECT * FROM н_люди WHERE ид = ?";
        crs.setCommand(sql);
        crs.setInt(1,123456);
        crs.execute();

        //Serialize CachedRowSet object.
        FileOutputStream fos = new FileOutputStream(CRS_FILE_LOC);
        ObjectOutputStream out = new ObjectOutputStream(fos);
        out.writeObject(crs);
        out.close();
        crs.close();
    }//end writeCachedRowSet()

    public static OracleCachedRowSet readCachedRowSet() throws Exception{
        //Read serialized CachedRowSet object from storage
        FileInputStream fis = new FileInputStream(CRS_FILE_LOC);
        ObjectInputStream in = new ObjectInputStream(fis);
        OracleCachedRowSet crs = (OracleCachedRowSet)in.readObject();
        fis.close();
        in.close();
        return crs;
    }//end readCachedRowSet()

}//end CachedRS



Для демонстрации использования объекта типа CachedRowSet при отсутствии соединения с базой данных в примере созданы два метода. Метод writeCachedRowSet() создает объект, заполняет его данными и сохраняет его. Метод readCachedRowSet() читает с диска сохраненный объект и возвращает его вызывающему методу. В реальных приложениях объекты сохраняются с использованием службы имен и каталогов посредством интерфейса JNDI. Для изучения этой возможности используйте в качестве службы имен и каталогов файловую систему (см. примеры предыдущего занятия) и самостоятельно модернизируйте приведенный выше пример.

Использование объектов типа CachedRowSet не ограничивается только просмотром данных. Можно изменять и удалять существующие строки, а так же добавлять новые. Объект типа CachedRowSet хранит в памяти и оригинальную и измененную копию данных и работает с ними при отсутствии соединения с базой. Для внесения изменений в базу из виртуальной таблицы объекта необходимо использование нового (не унаследованного от интерфейса ResultSet) метода – acceptChanges(). Вызов этого метода заставляет объект типа CachedRowSet соединиться с базой данных и внести в нее изменения. Если в процессе внесения изменений возникли ошибки, то выдается исключение SQLException. Ниже приведены примеры программного кода, иллюстрирующие эти возможности на данных таблиц, созданных на предыдущих занятиях.

//Populate a CachedRowSet object, crs
String sql = "SELECT * FROM номера_телефонов WHERE ид_л = ?";
crs.setCommand(sql);
crs.setInt(1,125704);
crs.execute();

//Make rowset updatable
crs.setReadOnly(false);

//*** Update example
//Move to first and only row and give myself a raise
crs.first();
crs.updateString(2,"1131313");
//Signal changes are finished
crs.updateRow();
//Write records to database
crs.acceptChanges();



//*** Insert new row example
//Move cursor to the insert row position
crs.moveToInsertRow();
//Add the data for the new row
crs.updateInt(1,120848);
crs.updateString(2,"9332323");
crs.updateString(3,"1−1−02");
crs.updateInt(4,1);
//write the rows to the rowset
crs.insertRow();
//Submit the data to the data source
crs.acceptChanges();



До тех пор, пока не выполнен метод acceptChanges(), можно «откатить» изменения данных, так как в объекте хранятся обе таблицы – и оригинальная (с исходными данными) и измененная. Для этого необходимо выполнить метод restoreOriginal(). Этот метод эквивалентен SQL-предложению ROLLBACK. Второй метод – cancelRowUpdates() - отменяет изменения, сделанные в текущей строке.

Для изучения указанных выше методов необходимо самостоятельно создать класс, демонстрирующий обновление существующих данных, добавление новых данных и отмену сделанных изменений, как полную, так и частичную.