JPA + Oracle + Blob

Estava precisando fazer um pequeno teste de campo blob com JPA no Oracle.
A atividade aparentemente é fácil, porém fiquei um dia inteiro pra fazê-la.
Segue os passos:

Tabela no oracle:
CREATE TABLE “TESTE_BLOB”
( “ID” NUMBER(6,0) NOT NULL ENABLE,
“IMAGEM” BLOB NOT NULL ENABLE,
“DESCRICAO” VARCHAR2(255 BYTE),
“NOME” VARCHAR2(50 BYTE),
CONSTRAINT “TESTE_BLOB_PK” PRIMARY KEY (“ID”) ENABLE
)

Entidade TesteBlob

@Entity(name = "TesteBlob")
@Table(name = "TESTE_BLOB")
public class TesteBlobTO {

private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(generator = "SequenceGenerator")
@SequenceGenerator(name = "SequenceGenerator", sequenceName = "SQ_TESTE_BLOB", initialValue = 1)
@Column(name = "ID")
private java.lang.Integer id;

@Column(name = "IMAGEM")
@Lob
@Basic(fetch=FetchType.LAZY)
private byte[] imagem;

@Column(name = "DESCRICAO")
private java.lang.String descricao;

@Column(name = "NOME")
private java.lang.String nome;

/* ... */

}

Até ai estava funcionando para arquivos de até 2kb. Quando tentava colocar um arquivo maior que isso ele dava os seguintes erros:

WARN [JDBCExceptionReporter] SQL Error: 17090, SQLState: null
ERROR [JDBCExceptionReporter] operação não permitida: streams type cannot be used in batching
ERROR [AbstractFlushingEventListener] Could not synchronize database state with session
Caused by: java.sql.SQLException: operação não permitida: streams type cannot be used in batching

Googlando por aí acabei achando uma solução, incluir no arquivo persistence.xml com a seguinte tag:

<property name=”hibernate.jdbc.batch_size”>0</property>

Ele aceitou arquivos com mais de 2k, mas quando tentava inserir arquivos com mais de 3kb ele dava um novo erro:

WARN [JDBCExceptionReporter] SQL Error: 1460, SQLState: 72000
ERROR [JDBCExceptionReporter] ORA-01460: unimplemented or unreasonable conversion requested

ERROR [AbstractFlushingEventListener] Could not synchronize database state with session
org.hibernate.exception.GenericJDBCException: could not insert: [testeblob.dataaccess.to.TesteBlobTO]

Caused by: java.sql.SQLException: ORA-01460: unimplemented or unreasonable conversion requested

Depois de ler caçar em vários lugares pela solução, vi um lugar sugerindo atualizar o driver da oracle. PErcebi que eu estava usando um driver antigo (ojdbc14.jar, mas vi gente usando um mais antigo ainda classes12.jar), troquei para
ojdbc5.jar (porque aqui ainda está com java 5, para java 6 tem o ojdbc6.jar) e tudo funcionou lindo e perfeitamente.

[]‘s

novembro 22, 2011 at 2:58 pm Deixe um comentário

Quebrando páginas com JSF

Olá,
Essa semana passei pelo problema de ter que quebrar páginas com um conteúdo de um datatable. Tentei usar o style=”page-break-after: always;” dentro de um datatable e não deu certo de jeito nenhum, não sei porquei. Foi então que resolvi procurar outra forma de iteração dos dados. Só consegui usando o componente dataList do tomahawk:

<t:dataList id="dataTable1"
    value="#{TesteQuebraPagina.lista}"
    var="item">
	<h:outputText style="page-break-after: always;"
               value="#{item}" />
</t:dataList>

Na exibição da página ele não aparece quebrado, mas na hora de imprimir ou visualizar a impressão, ele vai colocar cada item em um página.

março 29, 2011 at 5:45 pm Deixe um comentário

MySql Error 1005: Can’t create table (errno: 150) Problema Foreign Key

Achei essas dicas no site e achei interessante postar aqui também. Quando tentar criar uma foreign key no mysql e ele der a mensagem MySql Error 1005: Can’t create table (errno: 150), provavelmente o erro é algum desses itens:

