Tipo di dati Oracle JDBC e Oracle CHAR

Ho un problema spinoso con la gestione dei tipi di dati CHAR del driver JDBC Oracle. Prendiamo questa semplice tabella:

 create table x (c char(4)); insert into x (c) values ('a'); -- inserts 'a ' 

Quindi quando inserisco qualcosa in CHAR(4) , la stringa è sempre piena di spazi bianchi. Questo viene fatto anche quando eseguo query come questa:

 select * from x where c = 'a'; -- selects 1 record select * from x where c = 'a '; -- selects 1 record select * from x where c = 'a '; -- selects 1 record 

Qui, la costante 'a' è piena anche di spazi vuoti. Ecco perché il record viene sempre restituito. Ciò vale quando queste query vengono eseguite anche con un PreparedStatement JDBC. Ora la cosa complicata è quando voglio usare una variabile bind:

 PreparedStatement stmt = conn.prepareStatement("select * from x where c = ?"); stmt.setString(1, "a"); // This won't return any records stmt.setString(1, "a "); // This will return a record stmt.executeQuery(); 

Questa è una soluzione:

 PreparedStatement stmt = conn.prepareStatement("select * from x where trim(c) = trim(?)"); stmt.setString(1, "a"); // This will return a record stmt.setString(1, "a "); // This will return a record stmt.executeQuery(); 

EDIT : Ora questi sono i vincoli:

  • La soluzione precedente non è desiderabile in quanto modifica entrambi i contenuti di c e ? , E rende abbastanza difficile l’uso di indici su c .
  • Spostare la colonna da CHAR a VARCHAR (che dovrebbe essere, ovviamente) non è ansible

EDIT : I motivi di questi vincoli sono perché pongo questa domanda dal punto di vista dello sviluppatore di jOOQ , una libreria di astrazione del database. Quindi i miei requisiti sono di fornire una soluzione molto generica che non rompere nulla nel codice client di jOOQ. Questo è il motivo per cui non sono davvero un grande fan della soluzione alternativa. Ed è per questo che non ho accesso alla dichiarazione di quella colonna CHAR . Ma ancora, voglio essere in grado di gestire questo caso.

Cosa faresti invece? Qual è una buona pratica per gestire i tipi di dati CHAR quando voglio ignorare gli spazi bianchi finali?

Se vuoi

 stmt.setString(1, "a"); // This won't return any records 

per restituire un record, prova

 conn.prepareStatement("select * from x where c = cast(? as char(4))") 

Non vedo alcun motivo per utilizzare il tipo di dati CHAR anche se è char (1) in Oracle. Puoi cambiare il tipo di dati, invece?

La soluzione di Gary funziona bene. Ecco un’alternativa.

Se si utilizza un driver JDBC Oracle, la chiamata a prepareStatement() restituirà effettivamente un OraclePreparedStatement , che ha un metodo setFixedCHAR() che setFixedCHAR() automaticamente il pad degli input con spazi bianchi.

 String sql = "select * from x where c = ?"; OraclePreparedStatement stmt = (OraclePreparedStatement) conn.prepareStatement(sql); stmt.setFixedCHAR(1, "a"); ... 

Ovviamente, il cast è sicuro solo se stai utilizzando il driver Oracle.

L’unica ragione per cui ti suggerirei di utilizzare questa risposta rispetto a Gary è che puoi modificare le dimensioni delle colonne senza dover modificare il codice JDBC. Il driver esegue il rilievo del numero corretto di spazi senza che lo sviluppatore debba conoscere / gestire la dimensione della colonna.

Ho una buona soluzione per questo. Devi aggiungere una proprietà mentre ricevi la connessione dal database.

 NLS_LANG=american_america.AL32UTF8 

o in connessione Java puoi usare sotto il codice:

 java.util.Properties info = new java.util.Properties(); info.put ("user", user); info.put ("password",password); info.put("fixedString","TRUE"); info.put("NLS_LANG","american_america.AL32UTF8"); info.put("SetBigStringTryClob","TRUE"); String url="jdbc:oracle:thin:@"+serverName; log.debug("url="+url); log.debug("info="+info); Class.forName("oracle.jdbc.OracleDriver"); conn = DriverManager.getConnection(url,info);