Arquivo

Posts Tagged ‘oracle’

Como Encontrar Linhas Duplicadas no Banco de Dado

Ultimamente estou trabalhando bastante com consultas SQL. Essa dica pode ser bastante útil para você que deseja descobrir que linhas estão duplicadas no banco de dados de acordo com algum critério. Você precisa, basicamente, fazer um join da tabela analisada com ela mesma. Aqui há várias dicas de como fazer essa consulta, mas o select abaixo funcionou bem para mim:

SELECT *
FROM TABLE A
WHERE EXISTS (
  SELECT 1 FROM TABLE
  WHERE COLUMN_NAME = A.COLUMN_NAME
  AND ROWID < A.ROWID
)
Anúncios
Categorias:Programação Tags:,

Como Apagar e Recriar um Tablespace do Oracle

Essa é um dica rápida de como apagar e recriar um tablespace do Oracle.

1. Acesse o sqlplus

2. Apague o tablespace

DROP TABLESPACE OWNERTMP_DATA INCLUDING CONTENTS AND DATAFILES;

3. Crie o tablespace à partir do arquivo DBF

CREATE TABLESPACE OWNERTMP_DATA datafile '/oradata/BDTESTE/DATA/ownertmp_data01.dbf' 
size 100m autoextend on next 100m maxsize 1024m;

4. Altere o tablespace default

ALTER USER OWNERTMP IDENTIFIED BY OWNERTMP DEFAULT TABLESPACE OWNERTMP_DATA;
SELECT * FROM v$DATAFILE;

5. Importe os dados

imp OWNERTMP/OWNERTMP file='/oracle/product/10.2.4.0/admin/DBTESTE/dbdump' 

Referências