* Os dois campos relacionados da Foreign Key não tem o mesmo tipo ou tamanho. Por exemplo, se um campo é INT(10) o campo da chave precisa ser INT(10) também, se for colocado INT(11) ou TINYINY já não serve. Você deve verificar esses dados usando a query “SHOW CREATE TABLE tblname;”. Também confirme se ambos os campos são ‘signed’ ou ‘unsigned’. Conclusão, os campos devem ser EXATAMENTE o mesmo!
* O campo do qual você deseja se referenciar em outra tabela não tem um índice (index) ou não é uma chave primária (primary key). Se um dos campos que você deseja criar a relação não for uma “primary key”, você deve criar um índice para ela.
* O nome da Foreign Key já existe em alguma chave. Confira se o nome de sua Foreign Key é única no seu banco de dados. Simplesmente adicione alguns caracteres aleatórios no fim do nome de sua key para testar essa condição.
* Uma ou ambas as tabelas são MyISAM. Para utilizar Foreign Key ambas as tabelas devem usar InnoDB. Normalmente quando ambas as tabelas são MyISAM o MySQL não apresenta nenhuma mensagem de erro, simplesmente não cria a chave.
* Você pode ter especificado “CASCADE ON DELETE SET NULL”, enquanto o campo é “NOT NULL”, esse é o erro mais comum.
* Se o seu script está funcionando perfeitamente em um server, mas não em outro, verifique o collate ou charset, eles podem dar problemas caso sejam diferentes.
* Você pode ter um valor default (ex. default=0) na sua coluna Foreign Key.
* O nome de sua Foreign Key excede o limite de no máximo 64 caracteres.

Fonte: http://diariodecodigos.info/2010/01/mysql-error-1005-can%E2%80%99t-create-table-errno-150/

janeiro 22, 2011 at 11:56 pm 1 comentário

Otimizando Javascript e CSS da aplicação

Andei pesquisando ultimamente sobre otimização de aplicações e web e encontrei algo que merece ser publicado, a redução do código Javascript e CSS. A idéia é transformar o código:

/*
Método que atualiza o valor de algum
atributo passado como parametro
*/
function updateField( field, value )
{
    //arqui entraria um comentário
	field.value = value;
	window.close();
	/* aqui vem outro
	 só que de bloco
	*/
}

Para:

function updateField(b,a){b.value=a;window.close()};

