[Resolvido]JPA Update por SQL

Bom dia a todos, estou usando JPA para um projeto pequeno aqui na empresa e surgiu um necessidade de criar um sql de DELETE customizável então criei o seguinte SQL nativo que será executado pela JPA

String sql = "UPDATE sensores s SET id_particao=NULL "
                + "WHERE s.id_particao=?1";
///////////////////////
Query query = minhaEntityManager.createNativeQuery(sql, Sensor.class);
//parametros
query.setParameter(1, id_particao);
//Executo e mostro a resposta
System.out.println("Resultado final "+query.executeUpdate());

ao tentar executar o na última linha o seguinte erro ocorre

[quote]java.lang.IllegalStateException: You cannot call executeUpdate() on this query. It is the incorrect query type.
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.executeUpdate(EJBQueryImpl.java:528)
at persistence.SensorDAO.removerParticao(SensorDAO.java:186)
at persistence.ParticaoDAO.excluir(ParticaoDAO.java:97)…[/quote]

Queria saber se estou fazendo da maneira certa ou aonde estou errando?

Obs.: Uso EclipseLink e o código de SELECT roda dessa forma porém nunca tentei fazer com update nem delete, e pesquisando no google achei uma parte onde usava ‘ClassDescriptor’ porém não consegui implementa-la no meu código.

Tira o “1” aí após o ?
Essa consulta não dá para ser feito com jpql não ?
Para que nativeQuery. É só um update.

Então lele_vader obrigado pela resposta na minha alteração ficou dessa forma

String jpql = "UPDATE Sensor s SET s.idParticao = :novoid "
                    + "WHERE s.idParticao = :particao";
///////////////////////
Query q = minhaEntityManager.createQuery(jpql, Sensor.class);
//parametros
q.setParameter("novoid", null);
q.setParameter("particao", meuObjetoParticao);
///
System.out.println("Resultado final "+q.executeUpdate());

Também tentei somente removendo o ‘1’ que uso por causa de um bug do ‘LIKE’ do ‘SELECT’ porém o mesmo erro ocorre. Agora com o código que coloquei ai acima usando JPQL o erro muda para:

[quote]Exception Description: No transaction is currently active
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionWrapper.throwCheckTransactionFailedException(EntityTransactionWrapper.java:113)
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionWrapper.checkForTransaction(EntityTransactionWrapper.java:50)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.checkForTransaction(EntityManagerImpl.java:1776)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.executeUpdate(EJBQueryImpl.java:533)
at persistence.SensorDAO.removerParticao(SensorDAO.java:187)…[/quote]

Estou tentando outras combinações aqui. Se souber mais alguma coisa avisa aí, flw!

Esse erro já é diferente.
Ele diz que não tem transação ativa.
Como você instancia o entityManager ?
Está usando ejb ?

Outra coisa, não use concatenação de string em sql. Pode permitir sql injection.
Use stringbuilder e o método append para isso.

Beleza lele_vader agora o código que você me indicou estava certo procurei um pouco sobre o erro e foi preciso apenas adicionar 2 linhas, 1 antes de realizar o update e 1 depois para finalizar a transação, ficando assim o código final:

String jpql = "UPDATE Sensor s SET s.idParticao = :novoid " + "WHERE s.idParticao = :particao"; /////////////////////// Query q = minhaEntityManager.createQuery(jpql, Sensor.class); //parametros q.setParameter("novoid", null); q.setParameter("particao", meuObjetoParticao); /// minhaEntityManager.getTransaction().begin(); //iniciar transação System.out.println("Resultado final "+q.executeUpdate()); minhaEntityManager.getTransaction().commit(); //salvar estado

Vlw demais ajudou muito, bom fim de semana!

Calma ai.
Depois conserta umas coisas.
1- Tirar concatenação de string no sql.
Coloca assim:

