Blog sobre desenvolvimento de software (Java, muito Java!), inovação tecnológica e cotidiano do Universo de TI. Acesse notícias, tutoriais, material de cursos e eventos, código, desafios, soluções, opiniões, pensamentos, divagações, balbuciações e abobrinhas diversas. Deixe seu comentário!

quinta-feira, 29 de dezembro de 2011

Planilha para economizar com o Skype

Usuários de longa data do Skype sabem que o aplicativo pode diminuir significativamente sua conta de telefone. Alguns exemplos:
  • Qualquer tel. no Canadá R$0,06 por minuto, mais taxa de conexão de R$0,09 [1].
  • Tel. fixo no Rio de Janeiro → R$0,09 por minuto, mais taxa de conexão de R$0,189 [1].
  • Celular no Brasil R$0,54 por minuto, mais taxa de conexão de R$0,189 [1].
Bem melhor do que as tarifas do meu Net Fone (R$1,09, R$0,27 e R$1,53 respectivamente).
Outro ponto a ser considerado é que a qualidade da ligação é ótima: Com uma banda larga veloz e um bom microfone o uso do Skype é praticamente imperceptível para a outra ponta. Inclusive é possível configurar o Skype para exibir seu número de celular no identificador de chamadas do destinatário da ligação.

Gostou? Então que tal economizar ainda mais?
O Skype permite a compra de créditos em 15 moedas diferentes, cada qual com sua tabela de preços e taxas de conexão. Como o Skype não atualiza seus preços frequentemente e a taxa de câmbio sofre variação, é possível economizar bastante escolhendo a melhor moeda para comprar seus créditos. A diferença de preço é tão grande que a manobra vale a pena mesmo considerando a taxa de IOF de 6.38% incidente sobre compras internacionais no cartão de crédito.
Pensando nisso criei uma planilha no Google Spreadsheet que extrai informações sobre tarifas diretamente do site do Skype, bem como as cotações das moedas através do Google Finance. A planilha permite saber quanto custa sua ligação via Skype para o destino desejado e qual a melhor moeda para comprar seu crédito de acordo com a taxa de câmbio do momento (cotações sujeitas a atraso de 20 minutos de acordo com o disclaimer do Google).

Visão em tempo real da planilha para ligações para celulares do Brasil:

Planilha em tela cheia 

Com a cotação da Libra Esterlina em R$2,8945 como no momento da escrita desse post, é possível economizar mais R$0,15 por minuto ao ligar para celulares no brasil (12.6p = R$0,39 já acrescentando IOF).
A planilha serve como um "aplicativo web" permitindo que você preencha os dados da ligação e descubra a melhor moeda a ser utilizada de acordo com as características da sua chamada:


Para obter uma cópia editável da planilha siga os seguintes passos:
  1. Entre no Google Docs com sua conta do Google (é gratuito).
  2. Clique no link da planilha para abri-la em uma nova janela.
  3. Faça uma cópia do planilha clicando no menu File Make a Copy:

Pronto, sua cópia da planilha é editável e privada. Desse momento em diante você pode acessar o Google Docs, abrir sua cópia da planilha, preencher os dados da ligação (Call Info) e receber informações atualizadas sobre preços do Skype. Não é necessário fechar e abrir a planilha para obter novas cotações, isso é feito automaticamente em background.

