Spring JDBC para Oracle Types.

184 views
Skip to first unread message

cesar ricardo guerra arnaiz

unread,
Sep 16, 2012, 12:07:18 AM9/16/12
to Comunidad Spring Peru

Que tal amigos,


Una consulta tengo una duda al momento de manejar SPRING JDBC al momento de llamar a PROCEDURE, en un escenario especifico:
Este escenario es cuando quiero obtener el valor del retorno de un ORACLE TYPE. Ya he antes he manejado conectarme enviando contra un PROCEDURE: VARIABLES NORMALES (Input/Output), Cursores (Input/Output), Oracle Types (Solo INPUT), sin problemas pero el escenario de mandar a un 
PROCEDURE un ORACLE TYPE y que me responda junto con otros 2 VARCHAR un 3er parámetro de salida también de tipo ORACLE TYPE se me está complicando. Aquí el detalle de lo realizado:



-- ******************* TABLA: ******************* --
CREATE TABLE TB_SERV_LLAM(
SLL_ID INTEGER NOT NULL,
SLL_TELEFONO VARCHAR2( 20 ),
SLL_TECNOLOGIA VARCHAR2( 25 ),
SLL_CODIGO VARCHAR2( 10 ),
SLL_MENSAJE VARCHAR2( 100 ),
SLL_PAQUETE VARCHAR2( 25 ),
SLL_PLAN VARCHAR2( 25 ),
SLL_SALDO VARCHAR2( 10 )
)


-- ******************** TYPE: ******************* --
CREATE OR REPLACE TYPE LLAMADAS_TYPE AS OBJECT(
SLL_TELEFONO_IN VARCHAR2( 20 ),
SLL_TECNOLOGIA_IN VARCHAR2( 25 ),
SLL_CODIGO_IN VARCHAR2( 10 ),
SLL_MENSAJE_IN VARCHAR2( 100 ),
SLL_PAQUETE_IN VARCHAR2( 25 ),
SLL_PLAN_IN VARCHAR2( 25 ),
SLL_SALDO_IN VARCHAR2( 10 ),
CONSTRUCTOR FUNCTION LLAMADAS_TYPE RETURN SELF AS RESULT
);

CREATE OR REPLACE TYPE BODY LLAMADAS_TYPE
AS
CONSTRUCTOR FUNCTION LLAMADAS_TYPE RETURN SELF AS RESULT
AS
BEGIN
RETURN;
END;
END;


-- ******************** PROCEDURE (DENTRO DEL PAQUETE): ******************* --

PROCEDURE SP_OBTENER_SERV_LLAM_TYPE( LLAMADAS_TYPE_IN IN RGUERRA.LLAMADAS_TYPE,

ERR_OUT OUT VARCHAR2,
MESSAGE_OUT OUT VARCHAR2,
LLAMADAS_TYPE_OUT OUT RGUERRA.LLAMADAS_TYPE
) AS

LLAMADAS_TYPE_TEMP RGUERRA.LLAMADAS_TYPE;
SLL_CODIGO_AUX VARCHAR2( 20 ) := LLAMADAS_TYPE_IN.SLL_CODIGO_IN;

TEMP_01 VARCHAR2( 20 ) := '';
TEMP_02 VARCHAR2( 20 ) := '';
TEMP_03 VARCHAR2( 20 ) := '';
TEMP_04 VARCHAR2( 20 ) := '';
TEMP_05 VARCHAR2( 20 ) := '';
TEMP_06 VARCHAR2( 20 ) := '';
TEMP_07 VARCHAR2( 20 ) := '';

BEGIN
DBMS_OUTPUT.PUT_LINE( '- SLL_CODIGO_AUX: ' || SLL_CODIGO_AUX );
LLAMADAS_TYPE_TEMP := RGUERRA.LLAMADAS_TYPE(); --INICIALIZACION X CONSTRUCTOR.

IF( SLL_CODIGO_AUX IS NOT NULL ) THEN

