JPA não reconhece atualização realizada direta no BD

5 respostas
J

Pessoal estou usando JPA(EclipseLink) com Glassfish.

PROBLEMA:
A primeira vez que faço o select dos registros com status = INSERIDO, ele recupera 5 registros e faz a atualização do status para ENVIADO e tudo belezinha. Mas se eu alterar o status desses registros no banco de dados ‘na mão’ para INSERIDO, logo em seguida a essa atualização e rodar o método de novo acontece uma coisa estranha(pelo menos pra mim… hehehe :p)
O que acontece dessa segunda vez é:
Qdo faço o select no banco, ele traz os 5 registros que estão com status = INSERIDO (atualizado por mim, ‘na mão’, direto no banco de dados). Mas qdo dou um System.out.println no status desses registros, ele imprime ENVIADO e, logicamente, qdo vai fazer o merge, o campo STATUS não é atualizado, visto que para o JPA esse registro já estava com status ENVIADO (o que é mentira, pois no banco ele está com status INSERIDO).

Segue as classes que fazem parte do sistema e mais abaixo a saída do console da primeira e da segunda atualização.

Agradeço demais a quem puder me dar uma luz sobre esse assunto… Obrigado a todos.

JMSInicio

@RequestScoped
@Named(value="jmsInicio")
public class JMSInicio {
    @EJB()
    CadastroGeralLocal persCadastros;
    
    List<LocCadastroGeral> listaLocCadastroGeral = new ArrayList<LocCadastroGeral>();

    public List<LocCadastroGeral> enviarRegistrosInseridos() {
        listaLocCadastroGeral = new ArrayList<LocCadastroGeral>();
        System.out.println("Pesquisa Itens A Serem Enviados");
        listaLocCadastroGeral = persCadastros.getCadastrosPendentes(StatusMsgType.INSERIDO, DadosFilas.qtdeMsgPacote);
        System.out.println("Tamanho: " + listaLocCadastroGeral.size());
        for (LocCadastroGeral cad : listaLocCadastroGeral) {
            System.out.println("STATUS DO OBJETO RECUPERADO: " + cad.getLocCadastroLocal().getStatus());
            cad = persCadastros.atualizaCadastroEnviado(cad);
        }

        return listaLocCadastroGeral;
    }
}

CadastroGeralLocal

@Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRED)
@TransactionManagement(TransactionManagementType.CONTAINER)
public class CadastroGeralLocal extends BasicSession {

    public List<LocCadastroGeral> getCadastrosPendentes(StatusMsgType tipoStatus, Integer qtde) {
        Query q = getEm().createQuery("select cad "
                + "from LocCadastroGeral cad "
                + "where cad.locCadastroLocal.idCadastroServidor is null "
                + "and cad.locCadastroLocal.status = ?1 "
                + "order by cad.locCadastroLocal.numTentativas, cad.locCadastroLocal.dtInsert");
        q.setParameter(1, tipoStatus);
        if (qtde > 0) q.setMaxResults(qtde);
        return q.getResultList();
    }

    public LocCadastroGeral atualizaCadastroEnviado(LocCadastroGeral cad) {
        System.out.println("ENTROU atualizaCadastroEnviado - ATUALIZANDO campos");
        cad.getLocCadastroLocal().setStatus(StatusMsgType.ENVIADO);
        cad.getLocCadastroLocal().setNumTentativas( cad.getLocCadastroLocal().getNumTentativas() + 1 );
        cad = atualizar(cad);
        System.out.println("CADASTRO MUDOU PARA STATUS: " + cad.getLocCadastroLocal().getStatus());
        return cad;
    }

    public LocCadastroGeral atualizar(LocCadastroGeral cad) {
        System.out.println("VAI REALIZAR MERGE - STATUS DO CADASTRO: " + cad.getLocCadastroLocal().getStatus());
        cad = getEm().merge(cad);
        System.out.println("REALIZOU MERGE");
        return cad;
    }

}