Algumas dicas:
  • Na primeira vez que você abrir a planilha pode demorar um pouco para que os preços sejam exibidos. Da mesma forma, na primeira vez que você clicar no combo box Destination pode demorar um pouco para que os países sejam carregados.
  • Se ocorrer algum erro para obter uma cotação ou preço do site da Skype espere meia hora e tente de novo (lembre-se que a internet segue o princípio do melhor esforço :D).
  • Para mudar sua moeda clique em cima dos seus créditos e, no pop-up que irá abrir, clique no link Change indicado pela figura abaixo:

  • De acordo com documentação do Skype uma taxa extra de conversão pode incidir sobre seus créditos durante a operação de troca de moeda. Dessa forma, a melhor estratégia para economizar é comprar créditos de acordo com as ligações pretendidas e esgotá-los antes de mudar de moeda. Trocar de moeda várias vezes para economizar alguns centavos por ligação não vale a pena pois a taxa extra de conversão incidirá sobre todo o seu crédito restante.
  • Lembre-se que em compras por cartão de crédito a taxa de câmbio aplicada será determinada pela operadora do cartão próximo a data de fechamento da sua fatura. Logo, como em toda transação financeira, há riscos envolvidos - no caso, o da moeda escolhida super valorizar frente ao real. Eu venho comprando créditos em Libras Esterlinas e Wons sul-coreanos já faz algum tempo (mais de 2 anos) e não passei ainda por nenhuma situação em que essas moedas valorizassem o suficiente para fazer a compra em real valer a pena. Pelo contrário, em boa parte das vezes o real valoriza frente as outras moedas fazendo com que a operadora de cartão de crédito me debite a diferença. Tenha em mente porém que a taxa de câmbio é dependente do cenário econômico, e que o governo brasileiro tem um longo histórico de uso de manobras para controlar o câmbio (lembrando que manter o real desvalorizado frente ao dólar é um dos objetivos do Ministro Guido Mantega). Use a planilha por sua conta e risco.
  • Uma boa prática é comprar o crédito um pouco antes da sua fatura vencer, de forma a correr menos riscos relacionados a flutuação cambial.
  • Uma alternativa para não assumir o risco da flutuação cambial é usar o serviço de pagamentos PayPal que permite a conversão da moeda estrangeira em real através de uma cotação pré-fixada. Dessa forma você sabe exatamente o quanto será debitado do seu cartão de crédito na hora da compra. O trade-off é que as taxas de câmbio pré-fixadas não são tão competitivas quanto as pós. Novamente, no cenário econômico dos últimos dois anos, em nenhuma das compras que fiz a taxa pré-fixada valeria a pena.
Para encerrar o post com um conteúdo um pouco mais voltado para desenvolvedores, veja que essa planilha somente foi possível graças às funcionalidades fantásticas do Google Spreadsheet, em especial ImportHtml, ImportXML, e Google Finance. O recurso de listas suspensas também é muito interessante. Estou oficialmente distribuindo a planilha sob Licença Creative Commons. Sinta-se livre para estudar as fórmulas e criar trabalhos derivados.


  1. Taxa de conexão é aplicada somente uma vez por ligação. Preços da modalidade Pré-pago. É possível obter tarifas menores e isenção da taxa de conexão assinando um pacote mensal.

domingo, 8 de maio de 2011

Java: == vs equals()

Operadores e métodos de comparação são fonte de muitas dúvidas para programadores que estão iniciando em Java. De fato, o operador  ==, os métodos equals e hashCode são tópicos do Java Standard Edition 6 Programmer Certified Professional Exam para quem busca o título Oracle Certified Professional Java Programmer (antigo SCJP). Várias questões cobram, direta ou indiretamente, conhecimento sobre o assunto.
O operador == é usado para comparação entre tipos primitivos:
int x = 10;
if (x == 10) {
  System.out.println("x é 10!");
}
Já para tipos complexos o programador pode se surpreender com o resultado. O operador == só "funciona" com referências para o mesmo objeto:
public class Pessoa {
    
    private String cpf;

    public Pessoa(String cpf) {
        this.cpf = cpf;
    }
    
    public static void main(String[] args) {
        Pessoa p1 = new Pessoa("111.111.111-11");
        Pessoa p2 = new Pessoa("111.111.111-11");
        Pessoa p3 = p1;
        
        System.out.println(p1 == p2); // false
        System.out.println(p1 == p3); // true
    }
    
}
Esse resultado remete a uma decisão de design da linguagem. A regra de negócios para determinar se dois objetos são iguais é específica para cada aplicação. Enquanto o compilador pode assumir que duas referências para o mesmo objeto são iguais, assumir que dois objetos diferentes com os mesmos valores são iguais seria perigoso. Só porque uma pessoa tem um carro do mesmo modelo e cor do que o seu, isso não significa que os carros são iguais e você pode pegar o carro dela.
Eis o motivo de existência do método equals na classe Object. Para quem não está familiarizado com essa classe, ela serve de superclasse para todas as classes em Java, logo, seus métodos são universais, podendo ser chamados a partir de qualquer instância de qualquer objeto disponível em tempo de execução. A fim de definir a regra de comparação para objetos de uma classe deve-se sobrescrever o método equals no corpo da mesma: 
public class Pessoa {
    
    private String cpf;

    public Pessoa(String cpf) {
        this.cpf = cpf;
    }

