Java + Oracle CLOBs

willy_chaos

Hola a todos, estoy teniendo problemas con un código que ataca a una base de datos Oracle.

El tema es que los usuarios que rellenan los campos de una base de datos, estan de alguna forma metiendo caracteres extraños en la base de datos que luego cuando enviamos por el webservice no se reconocen correctamente. El caracter en si es una especie de apostrofe o comilla simple, pero no es tal.

El código hex del caracter es el 0x91, 0x92, 0x93 y 0x94.

El problema es que no podemos lanzar una query de update, porque los campos Clobs hay alguno que ocupa muchisimos caracteres y la Oracle nos dice que el buffer no tiene tanto tamaño para almacenar datos. Así que la opcion que hemos pensado es usar una utilidad Java que se encargue de obtener el texto del Clob , haga el replace, y actualice la base de datos con el nuevo texto.

A la funcion, le pasamos una tabla (string->nombreTabla) y el resultado de la query sobre esta tabla, y por cada campo de ese ResultSet detecta si es un CLOB o un VARCHAR2/CHAR y aplica la funcion de limpiaCaracteres. (previamente ya hemos puesto solo los campos en la SELECT que sean CLOB/VARCHAR2/CHAR). El problema que me da es que la primera row la hace correctamente, pero a la segunda falla.

El error que suelta Java (por culpa de la Oracle es):

        RESULTSET ROWS == 793 ## COLS == 31
## ROWID: [AALYTiAAFAAABlkAAA] TABLA: [gsbtas01] > Commit ##
## ROWID: [AALYTiAAFAAABlnAAB] TABLA: [gsbtas01] > Failure ## Fallo al cargar datos en el campo CAT_PREVIS
## ERROR: Se ejecuto Rollback
-------------- STACKTRACE INIT -------------
java.sql.SQLException: ORA-22990: LOB locators cannot span transactions

    at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:112)
    at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:331)
    at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:283)
    at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:278)
    at oracle.jdbc.driver.T4C8TTILob.receiveReply(T4C8TTILob.java:930)
    at oracle.jdbc.driver.T4C8TTIClob.write(T4C8TTIClob.java:435)
    at oracle.jdbc.driver.T4CConnection.putChars(T4CConnection.java:2666)
    at oracle.sql.CLOB.putChars(CLOB.java:433)
    at oracle.sql.CLOB.setString(CLOB.java:910)
    at utilitat.UtilitatCaractersExtranys.actualizaRegistrosEnTabla(UtilitatCaractersExtranys.java:267)
    at utilitat.UtilitatCaractersExtranys.execute(UtilitatCaractersExtranys.java:355)
    at utilitat.UtilitatCaractersExtranys.main(UtilitatCaractersExtranys.java:60)
-------------- STACKTRACE END --------------

platform@eSecretary:/usr/local/zonas/zona_autentica$ java utilitat.UtilitatCaractersExtranys
spoiler
W0rd

Puedes probar a actualizar el resulset mediante la api de java en lugar de tirar la sentencia sql.

https://docs.oracle.com/javase/7/docs/api/java/sql/ResultSet.html#updateClob(int,%20java.sql.Clob)

Vas recorriendo el resulset:

resultset.next();

Actualizas, resulset.updateClob("columna",objetoClob);

Aplicas cambios resultset.updateRow();

Saludos.

2 respuestas
NickNack

Atacar!!! Que gracia me hace la expresión xD La verdad es que Java + oracle... que asco de eleccion en ambos casos pero bueno... en algo tan 'complejo' y que son necesarios algunos datos para probar... pedir ayuda es un poco dificil.

Lo unico que te puedo decir es que probablemente el error venga de cleanNullString. ¿No puedes comparar utilizando terniarios?

str = (str) ? str: '';

(no de la funcion en si si no de la llamada a esta)

Viene del paso, no tengo tiempo de leerme el codigo entero pero tiene pinta de que estás intentado acceder al CLOB antes de llenarlo o antes de que termine de updatear, etc... Oracle JDBC es muy sensible XD

2 respuestas
Turco

#3 En mis proyectos uso una clase que hice para hacer get´s y post´s a apis externas que se llama ApiAttack xD

willy_chaos

#3 hay una comprobacion de si el clob es null. Si lo es entonces genero yo una string vacia, sino, el clean null string ya tiene cuidado tambien de las strings vacias.

Aun asi thx

#2 probare eso mañana, queria hacerlo todo en una query para evitar hacer por cada campo, pero si no hay mas remedio.. Lo hare, total es algo que ejecutaremos nosotros a mano de vez en cuando.

Thx por la info

Ranthas

Por el error me da que tienes accesos concurrentes a ese campo CLOB seguramente el parametro cursor

willy_chaos

El problema es que cuando he intentado hacer un clob.free() al final de la row y despues del stmt , no hace ni la primera porque el jdbc me dice que AbstractMethod y por lo que he leido es tema que se soluciona actualizando jdbc (cosa que no puedo hacer)

Probare despues del stmt.execute ponerlo a null

El problema de todo es que trabajamos con Java 6 y el JDBC creo que es el 1.4, asi que hay soluciones como el clob.free() que a nosotros no nos funciona

#2 he probado lo que comentabas con el updateClob y mas tarde el updateRow y nada, mismo error :(

1 respuesta
W0rd

#7 Muestra como creas el resulset cursor

1 respuesta
willy_chaos

#8

spoiler
1 respuesta
W0rd

#9 No he manejado mucho las bd en java pero tiene pinta de que tienes la tabla bloqueada, puedes probar a quitar restricciones, es decir, en un entorno aparte donde solo estes tu con la bd, para ver por donde se queda pillado.

El for update que hace exactamente, bloquea la fila para identificarla a la hora de cambiar?

Cuando probaste el

resulset.updateClob("columna",objetoClob);

Lo has hecho en el mismo resulset del select o has utilizado el nuevo resulset que creas en el método actualizaRegistrosEnTabla()

1 respuesta
willy_chaos

#10

El for update, lo tuve que poner puesto que si no, los clobs no me dejaban actualizar (fijate que la primera ROW la hace bien). Lei que se tiene que bloquear la fila para actualizarlos y encontre que dandole el FOR UPDATE lo hace.

El resultset es el mismo, se pasa a la funcion, pero es un objeto y por lo tanto el puntero es el mismo.

1 respuesta
W0rd

#11 Arriba veo que creas otro resulset para hacer el update,

try {
                    this.conOrcl.setAutoCommit(false);
                    PreparedStatement stmt = conOrcl.prepareStatement(update_for_rowid, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); // Creamos el statement para el registro

Edito: Bueno, no es un resulset, puede pasar que tengas pillada la fila con el select y no te funcione el update

willy_chaos

Es el statement , y la parte de donde aparece ResultSet es directamente la clase y el tipo static (es lo que normalmente se ha de indicar)

1 respuesta
9 días después
B

#13 Has probado a cerrar el Statement una vez utilizado? (stmt.close();)

1 respuesta
willy_chaos

#14 Si y sucede lo mismo. Incluso tambien hacer antes un stmt.clearParameters() y luego stmt.close()

willy_chaos

Actualizo para indicar que ya esta solucionado. Ha sido poner el commit al final del todo el bucle, de todas las rows, en vez de intentar hacer un commit por cada row del resultset.

1

Usuarios habituales