LocCadastroGeral

@Entity
@Table(name = "loc_cadastro_geral", catalog = "db_civil", schema = "local")
public class LocCadastroGeral implements Serializable {
    private static final long serialVersionUID = 1L;

    @OneToOne(cascade = CascadeType.ALL, mappedBy = "locCadastroGeral", fetch=FetchType.EAGER)
    private LocCadastroLocal locCadastroLocal;

}

LocCadastroLocal

@Entity
@Table(name = "loc_cadastro_local", catalog = "db_civil", schema = "local")
public class LocCadastroLocal implements Serializable {
    private static final long serialVersionUID = 1L;

    @JoinColumn(name = "fk_cadastro_local", referencedColumnName = "id_cadastro_local", insertable = false, updatable = false)
    @OneToOne(optional = false)
    private LocCadastroGeral locCadastroGeral;
}
Saída Console - 1a vez

[i]

INFO: Pesquisa Itens A Serem Enviados

BOM: SELECT t1.id_cadastro_local AS id_cadastro_local1, t1.fk_naturalidade_estado AS fk_naturalidade_estado2, t1.fk_naturalidade_cidade AS fk_naturalidade_cidade3, t1.num_cedula_foto AS num_cedula_foto4, t1.sexo AS sexo5, t1.rg AS rg6, t1.end_comp AS end_comp7, t1.cert_tipo AS cert_tipo8, t1.cer_livro AS cer_livro9, t1.end_cidade AS end_cidade10, t1.impresso AS impresso11, t1.cep AS cep12, t1.cpf AS cpf13, t1.rg_ric AS rg_ric14, t1.dt_nascimento AS dt_nascimento15, t1.end_estado AS end_estado16, t1.cert_folha AS cert_folha17, t1.fk_via AS fk_via18, t1.num_cedula_texto AS num_cedula_texto19, t1.nm_mae AS nm_mae20, t1.pis_pasep AS pis_pasep21, t1.cert_dt_exp AS cert_dt_exp22, t1.nm_pai AS nm_pai23, t1.fk_pagamento AS fk_pagamento24, t1.matricula AS matricula25, t1.doador AS doador26, t1.cert_num AS cert_num27, t1.cartorio AS cartorio28, t1.end_num AS end_num29, t1.nome AS nome30, t1.foto AS foto31, t1.endereco AS endereco32 FROM db_civil.local.loc_cadastro_local t0, db_civil.local.loc_cadastro_geral t1 WHERE (((t0.id_cadastro_servidor IS NULL) AND (t0.status = ?)) AND (t0.fk_cadastro_local = t1.id_cadastro_local)) ORDER BY t0.num_tentativas ASC, t0.dt_insert ASC LIMIT ? OFFSET ?

bind => [0, 5, 0]

BOM: SELECT fk_cadastro_local, num_tentativas, dt_insert, status, id_cadastro_servidor, fk_posto_identificacao FROM db_civil.local.loc_cadastro_local WHERE (fk_cadastro_local = ?)

bind => [52]

BOM: SELECT id_posto_identificacao, email_prefeito, cel_prefeito, tel_prefeito, descricao, VERSION, bairro, cep, impressao, email, responsavel, end_num, end_comple, tefefone, endereco, prefeito, celular FROM db_civil.local.loc_posto_identificacao WHERE (id_posto_identificacao = ?)

bind => [2]

BOM: SELECT fk_cadastro_local, num_tentativas, dt_insert, status, id_cadastro_servidor, fk_posto_identificacao FROM db_civil.local.loc_cadastro_local WHERE (fk_cadastro_local = ?)

bind => [125]

BOM: SELECT fk_cadastro_local, num_tentativas, dt_insert, status, id_cadastro_servidor, fk_posto_identificacao FROM db_civil.local.loc_cadastro_local WHERE (fk_cadastro_local = ?)

