[Resolvido] - Backup firebird

Caros colegas venho há muitos dias tentando implementar uma rotina de restauração de backup no sistema que estou desenvolvendo.

Estou utilizando Firebird 2.5 + JPA e para o backup/restore a api FBBackupManager.

Meu problema é apenas na opção de restauração, pode sempre é retornada a mensagem de que o banco de dados pode estar em uso.

Postarei a seguir os métodos que implementei, peço aos colegas que me dêem alguma dica de como resolver esse problema.

/*Este método deve ser chamado na tela de login, no momento em que o usuário tentar logar no sistema.*/
    public static EntityManager getConexao(Map properties) throws PersistenceException, GDSException {
        try {
            if (fabrica == null) {
                fabrica = Persistence.createEntityManagerFactory("fluxocaixaPU", properties);
            }
            return fabrica.createEntityManager();
        } catch (ClassCastException erro) {
            JOptionPane.showMessageDialog(null, "Erro ao gerar a conexão!\n\n" + erro.getMessage());
            return null;
        }
    }
public static void fecharEntityManagerFactory() {        
        fabrica.close();
        fabrica = null;
    }

método responsável por disparar a rotina de backup

private void jbtRestaurarActionPerformed(java.awt.event.ActionEvent evt) {                                             
        Map<String, String> dados = new HashMap();
        fecharConexoes();
        
        try {
            //pegar login e senha do usuário que tem poderes para fazer restore do banco de dados.
            FBBackupManager backup = new FBBackupManager();
            backup.setUser(DadosUsuario.getInstanceOf().getUsuario().getNome());
            backup.setPassword(DadosUsuario.getInstanceOf().getUsuario().getSenha());
            backup.setPort(3050);
            backup.setHost("127.0.0.1");
            backup.setDatabase("C:/bd/FluxoCaixa.FDB");
            backup.setBackupPath(getRestaurado());
            backup.setVerbose(true);
            backup.setRestoreReplace(true);
            backup.restoreDatabase();
            JOptionPane.showMessageDialog(null, "Restauração concluída com sucesso!");
            jtxCaminhoRestauracao.setText("");

        } catch (SQLException | NullPointerException | PersistenceException e) {
            JOptionPane.showMessageDialog(null, "Erro: " + e.getMessage());
            e.printStackTrace();
        } finally {
            try {
                dados.put("javax.persistence.jdbc.user", DadosUsuario.getInstanceOf().getUsuario().getNome());
                dados.put("javax.persistence.jdbc.password", DadosUsuario.getInstanceOf().getUsuario().getSenha());

                //conectando novamente ao banco de dados
                FabricaConexao.getConexao(dados);
                ConexaoRelatorio.getConexaoNova(dados);
            } catch (PersistenceException | GDSException ex) {
                JOptionPane.showMessageDialog(null, "Erro: " + ex.getMessage());
            }
        }
    }      
private void fecharConexoes() {
        //desconecta o banco de dados 
        FabricaConexao.fecharEntityManagerFactory();
    }

Por algum motivo sua conexão ainda está aberta quando você tentar restaurar o banco. Como o Firebird não restaura se houverem conexões ativas, dá esse erro.

Tente rever se seu método de fechar a conexão está fechando realmente (faça um teste: abre e feche a conexão e veja o status). Pode tentar usar uma ferramenta como sugerido aqui ou através dessa tela (no Windows):

Com o sistema ativo, clique em Refresh. Se houverem conexões, aparecerão em Number of attachments e number of databases.

Abraço.

TerraSkill, vc poderia me dar alguma dica sobre o fechamento da conexão?

Estou trabalhando com o padrão Singleton, simplesmente faço um close e atribuo nulo a variável que retornaria uma estância de entitymanager.

Não sei o que precisa ser feito, já pesquisei na internet mas não consegui resolver.

Tem muito tempo que não uso Firebird e Java, então não conseguirei ser muito preciso.

Aqui é sugerido que o uso de close() não é suficiente pois o JayBird usa um pool de conexões, e o ideal é usar shutdown(). Você pode investigar algo nesse sentido.

