sql >> Databasteknik >  >> RDS >> Oracle

Läs en ARRAY från en STRUKT som returneras av en lagrad procedur

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.



  1. Oracle jämför två olika datum

  2. MySQL - Välj med COUNT som returnerar en NULL-rad

  3. Duplicera rader i en primärnyckeltabell.

  4. Ställa in och identifiera radmål i genomförandeplaner