Uma outra opção, que utilizamos aqui na empresa, propicia uma transparência de banco de dados sem recompilação de código, mas você ainda não pode fugir do SQL.
Nós usamos a arquitetura em camadas do CESAR, daqui de recife, veja como ela funciona:
1 - A arquitetura é dividida em várias camadas
I - Fachada do sistema
II - Uma camada controladora
III - A camada de negócios
IV - O repositório de dados
Esta arquitetura permite que, se você precise fazer alguma alteração na aplicação, a penas a camada responsável é modificada, sem afetar as demais. Você pode até mesmo trocar uma camada por uma outra, implementada de forma diferente e tudo funciona blz.
Mas vamos a parte que interessa. Entre a camada de negócios e a camada de repositório, existe algumas interfaces com métodos de persistência apropriados para cada entidade persistente. Por exemplo, vamos supor que temos duas classes que precisam ser persistidas: Cliente e Pedido. Criariamos duas interfaces IRepositorioCliente e IRepositorioPedido com os métodos:
IRepositorioCliente
-------------------
getCliente(int idCliente);
getClientes();
updateCliente(Cliente c);
deleteCliente(int idCliente);
insertCliente(Cliente c);
IRepositorioPedido
------------------
getPedidos(int idCliente);
getPedido(Cliente c, int idPedido);
getPedido(int idPedido);
insertPedido(Pedido p);
Cliente e Pedido encapsulam os campos das tabelas correspondentes.
Agora para cada banco de dados em específico, criamos uma classe que implementa IRepositorioPedido ou IRepositórioCliente, por exemplo
ClienteOracle, ClientePSQL, ClienteMySQL, que ao implementar os métodos da interface, utilizam os SQL correspondente de cada banco.
Para a camada de negócios, existe então apenas a interface. Você cria instâncias do repositório através de uma Factory e se precisar mudar o banco, precisamos apenas mudar um property em algum arquivo de texto para que a Factory comece a criar repositórios do novo tipo de banco.
Desvantagens:
Você precisa ter uma classe para cada entidade persistente para cada banco.
Vantagens:
Seu repositório pode ser qualquer coisa, uma banco Oracle ou Interbase, um arquivo XML, um txt, um stream pela rede para algum servidor em outro lugar, entre outros.
Não há necessidade de recompilação (a não ser que o novo repositório não exista e ainda precise ser criado, mesmo assim, só precisa compilar o código do NOVO repositório)