Crea oggetti che implementano java.sql.SQLData
. In questo scenario, crea TEnclosure
e TAnimal
classi, che implementano entrambe SQLData
.
Per tua informazione, nelle versioni più recenti di Oracle JDBC, tipi come oracle .sql.ARRAY
sono deprecati a favore di java.sql
tipi. Anche se non sono sicuro di come scrivere un array (descritto di seguito) usando solo java.sql
API.
Quando implementi readSQL()
leggi i campi in ordine. Ottieni un java.sql.Array
con sqlInput.readArray()
. Quindi TEnclosure.readSQL()
sembrerebbe qualcosa del genere.
@Override
public void readSQL(SQLInput sqlInput, String s) throws SQLException {
id = sqlInput.readBigDecimal();
name = sqlInput.readString();
Array animals = sqlInput.readArray();
// what to do here...
}
Nota:readInt()
esiste anche, ma Oracle JDBC sembra fornire sempre BigDecimal
per NUMBER
Noterai che alcune API come java.sql.Array
hanno metodi che accettano una mappa di tipo Map<String, Class<?>>
Questa è una mappatura dei nomi dei tipi Oracle alla classe Java corrispondente che implementa SQLData
(ORAData
potrebbe funzionare anche tu?).
Se chiami semplicemente Array.getArray()
, otterrai Struct
oggetti a meno che il driver JDBC non sia a conoscenza delle mappature dei tipi tramite Connection.setTypeMap(typeMap)
. Tuttavia, l'impostazione di typeMap sulla connessione non ha funzionato per me, quindi utilizzo getArray(typeMap)
Crea la tua Map<String, Class<?>> typeMap
da qualche parte e aggiungi voci per i tuoi tipi:
typeMap.put("T_ENCLOSURE", TEnclosure.class);
typeMap.put("T_ANIMAL", TAnimal.class);
All'interno di un SQLData.readSQL()
implementazione, chiama sqlInput.readArray().getArray(typeMap)
, che restituisce Object[]
dove l'Object
voci o di tipo TAnimal
.
Ovviamente il codice da convertire in un List<TAnimal>
diventa noioso, quindi usa questa funzione di utilità e adattala alle tue esigenze per quanto riguarda la politica di elenco nullo o vuoto:
/**
* 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;
}
Scrittura di array
Capire come scrivere un array è stato frustrante, le API Oracle richiedono una connessione per creare un array, ma non hai una connessione ovvia nel contesto di writeSQL(SQLOutput sqlOutput)
. Fortunatamente, questo blog
ha un trucco/hack per ottenere OracleConnection
, che ho usato qui.
Quando crei un array con createOracleArray()
specificare il tipo di elenco (T_ARRAY_ANIMALS
) per il nome del tipo, NON il tipo di oggetto singolare.
Ecco una funzione generica per la scrittura di array. Nel tuo caso, listType
sarebbe "T_ARRAY_ANIMALS"
e passeresti in 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);
}
Note:
- Ad un certo punto ho pensato a
setTypeMap
era richiesto, ma ora quando rimuovo quella riga il mio codice funziona ancora, quindi non sono sicuro che sia necessario. - Non sono sicuro se dovresti scrivere null o un array vuoto, ma ho pensato che l'array vuoto sia più corretto.
Suggerimenti sui tipi di Oracle
- Oracle mette tutto in maiuscolo, quindi tutti i nomi dei tipi devono essere maiuscoli.
- Potrebbe essere necessario specificare
SCHEMA.TYPE_NAME
se il tipo non è nello schema predefinito. - Ricordati di
grant execute
sui tipi se l'utente con cui ti stai connettendo non è il proprietario.
Se hai eseguito sul pacchetto, ma non sul tipo,getArray()
genererà un'eccezione quando tenterà di cercare i metadati di tipo.
Primavera
Per gli sviluppatori che utilizzano Spring , potresti voler dare un'occhiata a Spring Data JDBC Extensions
, che fornisce SqlArrayValue
e SqlReturnArray
, utili per creare un SimpleJdbcCall
per una procedura che accetta un array come argomento o restituisce un array.
Capitolo 7.2.1 Impostazione dei valori ARRAY utilizzando SqlArrayValue per un parametro IN spiega come chiamare le procedure con i parametri dell'array.