Załóżmy, że chcemy umieścić w bazie danych Derby i odczytać z bazy danych Derby dowolny liczbowo podzbiór zbioru ‘stringów’, zakładając, że każdy ‘string’ jest elementem pewnego znanego zbioru o ściśle określonej liczbie elementów. np. Wybrać 5 imion z kalendarza imion. Kolejność ‘stringów’ w podzbiorze musi być zachowana przy odczycie.
W rozwiązaniu problemu uczestniczą dwie klasy:

  • AriaStringSet – klasa tworząca typ danych
  • R064 – klasa uruchamiająca
AriaStringSet
package aderby.types;

import java.io.*;
import java.util.*;

public class AriaStringSet implements Externalizable {
    private Set totalSet;
    private final LinkedHashSet elemSet;
    private String[] elems;

    public AriaStringSet() {
        totalSet = null;
        elems = null;
        elemSet = new LinkedHashSet<>();
    }

    public AriaStringSet(Set totalSet, String... elems) {
        this.totalSet = totalSet;
        this.elems = elems;
        elemSet = new LinkedHashSet<>();
        elemSet.addAll(Arrays.asList(elems));
        checkElems();
    }

    /**
     * Sprawdza czy wszystkie elementy dodawane naleza do zbioru
     *
     * @throws IllegalArgumentException - jeśli element nie pasuje do zbioru
     */
    public void checkElems() throws IllegalArgumentException {
        if (!totalSet.containsAll(elemSet)) {
            throw new IllegalArgumentException(
                    " co najmniej jeden z elementów nie należy do zbioru");
        }
    }

    public Set getTotalSet() {
        return totalSet;
    }

    public LinkedHashSet getElemSet() {
        return elemSet;
    }

    public String[] getElems() {
        Object[] temp = elemSet.toArray();
        String[] temp1 = new String[temp.length];
        for (int i = 0; i < temp1.length; i++) {
            temp1[i] = (String) temp[i];
        }
        return temp1;
    }

    public void setData(Set totalSet, String... elems) {
        this.totalSet = totalSet;
        this.elems = elems;
        elemSet.clear();
        elemSet.addAll(Arrays.asList(elems));
        checkElems();
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        checkElems();
        out.writeInt(elemSet.size());
        for (String e : elemSet) {
            out.writeUTF(e);
        }
    }

    @Override
    public void readExternal(ObjectInput in)
            throws IOException {
        int len = in.readInt();
        elems = new String[len];
        for (int i = 0; i < len; i++) {
            elems[i] = in.readUTF();
        }
        elemSet.clear();
        elemSet.addAll(Arrays.asList(elems));
    }
}

R064
package aderby.types;

import aderby.DerbyUtil;

import java.sql.*;
import java.util.*;

public class R064 {
    //polecenie SQl tworzące typ 'StringSet'
    public static final String createTypeStringSet = "CREATE TYPE AriaStringSet "
            + "EXTERNAL NAME 'aderby.types.AriaStringSet' LANGUAGE JAVA";
    //polecenie SQL tworzące tabele 'typy' w bazie 'r064'
    //Tabela zawiera 2 kolumny:
    //'id'INTEGER, automatycznie inkrementowana
    //'zestaw' umożliwiający dodanie obiektu klasy StringSet
    public static final String createTable = "CREATE TABLE typy("
            + "id INTEGER NOT NULL PRIMARY KEY GENERATED ALWAYS AS "
            + "IDENTITY(START WITH 1, INCREMENT BY 1), zestaw ARIASTRINGSET)";

    public static void main(String[] args) {
        //uruchomienie Derby
        DerbyUtil.startDerbyEngine("EmbeddedDriver");
        //utworzenie połączenia z bazą danych. Jeżeli baza nie istnieje
        //zostanie utworzona. Jeśli istnieje - zostanie użyta.
        Connection con = DerbyUtil.connectEmbeddedDB("C:/Przyklady/r064",
                ";create=true");
        Statement stat = null;
        try {
            //utworzenie polecenia
            stat = con.createStatement();
            //dodanie polecenia wsadowego tworzącego typ 'StringSet'
            stat.addBatch(createTypeStringSet);
            //dodanie polecenia wsadowego tworzącego tabele 'typy'
            stat.addBatch(createTable);
            //wykonanie poleceń
            stat.executeBatch();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        PreparedStatement stmt = null;
        //polecenie SQL wstawiające dane do tabeli 'typy'
        //'id' jest dodawane automatycznie
        //'zestaw' jest dodawany ręcznie
        String insertSQL = "INSERT INTO typy (id,  zestaw) values (DEFAULT,?)";
        //utworzenie zbioru
        Set zestaw = new TreeSet<>();
        //dodanie elementów, z których można będzie dokonać wyboru
        zestaw.add("AA");
        zestaw.add("BB");
        zestaw.add("CC");
        zestaw.add("DD");
        zestaw.add("EE");
        zestaw.add("FF");
        zestaw.add("GG");
        try {
            //utworzenie polecenia przygotowywanego
            stmt = con.prepareStatement(insertSQL);
            //wstawienie zestawu 3 z 7 dostępnych elementów
            stmt.setObject(1, new AriaStringSet(zestaw, "AA", "CC", "BB"));
            //wykonanie polecenia
            stmt.executeUpdate();
            //usuniecie parametrów, niezbędne przy wstawianiu parametrów
            //w pętli. Tutaj jest zbędne
            stmt.clearParameters();
        } catch (SQLException e1) {
            e1.printStackTrace();
        }
        //polecenie SQl wybierające dane z bazy
        String selectSQL = "SELECT  * FROM typy";
        PreparedStatement pstm = null;
        ResultSet rs = null;
        int id = -1;
        String[] jset = null;
        try {
            //przygotowanie polecenia
            pstm = con.prepareStatement(selectSQL);
            //wykonanie polecenia
            rs = pstm.executeQuery();
            //iteracja po zbiorze wynikowym
            while (rs.next()) {
                //pobranie 'id'
                id = rs.getInt("id");
                //pobranie 'zestawu' jako tablicy elementów
                jset = ((AriaStringSet) rs.getObject("zestaw")).getElems();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        //wydrukowanie danych
        System.out.println("id: " + id);
        DerbyUtil.print(Objects.requireNonNull(jset));
        //zamkniecie poleceń i połączenia
        DerbyUtil.close(rs);
        DerbyUtil.close(stat);
        DerbyUtil.close(stmt);
        DerbyUtil.close(pstm);
        DerbyUtil.close(con);
        //zatrzymanie silnika Derby
        DerbyUtil.shutdownDerbyEngine();
    }
}

Po uruchomieniu klasy na konsoli zobaczymy:

id: 1
[AA, CC, BB]

Pliki do ściągnięcia

Plik R064.zip (klasa)
Plik AriaStringSet.zip (klasa)
Aktualny (tworzony narastająco) plik module-info.java
Aktualny (tworzony narastająco) plik DerbyUtil.java
Pliki tworzone narastająco zastępują poprzednie pliki o tej samej nazwie i działają dla wszystkich wcześniej opublikowanych przykładów we wszystkich wpisach w projekcie. W przypadku pliku module-info.java może być potrzebne skreślenie niepotrzebnych wpisów.