sql >> Database teknologi >  >> RDS >> Oracle

Læs en ARRAY fra en STRUKT returneret af en lagret procedure

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.



  1. SQL Server 2016:Opret en tabel fra et SQL-script

  2. Få databaseskema med én forespørgsel?

  3. Hvordan current_timestamp() virker i PostgreSQL

  4. Gendan tablespace BRUGERE virker ikke. Oracle backup