    // Adaptado do código gerado pelo Netbeans
    @Override
    public boolean equals(Object obj) {
        // Um objeto é sempre igual a ele mesmo
        if (this == obj) {
            return true; 
        }
        // Um objeto nunca deve ser igual a null
        if (obj == null) {
            return false;
        }
        /* Uma pessoa só pode ser igual a outra pessoa.
         * Caso uma pessoa possa ser igual a uma de suas subclasses
         * use !(obj instanceof Pessoa)
         */
        if (getClass() != obj.getClass()) {
            return false;
        }
        // Converte a referencia para uma pessoa
        final Pessoa other = (Pessoa) obj;
        // Duas pessoas só podem ser comparadas se possuem CPF
        if (this.cpf == null || other.cpf == null) {
            return false;
        }        
        // Duas pessoas são iguais se possuem o mesmo CPF
        return this.cpf.equals(other.cpf);
    }
    
    public static void main(String[] args) {
        Pessoa p1 = new Pessoa("111.111.111-11");
        Pessoa p2 = new Pessoa("111.111.111-11");
        Pessoa p3 = p1;
        
        System.out.println(p1.equals(p2)); // true
        System.out.println(p1.equals(p3)); // true
    }
    
}
Agora sim... Veja que no meu exemplo duas pessoas só são iguais se possuem o mesmo CPF. Outros sistemas poderiam adotar critérios diferentes para comparar duas pessoas, ou mesmo comparar pessoas com outros objetos (do tipo Batatinha por exemplo). Observe que a comparação entre CPFs também foi feita através de equals; isso é possível pois a classe String (API padrão do Java) sobrescreve o método equals (o mesmo é valido para várias outras classes como Integer, Long, Boolean, etc).
Quem acompanhou até aqui deve estar se perguntando o que acontece se houver uma chamada para equals sem que seja feita a sobrescrita do método. Não passe vontade! Vá lá, comente o método equals e rode a aplicação. Eu espero...
Pronto? Eis o resultado:
System.out.println(p1.equals(p2)); // false
System.out.println(p1.equals(p3)); // true
Acontece a mesma coisa do que se utilizássemos o operador ==, ou seja, os objetos são comparados por referência. Para os curiosos, segue a implementação no método equals na classe Object que justifica o porquê:
public boolean equals(Object obj) {
   return (this == obj);
}
Uma regra que exige certo grau de disciplina do desenvolvedor é sempre sobrescrever o método hashCode junto com o método equals. Se essa regra não for respeitada, várias classes do Java que dependem da função hash do objeto apresentarão comportamento inconsistente para o mesmo (exemplos dessas classes são as collections HashSet e HashMap). 
O que é uma função hash e para que ela é utilizada fico devendo para um próximo post. Por enquanto assuma que o método hashCode deve mapear um objeto qualquer para um número inteiro para fins de comparação.
Lendo o javadoc do método hashCode, um ponto importante do contrato que deve ser respeitado é: Se a comparação entre dois objetos com equals retornar true os objetos devem possuir o mesmo hashCode (o inverso não é verdadeiro, ou seja, dois objetos com o mesmo hashCode não necessariamente precisam retornar true para a função equals). Segue uma implementação gerada pelo Netbeans que atende a esse contrato para a classe Pessoa:  
@Override
public int hashCode() {
   int hash = 5;
   hash = 37 * hash + (this.cpf != null ? this.cpf.hashCode() : 0);
   return hash;
}
Observe que:
  1. Toda pessoa com o mesmo CPF possui o mesmo hashCode.
  2. Como para o método equals, String e várias outras classes do Java sobrescrevem o método hashCode, sendo possível reaproveitá-lo em nossas implementações.
Cuidado: O uso incorreto do operador == com objetos complexos pode introduzir bugs difíceis de serem rastreados. Veja o seguinte exemplo com strings:
String s1 = "abacate";
String s2 = "abacate";
String s3 = new String("abacate");

System.out.println(s1 == "abacate"); // true
System.out.println(s1 == s2); // true 
System.out.println(s1 == s3); // false
Nesse caso s1 e s2 foram criados a partir do mesmo literal “abacate”. A JVM, por motivos de performance / eficiência no consumo de memória fez com que somente uma String contendo o valor “abacate” fosse de fato criada e ambas as referências apontassem para ela (através de um pool de strings). Já em s3 houve a criação de um objeto diferente em memória (com new), logo, o operador == irá retornar false.
Sempre é possível usar o método intern para obter o objeto String presente no pool, apto a ser comparado com o operador ==. Porém, esse é o típico caso em que você deve se perguntar qual a vantagem de fazer isso (e se o motivo não for convincente, simplesmente não faça).

