Opret objekter, der implementerer java.sql.SQLData
. I dette scenarie skal du oprette TEnclosure
og TAnimal
klasser, som begge implementerer SQLData
.
Bare FYI, i nyere Oracle JDBC-versioner, typer såsom oracle .sql.ARRAY
er forældet til fordel for java.sql
typer. Selvom jeg ikke er sikker på, hvordan man skriver et array (beskrevet nedenfor) ved kun at bruge java.sql
API.
Når du implementerer readSQL()
du læser felterne i rækkefølge. Du får en java.sql.Array
med sqlInput.readArray()
. Så TEnclosure.readSQL()
ville se sådan ud.
@Override
public void readSQL(SQLInput sqlInput, String s) throws SQLException {
id = sqlInput.readBigDecimal();
name = sqlInput.readString();
Array animals = sqlInput.readArray();
// what to do here...
}
Bemærk:readInt()
eksisterer også, men Oracle JDBC ser ud til altid at give BigDecimal
for NUMBER
Du vil bemærke, at nogle API'er såsom java.sql.Array
har metoder, der tager et typekort Map<String, Class<?>>
Dette er en mapping af Oracle-typenavne til deres tilsvarende Java-klasse, der implementerer SQLData
(ORAData
virker måske også?).
Hvis du bare kalder Array.getArray()
, får du Struct
objekter, medmindre JDBC-driveren kender til dine typetilknytninger via Connection.setTypeMap(typeMap)
. Indstilling af typeMap på forbindelsen virkede dog ikke for mig, så jeg bruger getArray(typeMap)
Opret dit Map<String, Class<?>> typeMap
et sted, og tilføj indgange til dine typer:
typeMap.put("T_ENCLOSURE", TEnclosure.class);
typeMap.put("T_ANIMAL", TAnimal.class);
Inden for en SQLData.readSQL()
implementering, kald sqlInput.readArray().getArray(typeMap)
, som returnerer Object[]
hvor Object
indgange eller af typen TAnimal
.
Selvfølgelig koden til at konvertere til en List<TAnimal>
bliver kedeligt, så brug bare denne hjælpefunktion og juster den til dine behov for så vidt angår nul vs tom liste politik:
/**
* 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;
}
Writing Arrays
Det var frustrerende at finde ud af, hvordan man skriver et array, Oracle API'er kræver en forbindelse for at oprette et array, men du har ikke en åbenlys forbindelse i forbindelse med writeSQL(SQLOutput sqlOutput)
. Heldigvis denne blog
har et trick/hack for at få OracleConnection
, som jeg har brugt her.
Når du opretter et array med createOracleArray()
du angiver listetypen (T_ARRAY_ANIMALS
) for typenavnet, IKKE singularobjekttypen.
Her er en generisk funktion til at skrive arrays. I dit tilfælde listType
ville være "T_ARRAY_ANIMALS"
og du ville sende List<TAnimal>
ind
/**
* 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);
}
Bemærkninger:
- På et tidspunkt tænkte jeg
setTypeMap
var påkrævet, men nu når jeg fjerner den linje virker min kode stadig, så jeg er ikke sikker på, om det er nødvendigt. - Jeg er ikke sikker på, om du skal skrive null eller et tomt array, men jeg antog, at det tomme array er mere korrekt.
Tips om Oracle-typer
- Oracle skriver alt med store bogstaver, så alle typenavne skal være store bogstaver.
- Du skal muligvis angive
SCHEMA.TYPE_NAME
hvis typen ikke er i dit standardskema. - Husk at
grant execute
på typer, hvis brugeren du forbinder med ikke er ejeren.
Hvis du har eksekveret på pakken, men ikke typen,getArray()
vil kaste en undtagelse, når den forsøger at lede efter type metadata.
Forår
For udviklere, der bruger Forår , kan du se på Spring Data JDBC Extensions
, som giver SqlArrayValue
og SqlReturnArray
, som er nyttige til at oprette et SimpleJdbcCall
for en procedure, der tager et array som et argument eller returnerer et array.
Kapitel 7.2.1 Indstilling af ARRAY-værdier ved hjælp af SqlArrayValue for en IN-parameter forklarer, hvordan man kalder procedurer med array-parametre.