Gravar imagem no banco de dados - Hibernate+VRaptor

Pessoal bom dia, terminei a apostila do vraptor e vi que na apostila ensina fazer métodos separados para gravar a imagem no disco, eu queria fazer a imagem gravar no banco de dados pois minha vontade é fazer um módulo GED no meu sistema, mas não sei qual melhor forma de fazer isso, tentei desta forma.

Entidade

@NotEmpty private String nome; @NotEmpty private String descricao; private Double preco; @Lob private byte[] imagem;

1° problema, no banco de dados o @Lob cria um varchar(255)

Como eu poderia salvar o binário do arquivo no banco de dados, e recuperar como imagem, tudo isso sem depender do disco, tudo via memória.


Meu tipo de dados estaria correto? ow eu poderia utilizar um tipo File? ow então UploadFile, meio sem lógica né?

Estou um pouco perdido, agradeceria se alguem podesse me auxiliar.

Obrigado.

Qual banco está usando?

Nesse caso quero que seja multibanco já que estou utilizando hibernate, existe essa possibilidade?

Mas no momento estou usando mysql

A funcionalidade de gerar DDL do Hibernate é experimental, como você pode ler na documentação, e não deve ser usado em produção. É somente para testes. Tanto que acontecem essas coisas bizarras como @Lob como varchar.

A solução é simples: vai lá no MySQL e altera o campo para blob ou seus derivados (longblob, etc).

Certo garcia-jj, obrigado pela opinião agora como eu faria para gravar e recuperar o uploadfile no banco de dados?

O hibernate vai te ajudar no processo de gravar no banco de dados, agora como vc está fazendo isso? É web com JSF?

No caso do JSF 2.0 eu fiz isso usando o primefaces, meu xhtml ficou assim

<h:outputLabel for="tipo" value="Tipo de Documento*:"/>
                        <h:selectOneMenu id="tipo" value="#{documentoBean.documento.tpdCodigo.tpdCodigo}"
                                         requiredMessage="Preenchimento requerido">
                            <f:selectItems value="#{documentoBean.tipos}" />											
                        </h:selectOneMenu>
                        <p:message for="tipo" />


                        <h:outputLabel for="arquivos" value="Documentos*: " />
                        <p:fileUpload id="arquivos" label="Selecionar" 
                                      multiple="false" auto="true" 
                                      fileUploadListener="#{documentoBean.fileUploadAction}"/>
                        <p:message for="arquivos" />

                    </h:panelGrid>

                    <p><h:commandButton value="Salvar"
                                        actionListener="#{documentoBean.adicionar}"
                                        action="list" styleClass="" /> 
                        <h:commandButton value="Cancelar"
                                         immediate="true"
                                         onclick="return confirm('Deseja cancelar o estado sendo editado?')"
                                         action="list"
                                         styleClass=""/>   
                    </p>

e no managed bean eu pego o que o fileUpload manda desse jeito

 @Override
    public void prepararAlterar(ActionEvent evt) {
    }

    public void fileUploadAction(FileUploadEvent event) throws Exception {       
        byte[] array = event.getFile().getContents();

        getDocumento().setArquivo(array);
        getDocumento().setNome(event.getFile().getFileName());
    }
 

O @Override é por causa de uma interface que meu bean implementa mais a ideia e pegar o arquivo e converter em array de bytes, isso a inteface do prime faz, depois eu seto isso no setArquivo que está mapeado como

private byte[] arquivo;

depois e só dar o save. Para recuperar eu fiz o seguinte.

public String fileDownloadAction() throws Exception {        
        FacesContext context = FacesContext.getCurrentInstance();
        
        setDocumento(dDao.findById(new Integer(getDocCod().getValue().toString()), false));

        byte[] arquivoBytes = getDocumento().getArquivo();
        context.getExternalContext().setResponseHeader("Content-disposition", "attachment;filename=" + getDocumento().getNome());
        context.getExternalContext().setResponseContentLength(arquivoBytes.length);

        context.getExternalContext().getResponseOutputStream().write(arquivoBytes, 0, arquivoBytes.length);
        context.getExternalContext().getResponseOutputStream().flush();
        context.getExternalContext().getResponseOutputStream().close();

        context.responseComplete();
        return "list";
    }

e na página xhtml

<h:commandLink action="#{documentoBean.fileDownloadAction}" value="baixar"/> 

clicando no commandLink ele busca o arquivo na base de dados, joga em um outputStream e força o download. Mas não sei como você está pretedendo fazer.

kivervinicius, eu acho realmente muito melhor se você gravar a imagem em disco mesmo, pois as queries vão ficar realmente lentas e pesadas, pode dar java heap space facilmente.
Eu sugiro a seguinte implementação:

Você recebe a imagem pelo método, então vc gera um código MD5 à partir dos bytes dessa imagem, assim você terá 1 código único por imagem e sempre que vier a mesma imagem você consegue descobrir pelo MD5 dela.

O código MD5 vai ter sempre 32 caracteres (tem que tomar cuidado quando gera, pois ele corta os zeros às esquerdas, precisa adicionar manualmente).

Ai você cria uma pasta dentro de WEB-INF chamada imagensMD5 e, cria um método que gera o caminho do MD5 separando de 2 caracteres em 2 caracteres e na última pasta você coloca a imagem.

Sugiro, também, que você renomeie a imagem para o código MD5 dela e grave no banco somente o código (varchar 32 caracteres). Assim você vai saber sempre qual é a imagem e onde ela está.

Para conseguir gravar ela na pasta dentro de WEB-INF basta receber ServletContext no construtor e pegar o path.
Já que a pasta WEB-INF não é acessível por urls normais, você deve retornar a imagem em um método que busca ela dentro de WEB-INF.
Esse mesmo método só será acessado pelo usuário que estiver logado.

Inclusive, se você quiser que os usuários não tenham acesso às imagens dos outros, você pode criar uma pasta dentro de imagensMD5 com o ID do usuário e colocar as imagens dentro dela.
Cada usuário só acessa a própria pasta.

Para exibir a imagem em uma página:

private StreamedContent foto;

try{
			FotoPK id = new FotoPK();
			id.setPesCodigo(getInscricao().getId().getPesCodigo());
			
			Foto img = fotoRep.findById(id);
		    setFoto(new DefaultStreamedContent(new ByteArrayInputStream(img.getFoto())));
		}catch(Exception e){
			
		}
<p:graphicImage alt="foto" value="#{historicoBean.foto}" height="40%" width="55%" style="float:left;"/>

ainda com primefaces.

Todas idéias são ótimas, mas será que não é viável mesmo não o uso de imagens no banco? Por serem arquivos confidenciais, (Sistema juridico) imagino que seria a melhor forma de segurança, gravar em bytes e recuperar depois os bytes e retomar o type original.

Não entendo a questão da segurança , os arquivos não estariam no servidor ? Eu fiz um misto entre imagem no banco e no disco, como as imagens viriam de outro sistema eu carregava esta imagem do banco de dados e salvava no disco , enquanto o arquivo existir no disco eu não carrego ela do banco de dados , e ao salvar uma nova imagem eu salvo no disco e no banco de dados .