Vytvořte objekty, které implementují java.sql.SQLData
. V tomto scénáři vytvořte TEnclosure
a TAnimal
třídy, které obě implementují SQLData
.
Jen pro informaci, v novějších verzích Oracle JDBC typy jako oracle .sql.ARRAY
jsou zastaralé ve prospěch java.sql
typy. I když si nejsem jistý, jak napsat pole (popsáno níže) pouze pomocí java.sql
API.
Když implementujete readSQL()
čtete pole v pořadí. Získáte java.sql.Array
pomocí sqlInput.readArray()
. Takže TEnclosure.readSQL()
vypadalo by to nějak takto.
@Override
public void readSQL(SQLInput sqlInput, String s) throws SQLException {
id = sqlInput.readBigDecimal();
name = sqlInput.readString();
Array animals = sqlInput.readArray();
// what to do here...
}
Poznámka:readInt()
také existuje, ale zdá se, že Oracle JDBC vždy poskytuje BigDecimal
pro NUMBER
Všimnete si, že některá rozhraní API, jako je java.sql.Array
mají metody, které berou mapu typů Map<String, Class<?>>
Toto je mapování názvů typů Oracle na jejich odpovídající třídu Java implementující SQLData
(ORAData
může fungovat také?).
Pokud zavoláte Array.getArray()
, získáte Struct
objektů, pokud ovladač JDBC neví o vašem mapování typů prostřednictvím Connection.setTypeMap(typeMap)
. Nastavení typeMap na připojení mi však nefungovalo, takže používám getArray(typeMap)
Vytvořte svou Map<String, Class<?>> typeMap
někde a přidejte položky pro vaše typy:
typeMap.put("T_ENCLOSURE", TEnclosure.class);
typeMap.put("T_ANIMAL", TAnimal.class);
V rámci SQLData.readSQL()
implementace, zavolejte sqlInput.readArray().getArray(typeMap)
, která vrátí Object[]
kde Object
záznamů nebo typu TAnimal
.
Samozřejmě kód převést na List<TAnimal>
je to únavné, takže použijte tuto funkci nástroje a upravte ji pro své potřeby, pokud jde o zásadu null vs. prázdný seznam:
/**
* 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;
}
Pole zápisu
Zjišťování, jak napsat pole, bylo frustrující, rozhraní Oracle API vyžadují připojení k vytvoření pole, ale nemáte zřejmé připojení v kontextu writeSQL(SQLOutput sqlOutput)
. Naštěstí tento blog
má trik/hack, jak získat OracleConnection
, který jsem použil zde.
Když vytvoříte pole pomocí createOracleArray()
určíte typ seznamu (T_ARRAY_ANIMALS
) pro název typu, NE singulární typ objektu.
Zde je obecná funkce pro psaní polí. Ve vašem případě listType
by bylo "T_ARRAY_ANIMALS"
a předali byste 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);
}
Poznámky:
- V jednu chvíli mě napadlo
setTypeMap
byl vyžadován, ale nyní, když tento řádek odstraním, můj kód stále funguje, takže si nejsem jistý, zda je to nutné. - Nejsem si jistý, jestli byste měli napsat null nebo prázdné pole, ale předpokládal jsem, že prázdné pole je správnější.
Tipy k typům Oracle
- Oracle vše píše velkými písmeny, takže všechny názvy typů by měly být velká.
- Možná bude nutné zadat
SCHEMA.TYPE_NAME
pokud typ není ve vašem výchozím schématu. - Nezapomeňte
grant execute
u typů, pokud uživatel, se kterým se připojujete, není vlastníkem.
Pokud jste spustili balíček, ale ne typ,getArray()
vyvolá výjimku, když se pokusí vyhledat metadata typu.
Jaro
Pro vývojáře používající Jaro , možná se budete chtít podívat na Spring Data JDBC Extensions
, který poskytuje SqlArrayValue
a SqlReturnArray
, které jsou užitečné pro vytvoření SimpleJdbcCall
pro proceduru, která bere pole jako argument nebo vrací pole.
Kapitola 7.2.1 Nastavení hodnot ARRAY pomocí SqlArrayValue pro parametr IN vysvětluje, jak volat procedury s parametry pole.