Статьи

Пользовательские типы Java (UDT) в БД Java

Java DB — это система управления реляционными базами данных, основанная на языке программирования Java и SQL. Это релиз Oracle проекта Apby Software Foundation с открытым исходным кодом Derby. Java DB включена в Java SE 7 SDK.

Пользовательский тип (UDT) — это класс Java, экземпляры (объекты) которого хранятся в столбцах таблицы базы данных. UDT определяются как тип данных столбца, а экземпляры UDT хранятся как значения столбца. UDT могут быть созданы и использованы в базе данных Java DB.

Ниже приводится содержание этого поста:

  1. Пользовательский тип (UDT)
  2. Создать и использовать UDT
  3. Проектирование UDT — рассмотрение влияния обновления UDT на существующие данные
  4. Пример кода
  5. Примечания и ссылки

1. Пользовательский тип (UDT)

UDT определяет тип данных в базе данных. UDT — это класс Java с модификатором открытого доступа. Этот класс реализует интерфейс java.io.Serializable. Класс должен быть виден в пути к классам приложения базы данных (или инструмента), ссылающегося на класс.

Класс UDT используется для определения типа данных таблицы или столбца представления — определенного пользователем типа данных. Данные UDT — это экземпляр (объект Java) класса UDT; и сохраняется как данные столбца. UDT также может упоминаться в хранимых процедурах и функциях (в Java DB они основаны на Java) как типы данных.

1.1. Атрибуты (характеристики)

  • UDT может иметь подтипы; и данные подтипа могут быть заполнены основным типом, то есть экземпляр подкласса класса UDT может быть заполнен как значение UDT. Например: (a) Java-класс Type1 и UDT, определенный в базе данных как dbtype1 , (b) Java-класс Subtype1 является подклассом Type1 , и (c) столбец таблицы dbtype1 также может быть заполнен экземпляром Subtype1 (в дополнение к экземплярам Type1 ).
  • UDT нельзя индексировать, упорядочивать или сравнивать; и не может использоваться с операторами, сгруппированными или агрегированными в выражениях SQL (например, =, LIKE, DISTINCT, GROUP…).

2. Создайте и используйте UDT

  • 2.1. Создать UDT
  • 2.2. Использование UDT

2.1.Создать UDT

Создайте класс Java и определите UDT в базе данных. Создайте класс Java, например TestType1.java (см. Код по адресу: 4.1 TestType1.java ), который будет использоваться в качестве UDT в базе данных. Скомпилируйте исходный код.

Оператор SQL CREATE TYPE создает UDT в базе данных. Синтаксис:

1
2
3
CREATE TYPE udtTypeName
EXTERNAL NAME javaClassName
LANGUAGE JAVA

Команда создает UDT в схеме по умолчанию или в указанной схеме, где:

  • udtTypeName является идентификатором имени для UDT в базе данных.
  • javaClassName — это полное имя класса Java.

Например, создайте базу данных UDT в Java DB с помощью инструмента ij ( ij — инструмент командной строки, включенный в Java DB. Ij — инструмент JDBC, используемый для выполнения интерактивных запросов к базе данных Java DB.):

1
2
ij> CONNECT 'jdbc:derby:testDB';
ij> CREATE TYPE type1 EXTERNAL NAME 'TestType1' LANGUAGE JAVA;

В приведенном выше примере testDB является существующей базой данных. UDT с именем type1 создается в базе данных testDB .

НОТА

Файл класса Java должен находиться в пути к классам, чтобы на него можно было ссылаться из инструмента ij .

2.1.1.Проверить, удалить и обновить UDT

Созданный UDT можно проверить с помощью следующей команды SQL:

1
ij> SELECT alias, javaclassname FROM SYS.SYSALIASES WHERE aliastype='A';

Чтобы удалить UDT из базы данных, используйте команду SQL DROP TYPE. Ниже приведен пример:

1
ij> DROP TYPE udtTypeName RESTRICT;

В приведенном выше примере udtTypeName является именем UDT, как определено в базе данных.

UDT нельзя отбросить, если объект базы данных использует (или ссылается) UDT. Например, (a) если столбец таблицы имеет тип UDT, этот UDT не может быть удален, пока не будет удален соответствующий столбец таблицы, или (b) если функция базы данных ссылается на класс (экземпляр) UDT, UDT не может быть отброшено, если функция не изменена, чтобы не ссылаться на этот класс UDT.