bind => [89]

BOM: SELECT fk_cadastro_local, num_tentativas, dt_insert, status, id_cadastro_servidor, fk_posto_identificacao FROM db_civil.local.loc_cadastro_local WHERE (fk_cadastro_local = ?)

bind => [132]

BOM: SELECT id_posto_identificacao, email_prefeito, cel_prefeito, tel_prefeito, descricao, VERSION, bairro, cep, impressao, email, responsavel, end_num, end_comple, tefefone, endereco, prefeito, celular FROM db_civil.local.loc_posto_identificacao WHERE (id_posto_identificacao = ?)

bind => [1]

BOM: SELECT fk_cadastro_local, num_tentativas, dt_insert, status, id_cadastro_servidor, fk_posto_identificacao FROM db_civil.local.loc_cadastro_local WHERE (fk_cadastro_local = ?)

bind => [56]

INFO: Tamanho: 5

INFO: STATUS DO OBJETO RECUPERADO: INSERIDO
INFO: ENTROU atualizaCadastroEnviado - ATUALIZANDO campos
INFO: VAI REALIZAR MERGE - STATUS DO CADASTRO: ENVIADO
INFO: REALIZOU MERGE
INFO: CADASTRO MUDOU PARA STATUS: ENVIADO
BOM: UPDATE db_civil.local.loc_cadastro_local SET num_tentativas = ?, status = ? WHERE (fk_cadastro_local = ?)
bind => [35, 1, 52]

INFO: STATUS DO OBJETO RECUPERADO: INSERIDO
INFO: ENTROU atualizaCadastroEnviado - ATUALIZANDO campos
INFO: VAI REALIZAR MERGE - STATUS DO CADASTRO: ENVIADO
INFO: REALIZOU MERGE
INFO: CADASTRO MUDOU PARA STATUS: ENVIADO
BOM: UPDATE db_civil.local.loc_cadastro_local SET num_tentativas = ?, status = ? WHERE (fk_cadastro_local = ?)
bind => [47, 1, 125]

INFO: STATUS DO OBJETO RECUPERADO: INSERIDO
INFO: ENTROU atualizaCadastroEnviado - ATUALIZANDO campos
INFO: VAI REALIZAR MERGE - STATUS DO CADASTRO: ENVIADO
INFO: REALIZOU MERGE
INFO: CADASTRO MUDOU PARA STATUS: ENVIADO
BOM: UPDATE db_civil.local.loc_cadastro_local SET num_tentativas = ?, status = ? WHERE (fk_cadastro_local = ?)
bind => [48, 1, 89]

INFO: STATUS DO OBJETO RECUPERADO: INSERIDO
INFO: ENTROU atualizaCadastroEnviado - ATUALIZANDO campos
INFO: VAI REALIZAR MERGE - STATUS DO CADASTRO: ENVIADO
INFO: REALIZOU MERGE
INFO: CADASTRO MUDOU PARA STATUS: ENVIADO
BOM: UPDATE db_civil.local.loc_cadastro_local SET num_tentativas = ?, status = ? WHERE (fk_cadastro_local = ?)
bind => [48, 1, 132]

INFO: STATUS DO OBJETO RECUPERADO: INSERIDO
INFO: ENTROU atualizaCadastroEnviado - ATUALIZANDO campos
INFO: VAI REALIZAR MERGE - STATUS DO CADASTRO: ENVIADO
INFO: REALIZOU MERGE
INFO: CADASTRO MUDOU PARA STATUS: ENVIADO
BOM: UPDATE db_civil.local.loc_cadastro_local SET num_tentativas = ?, status = ? WHERE (fk_cadastro_local = ?)
bind => [49, 1, 56]
[/i]

Saída Console - 2a atualização (fail)

[i]

INFO: Pesquisa Itens A Serem Enviados

