sql >> Database >  >> RDS >> Oracle

Lees een ARRAY van een STRUCT die is geretourneerd door een opgeslagen procedure

Maak objecten die java.sql.SQLData implementeren . Maak in dit scenario TEnclosure en TAnimal klassen, die beide SQLData . implementeren .

Ter informatie, in nieuwere Oracle JDBC-versies, typen zoals oracle .sql.ARRAY zijn verouderd ten gunste van java.sql soorten. Hoewel ik niet zeker weet hoe ik een array moet schrijven (hieronder beschreven) met alleen java.sql API.

Wanneer u readSQL() implement implementeert je leest de velden in volgorde. U krijgt een java.sql.Array met sqlInput.readArray() . Dus TEnclosure.readSQL() zou er ongeveer zo uitzien.

@Override
public void readSQL(SQLInput sqlInput, String s) throws SQLException {
    id = sqlInput.readBigDecimal();
    name = sqlInput.readString();
    Array animals = sqlInput.readArray();
    // what to do here...
}

Opmerking:readInt() bestaat ook, maar Oracle JDBC lijkt altijd BigDecimal . te bieden voor NUMBER

U zult merken dat sommige API's, zoals java.sql.Array hebben methoden die een type map nemen Map<String, Class<?>> Dit is een toewijzing van Oracle-typenamen aan hun corresponderende Java-klasse die SQLData implementeert (ORAData werkt misschien ook?).

Als je gewoon Array.getArray() . aanroept , je krijgt Struct objecten tenzij het JDBC-stuurprogramma op de hoogte is van uw typetoewijzingen via Connection.setTypeMap(typeMap) . Het instellen van typeMap op de verbinding werkte echter niet voor mij, dus ik gebruik getArray(typeMap)

Maak je Map<String, Class<?>> typeMap ergens en voeg vermeldingen toe voor uw typen:

typeMap.put("T_ENCLOSURE", TEnclosure.class);
typeMap.put("T_ANIMAL", TAnimal.class);

Binnen een SQLData.readSQL() implementatie, bel sqlInput.readArray().getArray(typeMap) , die Object[] . retourneert waar het Object vermeldingen of van het type TAnimal .

Natuurlijk de code om te converteren naar een List<TAnimal> wordt vervelend, dus gebruik gewoon deze hulpprogramma-functie en pas deze aan uw behoeften aan voor zover het beleid voor null versus lege lijsten betreft:

/**
 * 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;
}

Arrays schrijven

Uitzoeken hoe een array te schrijven was frustrerend, Oracle API's vereisen een verbinding om een ​​array te maken, maar je hebt geen duidelijke verbinding in de context van writeSQL(SQLOutput sqlOutput) . Gelukkig, deze blog heeft een truc/hack om de OracleConnection . te krijgen , die ik hier heb gebruikt.

Wanneer u een array maakt met createOracleArray() u het lijsttype specificeert (T_ARRAY_ANIMALS ) voor de typenaam, NIET het enkelvoudige objecttype.

Hier is een algemene functie voor het schrijven van arrays. In uw geval listType zou zijn "T_ARRAY_ANIMALS" en je zou doorgeven 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);
}

Opmerkingen:

  • Op een gegeven moment dacht ik setTypeMap was vereist, maar als ik die regel nu verwijder, werkt mijn code nog steeds, dus ik weet niet zeker of het nodig is.
  • Ik weet niet zeker of je null of een lege array moet schrijven, maar ik ging ervan uit dat de lege array correcter is.

Tips voor Oracle-typen

  • Oracle zet alles in hoofdletters, dus alle typenamen moeten in hoofdletters zijn.
  • Mogelijk moet u SCHEMA.TYPE_NAME opgeven als het type niet in uw standaardschema staat.
  • Vergeet niet om grant execute op typen als de gebruiker waarmee u verbinding maakt niet de eigenaar is.
    Als u het pakket uitvoert, maar niet het type, getArray() zal een uitzondering genereren wanneer het probeert te zoeken naar metadata van het type.

Lente

Voor ontwikkelaars die Spring gebruiken , wilt u misschien kijken naar Spring Data JDBC Extensions , die SqlArrayValue . biedt en SqlReturnArray , die handig zijn voor het maken van een SimpleJdbcCall voor een procedure die een array als argument neemt of een array retourneert.

Hoofdstuk 7.2.1 ARRAY-waarden instellen met SqlArrayValue voor een IN-parameter legt uit hoe procedures aangeroepen kunnen worden met array parameters.



  1. SUM met een spil om de algemene score te berekenen

  2. Hoe kan ik de tijdzoneregio ophalen van SQLPLUS?

  3. Sla tabellen over in mysqldump op basis van een patroon

  4. SQLSTATE[HY000] [2005] Onbekende MySQL-serverhost 'mysql1.alwaysdata.com:3306' (2)