Twórz obiekty, które implementują java.sql.SQLData
. W tym scenariuszu utwórz TEnclosure
i TAnimal
klasy, które implementują SQLData
.
Tylko FYI, w nowszych wersjach Oracle JDBC, typy takie jak oracle .sql.ARRAY
są przestarzałe na rzecz java.sql
typy. Chociaż nie jestem pewien, jak napisać tablicę (opisaną poniżej) używając tylko java.sql
API.
Kiedy implementujesz readSQL()
czytasz pola w kolejności. Otrzymujesz java.sql.Array
z sqlInput.readArray()
. Więc TEnclosure.readSQL()
wyglądałby mniej więcej tak.
@Override
public void readSQL(SQLInput sqlInput, String s) throws SQLException {
id = sqlInput.readBigDecimal();
name = sqlInput.readString();
Array animals = sqlInput.readArray();
// what to do here...
}
Uwaga:readInt()
również istnieje, ale Oracle JDBC wydaje się zawsze zapewniać BigDecimal
dla NUMBER
Zauważysz, że niektóre API, takie jak java.sql.Array
mają metody, które pobierają mapę typów Map<String, Class<?>>
Jest to mapowanie nazw typów Oracle do odpowiadającej im klasy Java implementującej SQLData
(ORAData
może też działać?).
Jeśli po prostu wywołasz Array.getArray()
, otrzymasz Struct
obiektów, chyba że sterownik JDBC wie o mapowaniach typu poprzez Connection.setTypeMap(typeMap)
. Jednak ustawienie typeMap na połączeniu nie działało dla mnie, więc używam getArray(typeMap)
Utwórz swój Map<String, Class<?>> typeMap
gdzieś i dodaj wpisy dla swoich typów:
typeMap.put("T_ENCLOSURE", TEnclosure.class);
typeMap.put("T_ANIMAL", TAnimal.class);
Wewnątrz SQLData.readSQL()
implementacja, wywołaj sqlInput.readArray().getArray(typeMap)
, który zwraca Object[]
gdzie Object
wpisy lub typu TAnimal
.
Oczywiście kod do konwersji na List<TAnimal>
staje się nużące, więc po prostu użyj tej funkcji użytkowej i dostosuj ją do swoich potrzeb, jeśli chodzi o zasadę zerowej i pustej listy:
/**
* Constructs a list from the given SQL Array
* Note: this needs to be static because it's called from SQLData classes.
*
* @param <T> SQLData implementing class
* @param array Array containing objects of type T
* @param typeClass Class reference used to cast T type
* @return List<T> (empty if array=null)
* @throws SQLException
*/
public static <T> List<T> listFromArray(Array array, Class<T> typeClass) throws SQLException {
if (array == null) {
return Collections.emptyList();
}
// Java does not allow casting Object[] to T[]
final Object[] objectArray = (Object[]) array.getArray(getTypeMap());
List<T> list = new ArrayList<>(objectArray.length);
for (Object o : objectArray) {
list.add(typeClass.cast(o));
}
return list;
}
Zapisywanie tablic
Ustalenie, jak napisać tablicę, było frustrujące, interfejsy Oracle API wymagają połączenia do utworzenia tablicy, ale nie masz oczywistego połączenia w kontekście writeSQL(SQLOutput sqlOutput)
. Na szczęście ten blog
ma sztuczkę / hack, aby uzyskać OracleConnection
, z którego tutaj korzystałem.
Kiedy tworzysz tablicę za pomocą createOracleArray()
określasz typ listy (T_ARRAY_ANIMALS
) dla nazwy typu, a NIE pojedynczego typu obiektu.
Oto ogólna funkcja do pisania tablic. W Twoim przypadku listType
byłoby "T_ARRAY_ANIMALS"
i możesz przekazać w List<TAnimal>
/**
* Write the list out as an Array
*
* @param sqlOutput SQLOutput to write array to
* @param listType array type name (table of type)
* @param list List of objects to write as an array
* @param <T> Class implementing SQLData that corresponds to the type listType is a list of.
* @throws SQLException
* @throws ClassCastException if SQLOutput is not an OracleSQLOutput
*/
public static <T> void writeArrayFromList(SQLOutput sqlOutput, String listType, @Nullable List<T> list) throws SQLException {
final OracleSQLOutput out = (OracleSQLOutput) sqlOutput;
OracleConnection conn = (OracleConnection) out.getSTRUCT().getJavaSqlConnection();
conn.setTypeMap(getTypeMap()); // not needed?
if (list == null) {
list = Collections.emptyList();
}
final Array array = conn.createOracleArray(listType, list.toArray());
out.writeArray(array);
}
Uwagi:
- W pewnym momencie pomyślałem, że
setTypeMap
było wymagane, ale teraz, gdy usunę tę linię, mój kod nadal działa, więc nie jestem pewien, czy jest to konieczne. - Nie jestem pewien, czy należy pisać tablicę null, czy pustą, ale założyłem, że pusta tablica jest bardziej poprawna.
Wskazówki dotyczące typów Oracle
- Oracle wszystko wielkimi literami, więc wszystkie nazwy typów powinny być pisane wielkimi literami.
- Może być konieczne określenie
SCHEMA.TYPE_NAME
jeśli typ nie znajduje się w domyślnym schemacie. - Pamiętaj o
grant execute
na typach, jeśli użytkownik, z którym się łączysz, nie jest właścicielem.
Jeśli wykonałeś na pakiecie, ale nie na typie,getArray()
wyrzuci wyjątek, gdy spróbuje wyszukać metadane typu.
Wiosna
Dla programistów używających Wiosny , możesz spojrzeć na Spring Data JDBC Extensions
, który zapewnia SqlArrayValue
i SqlReturnArray
, które są przydatne do tworzenia SimpleJdbcCall
dla procedury, która przyjmuje tablicę jako argument lub zwraca tablicę.
Rozdział 7.2.1 Ustawianie wartości ARRAY przy użyciu SqlArrayValue dla parametru IN wyjaśnia, jak wywoływać procedury z parametrami tablicowymi.