Чтобы обновить UDT с помощью обновленного кода Java, (пере) скомпилируйте класс UDT. Это влияет на объекты типа UDT. Это также может повлиять на данные, хранящиеся в объектах UDT, в зависимости от того, как UDT определяется и используется в приложении. См. Тему: 3. Проектирование UDT — учитывая влияние обновления UDT на существующие данные .

2.2. Использование UDT

Создание объектов базы данных с помощью UDT и манипулирование данными UDT (вставка, обновление, удаление и запрос). Данные UDT могут использоваться в базе данных либо с интерактивным SQL, либо с JDBC API в программе Java.

2.2.1. Интерактивный SQL

Далее описывается создание столбца таблицы базы данных типа UDT, вставка данных и запрос вставленных данных.

  • (i) Создать таблицу базы данных с UDT в качестве типа столбца.

    Например:

    1
    2
    3
    4
    CREATE TABLE test_table1 (
        id INT,
        type1col type1, // column with UDT
    )
  • (ii) Вставить данные в таблицу.

    Пользовательская функция базы данных используется для вставки данных в столбец таблицы, определенный с помощью UDT. Подробнее о создании пользовательской функции для вставки данных UDT в столбец таблицы см. 4.2. Example_Fn1 — Функция .

    Функция примера Example_Fn1 имеет подпись Example_Fn1 (вход String) и возвращает экземпляр TestType1 (где TestType1 — это класс Java, который представляет UDT).

    1
    ij> INSERT INTO test_table1(id, type1col) VALUES(1, Example_Fn1("udt value 1"));

    Приведенная выше команда SQL вставляет в таблицу строку со значением столбца UDT объекта Java TestType1 . Функция Example_Fn1 вызывает конструктор класса TestType1 с входным параметром String для построения объекта; и этот объект хранится в столбце таблицы.

  • (iii) Запрос вставленных данных.

    Пользовательская функция может быть использована для получения данных из столбца UDT. В следующем примере метод toString () переопределенного класса Object класса UDT TestType1 возвращает строковое значение сохраненного экземпляра.

    1
    2
    3
    4
    ij> SELECT * FROM test_table1;
    ID         | TYPE1COL
    -------------------------
    1          | udt value 1

2.2.2. Использование JDBC API

Интерфейсы PreparedStatement и ResultSet, определенные в пакете java.sql , используются для вставки и получения данных UDT базы данных соответственно.

  • Метод PreOredStatement setObject () используется для хранения данных UDT как объекта в столбце таблицы UDT. Метод setObject (int parameterIndex, Object obj) устанавливает значение назначенного параметра, используя данный объект.
  • Метод getObject () ResultSet используется для извлечения сохраненных данных UDT из столбца таблицы UDT. Метод getObject (int columnIndex) получает значение указанного столбца в текущей строке этого объекта ResultSet . Возвращенные данные являются объектом.

