Oi pessoal!
Tenho a url das imagens no banco e preciso que ao deletar os dados do imóvel e das fotos, as imagens da pasta tb sejam deletadas:
O método que faz a remoção do imóvel é este:
public void remove(Long idImovel) {
Imovel imovel = imovelDao.carrega(idImovel);
imovelDao.remove(imovel);
imagens.deleta(imovel); // aqui envia o imovel para o componente Imagens e assim deletar a imagem da pasta
// mensagem de Imovel removido com sucesso
result.include("mensagem", "Imovel removido com sucesso");
// redirecionamento para a listagem de imoveis
result.use(Results.logic()).redirectTo(ImovelController.class).lista();
}
O método deletar do componente:
public void deleta(Imovel imovel) {
// Preciso de um código para recuperar as URL's das imagens que estão no banco e deletar conforme abaixo
// Talvez precise de uma List de objetos fotos
File destino = new File(pastaImagens, urlImagem);
try {
destino.delete();
} catch (Exception e) {
throw new RuntimeException("Erro ao deletar imagem", e);
}
}
O miolo ali no método deleta que eu não estou conseguindo fazer, já que a única coisa que é enviada para o método deleta é “imovel”, precisaria na verdade das URL’s das imagens que estão no banco, sendo que ao deletar o imóvel os dados na tabela foto tb são deletados automáticamente, ai fico sem o caminho para deletar as imagens que estão na pasta.
Tentei algo do tipo:
public void deleta(Imovel imovel) {
File destino = new File(pastaImagens, imovel.getIdImovel() + "." + foto.getNome() + "." + imagem.getFileName());
try {
destino.delete();
} catch (Exception e) {
throw new RuntimeException("Erro ao deletar imagem", e);
}
}
Obviamente não vai funcionar nunca, pois somente o imóvel é passado como parãmetro, faltando o resto.
Alguém pode dar uma dica?
Abraço!!
Não dá pra vc deletar primeiro as imagens da pasta e depois partir pra deleção no banco?
deleta a imagem primeiro, e deixa um getter dentro do Imovel pra pegar o File da imagem … não precisa estar no banco…
Embora eu nunca trabalhe com acesso ao filesystem, acho errado você excluir primeiro a imagem do filesystem e depois do banco. Afinal, se der algum problema na base você faz rollback na base e o registor continua lá, porém a imagem já foi excluída do filesystem, e assim você fica com a base de dados quebrada.
Se você excluir primeiro o registro no banco e depois a imagem no filesystem, caso der erro ao excluir o registro a imagem e o registro permanecem no banco e integros. E se der erro ao excluir a mensagem, a transação não será efetivada (desde que você trate) e o registro permanecerá lá no banco, assim como a imagem.
No caso do problema do colega Guevara, você deve primeiro armazenar em alguma variável os dados da imagem que você quer apagar, depois excluir o registro. Se você não entender, me avise.
Oi pessoal!
Lucas, o problema que eu estou encontrando é como implementar esse método de remoção, quanto a ordem de remoção isso poderia ser tratado numa estrutura try/catch checando se a id do imovel bate com a fk do imovel na tabela Foto e se estiver no banco mandar deletar tanto da pasta como do banco.
Algo do tipo:
public void remove(Long idImovel) throws Exception {
try {
if (foto.getImovel().equals(idImovel)) {
Imovel imovel = imovelDao.carrega(idImovel);
imovelDao.remove(imovel);
imagens.deleta(imovel);
}
} catch (Exception e) {
throw new Exception("erro ao deletar imovel", e);
}
// mensagem de Imovel removido com sucesso
result.include("mensagem", "Imovel removido com sucesso");
// redirecionamento para a listagem de imoveis
result.use(Results.logic()).redirectTo(ImovelController.class).lista();
}
Mas preciso do método “deleta” lá no componente Imagens. =/
Se alguém, tiver uma idéia melhor do que essa agradeço a colaboração. =)
Garcia, para resolver esse ponto que vc citou a estrutura que eu montei logo acima não resolveria?
O que mais me preocupa é o método pra deletar, pois preciso deletar todas as imagens com a id do imóvel, sendo que para o componente Imagens só é passado o parâmetro imóvel. =/
Abraço!!
se o Imagens é um @Component é só receber o dao de imagens no construtor
Pois é Lucas, eu fiz isso, só têm um probleminha na FotoDAO, é que para recuperar a url das fotos a ID do imovel é uma FK, não posso pegar usando esta criteria:
public String getUrlFoto(Long idImovel) {
Criteria c = this.session.createCriteria(Foto.class);
c.add(Restrictions.eq("idImovel", idImovel));
c.setProjection(Projections.max("urlFoto"));
return (String) c.uniqueResult();
}
Precisaria de um:
select url_foto FROM foto WHERE imovel_id_imovel = id_imovel
Conseguindo montar essa criteria mandaria o resultado para o componente injetando no construtor e o método deleta teóricamente funcionaria, permitindo assim o método “remove” do ImovelController funcionar tb.
Estou no caminho certo ou existe outra forma de fazer isto?
Vc tem um relacionamento configurado com @ManyToOne ou @OneToOne?
se sim, vc pode fazer a restriction:
Restrictions.add("imovel.id", idImovel)
ou trocar tudo pra HQL (que eu acho mais fácil e melhor)
return session.createQuery("select f.urlFoto from Foto f where f.imovel.id = :id")
.setParameter("id", idImovel)
.uniqueResult();
Não deu boa Lucas. =/
O método de listar está retornando null pro FotoDAO, ai fiz de uma forma diferente, carrego um objeto foto e mando para o componente:
public void remove(Long idImovel) {
Imovel imovel = imovelDao.carrega(idImovel);
Foto foto = fotoDAO.carrega(idImovel);
imagens.deleta(foto);
imovelDao.remove(imovel);
// mensagem de Imovel removido com sucesso
result.include("mensagem", "Imovel removido com sucesso");
// redirecionamento para a listagem de imoveis
result.use(Results.logic()).redirectTo(ImovelController.class).lista();
}
No componente extraio a url e armazeno numa variável “caminho”:
public void deleta(Foto foto) {
String caminho = foto.getUrlFoto();
File destino = new File(pastaImagens, caminho );
try {
destino.delete();
} catch (Exception e) {
throw new RuntimeException("Erro ao deletar imagem", e);
}
}
Agora, pra isso funcionar preciso carregar um objeto foto pela fk e não pela id de foto e sim pelo id de imóvel:
public Foto carrega(Long idImovel) {
return (Foto) this.session.load(Foto.class, idImovel); // aqui é que está errado, teria que carregar foto pela fk imovel_id_imovel.
}
Parece que se esse método “carrega” funcionar, o resto vai funcionar. =)
ussa isso no seu método carrega:
return session.createQuery("select f.urlFoto from Foto f where f.imovel.id = :id")
.setParameter("id", idImovel)
.uniqueResult();
vc só precisa do relacionamento configurado
Deu certo Lucas, mas com esse parâmetro .uniqueResult(); só apaga uma foto da pasta? E se tiver mais fotos lá?
Não pude testar isso pq o displaytag tá listando errado, tenho um imóvel com três fotos, no banco está correto e na pasta tb, mas na hora de listar o displaytag tá listando três imóveis, quando na verdade existe apenas um. Creio que seja pelo fato de estar pegando as fotos junto, ai ele entende três imóveis. =/
se um imóvel tem várias fotos é só trocar uniqueResult() por list() e apagar todas
Lucas, o método só consegue apagar um imagem:
org.hibernate.NonUniqueResultException: query did not return a unique result: 5
Esse método que vc passou vou aproveitar para deletar uma imagem apenas, mas para deletar todas preciso de todas as url’s de foto do imóvel, fiz as alterações:
public List<Foto> getFotos(Long idImovel) {
return session.createQuery("select f.urlFoto from Foto f where f.imovel.id = :id")
.setParameter("id", idImovel).list();
}
public void remove(Long idImovel) {
Imovel imovel = imovelDao.carrega(idImovel);
//Remove as fotos do imovel
//String caminho = fotoDAO.getUrlFoto(idImovel);
List<Foto> caminho = fotoDAO.getFotos(idImovel);
imagens.deleta(caminho);
imovelDao.remove(imovel);
}
Agora, no método deleta lascou:
public void deleta(List<Foto> caminho) {
File destino = new File(pastaImagens, caminho );
try {
destino.delete();
} catch (Exception e) {
throw new RuntimeException("Erro ao deletar imagem", e);
}
}
O “caminho” não pode ser instanciado sendo que ele agora é uma Lista de url. =/
[quote=Guevara]Agora, no método deleta lascou:
public void deleta(List<Foto> caminho) {
File destino = new File(pastaImagens, caminho );
try {
destino.delete();
} catch (Exception e) {
throw new RuntimeException("Erro ao deletar imagem", e);
}
}
O “caminho” não pode ser instanciado sendo que ele agora é uma Lista de url. =/
[/quote]
public void deleta(List<Foto> caminhos) {
for(String caminho: caminhos) {
File destino = new File(pastaImagens, caminho );
try {
destino.delete();
} catch (Exception e) {
throw new RuntimeException("Erro ao deletar imagem", e);
}
}
}
Oi Garcia!
Na linha:
for(String caminho: caminhos)
Está mandando renomear o caminho para tipo Foto:
for(Foto caminho: caminhos)
Se renomear dá zica na linha para instanciar o caminho.
Guevara, desculpe, acabei me perdendo. O correto é usar Foto ao invés de String, já que você tem List de Foto.
Mas a sua IDE deveria reclamar disso e corrigir, não?
public void deleta(List<Foto> fotos) {
for(Foto foto: fotos) {
File destino = new File(pastaImagens, foto.getCaminho()); // ver aqui como pega o caminho
try {
destino.delete();
} catch (Exception e) {
throw new RuntimeException("Erro ao deletar imagem", e);
}
}
}
Pois é, a IDE não está me dando a opção de converter essa lista, agora ao rodar aparece o erro:
java.lang.ClassCastException: java.lang.String cannot be cast to br.com.imobiliaria.bean.Foto
br.com.imobiliaria.component.Imagens.deleta(Imagens.java:54)
É justamente nesta linha:
for(Foto foto: fotos)
Guevara, eu não tenho muito como adivinhar/descobrir quais são os tipos corretos. Mas isso você pode fazer.
O segredo aqui é que você tem uma lista de fotos, correto? Como para você excluir você precisa ter o caminho da foto, então você precisa percorrer a lista de fotos, pegar o caminho da imagem e excluir a foto.
Se você está recebendo List como argumento não há como ter esse class-cast.
Hmm, agora entendi… você está fazendo o return type do método da DAO errado.
public List<Foto> getFotos(Long idImovel) {
return session.createQuery("select f.urlFoto from Foto f where f.imovel.id = :id")
.setParameter("id", idImovel).list();
}
Você está fazendo um select em urlFoto que é String, porém você mascara isso como List. Altere seu dao para List e propague as alterações para o método Imagens.deleta.
Achei esta dica com o Iterator, creio que esta é a forma de fazer:
http://www.javapractices.com/topic/TopicAction.do?Id=125
Implementei aqui:
public void deleta(List<Foto> fotos) {
for(Iterator<String> it = fotos.iterator(); it.hasNext();) {
File destino = new File(pastaImagens, it.hasNext());
try {
destino.delete();
} catch (Exception e) {
throw new RuntimeException("Erro ao deletar imagem", e);
}
}
}
Mas não está aceitando, diz que o Iterator não é genérico, não entendi essa, se tenho url como String deveria aceitar não?
Pelo menos, as url estão em “fotos”.
Agora que eu vi seu último post, vou lá conferir no DAO