Read an ARRAY from a STRUCT returned by a stored procedure

后端 未结 4 1866
臣服心动
臣服心动 2021-02-09 13:35

In the database are three Oracle custom types (simplified) as follows:

create or replace TYPE T_ENCLOSURE AS OBJECT(
  ENCLOSURE_ID      NUMBER(32,0),
  ENCLOSUR         


        
4条回答
  •  渐次进展
    2021-02-09 14:19

    As long as a Oracle specific solution is sufficient, the key lies within the DTOs. All of them have to implement ORAData and ORADataFactory

    public class TAnimal implements ORAData, ORADataFactory {
        Integer animal_id, number_of_hairs;
    
        public TAnimal() { }
    
        // [ Getter and Setter omitted here ]
    
        @Override
        public Datum toDatum(Connection connection) throws SQLException {
            OracleConnection oracleConnection = connection.unwrap(OracleConnection.class);
            StructDescriptor structDescriptor = StructDescriptor.createDescriptor("zoo_schema.T_ANIMAL", oracleConnection);
            Object[] attributes = {
                    this.animal_id,
                    this.number_of_hairs
            };
            return new STRUCT(structDescriptor, oracleConnection, attributes);
        }
    
        @Override
        public TAnimal create(Datum datum, int sqlTypeCode) throws SQLException {
            if (datum == null) {
                return null;
            }
            Datum[] attributes = ((STRUCT) datum).getOracleAttributes();
            TAnimal result = new TAnimal();
            result.animal_id = asInteger(attributes[0]); // see TEnclosure#asInteger(Datum)
            result.number_of_hairs = asInteger(attributes[1]); // see TEnclosure#asInteger(Datum)
            return result;
        }
    
    }
    

    and

    public class TEnclosure implements ORAData, ORADataFactory {
    
        Integer enclosureId;
        String enclosureName;
        List animals;
    
        public TEnclosure() {
            this.animals = new ArrayList<>();
        }
    
        // [ Getter and Setter omitted here ]
    
        @Override
        public Datum toDatum(Connection connection) throws SQLException {
            OracleConnection oracleConnection = connection.unwrap(OracleConnection.class);
            StructDescriptor structDescriptor = StructDescriptor.createDescriptor("zoo_schema.T_ENCLOSURE", oracleConnection);
            Object[] attributes = {
                    this.enclosureId,
                    this.enclosureName,
                    null // TODO: solve this; however, retrieving data works without this
            };
            return new STRUCT(structDescriptor, oracleConnection, attributes);
        }
    
        @Override
        public TEnclosure create(Datum datum, int sqlTypeCode) throws SQLException {
            if (datum == null) {
                return null;
            }
            Datum[] attributes = ((STRUCT) datum).getOracleAttributes();
            TEnclosure result = new TEnclosure();
            result.enclosureId = asInteger(attributes[0]);
            result.enclosureName = asString(attributes[1]);
            result.animals = asListOfAnimals(attributes[2]);
            return result;
        }
    
        // Utility methods
    
        Integer asInteger(Datum datum) throws SQLException {
            if (datum == null)
                return null;
            else
                return ((NUMBER) datum).intValue(); // oracle.sql.NUMBER
        }
    
        String asString(Datum datum) throws SQLException {
            if (datum = null)
                return null;
            else
                return ((CHAR) datum).getString(); // oracle.sql.CHAR
        }
    
        List asListOfAnimals(Datum datum) throws SQLException {
            if (datum == null)
                return null;
            else {
                TAnimal factory = new TAnimal();
    
                List result = new ArrayList<>();
    
                ARRAY array = (ARRAY) datum; // oracle.sql.ARRAY
                Datum[] elements = array.getOracleArray();
                for (int i = 0; i < elements.length; i++) {
                    result.add(factory.create(elements[i], 0));
                }
                return result;
            }
        }
    }
    

    then fetching the data works like so:

        TEnclosure factory = new TEnclosure();
    
        Connection connection = null;
        OracleConnection oracleConnection = null;
        OracleCallableStatement oracleCallableStatement = null;
    
        try {
            connection = MyConnectionFactory.getConnection(SOME_CONNECTION_CONFIG);
            oracleConnection = connection.unwrap(OracleConnection.class);
            oracleCallableStatement = (OracleCallableStatement) oracleConnection.prepareCall("{? = call zoo_schema.zoo_utils.GET_ENCLOSURE( ? )}");
    
            oracleCallableStatement.registerOutParameter(1, OracleTypes.STRUCT, "zoo_schema.T_ENCLOSURE");
            oracleCallableStatement.setInt(2, 6);  // fetch data for ENCLOSURE#6
    
            // Execute query
            oracleCallableStatement.executeQuery();
    
            // Check result
            Object oraData = oracleCallableStatement.getORAData(1, factory);
            LOGGER.info("oraData is a {}", oraData.getClass().getName()); // acme.zoo.TEnclosure
    
        } finally {
            ResourceUtils.closeQuietly(oracleCallableStatement);
            ResourceUtils.closeQuietly(oracleConnection);
            ResourceUtils.closeQuietly(connection); // probably not necessary...
        }
    

提交回复
热议问题