Oracle
 sql >> база данни >  >> RDS >> Oracle

Прочетете ARRAY от STRUCT, върнат от съхранена процедура

Създайте обекти, които имплементират java.sql.SQLData . В този сценарий създайте TEnclosure и TAnimal класове, които и двата имплементират SQLData .

Само за информация, в по-новите версии на Oracle JDBC, типове като oracle .sql.ARRAY са отхвърлени в полза на java.sql видове. Въпреки че не съм сигурен как да напиша масив (описан по-долу), използвайки само java.sql API.

Когато внедрите readSQL() четете полетата по ред. Получавате java.sql.Array с sqlInput.readArray() . Така че TEnclosure.readSQL() ще изглежда нещо подобно.

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

Забележка:readInt() също съществува, но Oracle JDBC изглежда винаги предоставя BigDecimal за NUMBER

Ще забележите, че някои API като java.sql.Array имат методи, които приемат карта на типа Map<String, Class<?>> Това е съпоставяне на имена на типове на Oracle към съответния им Java клас, прилагащ SQLData (ORAData също може да работи?).

Ако просто извикате Array.getArray() , ще получите Struct обекти, освен ако JDBC драйверът знае за съпоставянията на вашите типове чрез Connection.setTypeMap(typeMap) . Задаването на typeMap на връзката обаче не работи за мен, така че използвам getArray(typeMap)

Създайте своя Map<String, Class<?>> typeMap някъде и добавете записи за вашите типове:

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

В рамките на SQLData.readSQL() изпълнение, извикайте sqlInput.readArray().getArray(typeMap) , който връща Object[] където Object записи или от тип TAnimal .

Разбира се кодът за преобразуване в List<TAnimal> става досадно, така че просто използвайте тази помощна функция и я коригирайте за вашите нужди, що се отнася до правилата за нулев срещу празен списък:

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

Записване на масиви

Измислянето как да се напише масив беше разочароващо, API на Oracle изискват връзка за създаване на масив, но нямате очевидна връзка в контекста на writeSQL(SQLOutput sqlOutput) . За щастие, този блог има трик/хак за получаване на OracleConnection , който съм използвал тук.

Когато създавате масив с createOracleArray() Вие определяте типа списък (T_ARRAY_ANIMALS ) за името на типа, НЕ за единичния тип обект.

Ето една обща функция за запис на масиви. Във вашия случай, listType ще бъде "T_ARRAY_ANIMALS" и ще преминете в 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);
}

Бележки:

  • В един момент си помислих setTypeMap беше задължително, но сега, когато премахна този ред, кодът ми все още работи, така че не съм сигурен дали е необходимо.
  • Не съм сигурен дали трябва да напишете null или празен масив, но предположих, че празният масив е по-правилен.

Съвети за типовете Oracle

  • Oracle пише всичко с главни букви, така че всички имена на типове трябва да са с главни букви.
  • Може да се наложи да посочите SCHEMA.TYPE_NAME ако типът не е във вашата схема по подразбиране.
  • Не забравяйте да grant execute върху типове, ако потребителят, с който се свързвате, не е собственик.
    Ако сте изпълнили пакета, но не и типа, getArray() ще хвърли изключение, когато се опита да търси метаданни за типа.

Пролетната

За разработчици, използващи Spring , може да искате да разгледате Spring Data JDBC Extensions , който предоставя SqlArrayValue и SqlReturnArray , които са полезни за създаване на SimpleJdbcCall за процедура, която приема масив като аргумент или връща масив.

Глава 7.2.1 Задаване на стойности на ARRAY с помощта на SqlArrayValue за IN параметър обяснява как да извиквате процедури с параметри на масив.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Свързване на Oracle с Amazon Aurora

  2. Как да съхраня резултата от избора в променлива в процедурата на Oracle

  3. Как да привлечем служителите към техните мениджъри

  4. oci_bind_by_name предотвратява ли безопасно SQL инжектирането?

  5. Oracle SQL връща редове по произволен начин, когато не се използва ред по клауза