SELECT X.SLL_TELEFONO,
X.SLL_TECNOLOGIA,
X.SLL_CODIGO,
X.SLL_MENSAJE,
X.SLL_PAQUETE,
X.SLL_PLAN,
X.SLL_SALDO
INTO TEMP_01,
TEMP_02,
TEMP_03,
TEMP_04,
TEMP_05,
TEMP_06,
TEMP_07
FROM RGUERRA.TB_SERV_LLAM X
WHERE X.SLL_ID = SLL_CODIGO_AUX;

--ASIGNACION DE 'TYPE':
LLAMADAS_TYPE_TEMP.SLL_TELEFONO_IN := TEMP_01;
LLAMADAS_TYPE_TEMP.SLL_TECNOLOGIA_IN := TEMP_02;
LLAMADAS_TYPE_TEMP.SLL_CODIGO_IN := TEMP_03;
LLAMADAS_TYPE_TEMP.SLL_MENSAJE_IN := TEMP_04;
LLAMADAS_TYPE_TEMP.SLL_PAQUETE_IN := TEMP_05;
LLAMADAS_TYPE_TEMP.SLL_PLAN_IN := TEMP_06;
LLAMADAS_TYPE_TEMP.SLL_SALDO_IN := TEMP_07;

LLAMADAS_TYPE_OUT := LLAMADAS_TYPE_TEMP;

--SALIDA:
ERR_OUT := '1';
MESSAGE_OUT := 'PROCESO EXITOSO [OBJETO]';
END IF;

EXCEPTION

WHEN OTHERS THEN
ERR_OUT := TO_CHAR( SQLCODE );
MESSAGE_OUT := SUBSTR( UPPER( SQLERRM ), 1, 100 );

END SP_OBTENER_SERV_LLAM_TYPE;