Resumindo: 
  • Use == para primitivos.
  • Use equals para tipos complexos.
  • Verifique sempre se as classes dos objetos que você está tentando comparar sobrescrevem equals corretamente.
  • Se não sobrescreverem, sobrescreva equals de acordo com sua regra de negócio.
  • Se você sobrescrever equals deverá também sobrescrever hashCode.
Dica: Gerando os métodos equals e hashCode com seu IDE favorito:
  • Netbeans: Alt + Insert  equals() and hashCode() 
  • Eclipse: Menu Superior  Source  Generate hashCode() and equals()
  • IntelliJ: Alt + Insert  equals() and hashCode()

Curiosidade: Veja que os IDES produzem implementações diferentes, porém válidas para ambos os métodos.


UPDATE: A pergunta Comportamento das diferentes formas de comparação em Java no Stack Overflow em Português está levantando algumas questões bem interessantes sobre esse assunto. Vale a pena conferir.

quinta-feira, 5 de maio de 2011

Netbeans 7.0

Uma nota rápida sobre o Netbeans 7.0.
Brinquei com o IDE nas últimas semanas e tive uma experiência excelente.
Segue uma lista das minhas features favoritas:
  • Melhorias no suporte ao Weblogic. Bingo! Estrelinha dourada para a Oracle. Não nego que o o Weblogic é meu Application Server predileto. A combinação Weblogic + Netbeans tem tudo para no futuro se tornar o melhor ambiente de desenvolvimento Java EE do mercado. 
  • Suporte ao Tomcat 7 e melhorias no suporte ao GlassFish. Subi alguns projetos de médio porte no Tomcat e fiz redeploy várias vezes sem esbarrar em nenhum OutOfMemoryError: PermGen space (outros programadores reportaram a mesma coisa; parabéns à equipe do Tomcat). O tempo de turnaround para deploy em ambos os containers também está menor no meu computador. Na minha opinião o Netbeans atualmente possui o melhor suporte para Java EE 6 entre todos os IDES do mercado (seguido de perto pelo IntelliJ).
  • Suporte ao JDK 7 não poderia faltar. As sugestões automáticas são bem legais para ajudar programadores a se acostumarem com o operador diamante, try-with-resources, multi-catch, switch sobre Strings, etc.
Ao contrário do que muita gente desconfiada (inclusive eu) acreditava, parece que há esperança para o Netbeans dentro da Oracle. Quanto mais suporte às tecnologias estratégicas da companhia for implementado no Netbeans (mesmo que através de plugins) melhor. Além do suporte ao banco de dados Oracle e ao Weblogic, aguardo melhorias no suporte à BPEL e Hudson. Ficaria muito feliz também se um dia a Oracle implementasse suporte para Weblogic Portal e SOA Suite no IDE. O Netbeans seria com certeza uma alternativa interessante ao JDeveloper e ao OEPE.

Faça o download da versão atual aqui.

terça-feira, 29 de março de 2011

Satisfação imediata com o banco de dados H2