Se a aplicação e o FBBackupManager estão usando a conexões diferentes, pode ser que a conexão de um dos dois esteja ativa (o FBBackupManager deveria ser esperto o suficiente para saber que não pode estar conectado ao tentar restaurar, mas não conheço essa API para afirmar). Se você faz o backup e logo em seguida o restore, pode ser que a conexão do backup ainda esteja aberta.

Pode também tentar fazer o restore pela linha de comando (gbak), sem ativar em momento nenhum o FBBackupManager. Isso ajuda a definir se a conexão ativa é no FBBackupManager ou na sua aplicação.

Abraço.

Agradeço muito pelo seu comentário, não fazia nem ideia de que o jaybird usava um pool internamente. E muito menos onde mais poderia fazer uma possível alteração no meu código.

Já baixei o manual do jaybird (versão 2.1) e acredito que vai ser extremamente útil, na verdade, obrigatório.

Enfim, fiz o seguinte procedimento: dentro do método que dispara a rotina de restauração, antes de invocar os métodos do objeto FBBackupManager, instanciei um objeto do tipo FBWrappingDataSource e chamei o método shutdown. O fato é que antes eu não conseguia restaurar minha base de dados (e depois da super dica que você me passou), depois de alterar conforme citei acima a restauração acontece perfeitamente.

Valeu mesmo, muito obrigado pela dica!

private void jbtRestaurarActionPerformed(java.awt.event.ActionEvent evt) {                                             
        Map<String, String> dados = new HashMap();
        fecharConexoes();
        
        try {                        
            FBWrappingDataSource wds = new FBWrappingDataSource();
            /*este método fechará todas as conexões, independentes de estarem ou não em uso.*/
            wds.shutdown();
            
            //pegar login e senha do usuário que tem poderes para fazer restore do banco de dados.
            FBBackupManager backup = new FBBackupManager();
            backup.setUser(DadosUsuario.getInstanceOf().getUsuario().getNome());
            backup.setPassword(DadosUsuario.getInstanceOf().getUsuario().getSenha());           
            
            
            backup.setPort(3050);
            backup.setHost("127.0.0.1");
            backup.setDatabase("C:/bd/FluxoCaixa.FDB");
            backup.setBackupPath(getRestaurado());
            backup.setVerbose(true);
            backup.setRestoreReplace(true);
            backup.restoreDatabase();
            JOptionPane.showMessageDialog(null, "Restauração concluída com sucesso!");
            jtxCaminhoRestauracao.setText("");
           // pool.bringDatabaseOnline();//coloca o banco online novamente

        } catch (SQLException | NullPointerException | PersistenceException e) {
            JOptionPane.showMessageDialog(null, "Erro: " + e.getMessage());
            e.printStackTrace();
        } finally {
            try {
                dados.put("javax.persistence.jdbc.user", DadosUsuario.getInstanceOf().getUsuario().getNome());
                dados.put("javax.persistence.jdbc.password", DadosUsuario.getInstanceOf().getUsuario().getSenha());

                //conectando novamente ao banco de dados
                FabricaConexao.getConexao(dados);
                ConexaoRelatorio.getConexaoNova(dados);
            } catch (PersistenceException | GDSException ex) {
                JOptionPane.showMessageDialog(null, "Erro: " + ex.getMessage());
            }
        }
    }

Que bom que funcionou. Como disse, faz tempo que não mexo em Firebird+Java. Só sei do problema da conexão estar ativa porque isso realmente enche o saco no caso do Firebird, daí juntei uma coisa com a outra.

Abraço.

A falta de experiência atrapalha bastante.

Agora só uma última dúvida, essa é uma particularidade do Firebird apenas? Se eu usar por exemplo o PostgreSql (ou qualquer outro banco de dados) não teria esse inconveniente?

Sinceramente, não sei. Nunca mexi com backup em Postgre. O esquema de backup do Firebird é simples: ele apaga o banco original e “descompacta” o backup no mesmo lugar. Não é o que chamaria de genial :smile: , mas funciona.

Se tiver a oportunidade de testar outros bancos, acho que vale o teste. Ainda mais se puder deixar sua aplicação independente do banco (algo positivo, se não aumentar muito a complexidade).

Abraço.