Създайте обекти, които имплементират 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 параметър обяснява как да извиквате процедури с параметри на масив.