Bancos de dados
A camada System.DB e os drivers de Firebird, PostgreSQL, MySQL e SQLite, com dinheiro exato e parâmetros nomeados.
O System.DB é uma camada única de acesso a dados com um driver por banco. O código que fala com a camada não muda quando o banco muda.
| Driver | Módulo | Observação |
|---|---|---|
| Firebird | System.DB.Firebird | |
| PostgreSQL | System.DB.PostgreSql | texto e linha em formato binário |
| MySQL | System.DB.MySql | texto e binário |
| SQLite | System.DB.SqLite | embutido na RTL: sem servidor, nada a instalar |
Um exemplo completo#
program SqliteDemo;
uses
System.DB,
System.DB.SqLite;
var
Conn: TDbConnection;
procedure Run(const ASql: UString);
var
Query: TDbQuery;
begin
Query := Conn.CreateQuery(ASql);
try
Query.SetNoParams;
Query.Execute;
finally
Query.Dispose;
end try;
end procedure;
procedure AddProduct(const AName: UString; const APrice: Currency64);
var
Query: TDbQuery;
begin
Query := Conn.CreateQuery('insert into product (name, price) values (:name, :price)');
try
Query.ParamByName('name').AsUString := AName;
Query.ParamByName('price').AsCurrency64 := APrice;
Query.Execute;
finally
Query.Dispose;
end try;
end procedure;
var
Params: TDbConnectParams;
Query: TDbQuery;
Price: Currency64;
begin
Params.FDbTypeId := TDatabaseType.SqLite;
Params.FDatabase := 'demo_sqlite.db';
DeleteFile(Params.FDatabase);
Conn := TDbConnection.StdCreateAndConnect(Params, nil, nil);
try
Run('create table product (id integer primary key autoincrement, name text, price decimal(18,4))');
// Currency64: dinheiro exato, sem passar por ponto flutuante
Price := 19.99;
AddProduct('Caneta', Price);
Price := 3.5;
AddProduct('Caderno', Price);
Query := Conn.CreateQuery('select name, price from product order by name');
try
Query.SetNoParams;
Query.Open;
if not Query.IsEmpty then
repeat
WriteLn(Query.FieldByName('name').AsUStringDef, ': ',
Query.FieldByName('price').AsCurrency64Def);
until not Query.MoveNext;
finally
Query.Dispose;
end try;
finally
Conn.Dispose;
end try;
end.Caderno: 3.5000
Caneta: 19.9900A forma é sempre a mesma:
- Conectar: preencha um
TDbConnectParams(o tipo do banco emFDbTypeId, oFDatabasee, nos bancos de servidor, host, porta e credenciais) e chameTDbConnection.StdCreateAndConnect. - Consultar:
Conn.CreateQuery(sql)devolve umTDbQuery. Para uma consulta sem parâmetros,SetNoParams; para parâmetros nomeados (:name),ParamByName('name').AsUString := …. - Executar ou abrir:
Executepara o que não devolve linhas;Openpara umselect. - Percorrer:
IsEmptyeMoveNext(if not Query.IsEmpty then repeat … until not Query.MoveNext;). Cada coluna sai deFieldByName('col')com um conversor (AsUStringDef,AsInt64Def,AsCurrency64Def,AsFloat64Def,AsDateDef,AsBytesDef…). - Liberar:
Query.DisposeeConn.Dispose, emtry…finally.
Transações: Commit e Rollback na conexão. O parâmetro que não foi vinculado vira NULL, não erro.
O que o driver garante#
Os drivers de SQLite, MySQL e PostgreSQL foram escritos e provados contra as armadilhas que mais machucam em aplicativo de dados. Cada armadilha tem uma "sonda" (um programa de teste contra um banco de verdade):
- Dinheiro exato. O SQLite não tem tipo decimal, e o caminho ingênuo (ler como
double) perde centavo em valor grande. A leitura passa porCurrency64, e a sonda confere até o valor de oito dígitos com a quarta casa. - Nulo não é cadeia vazia. Um driver que escreve
''quando a coluna é nula perde, em silêncio, a diferença entre "não informado" e "informado vazio". Aqui o valor tem um estado de nulo (AsUString.IsValid). - BLOB de ida e volta, com byte zero no meio. Byte zero é o que quebra quem trata binário como cadeia terminada em zero.
- Transação desfeita. Um
Rollbackque não desfaz é pior que não ter transação. - Reabrir a mesma consulta.
CloseeOpende novo não deixam os campos duplicados. - Parâmetro nomeado, inclusive o não vinculado.
Escolhendo o banco no tempo de execução#
O TDatabaseType (Firebird, PostgreSql, SqLite, MySql) e o TDatabaseTypeAndVersion (PostgreSql_9…PostgreSql_13, SqLite_3…) identificam o banco e a versão; a conexão os expõe, e o código pode se adaptar ao dialeto.