1. [https://beginningoracle.wordpress.com/2008/04/25/importing-and-exporting-oracle-dmp-files/]
2. [http://www.oracle.com/technetwork/pt/articles/database-performance/introducao-conceito-de-tablespaces-495850-ptb.html]

Mapeamento de Oracle Types em Java

Uma das vantagens da orientação a objetos é a reutilização de código. Em PL/SQL, o equivalente à esse aspecto da orientação a objetos é o uso de tipos. Os tipos permitem a separação de interfaces e detalhes de implementação. Os tipos encapsulam estruturas de dados e as funções e procedures que as manipulam.

Vamos explicar o funcionamento de cada estrutura necessárias através da implementação de uma procedure que retorna um array de tipos Pessoa. Primeiro, vamos criar uma tabela para armazenar os dados das pessoas.

CREATE TABLE TBL_PESSOA
(
  PK     NUMBER(3) NOT NULL,
  NOME   VARCHAR2(64) NOT NULL,
  IDADE  NUMBER(2) NOT NULL
);

E em seguida vamos inserir alguns registros para validar a procedure e o tipo que serão criados.

INSERT INTO TBL_PESSOA
VALUES (1,'JOSÉ', 30);
INSERT INTO TBL_PESSOA
VALUES (2,'JOÃO', 35);

Vamos definir a interface do tipo TYPE_PESSOA. Note a função CONSTRUIR designada como STATIC. Métodos STATIC, diferente de métodos MEMBER, devem ser ivocados no tipo e não em uma instância. Esse método é apenas um facilitador para criar instâncias do tipo.

CREATE OR REPLACE TYPE TYPE_PESSOA AS OBJECT (
   PK    NUMBER(3),    -- Identificador
   NOME  VARCHAR2(64), -- Nome
   IDADE NUMBER(2),    -- Idade
   STATIC FUNCTION CONSTRUIR RETURN TYPE_PESSOA
);

Em seguida, vem a implementação ou BODY do tipo. Na implementação do tipo definimos o comportamento da função CONSTRUIR. Ela apenas instancia TYPE_PESSOA com valores nulos. Essa instância será preenchida na procedure que será definida posteriormente.

CREATE OR REPLACE TYPE BODY TYPE_PESSOA AS
   STATIC FUNCTION CONSTRUIR RETURN TYPE_PESSOA IS
     REC TYPE_PESSOA;
   BEGIN
     REC := TYPE_PESSOA(NULL,NULL,NULL);
     RETURN REC;
   END CONSTRUIR;
END;

Como nosso retorno será um array de registros de Pessoas, vamos definir um tipo auxiliar que armazenará TYPE_PESSOA em tabela durante a execução da procedure. Sendo assim, TYPE_PESSOA_LISTA é um array de TYPE_PESSOA.

CREATE OR REPLACE TYPE TYPE_PESSOA_LISTA AS
TABLE OF TYPE_PESSOA;

A última estrutura de banco de dados que precisaremos implementar é a procedure. Essa procedure busca pessoas e popula um TYPE_PESSOA para cada ocorrência e as armazena no tipo auxiliar TYPE_PESSOA_LISTA. Nessa implementação, usei labels e loops para navegar pelos registros, mas assumo que se você precisou criar um tipo, que é uma estrutura complexa, deve conhecer bem os camandos básicos, tratamento de erros – que não mostrei aqui – e como funciona a declaração de variáveis e atribuição de valores em PL/SQL.

 
CREATE OR REPLACE PROCEDURE PROCEDURE_BUSCAR_USUARIOS
   (P_USUARIO_LISTA OUT NOCOPY TYPE_PESSOA_LISTA)
IS

V_PK NUMBER(3);
V_NOME VARCHAR2(64);
V_IDADE NUMBER(2);

BEGIN
   V_PK := 0;
   V_NOME := '';
   V_IDADE := 0;
   P_USUARIO_LISTA := TYPE_PESSOA_LISTA();

   <<LABEL_PESSOA>>;
   FOR V_PESSOA IN (
      SELECT TP.PK, TP.NOME, TP.IDADE
         INTO V_PK, V_NOME, V_IDADE
         FROM TBL_PESSOA TP
      )LOOP

     V_PK := V_PESSOA.PK;
     V_NOME := V_PESSOA.NOME;
     V_IDADE := V_PESSOA.IDADE;
      
     P_USUARIO_LISTA.EXTEND(1);
     P_USUARIO_LISTA(P_USUARIO_LISTA.COUNT) := TYPE_PESSOA.CONSTRUIR();
     P_USUARIO_LISTA(P_USUARIO_LISTA.COUNT).PK := V_PK;
     P_USUARIO_LISTA(P_USUARIO_LISTA.COUNT).IDADE := V_IDADE;
     P_USUARIO_LISTA(P_USUARIO_LISTA.COUNT).NOME := V_NOME;
   END LOOP LABEL_PESSOA;

END PROCEDURE_BUSCAR_USUARIOS;

Agora vamos passar para a implementação do código Java. Comecemos pela definição de um POJO Pessoa.

public class Pessoa {
   private Integer id;
   private String nome;
   private Integer idade;
   // getters e setters
   public Pessoa(Integer id, Integer idade, String nome) {
      this.id = id;
      this.idade = idade;
      this.nome = nome;
   }
}

A interface SQLData é utilizada para implementar um mapeamento mais refinado e granular de um tipo de dado definido no banco de dados – não necessariamente aquele tipo que criamos – para uma classe Java. A implementação do SQLData receberá um SQLInput, que é um objeto entregue pelo driver do banco de dados e que permite acesso ou gravação de valores. Vamos definir uma classe que implementa um SQLData para facilitar a extração das informações que virão da procedure por meio daquele array de tipos que definimos.

import java.sql.SQLData;
import java.sql.SQLException;
import java.sql.SQLInput;
import java.sql.SQLOutput;

public class PessoaOracleType implements SQLData {
   private int id;
   private String nome;
   private int idade;

   private String sqSQLDatal_type;

   public String getSQLTypeName() {
      return sql_type;
   }

   public int getId() {
      return id;
   }

   public String getNome() {
      return nome;
   }

   public int getIdade() {
      return idade;
   }

   public void readSQL(SQLInput stream, String type) 
      throws SQLException {
      sql_type = type;
      id = stream.readBigDecimal().intValue();
      nome = stream.readString();
      idade = stream.readBigDecimal().intValue();
   }

   public void writeSQL(SQLOutput stream) 
      throws SQLException {
      stream.writeInt(id);
      stream.writeString(nome);
      stream.writeInt(idade);
   }
}

Vamos utilizar a interface Work do Hibernate para ter acesso à Connection que será utilizada para ivocarmos a procedure criada. Uma classe curiosa que vai aparecer nessa implementação é a STRUCT, que é fornecida pelo driver do Oracle. Uma instância de STRUCT é a materialização de um objeto Oracle quando não há um mapeamento Java/SQL explícito. No nosso caso, cada linha do array de tipos retornado pela chamada da procedure será uma STRUCT.


import java.sql.Array;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLInput;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.sql.rowset.serial.SQLInputImpl;

import oracle.jdbc.OracleTypes;
import oracle.sql.STRUCT;

import org.hibernate.Session;
import org.hibernate.jdbc.Work;

public class Procedure {
   private List<Pessoa> pessoas;
   private Session session;

   public Procedure(Session session) {
      this.session = session;
   }

   public List<Pessoa> getPessoas() {
      return pessoas;
   }

   public void execute() {
      session.doWork(new Work() {
         @Override
         public void execute(Connection con) throws SQLException {
            CallableStatement callStm = null;
            try {
               Map<String, Class<?>> map = con.getTypeMap();
               map.put("TYPE_PESSOA_LISTA", PessoaOracleType.class);
               callStm = con.prepareCall(
                 "{call PROCEDURE_BUSCAR_USUARIOS(?)}");
               registrarParametroSaida(callStm, "P_USUARIO_LISTA", 
                 OracleTypes.ARRAY, "TYPE_PESSOA_LISTA");
               callStm.execute();
               pessoas = extrair(callStm);
            } finally {
               if (callStm != null) {
                  callStm.close();
               }
            }
         }
      });
   }

   private void registrarParametroSaida(CallableStatement cs, 
     String nomeParametro, int oracleTypes, String typeName)
     throws SQLException {
     cs.registerOutParameter(nomeParametro, oracleTypes, typeName);
   }

   @SuppressWarnings("unchecked")
   private List<Pessoa> extrair(CallableStatement cs) 
      throws SQLException {
      List<Pessoa> pessoas = new ArrayList<Pessoa>();
      Array array = cs.getArray("P_USUARIO_LISTA");
      ResultSet rs = array.getResultSet();

      while (rs.next()) {
         STRUCT row = (STRUCT) rs.getObject(2);
         PessoaOracleType vot = new PessoaOracleType();
         SQLInput input = 
           new SQLInputImpl(row.getAttributes(), row.getMap());
         vot.readSQL(input, row.getSQLTypeName());
         Pessoa pessoa = 
           new Pessoa(vot.getId(), vot.getIdade(), vot.getNome());
         pessoas.add(pessoa);
      }
      return pessoas;
   }
}

Por fim, implemente um método no DAO para fazer a chamada da procedure.


@Stateless
public class PessoaDao
{
   @PersistenceContext(unitName = "pu")
   private EntityManager em;

   private Session getSession(){
      return (Session) em.getDelegate();
   }

   public List<Pessoa> listar() {
      Procedure procedure = new Procedure(getSession());
      procedure.execute();
      return procedure.getPessoas();
   }
}

Conclusão

Lidar com estruturas de banco de dados e com esses tipos do Oracle em especial é particularmente difícil para um desenvolvedor de software, mas tenha em mente que esses problemas se apresentam de vez em quando e você, como engenheiro de software e não um mero desenvolverdor, deve encontrar a melhor solução dadas as restrições do projeto como as que discuti aqui.

Referências

1. [https://docs.oracle.com/cd/B19306_01/appdev.102/b14261/objects.htm]
2. [https://docs.oracle.com/cd/B28359_01/appdev.111/b28371/adobjbas.htm]
3. [https://docs.oracle.com/javase/7/docs/api/java/sql/SQLData.html]
4. [https://docs.oracle.com/javase/tutorial/jdbc/basics/sqlcustommapping.html]
5. [https://docs.oracle.com/javase/7/docs/api/java/sql/SQLInput.html]
6. [https://docs.oracle.com/cd/A87860_01/doc/java.817/a83724/oraoot3.htm]
7. [https://docs.oracle.com/cd/A87860_01/doc/java.817/a83724/oraoot1.htm]
8. [http://docstore.mik.ua/orelly/oracle/prog2/ch18_02.htm]
9. [http://blog.mclaughlinsoftware.com/2012/02/14/how-to-use-object-types/]
10. [https://docs.oracle.com/cd/B13789_01/appdev.101/b10807/10_objs.htm]
11. [https://docs.oracle.com/cd/B28359_01/appdev.111/b28371/adobjplsql.htm#i7530]
12. [https://docs.oracle.com/cd/B19306_01/appdev.102/b14261/objects.htm#i20461]

Mapeamento de Stored Procedures do Oracle em Java

Uma procedure é um conjunto de declarações PL/SQL que pode ser chamada por um nome e pode aceitar argumentos (parâmetros). Como uma procedure é armazenada como um schema no banco de dados, o código pode ser reutilizado. Essas características tornam uma procedure mais eficiente que um bloco anônimo.

Como adiantei nesse artigo, procedures podem retornar valor. Vamos fazer uma procedure que, dado o identificador de uma pessoa, retorna o próprio identificador, a idade e o nome.

1. Crie uma tabela para armazenar os dados das pessoas:

CREATE TABLE TBL_PESSOA
(
  PK     NUMBER(3) NOT NULL,
  NOME   VARCHAR2(64) NOT NULL,
  IDADE  NUMBER(2) NOT NULL
);

2. Vamos inserir um registro para poder validar a procedure:

INSERT INTO TBL_PESSOA
VALUES (1,'JOSÉ', 30);

3. Defina a procedure. Note que a implementação da nossa procedure tem apenas um select simples restrito ao parâmetro de entrada e faz a atribuição de valores aos parâmetros de saída:

CREATE OR REPLACE PROCEDURE PROCEDURE_BUSCAR_USUARIO
  (P_IN_ID IN NUMBER, P_ID OUT NUMBER, 
   P_IDADE OUT NUMBER, P_NOME OUT VARCHAR2)
IS
PK NUMBER;
NOME VARCHAR2(64);
IDADE NUMBER;
BEGIN
   SELECT TP.PK, TP.NOME, TP.IDADE
   INTO PK, NOME, IDADE
   FROM TBL_PESSOA TP
   WHERE TP.PK = P_IN_ID;
   P_ID := PK;
   P_NOME := NOME;
   P_IDADE := IDADE;
END PROCEDURE_BUSCAR_USUARIO;

4. Vamos definir a classe Pessoa. Como não estamos trabalhando com ORM, não é necessário anotações da JPA. Basta um POJO:

public class Pessoa {
   private Integer id;
   private String nome;
   private Integer idade;
   // getters e setters
   public Pessoa(Integer id, Integer idade, String nome) {
      this.id = id;
      this.idade = idade;
      this.nome = nome;
   }
}

5. Vamos utilizar a interface Work do Hibernate para ter acesso à Connection:

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.Session;
import org.hibernate.jdbc.Work;

public class Procedure {
   private Pessoa pessoa;
   private Session session;
   public Procedure(Session session) {
      this.session = session;
   }
   public Pessoa getPessoa() {
      return pessoa;
   }
   public void execute(final Integer idPessoa) {
      session.doWork(new Work() {
         @Override
         public void execute(Connection con) throws SQLException {
            CallableStatement callStm = null;
            try {
               callStm = con.prepareCall(
                "{call PROCEDURE_BUSCAR_USUARIO(?, ?, ?, ?)}");
               registrarParametroEntrada(callStm, "P_IN_ID", idPessoa);
               registrarParametroSaida(callStm, "P_ID", Types.INTEGER);
               registrarParametroSaida(callStm, "P_IDADE", Types.INTEGER);
               registrarParametroSaida(callStm, "P_NOME", Types.VARCHAR);
               callStm.execute();
               Integer id = extrairInteger(callStm, "P_ID");
               Integer idade = extrairInteger(callStm, "P_IDADE");
               String nome = extrairString(callStm, "P_NOME");
               pessoa = new Pessoa(id, idade, nome);
            } finally {
               if (callStm != null) {
                  callStm.close();
               }
            }
         }
      });
   }

   private <T extends Object> T extrair(CallableStatement cs,  
        String nomeParametro, Class<T> clazz)
      throws SQLException {
      Object object = cs.getObject(nomeParametro);
      if (object == null) {
         return null;
      }
      return clazz.cast(object);
   }
   
   private String extrairString(CallableStatement cs, 
        String nomeParametro) throws SQLException {
      return extrair(cs, nomeParametro, String.class);
   }

   private Integer extrairInteger(CallableStatement cs, 
        String nomeParametro) throws SQLException {
      return extrair(cs, nomeParametro, Integer.class);
   }

   private void registrarParametroEntrada(CallableStatement cs, 
        String nomeParametro, Integer valor)
      throws SQLException {
      cs.setObject(nomeParametro, valor, Types.INTEGER);
   }

    private void registrarParametroSaida(CallableStatement cs, 
      String nomeParametro, int sqlType) throws SQLException {
      cs.registerOutParameter(nomeParametro, sqlType);
   }
}

6. Por fim, crie um método no seu DAO para acessar a procedure:

@Stateless
public class PessoaDao
{
   @PersistenceContext(unitName = "pu")
   private EntityManager em;

   private Session getSession(){
      return (Session) em.getDelegate();
   }

   public Pessoa buscar(final Integer idPessoa) {
      Procedure procedure = new Procedure(getSession());
      procedure.execute(idPessoa);
      return procedure.getPessoa();
   }
}

Referências

1. [https://docs.oracle.com/cd/B28359_01/appdev.111/b28843/tdddg_procedures.htm]
2. [https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_5009.htm]
3. [https://justonedeveloper.wordpress.com/2013/05/29/convert-hibernate-session-to-jdbc-connection/]

Mapeamento de Stored Functions do Oracle em Java

Um stored function é um conjunto de declarações PL/SQL referenciado por um nome. Funções são muito similares à procedures exceto pelo fato de que as funções retornam valor para o ambiente em que são chamadas. Também é possível retornar valores com procedures, mas esse é um assunto para outro artigo. Quando você cria uma função, você pode definir parâmetros. Há três tipos de parâmetros que podem ser declarados:

IN: o parâmetro pode ser referenciado pela função, mas não pode ser sobrescrito.

OUT: o parâmetro não pode ser referenciado pela função, mas pode ser sobrescrito.

IN OUT: o parâmetro pode ser referenciado e sobrescrito pela funcão.

Vamos criar uma função que retorna a idade de uma pessoa armazenada em banco dado seu identificador.

1. Crie uma tabela para armazenar os dados das pessoas:

CREATE TABLE TBL_PESSOA
(
  PK     NUMBER(3) NOT NULL,
  NOME   VARCHAR2(64) NOT NULL,
  IDADE  NUMBER(2) NOT NULL
);

2. Vamos inserir um registro para poder validar a função:

INSERT INTO TBL_PESSOA
VALUES (1,'JOSÉ', 30);

3. Defina a função. Note o parâmetro de entrada e o retorno bem explícitos:

CREATE OR REPLACE FUNCTION FUNCTION_BUSCAR_IDADE_USUARIO(P_IN_ID IN NUMBER)
RETURN NUMBER
IS
P_IDADE NUMBER;
BEGIN
   SELECT TP.IDADE
   INTO P_IDADE
   FROM TBL_PESSOA TP
   WHERE TP.PK = P_IN_ID;
   RETURN (P_IDADE);
END FUNCTION_BUSCAR_IDADE_USUARIO;

4. Vamos definir a classe Pessoa. Como não estamos trabalhando com ORM, não é necessário anotações da JPA. Basta um POJO:

public class Pessoa {
   private Integer id;
   private String nome;
   private Integer idade;
   // getters e setters
   public Pessoa(Integer id, Integer idade, String nome) {
      this.id = id;
      this.idade = idade;
      this.nome = nome;
   }
}

5. Vamos utilizar a interface Work do Hibernate para ter acesso à Connection:

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.Session;
import org.hibernate.jdbc.Work;

public class Function {
   private Integer idade;
   private Session session;

   public Function(Session session) {
      this.session = session;
   }

   public Integer getIdade() {
      return idade;
   }

   public void execute(final Integer idPessoa) {
      session.doWork(new Work() {
	@Override
        public void execute(Connection con) throws SQLException {
           CallableStatement callStm = null;
           try {
              callStm = 
                 con.prepareCall("{ ? = call FUNCTION_BUSCAR_IDADE_USUARIO(?)}");
              callStm.registerOutParameter(1, Types.INTEGER);
              callStm.setObject(2, idPessoa, Types.INTEGER);
              callStm.execute();
              idade = callStm.getInt(1);
           } finally {
              if (callStm != null) {
                 callStm.close();
              }
           }
        }
      });
   }
}

6. Por fim, crie um método no seu DAO para acessar a função:

@Stateless
public class PessoaDao
{
   @PersistenceContext(unitName = "pu")
   private EntityManager em;

   private Session getSession(){
      return (Session) em.getDelegate();
   }

   public Integer buscarIdade(final Integer idPessoa) {
      Function function = new Function(getSession());
      function.execute(idPessoa);
      return function.getIdade();
   }
}

Referências

1. [https://docs.oracle.com/cd/B28359_01/appdev.111/b28843/tdddg_procedures.htm]
2. [https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_5009.htm]
3. [https://justonedeveloper.wordpress.com/2013/05/29/convert-hibernate-session-to-jdbc-connection/]
4. [http://www.javamadesoeasy.com/2015/07/jdbc-calling-oracle-database-function.html]
5. [https://docs.oracle.com/cd/F49540_01/DOC/java.815/a64686/04_call5.htm]
6. [https://www.techonthenet.com/oracle/functions.php]

Como Recuperar a Data Atual do Banco de Dados

Uma das informações que com alguma frequência preciso é a data e a hora atuais. Você sabe que não podemos confiar na data da máquina do cliente e particularmente considero uma má prática utilizar a data da máquina onde está o servidor de aplicação. O ambiente onde está a aplicação pode ser formado por um cluster onde as máquinas não necessariamente compartilham da mesmas data e hora. Se tempo é crítico para sua apicação, você precisa que ela venha de um local centralizado e confiável.

Não sou fã de utilizar SQL nativo e nem de criar coisas no banco de dados que me retornem alguma informação pontual, como procedures e views, mas se você não tem uma central horária (servidor NTP), o banco de dados acaba se tornando um caminho válido, pois teoricamente há DBAs e um pessoal de infraestrutura cuidando de vários aspectos técnicos desse ambiente, como o hardening, o monitoramento dos processos, o gerenciamento de patches, provisionamento de recursos, etc. É claro que tanto zelo com um servidor tão estratégico dá crédibilidade às informações que queiramos extrair de lá.

Vamos utilizar a nossa já conhecida interface ReturningWork para acessar a Connection através da Session do Hibernate* e de lá extrair o nome do banco de dados utilizado consultando a instância de um DatabaseMetadata, que é uma interface implementada pelo fabricante do driver JDBC. Se você sabe qual é o banco de dados, e isso em geral é verdade, não é necessário fazer isso:

   Session hibernateSession = entityManager.unwrap(Session.class);
   String databaseName = hibernateSession.doReturningWork(
      new ReturningWork<String>() {
      @Override
      public String execute(Connection connection) throws SQLException {
         return connection.getMetaData().getDatabaseProductName();
      }
   });

Nesse ponto é interessante que você crie uma enum para mapear a forma de se fazer a consulta que retorna a data em cada um dos SGBDs que você utiliza. Criei uma para o Oracle e outra para o MySQL, mas omiti o método que determina qual é a enum correta assim como os getters:

public enum DBDialect {
   ORACLE("Oracle", 
      "SELECT TO_CHAR(SYSDATE, 'dd/MM/yyyy HH24:mi:ss') FROM DUAL"),
   MYSQL("MySQL", 
      "SELECT DATE_FORMAT(SYSDATE(),'%d/%m/%Y %H:%i:%s')");

   // (...) demais métodos para recuperar o dialeto correto

}

Sabendo o nome do banco de dados, podemos utilizar a sintaxe de consulta correta para extrair a data através de uma Native Query:

   DBDialect dbDialect = DBDialect.find(databaseName);
   Query query = entityManager.createNativeQuery(dbDialect
      .getCurrentDateQuery());
   String valorData = (String) query.getSingleResult();
   SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
   Date dataAtual = dateFormat.parse(valorData);

Notas

* Essa implementação é dependente do framework ORM, mas cada decisão que você toma deve ter prós que superem os contras segundo algum critério. Em um mundo ideal, deveríamos depender das abstrações e não das implementações. Em outras palavras, trata-se do “D” (Dependency inversion principle) do SOLID.

Referências

1. http://www.coderanch.com/t/622811/ORM/databases/createNativeQuery-JPA
2. http://openjpa.apache.org/builds/1.2.3/apache-openjpa/docs/jpa_overview_sqlquery_obj.html
3. http://stackoverflow.com/questions/6707115/get-hold-of-a-jdbc-connection-object-from-a-stateless-bean
4. http://stackoverflow.com/questions/20629523/how-to-know-database-type-programmatically-in-jpa
5. http://stackoverflow.com/questions/16994226/how-to-get-datasource-or-connection-from-jpa2-entitymanager-in-java-ee-6

Como Contornar Problemas de Encoding no Banco de Dados

Quando strings são inseridas no banco de dados diretamente via script, a aplicação não tem controle sobre o tipo de encoding que foi utilizado. Tentei forçar o encoding dessas strings para UTF-8 estaticamente no header da página e manualmente no código Java, mas nada funcionava.

Comecei a analisar o que ocorria no banco de dados. O NLS_CHARACTERSET define, no banco de dados Oracle, quais caracteres poderão ser armazenados utilizando os tipos de dados CHAR, VARCHAR2, LONG e CLOB. Rodei a consulta abaixo que foi indicada aqui e vi que o encoding setado era WE8MSWIN1252, o que não permite acentuação:

SELECT VALUE
FROM NLS_DATABASE_PARAMETERS
WHERE PARAMETER = 'NLS_CHARACTERSET';

Esperava que o resultado dessa consulta fosse UTF8 ou algo como AL32UTF8, que de acordo com esse fórum é um subtipo de UTF-8 que utiliza um espaço de armazenamento maior (4 bytes ao invés de 2), tem todos os caracteres do super-tipo e ainda possui caracteres extras.

Após uma conversa com o DBA, descobri que os caracteres acentuados eram concatenados ao resultado no meio da implementação da procedure que era chamada pela aplicação. Sendo assim, procurei outra abordagem.

Um colega me contou que o Oracle tem funções que permitem descobrir qual é o código ASCII correspondente a um caractere e vice-versa. Algo equivalente ao que fazemos em Java:

int charToAscii(char character){
return (int)character;
}

char asciiToChar(int ascii){
return (char)ascii;
}

Para descobrir o caractere correspondente ao código ASCII, utilize a função CHR([CODIGO_ASCII]) e para descobrir o código ASCII correspondente ao caractere utilize a função ASCII([CARACTERE]). Exemplo:

-- para descobrir o caractere correspondente ao código ASCII 231
SELECT CHR(231) FROM DUAL;

-- para descobrir o código ASCII correspondente ao caracteres 'ç'
SELECT ASCII('ç') FROM DUAL;

Conhecendo o código do caractere, podemos alterar nossa stored procedure:

CREATE OR REPLACE PROCEDURE findDica(
	   dica OUT VARCHAR2)
IS
BEGIN

  SELECT 'Dirija com seguran' || CHR(231) || 'a, amigo.'
  INTO dica 
  FROM  DUAL;

END;
/
Categorias:Programação Tags:,