Следующие фрагменты кода Java показывают использование:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
// insert data into a table
int idValue = 2;
TestType1 obj = new TestType1("udt value 2");
PreparedStatement ps = conn.prepareStatement("INSERT INTO test_table1 VALUES (?, ?)";
ps.setInt(1, idValue); // where 1 is the parameter index
ps.setObject(2, obj); // UDT data
ps.executeUpdate();
...
// retrieve data from a table
PreparedStatement ps = conn.prepareStatement("SELECT * FROM test_table1");
ResultSet rs = ps.executeQuery();
while (rs.next()) {
int idValue = rs.getInt(1);
    TestType1 testType1 = (TestType1) rs.getObject(2);
// where 2 is the column index in ResultSet object
    ...
}

НОТА

  1. В приведенном выше коде conn является объектом подключения к базе данных.
  2. Файл класса UDT Java должен находиться в пути к классу, чтобы ссылаться из кода JDBC.
  3. Проектирование UDT — рассмотрение влияния обновления UDT на существующие данные

UDT используется для хранения данных. Этот UDT (и данные) могут меняться (то есть развиваться) в течение жизненного цикла приложения. Необходимо разработать UDT, чтобы принять это во внимание.

Кроме того, обратите внимание, что класс UDT всегда реализует интерфейс Serializable . Необходимо учитывать эффекты сериализации данных и различных версий объектов данных UDT. В простых случаях может быть достаточно просто компилировать измененный код класса UDT.

Вот два способа, которыми UDT может быть разработан и использован.

  • Класс UDT реализует S erializable, и приложение (с UDT) использует приложения преобразования данных по мере развития данных UDT.
  • Класс UDT реализует Externalizable (вместо Serializable ) и использует функциональность преобразования данных в классе UDT. Пример с кодом класса UDT показан ниже.

О Externalizable

Класс UDT должен реализовывать интерфейс Serializable ; и java.io.Externalizable расширяет Serializable .

Когда этот интерфейс реализован, в поток сериализации записывается только идентификатор класса (а не состояние) экземпляра Externalizable . Класс несет ответственность за сохранение и восстановление содержимого (состояния) его экземпляров.

Есть два метода, которые должны быть реализованы:

  • readExternal (ObjectInput in): объект реализует метод readExternal () для восстановления его содержимого.
  • writeExternal (ObjectOutput out): объект реализует метод writeExternal () для сохранения своего содержимого.

3.1 Класс UDT реализует Сериализуемый

Класс UDT реализует Serializable, и приложение (которое использует UDT) использует приложения преобразования данных по мере развития данных UDT — следующие шаги обзора:

  • UDT реализует Сериализуемый .
  • Создайте и используйте UDT с начальной версией.
  • Сохраните (сохраните) данные предыдущей версии, прежде чем обновлять UDT следующей версией.
  • Создайте следующую версию UDT (обновите более раннюю версию).
  • Преобразуйте данные исходной версии в данные текущей обновленной версии.

Обратите внимание, что в этом случае все данные предыдущей версии преобразуются в текущую (новую) версию одновременно.

3.2 Класс UDT реализует Externalizable

Класс UDT реализует Externalizable (вместо Serializable ) и использует в нем функциональность преобразования данных — следующие шаги обзора:

  • UDT реализует Externalizable .
  • Создайте и используйте UDT с начальной версией.
  • Никаких действий не требуется перед обновлением UDT следующей версией.
  • Создайте следующую версию UDT (обновите более раннюю версию), со встроенными функциями преобразования данных.

В этом случае данные предыдущей версии преобразуются в текущую (новую) версию всякий раз, когда данные запрашиваются или обновляются. Информация о версии включена в класс UDT.

Ниже приведен пример с подробностями и кодом для класса UDT.

  1. Создайте класс UDT с версией 1: Testtype2.java (см. Код по адресу: 4.3Testtype2.java (версия 1) )
  2. Compile
  3. Создайте UDT в базе данных: type2
  4. Создайте таблицу со столбцом UDT: test_table3
  5. Вставьте данные в столбец UDT
  6. Запрос данных UDT
  7. Обновите класс UDT с версией 2: Testtype2.java (см. Код по адресу: 4.3 Testtype2.java (версия 2) )
  8. Compile
  9. Вставьте (версия 2) данные в столбец UDT
  10. Запрос данных UDT — данные версии 1 и версии 2

НОТА

Пример в этом разделе не показывает подробности или код команд и функций SQL, используемых для вставки и запроса данных UDT. Они могут быть похожи на пример, показанный ранее в этом посте.

4. Пример кода

  • 4.1. TestType1.java
  • 4.2. Example_Fn1 — Функция
  • 4.3.Testtype2.java (версия 1)
  • 4.4.Testtype2.java (версия 2)

4.1.TestType1.java

01
02
03
04
05
06
07
08
09
10
11
12
13
public class TestType1
        implements java.io.Serializable {
    private String value = "DEFAULT";
    public TestType1() {
    }
    public TestType1(String s) {
        value = s;
    }
    @Override
    public String toString() {
        return value;
    }
} // class

4.2.Example_Fn1 — Функция

Это функция для вставки данных в столбец UDT, упомянутая в примере в 2.2. Используйте UDT .

  • (i) Создать класс Java с открытым статическим методом с функциональностью функции.
  • (ii) Создайте функцию в базе данных с помощью команды CREATE FUNCTION.

Ниже приведены класс Java для функции и команда CREATE FUNCTION. Класс Java компилируется, и команда CREATE FUNCTION запускается с использованием инструмента ij в интерактивном режиме.

1
2
3
4
5
public class FunctionClass {
    public static TestType1 FnMethod1(String s) {
        return new TestType1(s);
    }
}
1
2
3
4
5
6
CREATE FUNCTION Example_Fn1(VARCHAR(25))
RETURNS type1
LANGUAGE JAVA
PARAMETER STYLE JAVA
NO SQL
EXTERNAL NAME 'FunctionClass.FnMethod1';

НОТА

Команды SQL SHOW FUNCTIONS и DROP FUNCTION используются из ij для проверки созданной функции и ее удаления из базы данных соответственно.

4.3.Testtype2.java (версия 1)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import java.io.*;
public class Testtype2
        implements Externalizable {
    private static final long serialVersionUID = 1L;
    private static final int FIRST_VERSION = 1; // initial version id
    private String value = "DEFAULT";
    public Testtype2() {
    }
    public Testtype2(String s) {
        value = s;
    }
    @Override
    public void writeExternal(ObjectOutput out)
            throws IOException {
        // first write the version id
out.writeInt(FIRST_VERSION);
        // next write the state
out.writeObject(value);
    }
    @Override
    public void readExternal(ObjectInput in)
            throws IOException, ClassNotFoundException {
        // read the version id
int version = in.readInt();
        if (version < FIRST_VERSION) {  
            throw new IOException("Corrupt data stream (no such version).");
        }
        if (version > FIRST_VERSION) {
            throw new IOException("Can't deserialize from the future versions.");
        }
        // read object (state)
        value = (String) in.readObject() + "_V" + version;
    } // readExternal()
    @Override
    public String toString() {
        return value;
    }
} // version 1 class

4.4.Testtype2.java (версия 2)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import java.io.*;
public class Testtype2
        implements Externalizable {
    private static final long serialVersionUID = 1L;
    private static final int FIRST_VERSION = 1; // initial version id
    private static final int NEW_VERSION = 2;
    private String value = "DEFAULT";
    private double newData;
    public Testtype2() {
    }
    public Testtype2(String s, double i) {
        value = s;
newData = i;
    }
    @Override
    public void writeExternal(ObjectOutput out)
            throws IOException {
        // first write the version id
out.writeInt(NEW_VERSION);
        // next write the state
out.writeObject(value);
out.writeDouble(newData);
    }
    @Override
    public void readExternal(ObjectInput in)
            throws IOException, ClassNotFoundException {
        if (version < FIRST_VERSION) {  
            throw new IOException("Corrupt data stream (no such version).");
        }
        if (version > NEW_VERSION) {
            throw new IOException("Can't deserialize from the future versions.");
        }
        // read object
        value = (String) in.readObject() + "_V" + version;
        // read new version's data
if (version == NEW_VERSION) {
newData = in.readDouble();
        }
        else { // if FIRST_VERSION
            // newData is its default value, 0
        }
    } // readExternal()
    @Override
    public String toString() {
        return value + ":" + newData;
    }
} // version 2 class

НОТА

  1. Метод readExternal () от Externalizable должен читать значения в той же последовательности и с теми же типами, которые были записаны методом writeExternal () .
  2. В приведенном выше примере кода переменная serialVersionUID является необязательной.

5. Примечания и ссылки

  • Пример использования в приложении текстового редактора Java Swing: текстовый редактор с графическим интерфейсом создает текстовый документ как экземпляр класса java.swing.text.PlainDocument . Класс Java UDT создается с содержимым, таким как экземпляр PlainDocument , имя документа, дата создания и т. Д., И используется в приложении для хранения данных.
  • База данных Oracle 10g поддерживает создание и использование UDT на основе Java; они называются типами SQLJ . Классы Java, представляющие UDT, реализуют интерфейс java.sql.SQLData или oracle.sql.ORAData , а не java.io.Serializable . Эти UDT создаются с помощью оператора SQL CREATE TYPE, хранятся на сервере и доступны через SQL.
  • Ссылка на Apache Derby> Документация (руководства по 10.8): http://db.apache.org/derby/manuals/index.html