Esse pequeno pedaço de código já mostra uma diferença significativa, imagina agora para aqueles arquivos javascript enormes com todas as funções do seu sistema?
Se parar para analisar, os javascript que são fornecidos para Ajax, JQuery e coisas do gênero também fazem isso.
O Yahoo disponibiliza um jar (http://yuilibrary.com/downloads/#yuicompressor) que faz isso automático. Basta executar a linha de comando: java -jar yuicompressor-x.y.z.jar “nomeDoArquvo.js”, assim ele vai gerar na saída padrão o código reduzido. Com o parâmetro -o você escolhe o nome do novo arquivo a ser gerado.
Um problema de fazer isso durante o desenvolvimento é que se você precisar dar manutenção no código, esquece. Para resolver esse novo problema, eu fiz uma manutenção na target do build.xml que gera o EAR da aplicação. Ele faz um backup dos javascript, reduz todos os códigos e depois de gerar o WAR ele restaura os javascripts anteriores.

<!-- fazer o backup dos javascripts -->
<echo message="Criando a pasta de backup dos javascripts" />
<mkdir dir="${web.root.dir}/javascriptBackup" />
<echo message="Copiando os javascripts" />
<copy todir="${web.root.dir}/javascriptBackup">
	<fileset dir="${web.root.dir}/javascript/" />
</copy>

<!-- Minizando os javascripts -->
<echo message="Minimizando os javascripts" />
<taskdef name="for" classname="net.sf.antcontrib.logic.For" />
<for param="file">
	<path>
		<fileset dir="${web.root.dir}\javascript\" includes="*.js" />
	</path>
	<sequential>
		<apply executable="java" parallel="false">
			<fileset file="@{file}" />
			<arg line="-jar" />
			<arg line="ignorelib\yuicompressor-2.4.2.jar" />
			<arg line="-o" />
			<arg line="'@{file}'" />
			<arg line="'@{file}'" />
		</apply>

	</sequential>
</for>

<!-- Gerando o WAR ou EAR -->
<jar destfile="${build.result.folder}/${full.name}">
	<fileset dir="${web.root.dir}" includes="**/*.*" excludes="WEB-INF/classes/**/*.*" />
	<fileset dir="${web.root.dir}/../temp.folder" includes="**" />
	<manifest>
		<attribute name="Class-Path" value="${manifest.classpath}" />
	</manifest>
</jar>

<!-- Voltando o backup -->
<copy todir="${web.root.dir}/javascript" overwrite="true">
	<fileset dir="${web.root.dir}/javascriptBackup/" />
</copy>

<!-- Apagando o backup -->
<delete dir="${web.root.dir}/javascriptBackup/" />

Aqui só citei o caso do javascript, mas o CSS é da mesma forma.

[]‘s

setembro 22, 2010 at 5:39 pm 2 comentários

Comandos Oracle

Alguns comandos Oracle que volta e meia eu precisa dar uma googlada para encontrar:

Ver o código fonte de Views:
SELECT owner, text FROM all_views WHERE owner = ‘UWCLASS’ AND view_name = ‘PERSON_VIEW’;
ou
SELECT dbms_metadata.get_ddl(‘VIEW’, ‘PERSON_VIEW’) FROM dual;

Ver código fonte de Objetos:
SELECT * FROM user_source;

Ver objetos que estão inválidos no banco:
SELECT ‘alter ‘||object_type||’ ‘||object_name||’ compile;’ FROM user_objects WHERE status=’INVALID’;

Exportar o banco de dados:
EXP USERID=user/password FILE=C:\arquivo.DMP OWNER=user;

Importar um banco de dados: (lembrando que se foi feito por um usuário DBA, deve ser importado por um usuário DBA)
IMP USERID=user/password FILE=C:\dump.DMP FROMUSER=usuarioOrigem TOUSER=usuarioDestino FEEDBACK=5000 STATISTICS=NONE;

Depois junto mais e publico.

[]‘s

agosto 31, 2010 at 11:55 pm 5 comentários

Refresh reenviando os dados

Imagine o seguinte cenário: Você termina de preencher os campos de uma página de inclusão. Ao clicar em OK, o sistema envia o formulário e executa um forward para a página de listagem do seu cadastro com a mensagem: “Registro incluído com sucesso.”

No JSF (e imagino que em várias outras soluções MVC) quando você dá um refresh (F5), o sistema reenvia os dados que você preencheu na tela de cadastro e acaba incluindo um novo registro (ou tentando incluir, caso tenha problema com algum campo que não pode se repetir), e na atual listagem aparece dois dados iguais.

Andei pesquisando a maneira mais simples de não dar um forward após a inclusão do dado. Acabei caindo numa boa explicação do Rafael Pontes sobre a diferença entre o forward e redirect.

Vamos a solução:

Na regra de navegação, agora foi usada uma tag (<redirect />)na passagem do cadastro para a listagem:

<navigation-rule>
<from-view-id>/empresa/cadastroEmpresa.jsp</from-view-id>
<navigation-case>
<from-outcome>listagem</from-outcome>
<to-view-id>/empresa/listagemEmpresa.jsp</to-view-id>
<redirect />
</navigation-case>
</navigation-rule>

Com isso perdemos as nossas mensagens adicionadas ao messages através do método FacesContext.getCurrentInstance().addMessage(), já que essas mensagens são válidas só por requisição. A solução para contornar esse problema foi a utilização de alguma classe que tem o escopo de sessão. A escolhida no meu caso, foi a classe de login do usuário, que é a classe que ele carrega para “provar” que é um usuário cadastrado e logado no sistema. Dentro dessa classe incluí:

private List<String> mensagens;
public void showMensagens() {
if(this.mensagens==null)
this.mensagens = new ArrayList<String>();
for(String msg : this.mensagens)
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, msg, null));
this.mensagens = new ArrayList<String>();
}


public void addMensagens(String mensagem) {
if(this.mensagens==null)
this.mensagens = new ArrayList<String>();
this.mensagens.add(mensagem);
}

e na classe ancestral dos backbeans o seguinte método:

protected void addNewMessage(String msg) {
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
HttpSession session = (HttpSession) externalContext.getSession(false);
LoginCodebehind login = (LoginCodebehind) session.getAttribute("login");
login.addMensagens(msg);
}


protected void showMessages() {
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
HttpSession session = (HttpSession) externalContext.getSession(false);
LoginCodebehind login = (LoginCodebehind) session.getAttribute("login");
login.showMensagens();
}