Até hoje lembro do meu primeiro contato com bancos de dados (aproveito para avisar que os próximos parágrafos terão pouco a ver com a tecnologia do título. Se a idéia é aprender sobre o uso de H2, pule direto para ). Eu estava no segundo ano da faculdade, era bolsista e tinha todo o tempo livre do mundo, então decidi instalar e configurar um ambiente de desenvolvimento com uma versão antiga do Ubuntu e uma versão mais antiga ainda do PostgreSQL.
Foi uma experiência bastante enlouquecedora enriquecedora. Em um único sprint aprendi como trabalhar com autenticação e autorização no Linux e no PostgreSQL. Eu lembro o quanto penei para trocar a senha do usuário postgres, executar o comando psql com esse usuário, criar um novo usuário que coincidisse com o meu login do Linux no banco, criar um novo banco com o mesmo nome do usuário, criar um novo schema nesse banco para testes e assim por diante. Depois que consegui conectar no banco com o meu usuário usando o comando psql levei mais pelo menos um dia para descobrir como conectar via JDBC.
Não satisfeito com todo esse trabalho, alguns meses depois resolvi compilar e instalar na unha uma versão mais nova do banco com uma série de extensões... Foram dias brigando com o comando make (e as dependências da instalação), switches do SGBD, arquivos de configuração e coisas de dia a dia do Linux como chown, chmod, ln, configuração da variável PATH, Sys-V, etc. Quando finalmente consegui fazer tudo rodar direito eu já estava me sentindo bem familiarizado com o Linux (bem... vivendo uma ilusão na verdade, pois eu poderia ter adicionado meia dúzia de repositórios com binários que facilitariam bastante o processo).
Não preciso nem dizer que graças a minha falta de experiência tachei o PostgreSQL de complicado, acadêmico e burocrático antes mesmo de começar a usá-lo.
Um ano depois, já no mercado de trabalho, tive um contato inicial bem menos traumático com o MySQL. Um apt-get e meia dúzia de pequenas edições em arquivos de configuração depois e tudo já estava rodando em um ambiente com  Apache + PHP + phpMyAdmin + MySQL. Criei os bancos e tudo que precisava com o phpMyAdmin (tomando o cuidado de criticar injustamente o pgAdmin em toda oportunidade de comparação que tive), modelei o banco com o DBDesigner (depois conheci o MySQLWorkbench que uso até hoje) e consegui conectar via JDBC na primeira tentativa. Foi rápido e satisfatório.
Semanas depois quando descobri que as foreign keys não funcionavam (devido a engine default MyISAM) e que as constraints do tipo CHECK eram silenciosamente ignoradas, não me importei muito com o trabalho extra de alterar a engine de trocentas tabelas e reescrever o código das constraints CHECK do lado da aplicação. Em contraste passei meses implicando com coisas menores do PostgreSQL como o fato de ter que especificar mais colunas do que realmente precisava na cláusula GROUP BY, bem como o uso de DISTINCT ON, ILIKEetc.
Moral da história: Eu admito ter um peso e duas medidas para os dois mais populares SGBDs Open Source do mercado. Depois de ter desenvolvido muitas aplicações e participado da instalação e configuração de diversos outros bancos (como Oracle, DB2, SQL Server, Sybase, etc) nos mais diversos ambientes - com diferentes requisitos (e camadas de complexidade) - posso dizer que ambos os bancos são ótimos. Mesmo assim, quando a escolha cabe a mim, eu acabo usando o MySQL. E por que? Porque em um período crucial do meu aprendizado acabei tendo uma experiência mais satisfatória com ele.

Hoje tenho a grata oportunidade de treinar outros programadores. Volta e meia isso significa ajudá-los em seus primeiros passos com SQL. Por experiência própria, sei que esses primeiros contatos com o SGBD podem fazer toda a diferença na formação do aluno. Só aprenderão aqueles que fizerem exercícios, e só farão exercícios aqueles que conseguirem quebrar a barreira da instalação e configuração do ambiente.
A situação ideal seria que meus alunos pudessem criar bancos e executar todo tipo de comando SQL sem burocracia. Também é importante que usem um banco de dados confiável, com bom suporte aos padrões de SQL e que rode nos mais diversos sistemas operacionais. Se o banco puder rodar embbeded ajuda bastante, pois dessa forma é possível criar exercícios que vão direto ao ponto em projetos pré-configurados.

Conheça o H2, um banco de dados puramente implementado em Java que faz tudo isso. Em um único jar com pouco mais de 1Mb ele traz: um poderoso SGBD que pode rodar tanto em modo embbeded quanto client-server, um driver JDBC tipo 4 e várias ferramentas como uma interface de administração web com Auto Completion para SQL.
É possível fazer o download do instalador para Windows, do zip multi-plataforma, ou ainda apenas do jar do SGBD. Com o jar ou a versão zipada nenhuma instalação é necessária. Basta clicar duas vezes em h2-[versão].jar que o console do banco aparecerá no seu navegador padrão. Se o seu sistema suportar o conceito de “Área de notificação” (classe SystemTray do Java) um ícone amarelo também deve aparecer; a partir deste você pode, a qualquer momento, acessar o console.


