Bom dia!
Consegui uma solução para esse problema na semana passada, em uma mini aplicação que fiz para controle pessoal de ponto.
Valeu a pena fazer a aplicação, porquê me ajudou a aprender, pelo menos, a fazer um banco de dados embedded automatizado (sem ter que ficar copiando arquivo de banco de dados para a máquina ou fazer isso manualmente)… que seria o mínimo necessário para rodar um pequeno programa que se “virasse” com isso.
Para programas distribuídos em rede e que acessam uma mesma base de dados, exigem a instalação de um SGBD mais completo e impreterivelmente não dá para utilizar essas soluções. Você precisaria instalar o SGBD em rede, configurá-lo e poderia haver uma aplicação adminitrativa para rodar “scripts” para criar as tabelas e configurar o ambiente local…algo do tipo.
A solução que encontrei para banco de dados embedded foi o SQLite http://www.sqlite.org/ (e há outras opções, como H2 e as citadas nos tópicos acima). Os bancos embedded são bancos em arquivos, assim como o ACCESS (mas muito melhores, é claro ).
Para todos os passos abaixo, você deve executá-los na inicialização do programa, de forma que caso haja algum erro, sua aplicação possa informar o usuário logo no início, evitando que o mesmo insira dados no programa a toa…
Antes de iniciar, adicione ao CLASSPATH do programa o jar do driver JDBC do SQLite: http://www.zentus.com/sqlitejdbc/. Ele que fará o trabalho de criar as tabelas e rodará suas consultas
- Na inicialização instancio um arquivo .db com uma localização relativa (desse modo, posso usar em diversas máquinas - isso é importante, porquê as pastas de usuário podem ser locais ou em rede, assim como os sistemas operacionais podem variar a pasta).
Esse arquivo será o meu banco de dados. Ele, inicialmente será um arquivo vazio, sem nada:
private static final java.io.File DATABASE = new java.io.File(
System.getProperty("user.home")
+ System.getProperty("file.separator")
+ ".pontoj2se"
+ System.getProperty("file.separator")
+ "ponto.db");
Na minha máquina, ficou esse caminho: C:\Users\diego.queres.pontoj2se\ponto.db
Esse arquivo pode existir ou não (caso seja a primeira execução do programa).
- Se o arquivo DATABASE não existir, chamo uma função que irá criar os diretórios pais (.pontoj2se) e que criará as tabelas. Ele irá fazer uma conexão JDBC com o arquivo DATABASE recém criado e os comandos SQL que você enviar irão alterar o arquivo para comportar as tabelas.
Na inicialização do programa, chamo:
a) o método checkDatabase() para verificar a existência do bd. Caso não exista:
b) chamo o método createNewDatabase()
A seqüência está indicada nos comentários dos métodos abaixo:
//a) - Rodo na inicialização do programa
//Se não existir o arquivo de banco de dados, o programa roda o método para criar um arquivo de banco de dados
public static void checkDatabase() throws Exception {
if (!DATABASE.exists()) {
createNewDatabase();
}
}
//Cria conexões com o banco
public static Connection getConnection() throws Exception {
Class.forName("org.sqlite.JDBC");
Connection conn =
DriverManager.getConnection("jdbc:sqlite:" + DATABASE.getPath());
return conn;
}
/* b) - Cria um novo banco de dados
Você deve rodar aqui todos os comandos necessários para fazer a configuração inicial do banco - criação de tabelas, usuários (se o banco comportar esse recurso), inserção de registros iniciais, etc.
*/
public static void createNewDatabase() throws Exception {
try {
DATABASE.getParentFile().mkdirs(); //Cria os diretórios pai do arquivo (caso não existam)
DATABASE.createNewFile(); //Cria o arquivo do banco
if (!DATABASE.exists()) { //Caso o arquivo ainda não exista, após os comandos acima, dispara exceção
throw new Exception("Erro ao gravar o arquivo de banco de dados.");
}
Connection conn = getConnection();
Statement s = conn.createStatement();
//Execução dos comandos sql para configuração inicial do banco
s.execute("CREATE TABLE IF NOT EXISTS PONTO ("
+ "DIA DATETIME PRIMARY KEY, "
+ "ENTRADA DATETIME, "
+ "SAIDA_ALMOCO DATETIME, "
+ "RETORNO_ALMOCO DATETIME, "
+ "SAIDA DATETIME"
+ ")");
} catch (Exception ex) {
throw new Exception("Erro na criação do banco de dados\n" + ex.getMessage());
}
}
Serviço completo. Agora as próximas conexões com o banco, você chama o método getConnection() e o arquivo estará salvo. O bom é que o banco fica salvo no computador, mesmo que o programa seja atualizado ou sofra algum dano.
Estou usando um jar em WEBSTART e ele se autoconfigura (e cria o banco) em qualquer máquina que testei o programa.
Seria interessante criar métodos para backup também… porquê nunca se sabe, mas o arquivo do banco um dia pode ser corrompido… Seguem os métodos que criei:
//Cria um backup do banco de dados.
//O parâmetro arquivoBkp é o novo arquivo que receberá os dados de backup.
public static void backupDatabase(File arquivoBkp) throws Exception {
//Verificações iniciais
if (!DATABASE.exists()) {
throw new Exception("Não foi possível fazer backup porquê o arquivo de dados não foi localizado!");
}
if (!arquivoBkp.isDirectory() && !arquivoBkp.getName().toLowerCase().endsWith(".db")) {
arquivoBkp = new File(arquivoBkp.getPath() + ".db");
}
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream(DATABASE), BUFFER);
bos = new BufferedOutputStream(new FileOutputStream(arquivoBkp), BUFFER);
int byteLido;
while ((byteLido = bis.read()) != -1)
{
bos.write(byteLido);
}
} finally {
if (bos != null) {
bos.flush();
bos.close();
}
if (bis != null) {
bis.close();
}
}
}
//Recupera o backup e salva por cima do arquivo de banco de dados DATABASE.
public static void recoverBackupDatabase(File arquivoBkp) throws Exception {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream(arquivoBkp), BUFFER);
bos = new BufferedOutputStream(new FileOutputStream(DATABASE), BUFFER);
int byteLido;
while ((byteLido = bis.read()) != -1)
{
bos.write(byteLido);
}
} finally {
if (bos != null) {
bos.flush();
bos.close();
}
if (bis != null) {
bis.close();
}
}
}
O arquivo da classe completa está no anexo.
Também se pode criar o arquivo do bd, estabelendo a conexão com o path (caminho completo) do arquivo do bd (mesmo se ele não existisse) e rodando os scripts de criação de tabelas. No entanto, se as pastas “pai” não existissem, ele não seria capaz de criar o arquivo do bd. Por isso, os passos que estou usando para criar o arquivo do bd são mais garantidos.