Skapa objekt som implementerar java.sql.SQLData
. I det här scenariot skapar du TEnclosure
och TAnimal
klasser, som båda implementerar SQLData
.
Bara för att veta, i nyare Oracle JDBC-versioner, typer som oracle .sql.ARRAY
är utfasade till förmån för java.sql
typer. Även om jag inte är säker på hur man skriver en array (beskrivs nedan) med bara java.sql
API.
När du implementerar readSQL()
du läser fälten i ordning. Du får en java.sql.Array
med sqlInput.readArray()
. Så TEnclosure.readSQL()
skulle se ut ungefär så här.
@Override
public void readSQL(SQLInput sqlInput, String s) throws SQLException {
id = sqlInput.readBigDecimal();
name = sqlInput.readString();
Array animals = sqlInput.readArray();
// what to do here...
}
Obs:readInt()
finns också, men Oracle JDBC verkar alltid tillhandahålla BigDecimal
för NUMBER
Du kommer att märka att vissa API:er som java.sql.Array
har metoder som tar en typkarta Map<String, Class<?>>
Detta är en mappning av Oracle-typnamn till deras motsvarande Java-klass som implementerar SQLData
(ORAData
kanske fungerar också?).
Om du bara anropar Array.getArray()
, får du Struct
objekt om inte JDBC-drivrutinen känner till dina typmappningar via Connection.setTypeMap(typeMap)
. Men att ställa in typeMap på anslutningen fungerade inte för mig, så jag använder getArray(typeMap)
Skapa din Map<String, Class<?>> typeMap
någonstans och lägg till poster för dina typer:
typeMap.put("T_ENCLOSURE", TEnclosure.class);
typeMap.put("T_ANIMAL", TAnimal.class);
Inom en SQLData.readSQL()
implementering, anrop sqlInput.readArray().getArray(typeMap)
, som returnerar Object[]
där Object
poster eller av typen TAnimal
.
Självklart koden som ska konverteras till en List<TAnimal>
blir tråkigt, så använd bara den här verktygsfunktionen och justera den efter dina behov så långt som policy för noll vs tom list:
/**
* 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;
}
Skrivmatriser
Att ta reda på hur man skriver en array var frustrerande, Oracle API:er kräver en anslutning för att skapa en array, men du har inte en uppenbar anslutning i sammanhanget writeSQL(SQLOutput sqlOutput)
. Lyckligtvis den här bloggen
har ett trick/hack för att få OracleConnection
, som jag har använt här.
När du skapar en array med createOracleArray()
du anger listtypen (T_ARRAY_ANIMALS
) för typnamnet, INTE singularobjekttypen.
Här är en generisk funktion för att skriva arrayer. I ditt fall, listType
skulle vara "T_ARRAY_ANIMALS"
och du skulle skicka 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);
}
Anmärkningar:
- Vid ett tillfälle tänkte jag
setTypeMap
krävdes, men nu när jag tar bort den raden fungerar min kod fortfarande, så jag är inte säker på om det är nödvändigt. - Jag är inte säker på om du ska skriva null eller en tom array, men jag antog att den tomma arrayen är mer korrekt.
Tips om Oracle-typer
- Oracle har allt med versaler, så alla typnamn bör vara versaler.
- Du kan behöva ange
SCHEMA.TYPE_NAME
om typen inte finns i ditt standardschema. - Kom ihåg att
grant execute
på typer om användaren du ansluter till inte är ägaren.
Om du har kört på paketet, men inte typen,getArray()
ger ett undantag när den försöker leta efter metadata av typen.
Vår
För utvecklare som använder våren , du kanske vill titta på Spring Data JDBC Extensions
, som tillhandahåller SqlArrayValue
och SqlReturnArray
, som är användbara för att skapa ett SimpleJdbcCall
för en procedur som tar en array som ett argument eller returnerar en array.
Kapitel 7.2.1 Ställa in ARRAY-värden med SqlArrayValue för en IN-parameter förklarar hur man anropar procedurer med arrayparametrar.