sql >> Databáze >  >> RDS >> Oracle

Přečtěte ARRAY ze STRUCT vráceného uloženou procedurou

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.



  1. SQL dotaz:Smazat všechny záznamy z tabulky kromě posledních N?

  2. sqlalchemy + baňka, získávání všech příspěvků za den

  3. Jak vložit javascript do databáze mysql?

  4. Spočítat všechny záznamy, které neexistují, do jiné tabulky - SQL Query