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
setTypeMapbyl 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_NAMEpokud typ není ve vašem výchozím schématu. - Nezapomeňte
grant executeu 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.