StringBuilder sql = new StringBuilder();
sql.append(""UPDATE Sensor s SET s.idParticao = :novoid");
sql.append("WHERE s.idParticao = :particao");
Query q = minhaEntityManager.createQuery(sql.toString(), Sensor.class);

2-Colocar rollback em catch e finally fechar o entityManager, considerando que você não está usando um ejb para fazer isso para você.

Para criar a meu EntityManager uso uma classe genérica que possui os métodos CRUD inclusive o de instanciar a EntityManager, caso personalize que nem agora crio outra classe que herde dessa, o método que uso é representado abaixo:

public EntityManagerFactory getEMF() throws Exception{ try{ if (emf == null || !emf.isOpen()) { emf = Persistence.createEntityManagerFactory("AppDesktopGerenciaPU"); } return emf; }catch(Exception e){ Logger.getLogger(BaseDAO.class.getName()).log(Level.SEVERE, null, e); throw new Exception(Mensagens.erroConectarBanco); } }

depois só o chamo caso for realizar alguma transação

[code]protected EntityManager em;

em = getEMF().createEntityManager();
[/code]

Outra coisa do modo que estou usando já não está fazendo a concatenação?

Vou mostrar o código por completo com algumas alterações propostas, me corrija caso haja algum erro:

public void removerParticao(Particao particao) throws Exception{ em = getEMF().createEntityManager(); try { StringBuilder jpql = new StringBuilder(); jpql.append("UPDATE Sensor s SET s.idParticao = :novoid "); jpql.append("WHERE s.idParticao = :particao"); /////////////////////// Query q = em.createQuery(jpql.toString(), Sensor.class); //parametros q.setParameter("novoid", null); q.setParameter("particao", particao); /// em.getTransaction().begin(); System.out.println("Resultado final "+q.executeUpdate()); em.getTransaction().commit(); } catch (Exception e) { try{ em.getTransaction().rollback(); }catch(Exception ex){} Logger.getLogger(BaseDAO.class.getName()).log(Level.SEVERE, null, e); throw new Exception(Mensagens.erroRemoverParticaoDoSensor); } finally { em.close(); emf.close(); } }

flw!

A entityManagerFactory só precisa ser instanciada uma vez.
Não precisa a cada transação recriá-la, precisa pegar é a entityManager, igual você faz em createEntityManager()

O seu projeto é web, usa spring ?
Porque se sim não precisa fazer isso que o spring gerencia isso para você, ou se estiver usando um ejb.

[quote=lele_vader]A entityManagerFactory só precisa ser instanciada uma vez.
Não precisa a cada transação recriá-la, precisa pegar é a entityManager, igual você faz em createEntityManager()

O seu projeto é web, usa spring ?
Porque se sim não precisa fazer isso que o spring gerencia isso para você, ou se estiver usando um ejb.

[/quote]

Então estou mexendo com um projeto Java Desktop ao chamar minha instância eu chamo o método getEmf() que está na minha classe genérica BaseDao lá também tem 2 objetos que são

protected EntityManager em;
protected EntityManagerFactory emf = null;

e guardo as instancias lá porém sempre fecho a conexão no finally e recrio-a caso for usa-la novamente. As vezes também deixo um atributo booleano que representa se eu quero ou não fechar a conexão caso ‘false’ não fecho a conexão reaproveitando a conexão já aberta.

Ah.Não tinha visto.
Então o entityManagerFactory você não precisa fechar.

O seu método getEMF pode ver se o entityManagerFactory é null.
Se sim você o cria, senão só retorna o já existente.

[quote=lele_vader]Depois conserta umas coisas.
1- Tirar concatenação de string no sql.
Coloca assim:

StringBuilder sql = new StringBuilder();
sql.append(""UPDATE Sensor s SET s.idParticao = :novoid");
sql.append("WHERE s.idParticao = :particao");
Query q = minhaEntityManager.createQuery(sql.toString(), Sensor.class);

[/quote]
Isto é desnecessário. O próprio compilador fará isso. Alias, não tendo nenhuma variável na String, é mais eficaz deixar o + do que usar o append, já que o compilador tratará como uma única String e não precisa de StringBuilder.

Se você concatenar string a cada string concatenada ele cria outra.
String é imutável.

Não disse nada que contrariasse isso. Simplesmente neste caso concreto o StringBuilder não só não é melhor como é mesmo pior do que usar String.