-- ******************** MÉTODO JAVA: ******************* --
/**
* obtenerServicioLlamadasType
* @param objServicioLlamadasType
* @return DataBaseValidatorBean
*/
@Override
public DataBaseValidatorBean obtenerServicioLlamadasType( final ServicioLlamadasTypeBean objServicioLlamadasType ){
this.logger.info( "*********** [INICIO] - DENTRO: [obtenerServicioLlamadasType - DAO] ***********" );

JdbcTemplate objJdbcTemplate = null;
DataBaseValidatorBean objDataBaseValidatorBean = null;
String OWNER       = null;
String PAQUETE     = null;
String PROCEDURE = null;
long tiempoInicio = System.currentTimeMillis();

try{
this.logger.info( "DATA SOURCE: [" + this.servicioLlamadasJNDI + "]" );
this.servicioLlamadasJNDI.setLoginTimeout( UtilPropertiesMapper.DB_TIMEOUT_CONEXION );

OWNER         = UtilPropertiesMapper.DB_OWNER;
PAQUETE      = UtilPropertiesMapper.DB_PAQUETE_OBTENER_TYPE;
PROCEDURE = UtilPropertiesMapper.DB_PROCEDURE_OBTENER_TYPE;

this.logger.info( "PROCEDURE: [" + OWNER + "." + PAQUETE + "." + PROCEDURE + "]" );

this.objJdbcCall   = new SimpleJdbcCall( this.servicioLlamadasJNDI );
objJdbcTemplate = this.objJdbcCall.getJdbcTemplate();
objJdbcTemplate.setQueryTimeout( UtilPropertiesMapper.DB_TIMEOUT_EXECUTION );

this.objJdbcCall.withSchemaName( OWNER );
this.objJdbcCall.withCatalogName( PAQUETE );
this.objJdbcCall.withProcedureName( PROCEDURE );
this.objJdbcCall.addDeclaredParameter( new SqlParameter(  "LLAMADAS_TYPE_IN", OracleTypes.STRUCT, UtilPropertiesMapper.DB_TYPE_LLAMADAS )  );
this.objJdbcCall.addDeclaredParameter( new SqlOutParameter( "ERR_OUT", OracleTypes.VARCHAR ) );
this.objJdbcCall.addDeclaredParameter( new SqlOutParameter( "MESSAGE_OUT", OracleTypes.VARCHAR ) );
this.objJdbcCall.addDeclaredParameter( new SqlOutParameter( "LLAMADAS_TYPE_OUT", OracleTypes.STRUCT, UtilPropertiesMapper.DB_TYPE_LLAMADAS, new SqlReturnType(){
@Override
public Object getTypeValue( CallableStatement cs,
int colIndx,
int sqlType,
String typeName ) throws SQLException{
STRUCT item            = (STRUCT)cs.getObject( colIndx );
Object[] arregloOut = item.getAttributes();
ServicioLlamadasTypeBean objLlamadasRESP = new ServicioLlamadasTypeBean();

objLlamadasRESP.setTelefono( (String)arregloOut[ 0 ] );
objLlamadasRESP.setTecnologia( (String)arregloOut[ 1 ] );
objLlamadasRESP.setCodigo( (String)arregloOut[ 2 ] );
objLlamadasRESP.setMensaje( (String)arregloOut[ 3 ] );
objLlamadasRESP.setPaquete( (String)arregloOut[ 4 ] );
objLlamadasRESP.setPlan( (String)arregloOut[ 5 ] );
objLlamadasRESP.setSaldo( (String)arregloOut[ 6 ] );

return objLlamadasRESP;
}
} ) );
this.objJdbcCall.compile();

SqlTypeValue sqlTypeINPUT = new AbstractSqlTypeValue(){
protected Object createTypeValue( Connection conexion,
int sqlType,
String typeName ) throws SQLException{
StructDescriptor itemDescriptor = new StructDescriptor( typeName, conexion );
ServicioLlamadasTypeBean objLlamadas = objServicioLlamadasType;

STRUCT item = new STRUCT( itemDescriptor, conexion, new Object[]{
objLlamadas.getTelefono(),
objLlamadas.getTecnologia(),
objLlamadas.getCodigo(),
objLlamadas.getMensaje(),
objLlamadas.getPaquete(),
objLlamadas.getPlan(),
objLlamadas.getSaldo()
} );

return item;
}
};

Map objMapIN = new HashMap();
objMapIN.put( "LLAMADAS_TYPE_IN", sqlTypeINPUT );       //IN
objMapIN.put( "ERR_OUT",           OracleTypes.VARCHAR ); //OUT
objMapIN.put( "MESSAGE_OUT",  OracleTypes.VARCHAR ); //OUT
objMapIN.put( "LLAMADAS_TYPE_OUT", OracleTypes.STRUCT ); //OUT
this.objJdbcCall.execute( objMapIN );


//PARAMETROS [OUT]:
Map objMapOUT = this.objJdbcCall.execute( objMapIN );

String vCodigoErr_OUT = (String)objMapOUT.get( "ERR_OUT" );
String vMensajeErr_OUT = (String)objMapOUT.get( "MESSAGE_OUT" );
ServicioLlamadasTypeBean objServicioLlamadasTypeBean = (ServicioLlamadasTypeBean)objMapOUT.get( "LLAMADAS_TYPE_OUT" );

this.logger.info( "ERR_OUT: " + vCodigoErr_OUT );
this.logger.info( "MESSAGE_OUT: " + vMensajeErr_OUT );
this.logger.info( "LLAMADAS_TYPE_OUT: " + objServicioLlamadasTypeBean );

objDataBaseValidatorBean = new DataBaseValidatorBean();
objDataBaseValidatorBean.setCodigo_OUT( vCodigoErr_OUT );
objDataBaseValidatorBean.setMensaje_OUT( vMensajeErr_OUT );
objDataBaseValidatorBean.setObjLlamadasBeanType( objServicioLlamadasTypeBean );
}
catch( SQLException e ){
this.logger.error( "ERROR: [SQLException] - [" + e.getMessage() + "] ", e );

objDataBaseValidatorBean = new DataBaseValidatorBean();
objDataBaseValidatorBean.setCodigo_OUT( UtilPropertiesMapper.ESTADO_NOK );
objDataBaseValidatorBean.setMensaje_OUT( e.getMessage() );
objDataBaseValidatorBean.setListaDatos( null );
}
catch( Exception e ){
this.logger.error( "ERROR: [Exception] - [" + e.getMessage() + "] ", e );

objDataBaseValidatorBean = new DataBaseValidatorBean();
objDataBaseValidatorBean.setCodigo_OUT( UtilPropertiesMapper.ESTADO_NOK );
objDataBaseValidatorBean.setMensaje_OUT( e.getMessage() );
objDataBaseValidatorBean.setCodServLlam( null );
}

finally{
this.logger.info( "Tiempo TOTAL Proceso: [" + (tiempoInicio-System.currentTimeMillis()) + " milisegundos ]" );
this.logger.info( "*********** [FIN] - DENTRO: [registrarServicioLlamadasType - DAO] ***********" );
}

return objDataBaseValidatorBean;
}