O primeiro método busca no contexto o meu objeto de sessão login e adiciona a mensagem. Esse método será chamado após uma inclusão com sucesso como por exemplo:

public void actionGravar(ActionEvent event) {
try {
EmpresaBO.getInstance().create(empresaTO);
this.addNewMessage("Empresa cadastrada com sucesso.");
} catch (RegraNegocioException e) {
e.printStackTrace();
this.addErrorMessage(e);
throw new AbortProcessingException();
}

E o segundo método será chamado pelas telas de listagem, para exibirem as mensagem quando houverem.

public ListagemEmpresa() {
try {
this.showMessages();
empresaList = EmpresaBO.getInstance().findAll();
} catch (RegraNegocioException e) {
this.addErrorMessage(e);
}
}

Esse solução foi feito para exibir somente as mensagens de sucesso, já que teoricamente são as únicas que passam de uma página para a outra. Quando houver a necessidade de exibir mensagem de erro, não deverá mudar de tela, então vai exibir no mesmo request. Mas é possível adicionar um parâmetro nas chamadas para que seja acrescentada o tipo da mensagem.

[]‘s

setembro 29, 2008 at 3:34 am 1 comentário

“A ordem das cláusulas altera o resultado”

Sempre acreditei que a ordem das cláusulas numa consulta comum não interfere em nada no resultado. Porém, passei um dia investigando uma consulta que estava dando erro. A correção veio como de uma simples alteração na ordem das cláusulas. Difícil acreditar, né?
Esse erro aconteceu no Oracle, ao chegar em casa fui testar em outro e banco e pimba, aconteceu novamente. No SqlServer aconteceu o erro, já no mysql não consegui retratar.

Chega de embromação e vamos ao erro.
Vamos criar um simples tabela.

CREATE TABLE [tabela] (
[TIPO] [varchar](1),
[valor] [varchar] (3)
)

Na primeira coluna armazenaremos o tipo de dado que tem na seguda.

insert into tabela1 (tipo, valor) values (‘N’, ’1′);
insert into tabela1 (tipo, valor) values (‘N’, ’2′);
insert into tabela1 (tipo, valor) values (‘A’, ‘A’);
insert into tabela1 (tipo, valor) values (‘A’, ‘B’);

Agora uma simples consulta do tipo:

select * from tabela1
where 1=1
and convert(numeric, valor) > 0
and tipo = ‘N’

Gera o seguinte resultado:

Server: Msg 8114, Level 16, State 5, Line 1
Error converting data type varchar to numeric.

Mas mudando a ordem para :

select * from tabela1
where 1=1
and tipo = ‘N’
and convert(numeric, valor) > 0

Obtemos:

TIPO valor
—- —–
N 1
N 2
(2 row(s) affected)

Ou no Oracle:

select * from tabela
where 1=1
and to_number(valor) > 0
and tipo = ‘N’

Detalhes: Isso aconteceu na versão do Oracle do trabalho e não na versão da minha casa.

Qual a possível explicação?
O mais perto que conseguimos acreditar, é no famoso curto circuito que estudamos em PD1. Ou seja, as cláusulas são avaliadas em algum sentido (no sqlserver de cima para baixo e no oracle de baixo para cima) e quando encontram alguma expressão que é falsa, ele já descarta o registro que está sendo avaliado. No nosso caso ele corre o risco de gerar um erro antes da primeira cláusula falsa, o que corre o risco de parar todo um processamento envolvido.

Soluções?

Fazer subconsultas para que não corra o risco de encontrar um erro, causa overhead mas não causa erro.

Outra coisa que aprendi sobre esse assunto, é que a ordem das tabelas no FROM da consulta também interfere em algumas consultas. Pelo menos no oracle ele olha as tabelas de trás para frente, então o ideal é colocar as tabelas com menos registros no final da lista, para que ele possa diminuir o trabalho de juntar tudo.

Estou a espera de melhores explicações e melhores soluções.

[]‘s

agosto 27, 2008 at 3:16 am 1 comentário

Hello Blog!

Agora tenho um blog. Grande merda. Pretendo colocar dicas e descobertas do mundo da programação (leia-se java) e outras coisas da computação.

[]‘s

agosto 27, 2008 at 2:13 am 1 comentário


Categorias

  • Blogroll

  • Feeds


    Seguir

    Obtenha todo post novo entregue na sua caixa de entrada.