Crie objetos que implementam
java.sql.SQLData
. Nesse cenário, crie TEnclosure
e TAnimal
classes, que implementam SQLData
. Apenas para sua informação, nas versões mais recentes do Oracle JDBC, tipos como oracle .sql.ARRAY estão obsoletos em favor de
java.sql
tipos. Embora eu não tenha certeza de como escrever um array (descrito abaixo) usando apenas java.sql
API. Quando você implementa
readSQL()
você lê os campos em ordem. Você obtém um java.sql.Array
com sqlInput.readArray()
. Então TEnclosure.readSQL()
ficaria algo assim. @Override
public void readSQL(SQLInput sqlInput, String s) throws SQLException {
id = sqlInput.readBigDecimal();
name = sqlInput.readString();
Array animals = sqlInput.readArray();
// what to do here...
}
Nota:
readInt()
também existe, mas o Oracle JDBC parece sempre fornecer BigDecimal
para NUMBER
Você notará que algumas APIs como
java.sql.Array
tem métodos que recebem um mapa de tipo Map<String, Class<?>>
Este é um mapeamento de nomes de tipo Oracle para sua classe Java correspondente implementando SQLData
(ORAData
pode funcionar também?). Se você apenas chamar
Array.getArray()
, você obterá Struct
objetos, a menos que o driver JDBC saiba sobre seus mapeamentos de tipo por meio de Connection.setTypeMap(typeMap)
. No entanto, definir typeMap na conexão não funcionou para mim, então eu uso getArray(typeMap)
Crie seu
Map<String, Class<?>> typeMap
em algum lugar e adicione entradas para seus tipos:typeMap.put("T_ENCLOSURE", TEnclosure.class);
typeMap.put("T_ANIMAL", TAnimal.class);
Dentro de um
SQLData.readSQL()
implementação, chame sqlInput.readArray().getArray(typeMap)
, que retorna Object[]
onde o Object
entradas ou do tipo TAnimal
. Claro que o código para converter para um
List<TAnimal>
fica entediante, então apenas use esta função de utilitário e ajuste-a para suas necessidades na medida em que a política de lista nula vs vazia:/**
* 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;
}
Gravando matrizes
Descobrir como escrever uma matriz foi frustrante, as APIs Oracle exigem uma conexão para criar uma matriz, mas você não tem uma conexão óbvia no contexto de
writeSQL(SQLOutput sqlOutput)
. Felizmente, este blogue
tem um truque/hack para obter o OracleConnection
, que usei aqui. Quando você cria um array com
createOracleArray()
você especifica o tipo de lista (T_ARRAY_ANIMALS
) para o nome do tipo, NÃO o tipo de objeto singular. Aqui está uma função genérica para escrever arrays. No seu caso,
listType
seria "T_ARRAY_ANIMALS"
e você passaria 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);
}
Notas:
- A certa altura, pensei em
setTypeMap
era necessário, mas agora, quando removo essa linha, meu código ainda funciona, então não tenho certeza se é necessário. - Não tenho certeza se você deve escrever null ou uma matriz vazia, mas presumi que a matriz vazia é mais correta.
Dicas sobre os tipos Oracle
- O Oracle coloca tudo em maiúsculas, portanto, todos os nomes de tipo devem ser em maiúsculas.
- Talvez seja necessário especificar
SCHEMA.TYPE_NAME
se o tipo não estiver em seu esquema padrão. - Lembre-se de
grant execute
em tipos se o usuário com o qual você está se conectando não for o proprietário.
Se você tiver executado no pacote, mas não no tipo,getArray()
lançará uma exceção quando tentar procurar metadados de tipo.
Primavera
Para desenvolvedores que usam o Spring , você pode dar uma olhada em Spring Data JDBC Extensions , que fornece
SqlArrayValue
e SqlReturnArray
, que são úteis para criar um SimpleJdbcCall
para um procedimento que recebe um array como argumento ou retorna um array. Capítulo 7.2.1 Definindo valores ARRAY usando SqlArrayValue para um parâmetro IN explica como chamar procedimentos com parâmetros de matriz.