BOM: SELECT t1.id_cadastro_local AS id_cadastro_local1, t1.fk_naturalidade_estado AS fk_naturalidade_estado2, t1.fk_naturalidade_cidade AS fk_naturalidade_cidade3, t1.num_cedula_foto AS num_cedula_foto4, t1.sexo AS sexo5, t1.rg AS rg6, t1.end_comp AS end_comp7, t1.cert_tipo AS cert_tipo8, t1.cer_livro AS cer_livro9, t1.end_cidade AS end_cidade10, t1.impresso AS impresso11, t1.cep AS cep12, t1.cpf AS cpf13, t1.rg_ric AS rg_ric14, t1.dt_nascimento AS dt_nascimento15, t1.end_estado AS end_estado16, t1.cert_folha AS cert_folha17, t1.fk_via AS fk_via18, t1.num_cedula_texto AS num_cedula_texto19, t1.nm_mae AS nm_mae20, t1.pis_pasep AS pis_pasep21, t1.cert_dt_exp AS cert_dt_exp22, t1.nm_pai AS nm_pai23, t1.fk_pagamento AS fk_pagamento24, t1.matricula AS matricula25, t1.doador AS doador26, t1.cert_num AS cert_num27, t1.cartorio AS cartorio28, t1.end_num AS end_num29, t1.nome AS nome30, t1.foto AS foto31, t1.endereco AS endereco32 FROM db_civil.local.loc_cadastro_local t0, db_civil.local.loc_cadastro_geral t1 WHERE (((t0.id_cadastro_servidor IS NULL) AND (t0.status = ?)) AND (t0.fk_cadastro_local = t1.id_cadastro_local)) ORDER BY t0.num_tentativas ASC, t0.dt_insert ASC LIMIT ? OFFSET ?

bind => [0, 5, 0]

BOM: SELECT fk_cadastro_local, num_tentativas, dt_insert, status, id_cadastro_servidor, fk_posto_identificacao FROM db_civil.local.loc_cadastro_local WHERE (fk_cadastro_local = ?)

bind => [60]

INFO: Tamanho: 5

INFO: STATUS DO OBJETO RECUPERADO: ENVIADO

INFO: ENTROU atualizaCadastroEnviado - ATUALIZANDO campos

INFO: VAI REALIZAR MERGE - STATUS DO CADASTRO: ENVIADO

INFO: REALIZOU MERGE

INFO: CADASTRO MUDOU PARA STATUS: ENVIADO

BOM: UPDATE db_civil.local.loc_cadastro_local SET num_tentativas = ? WHERE (fk_cadastro_local = ?)

bind => [36, 52]

INFO: STATUS DO OBJETO RECUPERADO: ENVIADO
INFO: ENTROU atualizaCadastroEnviado - ATUALIZANDO campos
INFO: VAI REALIZAR MERGE - STATUS DO CADASTRO: ENVIADO
INFO: REALIZOU MERGE
INFO: CADASTRO MUDOU PARA STATUS: ENVIADO
BOM: UPDATE db_civil.local.loc_cadastro_local SET num_tentativas = ? WHERE (fk_cadastro_local = ?)
bind => [48, 125]

INFO: STATUS DO OBJETO RECUPERADO: INSERIDO
INFO: ENTROU atualizaCadastroEnviado - ATUALIZANDO campos
INFO: VAI REALIZAR MERGE - STATUS DO CADASTRO: ENVIADO
INFO: REALIZOU MERGE
INFO: CADASTRO MUDOU PARA STATUS: ENVIADO
BOM: UPDATE db_civil.local.loc_cadastro_local SET num_tentativas = ?, status = ? WHERE (fk_cadastro_local = ?)
bind => [49, 1, 60]

