Oracle
 sql >> Baza danych >  >> RDS >> Oracle

Odczytaj ARRAY ze STRUCT zwróconego przez procedurę składowaną

Twórz obiekty, które implementują java.sql.SQLData . W tym scenariuszu utwórz TEnclosure i TAnimal klasy, które implementują SQLData .

Tylko FYI, w nowszych wersjach Oracle JDBC, typy takie jak oracle .sql.ARRAY są przestarzałe na rzecz java.sql typy. Chociaż nie jestem pewien, jak napisać tablicę (opisaną poniżej) używając tylko java.sql API.

Kiedy implementujesz readSQL() czytasz pola w kolejności. Otrzymujesz java.sql.Array z sqlInput.readArray() . Więc TEnclosure.readSQL() wyglądałby mniej więcej tak.

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

Uwaga:readInt() również istnieje, ale Oracle JDBC wydaje się zawsze zapewniać BigDecimal dla NUMBER

Zauważysz, że niektóre API, takie jak java.sql.Array mają metody, które pobierają mapę typów Map<String, Class<?>> Jest to mapowanie nazw typów Oracle do odpowiadającej im klasy Java implementującej SQLData (ORAData może też działać?).

Jeśli po prostu wywołasz Array.getArray() , otrzymasz Struct obiektów, chyba że sterownik JDBC wie o mapowaniach typu poprzez Connection.setTypeMap(typeMap) . Jednak ustawienie typeMap na połączeniu nie działało dla mnie, więc używam getArray(typeMap)

Utwórz swój Map<String, Class<?>> typeMap gdzieś i dodaj wpisy dla swoich typów:

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

Wewnątrz SQLData.readSQL() implementacja, wywołaj sqlInput.readArray().getArray(typeMap) , który zwraca Object[] gdzie Object wpisy lub typu TAnimal .

Oczywiście kod do konwersji na List<TAnimal> staje się nużące, więc po prostu użyj tej funkcji użytkowej i dostosuj ją do swoich potrzeb, jeśli chodzi o zasadę zerowej i pustej listy:

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

Zapisywanie tablic

Ustalenie, jak napisać tablicę, było frustrujące, interfejsy Oracle API wymagają połączenia do utworzenia tablicy, ale nie masz oczywistego połączenia w kontekście writeSQL(SQLOutput sqlOutput) . Na szczęście ten blog ma sztuczkę / hack, aby uzyskać OracleConnection , z którego tutaj korzystałem.

Kiedy tworzysz tablicę za pomocą createOracleArray() określasz typ listy (T_ARRAY_ANIMALS ) dla nazwy typu, a NIE pojedynczego typu obiektu.

Oto ogólna funkcja do pisania tablic. W Twoim przypadku listType byłoby "T_ARRAY_ANIMALS" i możesz przekazać w 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);
}

Uwagi:

  • W pewnym momencie pomyślałem, że setTypeMap było wymagane, ale teraz, gdy usunę tę linię, mój kod nadal działa, więc nie jestem pewien, czy jest to konieczne.
  • Nie jestem pewien, czy należy pisać tablicę null, czy pustą, ale założyłem, że pusta tablica jest bardziej poprawna.

Wskazówki dotyczące typów Oracle

  • Oracle wszystko wielkimi literami, więc wszystkie nazwy typów powinny być pisane wielkimi literami.
  • Może być konieczne określenie SCHEMA.TYPE_NAME jeśli typ nie znajduje się w domyślnym schemacie.
  • Pamiętaj o grant execute na typach, jeśli użytkownik, z którym się łączysz, nie jest właścicielem.
    Jeśli wykonałeś na pakiecie, ale nie na typie, getArray() wyrzuci wyjątek, gdy spróbuje wyszukać metadane typu.

Wiosna

Dla programistów używających Wiosny , możesz spojrzeć na Spring Data JDBC Extensions , który zapewnia SqlArrayValue i SqlReturnArray , które są przydatne do tworzenia SimpleJdbcCall dla procedury, która przyjmuje tablicę jako argument lub zwraca tablicę.

Rozdział 7.2.1 Ustawianie wartości ARRAY przy użyciu SqlArrayValue dla parametru IN wyjaśnia, jak wywoływać procedury z parametrami tablicowymi.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Definiowanie zestawu znaków dla kolumny Dla tabel bazy danych Oracle

  2. Komunikat o błędzie programu sqldeveloper:Karta sieciowa nie może nawiązać błędu połączenia

  3. Utwórz sekwencję za pomocą START WITH z zapytania

  4. ORA-29278:Przejściowy błąd SMTP:usługa niedostępna podczas uruchamiania UTL_MAIL

  5. Jak wydobyć dane z systemu SAP ABAP?