El error que me muestra al ejecutar el PROCESO es el siguiente (indica un tipo de dato invalido, pero ya validé que todos los tipos de campos estan bien):



org.springframework.jdbc.BadSqlGrammarException: CallableStatementCallback; bad SQL grammar [{call RGUERRA.PKG_SERVICIO_LLAMADAS.SP_OBTENER_SERV_LLAM_TYPE(?, ?, ?, ?)}]; nested exception is java.sql.SQLSyntaxErrorException: ORA-00902: tipo de dato no válido

at org.springframework.jdbc.support.SQLExceptionSubclassTranslator.doTranslate(SQLExceptionSubclassTranslator.java:94)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:80)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:969)
at org.springframework.jdbc.core.JdbcTemplate.call(JdbcTemplate.java:1003)
at org.springframework.jdbc.core.simple.AbstractJdbcCall.executeCallInternal(AbstractJdbcCall.java:391)
at org.springframework.jdbc.core.simple.AbstractJdbcCall.doExecute(AbstractJdbcCall.java:376)
at org.springframework.jdbc.core.simple.SimpleJdbcCall.execute(SimpleJdbcCall.java:177)
at pe.com.javaman.dummy.servicioLlamadasWS.dao.ServicioLlamadasDaoImp.obtenerServicioLlamadasType(ServicioLlamadasDaoImp.java:366)



Si me apoyan con algún dado se los agradecería ya que en Internet no hay mucha info sobre: SimpleJdbcCall  para SqlReturnType.


Saludos.



Cesar Guerra.

ROGER

unread,
Sep 16, 2012, 12:27:15 AM9/16/12
to spring-user...@googlegroups.com
Hola Cesar.

Hace un tiempo me salio un error Similar lo resolví cambiando la versión del jar en mi caso OJDBC14.JAR por  OJDBC6.JAR 

Saludos.

--
Has recibido este mensaje porque estás suscrito al grupo "Spring User Group Peru" de Grupos de Google.
Para publicar una entrada en este grupo, envía un correo electrónico a spring-user...@googlegroups.com.
Para anular tu suscripción a este grupo, envía un correo electrónico a spring-user-group...@googlegroups.com
Para tener acceso a más opciones, visita el grupo en http://groups.google.com/group/spring-user-group-peru?hl=es.



--
Atentamente.
ROGER CARRASCO ARANGO    
Analista y  Programador de Sistemas
Contactos:
Cel:  992684195          
Email: roger...@gmail.com

cesar ricardo guerra arnaiz

unread,
Sep 16, 2012, 12:31:34 AM9/16/12
to Comunidad Spring Peru
Hola Roger,

Gracias por responder la verdad que es un problema bien raro este escenario que estoy probando, ya que vengo tiempo trabajando con Spring JDBC sin problemas pero me solicitaron este escenario y me resulto este problema. Quizás deba ser eso ya que en mi APP manejo el API: OJDBC14.jar, desplegado en un Weblogic Server 11. Voy a validar lo que mencionas.


Saludos.


Date: Sat, 15 Sep 2012 23:27:15 -0500
Subject: Re: [springperu] Spring JDBC para Oracle Types.
From: roger...@gmail.com
To: spring-user...@googlegroups.com

cesar ricardo guerra arnaiz

unread,
Sep 16, 2012, 12:54:58 AM9/16/12
to Comunidad Spring Peru
Al cambiar de .jar ahora me muestra este error, pero ya es mas especifico el error: 