INFO: STATUS DO OBJETO RECUPERADO: ENVIADO
INFO: ENTROU atualizaCadastroEnviado - ATUALIZANDO campos
INFO: VAI REALIZAR MERGE - STATUS DO CADASTRO: ENVIADO
INFO: REALIZOU MERGE
INFO: CADASTRO MUDOU PARA STATUS: ENVIADO
BOM: UPDATE db_civil.local.loc_cadastro_local SET num_tentativas = ? WHERE (fk_cadastro_local = ?)
bind => [49, 89]

INFO: STATUS DO OBJETO RECUPERADO: ENVIADO
INFO: ENTROU atualizaCadastroEnviado - ATUALIZANDO campos
INFO: VAI REALIZAR MERGE - STATUS DO CADASTRO: ENVIADO
INFO: REALIZOU MERGE
INFO: CADASTRO MUDOU PARA STATUS: ENVIADO
BOM: UPDATE db_civil.local.loc_cadastro_local SET num_tentativas = ? WHERE (fk_cadastro_local = ?)
bind => [49, 132]
[/i]

5 Respostas

D

Isso acontece por causa do cache do Hibernate ou da implementação da JPA que você está usando e (in)felizmente não tem como alterar esse comportamento.
Se você reiniciar sua aplicação você vai perceber que ai sim, os dados estarão de acordo com o estado do banco.

[]´s

J

Exatamente isso. Se eu reinicia-la, o cache desaparece e ela atualiza normalmente, como na primeira vez.

Não tem mesmo como forçar essa ‘ida’ ao banco de dados pra trazer os valores reais que estão no BD?

‘Engraçado’ é que a consulta no banco retorna os 5 registros q tiveram seus status atualizados na mão. Ou seja, qdo é feito o select, ele reconhece os registros atualizados na mão, ou seja, tá indo no banco de dados.

O que me parece é que o select faz a sua parte corretamente, mas qdo esses registros são carregados pra memória, o JPA reconhece que os mesmo já estão lá e não atualiza os valores desses registros que já foram carregados.

É isso mesmo? Tem alguma forma de forçar uma atualização desses objetos carregados do banco de dados?

Valeu.

J

Seguinte, antes de dormir, resolvi fazer uma ultima tentativa e… adivinhem, funcionou… agora n sei se isso é certo.

Vejam se existe uma maneira mais correta de se fazer isso.

Alterei o método getCadastrosPendentes, fazendo agora um foreach e fazendo um refresh pra cada item da lista retornado:

public List<LocCadastroGeral> getCadastrosPendentes(StatusMsgType tipoStatus, Integer qtde) {
        String str = "select cad "
                + "from LocCadastroGeral cad "
                + "where cad.locCadastroLocal.idCadastroServidor is null "
                + "and cad.locCadastroLocal.status = ?1 "
                + "order by cad.locCadastroLocal.numTentativas, cad.locCadastroLocal.dtInsert";
        List<LocCadastroGeral> t = getLimitedList(LocCadastroGeral.class, str, qtde,tipoStatus);
        for (LocCadastroGeral locCadastroGeral : t) {
            getEm().refresh(locCadastroGeral);
        }
        
        return t;
}

Além disso coloquei um campo VERSION nas entidades.

@Column(name="version")
    @Version
    private Long version;

    public Long getVersion() {
        return version;
    }

    public void setVersion(Long version) {
        this.version = version;
    }

Existe outra maneira, através de annotation ou alguma propriedade para que o JPA faça isso ‘automaticamente’ pra mim, ou alguem sabe outra solução? Obrigado.

B

Ola, tem um tempão que não mexo com JPA, mas tinha um metodo refresh em algum lugar para forçar esta ida ao banco… só não lembro onde.

F

o refresh vai buscar todos os relacionamentos entre os objetos, outra alternativa é dar um clear na sessao antes de dar o select assim o cache é zerado e os dados buscados diretamente do BD, faça um teste quando voce faz o refresh para cada objeto ele varre todos os relacionamentos mesmo estando em LAZY

Criado 7 de novembro de 2010
Ultima resposta 8 de nov. de 2010
Respostas 5
Participantes 4