Na tela de login você deve indicar uma URL JDBC para o banco. A parcela da URL depois do jdbc:h2: aponta para um arquivo no sistema operacional - o caminho pode ser absoluto (começando com “/”), relativo ao diretório atual (começando com “./”) ou ao diretório do usuário no SO (começando com “~/”). Também será necessário entrar com o login e senha do administrador.
Se a URL apontar para um arquivo não existente o banco será criado (verifique que um arquivo [nome do banco].db apareceu no local indicado); também será criado um usuário administrador com o login e senha informados.


Pronto, isso é tudo que precisa ser feito para criar um banco e acessá-lo em modo embbeded. Para que múltiplos processos possam acessar o mesmo banco concorrentemente basta adicionar o parâmetro AUTO_SERVER=TRUE no fim da URL. Por exemplo:
jdbc:h2:~/Projetos/DBs/h2/RH/RH;AUTO_SERVER=TRUE
É possível ainda fazer com que um dos processos aja exclusivamente como servidor e os outros exclusivamente como clientes. Clientes podem conectar em servidores remotos via TCP/IP ou SSL (veja a documentação aqui).
Uma vez conectado, a tela de administração aparecerá. No lado esquerdo você verá todos os objetos (schemas, tabelas, usuários, constraints, etc) do banco em uma Tree View. No lado direito você pode entrar com comandos SQL (contando com a funcionalidade de Auto Completion para te ajudar). O resultado dos comandos é exibido na parte inferior da tela.


Que tal um Test Drive do banco? Vamos criar um pequeno schema definido pela figura abaixo, populá-lo, e executar algumas queries de complexidade média para ver como a engine responde.


Começando pelo DDL, vamos criar as tabelas e constraints:
-- ----------------------------------------------------
-- Tabela departamento
-- ----------------------------------------------------
CREATE TABLE departamento 
(
  -- IDENTITY = BIGINT + auto incremento + PRIMARY KEY
  id IDENTITY ,
  nome VARCHAR(45) NOT NULL ,
);

-- Constraint nomeada
ALTER TABLE departamento 
ADD CONSTRAINT departamento_nome_unq UNIQUE(nome);

-- ----------------------------------------------------
-- Tabela funcionario
-- ----------------------------------------------------
CREATE TABLE funcionario 
(
  id IDENTITY ,
  nome VARCHAR(255) NOT NULL ,
  -- Valor default
  salario DECIMAL(10,2) NOT NULL DEFAULT 545.00,
  departamento_id INT NOT NULL ,
);

-- Foreign Keys não poderiam faltar 
ALTER TABLE funcionario 
ADD CONSTRAINT funcionario_departamento_fk 
FOREIGN KEY (departamento_id) REFERENCES departamento (id);

-- Funciona!
ALTER TABLE funcionario
ADD CONSTRAINT funcionario_salario_chk CHECK (salario >= 545.00);

-- ----------------------------------------------------
-- Tabela bonus
-- ----------------------------------------------------
CREATE TABLE bonus 
(
  id IDENTITY ,
  valor DECIMAL(10,2) NOT NULL DEFAULT 0.00 ,
  -- Que tal usar uma função?
  data DATE NOT NULL DEFAULT CURRENT_DATE ,
  funcionario_id BIGINT NOT NULL ,
);

ALTER TABLE bonus 
ADD CONSTRAINT bonus_funcionario_fk
FOREIGN KEY (funcionario_id) REFERENCES funcionario (id);
 
-- Precisa de índices? Sem problemas
CREATE INDEX bonus_data_idx ON bonus(data);
Bem direto né? E agora inserts de múltiplas linhas a la MySQL:
INSERT INTO departamento (nome) 
VALUES ('Administrativo'),  
       ('TI'), 
       ('Vendas');     

INSERT INTO funcionario (nome, salario, departamento_id)  
VALUES ('Auxiliar Administrativo', 2000.00, 1),
       ('GP I', 5500.00, 1),
       ('GP II', 7000.00, 1),
       ('Programador I', 3000.00, 2),
       ('Programador II', 6000.00, 2),
       ('Programador III', 10000.00, 2),
       ('Vendedor I', 1500.00, 3),
       ('Vendedor II', 4500.00, 3);
   
INSERT INTO bonus (valor, data, funcionario_id)
VALUES (15000.00, '2010-03-20', 7),
       (15000.00, '2010-07-03', 7),
       (20000.00, '2009-09-09', 8),
       (60000.00, '2010-10-06', 8),
       (45000.00, '2010-11-15', 8);