SERVER: AdminServer [ INFO] [16-09-2012 04:48:00.216] (ServicioLlamadasDaoImp.java:287) - *********** [INICIO] - DENTRO: [obtenerServicioLlamadasType - DAO] ***********
SERVER: AdminServer [ INFO] [16-09-2012 04:48:00.217] (ServicioLlamadasDaoImp.java:297) - DATA SOURCE: [weblogic.jdbc.common.internal.RmiDataSource@17f1907]
SERVER: AdminServer [ INFO] [16-09-2012 04:48:00.218] (ServicioLlamadasDaoImp.java:304) - PROCEDURE: [RGUERRA.PKG_SERVICIO_LLAMADAS.SP_OBTENER_SERV_LLAM_TYPE]
SERVER: AdminServer [ERROR] [16-09-2012 04:48:01.022] (ServicioLlamadasDaoImp.java:393) - ERROR: [Exception] - [weblogic.jdbc.wrapper.Struct_oracle_sql_STRUCT cannot be cast to oracle.sql.STRUCT] 
java.lang.ClassCastException: weblogic.jdbc.wrapper.Struct_oracle_sql_STRUCT cannot be cast to oracle.sql.STRUCT
at pe.com.javaman.dummy.servicioLlamadasWS.dao.ServicioLlamadasDaoImp$2.getTypeValue(ServicioLlamadasDaoImp.java:322)
at org.springframework.jdbc.core.JdbcTemplate.extractOutputParameters(JdbcTemplate.java:1096)
at org.springframework.jdbc.core.JdbcTemplate$5.doInCallableStatement(JdbcTemplate.java:1015)
at org.springframework.jdbc.core.JdbcTemplate$5.doInCallableStatement(JdbcTemplate.java:1)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:953)
at org.springframework.jdbc.core.JdbcTemplate.call(JdbcTemplate.java:1003)
at org.springframework.jdbc.core.simple.AbstractJdbcCall.executeCallInternal(AbstractJdbcCall.java:391)
at org.springframework.jdbc.core.simple.AbstractJdbcCall.doExecute(AbstractJdbcCall.java:376)
at org.springframework.jdbc.core.simple.SimpleJdbcCall.execute(SimpleJdbcCall.java:177)
at pe.com.javaman.dummy.servicioLlamadasWS.dao.ServicioLlamadasDaoImp.obtenerServicioLlamadasType(ServicioLlamadasDaoImp.java:366)
at pe.com.javaman.dummy.servicioLlamadasWS.services.ServicioLlamadasServiceImpl.servicioLlamadas(ServicioLlamadasServiceImpl.java:204)
at pe.com.javaman.dummy.servicioLlamadasWS.ws.ServicioLlamadasPortTypeImpl.servicioLlamadas(ServicioLlamadasPortTypeImpl.java:63)

Saludos.


From: cesarricar...@hotmail.com
To: spring-user...@googlegroups.com
Subject: RE: [springperu] Spring JDBC para Oracle Types.
Date: Sun, 16 Sep 2012 04:31:34 +0000

Gustavo Guanilo Albino

unread,
Sep 16, 2012, 11:18:09 AM9/16/12
to spring-user...@googlegroups.com
trata de obtener la implementación del vendor en el struct
java.sql.Struct struct =
(weblogic.jdbc.vendor.oracle.OracleStruct)(rs.getObject(2));

2012/9/15 cesar ricardo guerra arnaiz <cesarricar...@hotmail.com>



--
Saludos

Juan Gustavo Guanilo Albino
Software Developer



cesar ricardo guerra arnaiz

unread,
Sep 16, 2012, 12:50:23 PM9/16/12
to Comunidad Spring Peru
Gracias doc,

En si me base en la documentación de SPRING referente al manejo de SimpleJdbcCall  para la interface SqlReturnType que es la encargada del manejo de TYPES para el retorno.

http://static.springsource.org/spring/docs/current/spring-framework-reference/html/jdbc.html   (13.7.4 Handling complex types for stored procedure calls)

Ahí veo que están manejando la clase oracle.sql.STRUCT para la recepcion como para el parsing, pero a mi me genero el problema anterior mencionado.

La 2 soluciones aportadas me ayudaron:

- El cambio de API al OJDBC6.jar y el parsing de STRUT del vendor. 

Aquí la parte modificada:

           this.objJdbcCall.withSchemaName(    OWNER   );
           this.objJdbcCall.withCatalogName(   PAQUETE );
           this.objJdbcCall.withProcedureName( PROCEDURE );               
           this.objJdbcCall.addDeclaredParameter( new SqlParameter(    "LLAMADAS_TYPE_IN",  Types.STRUCT, UtilPropertiesMapper.DB_TYPE_LLAMADAS ) );  
           this.objJdbcCall.addDeclaredParameter( new SqlOutParameter( "ERR_OUT",            Types.VARCHAR ) );
           this.objJdbcCall.addDeclaredParameter( new SqlOutParameter( "MESSAGE_OUT",    Types.VARCHAR ) );
           this.objJdbcCall.addDeclaredParameter( new SqlOutParameter( "LLAMADAS_TYPE_OUT", Types.STRUCT, UtilPropertiesMapper.DB_TYPE_LLAMADAS, new SqlReturnType(){            
              @Override
              public Object getTypeValue( CallableStatement cs, 
                              int               colIndx, 
                              int               sqlType, 
                                      String           typeName ) throws SQLException{
             Struct   item       = ((OracleStruct)cs.getObject( colIndx ) );
             Object[] arregloOut = item.getAttributes(); 
             ServicioLlamadasTypeBean objLlamadasRESP = new ServicioLlamadasTypeBean();
             
             objLlamadasRESP.setTelefono(   (String)arregloOut[ 0 ] );
             objLlamadasRESP.setTecnologia( (String)arregloOut[ 1 ] );
             objLlamadasRESP.setCodigo(     (String)arregloOut[ 2 ] );
             objLlamadasRESP.setMensaje(    (String)arregloOut[ 3 ] );
             objLlamadasRESP.setPaquete(    (String)arregloOut[ 4 ] );
             objLlamadasRESP.setPlan(       (String)arregloOut[ 5 ] );
             objLlamadasRESP.setSaldo(      (String)arregloOut[ 6 ] );
               
             return objLlamadasRESP;
            }
           } ) );   
           this.objJdbcCall.compile();


SALIDA DE LOG:

SERVER: AdminServer [ INFO] [16-09-2012 16:37:59.061] (ServicioLlamadasDaoImp.java:287) - *********** [INICIO] - DENTRO: [obtenerServicioLlamadasType - DAO] ***********
SERVER: AdminServer [ INFO] [16-09-2012 16:37:59.061] (ServicioLlamadasDaoImp.java:297) - DATA SOURCE: [weblogic.jdbc.common.internal.RmiDataSource@1655fb9]
SERVER: AdminServer [ INFO] [16-09-2012 16:37:59.062] (ServicioLlamadasDaoImp.java:304) - PROCEDURE: [RGUERRA.PKG_SERVICIO_LLAMADAS.SP_OBTENER_SERV_LLAM_TYPE]
SERVER: AdminServer [ INFO] [16-09-2012 16:37:59.317] (ServicioLlamadasDaoImp.java:373) - ERR_OUT:     1
SERVER: AdminServer [ INFO] [16-09-2012 16:37:59.318] (ServicioLlamadasDaoImp.java:374) - MESSAGE_OUT: PROCESO EXITOSO [OBJETO]
SERVER: AdminServer [ INFO] [16-09-2012 16:37:59.319] (ServicioLlamadasDaoImp.java:375) - LLAMADAS_TYPE_OUT: ServicioLlamadasTypeBean [telefono=5214956, tecnologia=PREPAGO, codigo=0, mensaje=OK, paquete=PAQUETON, plan=FAMILIA PREPAGO, saldo=5000]
SERVER: AdminServer [ INFO] [16-09-2012 16:37:59.320] (ServicioLlamadasDaoImp.java:407) - Tiempo TOTAL Proceso: [-259 milisegundos ]
SERVER: AdminServer [ INFO] [16-09-2012 16:37:59.320] (ServicioLlamadasDaoImp.java:408) - *********** [FIN] - DENTRO: [registrarServicioLlamadasType - DAO] **********


Gracias.


Date: Sun, 16 Sep 2012 10:18:09 -0500

Subject: Re: [springperu] Spring JDBC para Oracle Types.
Reply all
Reply to author
Forward
0 new messages