Veja que todas as constraints estão sendo respeitadas (tente inserir um funcionário que ganhe menos de R$545,00 ou atribuir um valor inválido em um campo que tenha uma foreign key e você se deparará com um exceção).
Mas e as queries? Vamos resolver um primeiro exercício: Determinar, para cada departamento, a quantidade de funcionários e o salário médio.
   SELECT 
         d.nome AS `Departamento`, 
         COUNT(f.id) AS `Numero de Funcionários`, 
         ROUND(AVG(f.salario),2) AS `Média Salarial`
    FROM  
         departamento d 
         LEFT OUTER JOIN funcionario f ON (d.id = f.departamento_id)
GROUP BY
         d.id
ORDER BY
         d.nome;
Perfeito. Essa query usa de tudo um pouco, alias, group by, order by, joins e funções (veja a documentação). Repare que temos o melhor de dois mundos: um banco que não exige a repetição de colunas funcionalmente dependentes na cláusula group by e respeita as constraints do tipo CHECK.
Mas que tal complicar um pouco mais? A definição do exercício número dois é: Encontrar todos os vendedores que ganharam mais de R$10.000,00 em bônus nos últimos doze meses; determinar o total de bônus recebidos por cada vendedor no perído e ordená-los de acordo (começando com o vendedor com a maior soma de bônus e terminando com o vendedor com a menor).
SET @MESES = 12; 
SET @BONUS = 10000.00;

  SELECT 
         f.id,
         f.nome AS `Vendedor`,
         SUM(b.valor) AS `Bonus` 
    FROM
         funcionario f 
         INNER JOIN departamento d ON (f.departamento_id = d.id)  
         INNER JOIN bonus b ON (f.id = b.funcionario_id)
   WHERE
         d.nome = 'Vendas'
         AND b.data >= DATEADD('MONTH', -@MESES, CURRENT_DATE)
GROUP BY
         f.id 
  HAVING
         `Bonus` >= @BONUS
ORDER BY
         `Bonus` DESC; 
Novamente tudo no lugar. Aí está nossa cláusula having, múltiplos joins e até mesmo o uso de variáveis. Deixo para o leitor brincar com subqueries, UNION, MINUS, EXCEPT, INTERSECT, etc.
O H2 têm uma quantidade de funcionalidades que deixa muitos outros bancos conhecidos no chinelo. Saindo do básico (views, sequences, triggers, stored procedures, controle de transações, controle de usuários, roles, análise de planos de execução , etc) algumas features interessantes que lembro de cabeça são o modo in memory (com ótima performance, excelente para caches), a habilidade de emular outros databases, queries recursivas, suporte a índices do tipo full text (nativos ou integrados ao Lucene) e suporte básico a clusterização.
Os veteranos de Java que sentem calafrios quando escutam a combinação de palavras embedded database podem ficar tranquilos. Uso o H2 já há alguns anos e nunca tive sequer um banco corrompido. Cheguei a fazer testes em que bati forte no banco com o JMeter para estressar um aplicativo web e observei um curva de degradação de performance bem saudável. O controle transacional é confiável (inclusive quando operações do H2 participam de transações XA), e, no geral, o otimizador de queries faz um bom trabalho (apesar de algumas limitações).
Bom para não escrever um artigo muito tendencioso, informo que o H2  não deve ser considerado um substituto para databases tradicionais em ambiente de produção. Segue um link do site oficial que fala sobre algumas de suas limitações (diga-se de passagem, 4TB de armazenamento é um limite bastante generoso). Também é necessário dizer que por padrão o banco usa table locks, o que reduz bastante a performance quando uma tabela sofre muitos comandos de escrita concorrentes (porém, já existe suporte experimental a MVCC).
Comprou a idéia? Não custa experimentar, é grátis.
Ah! Já que estamos falando de dinheiro, quem está por trás desse banco é o Thomas Müller, o mesmo criador do HSQLDB. Esse segundo banco você já deve ter usado mesmo que não saiba... Por padrão ele é incluído nos softwares Mathematica, Open Office Base, Liferay, JBoss, Jira, Hibernate e JForum, além, é claro, do super renomado software de contabilidade Open Source que escrevi na faculdade, o AAS hehehe). Você encontra um dotão “Donate” do PayPal na página do H2.