Attention! This model is just an example. Your subject area may be more complicated, or completely different. The model used in this article is simplified as much as possible in order not to clutter up the description of working with components with the description of creating and modifying the data model. |
// xAppPath := ExtractFileDir(Application.ExeName) + PathDelim; FDPhysFBDriverLink.VendorLib := xAppPath + 'fbclient' + PathDelim + 'fbclient.dll';
Important! If you are compiling a 32 bit application, then you must use the 32 bit fbclient.dll library. For 64 bit - 64 bit. In addition to the fbclient.dll file, it is advisable to place the msvcp80.dll and msvcr80.dll libraries (for Firebird 2.5), and msvcp100.dll and msvcr100.dll (for Firebird 3.0) in the same folder. These libraries can be found either in the bin subfolder (Firebird 2.5) or in the root folder of the server (Firebird 3.0). In order for the application to correctly display its own firebird errors, you must also copy the file firebird.msg. For Firebird 2.5 and earlier, it should be one level higher than the client library catalog, i.e. in our case in the application directory. For Firebird 3, it should be located in the client library directory, i.e. in the fbclient directory. |
The board Even if your application will work with Firebird in Embedded mode, it is more convenient to conduct development under a full-fledged server. The fact is that in Embedded mode, Firebird works in the same address space with your application, which can lead to undesirable consequences if errors occur in your application. In addition, at the time of development, the Delphi environment and your application are separate applications using Embedded. Until version 2.5, they cannot work with one base at the same time. |
Parameter | Purpose |
---|---|
Pooled | Whether the connection pool is used. |
Database | The path to the database or its alias, as defined in the aliases.conf configuration file (or databases.conf) of the Firebird server. |
User_Name | Username. |
Password | Password. |
OSAuthent | Whether authentication is used by the operating system. |
Protocol | Connection protocol The following values ​​are allowed:
|
Server | Server name or IP address. If the server is running on a non-standard port, then you must also specify the port through a slash, for example, localhost / 3051. |
SQLDialect | Dialect. Must match the database dialect. |
RoleName | Role name |
CharacterSet | The name of the connection character set. |
// xIniFile := TIniFile.Create(xAppPath + 'config.ini'); try xIniFile.ReadSectionValues('connection', FDConnection.Params); finally xIniFile.Free; end;
[connection] DriverID=FB Protocol=TCPIP Server=localhost/3051 Database=examples OSAuthent=No RoleName= CharacterSet=UTF8
Comment In fact, the general settings are usually found in% AppData% \ Manufacture \ AppName and saved there by the installer of the application. However, during development it is convenient for the settings file to be placed somewhere closer, for example, in the folder with the application. Note that if your application is installed in the Program Files folder and the configuration file is in the same place, then this file will be virtualized into Program Data, or there will be problems with its modification and subsequent reading of the new settings. |
// 3 , xLoginCount := 0; xLoginPromptDlg := TLoginPromptForm.Create(Self); while (xLoginCount < MAX_LOGIN_COUNT) and (not FDConnection.Connected) do begin try if xLoginPromptDlg.ShowModal = mrOK then FDConnection.Open( xLoginPromptDlg.UserName, xLoginPromptDlg.Password) else xLoginCount := MAX_LOGIN_COUNT; except on E: Exception do begin Inc(xLoginCount); Application.ShowException(E); end end; end; xLoginPromptDlg.Free; if not FDConnection.Connected then Halt;
Parameter | Purpose |
---|---|
Connection | Communication with the FDConnection component. |
Options.AutoCommit | Manages the automatic start and completion of the transaction. The default is True. If the value of this property is set to True, then FireDAC does the following:
In Firebird, automatic transaction management is emulated by the FireDAC components themselves. |
Options.AutoStart | Manages the automatic start of a transaction. Default is True. |
Options.AutoStop | Manages the automatic completion of a transaction. Default is True. |
Options.DisconnectAction | The action that will be performed when the connection is closed, if the transaction is active. The default is xdCommit. The following options are possible:
In other access components, the default value for the similar xdRollback property. Therefore, it is necessary to set this property manually to the value that is really required. |
Options.EnableNested | Manages nested transactions. The default is True. When a transaction is active, the next StartTransaction call will create a nested transaction. FireDAC emulates nested transactions using savepoints if the DBMS does not explicitly support nested transactions. To disable nested transactions, set EnableNested to False and the next call to StartTransaction will raise an exception. Firebird does not explicitly support nested transactions. |
Options.Isolation | Specifies the isolation level of the transaction. This is the most important property of a transaction. The default is xiReadCommited. The following options are possible:
|
Options.Params | DBMS specific transaction parameters. Currently used only for Firebird and Interbase. Possible values:
|
Options.ReadOnly | Indicates whether the transaction is read only. Default is False. If set to True, then no changes within the current transaction are possible, in Firebird in this case there is no read value in the transaction parameters. Setting this property to True allows the DBMS to optimize the use of resources. |
Comment Such a transaction cannot be used for reports (especially if they use several consecutive requests), because a transaction with the READ COMMITED isolation mode during re-reading of data will see all new committed changes. For reports, it is recommended to use a read-only short transaction with SNAPSHOT isolation mode (Options.Isolation = xiSnapshot and Options.ReadOnly = True). In this example, working with reports is not considered. |
Comment In some cases, you can make the FDQuery component editable without setting the UpdateObject property and prescribing Insert / Update / Delete queries by simply setting the UpdateOptions.RequestLive = True property, while modifying queries will be generated automatically. However, this approach has many limitations on the basic SELECT query, so do not rely on it. |
Parameter | Purpose |
---|---|
Connection | Communication with the FDConnection component. |
MasterSource | Reference to the Master Data Source (TDataSource) for FDQuery, used as Detail. |
Transaction | The transaction within which the query specified in the SQL property will be executed. If the property is not specified, the default transaction will be used to connect. |
UpdateObject | Communication with the FDUpdateSQL component, which provides the “editable” data set when a SELECT query does not meet the requirements for automatically generating modifying queries when UpdateOptions.RequestLive = True is set. |
UpdateTransaction | The transaction within which the modifying queries will be executed. If the property is not specified, a transaction from the Transaction property will be used. |
UpdateOptions.CheckRequired | If the CheckRequired property is set to True, then FireDac controls the Required property of the corresponding fields, i.e. fields with NOT NULL constraint. The default is set to true. If CheckRequired = True and no value is assigned to the field with the property Required = True, then an exception is raised when the Post method is called. This may be undesirable if the value of this field can be assigned later in the BEFORE triggers. |
UpdateOptions.EnableDelete | Determines whether deleting a record from the dataset is allowed. If EnableDelete = False, an exception will be raised when the Delete method is called. |
UpdateOptions.EnableInsert | Determines whether insertion of a record into a dataset is allowed. If EnableInsert = False, then an exception will be raised when calling the Insert / Append method. |
UpdateOptions.EnableUpdate | Determines whether a record is allowed to be changed in the dataset. If EnableUpdate = False, an exception will be raised when the Edit method is called. |
UpdateOptions.FetchGeneratorsPoint | Controls the moment when the next value of the generator specified in the UpdateOptions.GeneratorName property or the GeneratorName property of the auto-increment field AutoGenerateValue = arAutoInc is received. It has the following options:
The default is gpDeffered. |
UpdateOptions.GeneratorName | The name of the generator to extract the next value of the auto-increment field. |
UpdateOptions.ReadOnly | Indicates whether the dataset is read only. Default is False. If the value of this property is set to True, then the values ​​of the properties EnableDelete, EnableInsert and EnableUpdate will be automatically set to False. |
UpdateOptions.RequestLive | Setting RequestLive to True makes the request “live”, i.e. editable if possible. In this case, Insert / Update / Delete requests will be generated automatically. This option imposes many restrictions on the SELECT query, introduced for backward compatibility with the BDE and is not recommended. |
UpdateOptions.UpdateMode | Responsible for checking the modification record. This property made it possible to control the possible “overlapping” of updates for cases when a user performs editing a “long” record, and another user can manage to edit the same record and save it earlier. That is, the first user at the editing stage will not even know that the record has already changed, perhaps more than once, and will be able to “wipe” these updates with his:
The last check corresponds to the request automatically generated for UpdateSQL. , where . , , upWhereChanged update table set… — , set , . , UpdateSQL. , ProviderFlags . |
CachedUpdates | , . True, (Insert/Post, Update/Post, Delete) , . , ApplyUpdates. . False. |
SQL | SQL . SELECT , Open. Execute ExecSQL. |
Comment FireDac , (TFDConnection.Connected = True) (TFDTransaction.Options.AutoStart = True). . , , TFDConnection SYSDBA. TFDConnection.Connected . , . |
Comment Because product_id is not included in Updating Fields, it is not in the generated insert query. It is assumed that this column is filled automatically with a trigger (with a generator), or this is an IDENTITY column (starting with Firebird 3.0). When getting the generator value for this column from the server, it is recommended to manually add the PRODUCT_ID column to the RETURNING clause of the INSERT statement. |
Parameter | Purpose |
---|---|
Connection | Communication with the FDConnection component. |
DeleteSQL | SQL query to delete the record. |
FetchrowSQL | SQL query to return one current (updated, inserted) record. |
InsertSQL | SQL query to insert record. |
LockSQL | SQL query to lock one current record. (FOR UPDATE WITH LOCK). |
ModifySQL | SQL query to modify the record. |
UnlockSQL | SQL query to unlock the current record. In Firebird does not apply. |
Parameter | |
---|---|
Connection | FDConnection. |
Transaction | , SQL . |
CommandKind | .
SQL . |
CommandText | SQL . |
Comment trRead , , dmMain. |
SELECT customer_id, name, address, zipcode, phone FROM customer ORDER BY name
INSERT INTO customer (customer_id, name, address, zipcode, phone) VALUES (:new_customer_id, :new_name, :new_address, :new_zipcode, :new_phone)
UPDATE customer SET name = :new_name, address = :new_address, zipcode = :new_zipcode, phone = :new_phone WHERE (customer_id = :old_customer_id)
DELETE FROM customer WHERE (customer_id = :old_customer_id)
SELECT customer_id, name, address, zipcode, phone FROM customer WHERE customer_id = :old_customer_id
procedure TCustomerForm.actEditRecordExecute(Sender: TObject); var xEditor: TEditCustomerForm; begin xEditor := TEditCustomerForm.Create(Self); try xEditor.OnClose := CustomerEditorClose; xEditorForm.DataSource := DataSource; xEditor.Caption := 'Edit customer'; qryCustomer.CachedUpdates := True; qryCustomer.Edit; xEditor.ShowModal; finally xEditor.Free; end; end;
procedure TCustomerForm.CustomerEditorClose (Sender: TObject; var Action: TCloseAction); begin if TForm(Sender).ModalResult <> mrOK then begin // qryCustomer.Cancel; qryCustomer.CancelUpdates; // qryCustomer.CachedUpdates := False; // Action := caFree; Exit; end; try // qryCustomer.Post; // trWrite.StartTransaction; // if (qryCustomer.ApplyUpdates = 0) then begin // qryCustomer.CommitUpdates; // trWrite.Commit; end else begin raise Exception.Create(qryCustomer.RowError.Message); end; qryCustomer.CachedUpdates := False; Action := caFree; except on E: Exception do begin // if trWrite.Active then trWrite.Rollback; Application.ShowException(E); // , Action := caNone; end; end; end;
SELECT product_id, name, price, description FROM product ORDER BY name
INSERT INTO PRODUCT (NAME, PRICE, DESCRIPTION) VALUES (:NEW_NAME, :NEW_PRICE, :NEW_DESCRIPTION) RETURNING PRODUCT_ID
SELECT invoice.invoice_id AS invoice_id, invoice.customer_id AS customer_id, customer.NAME AS customer_name, invoice.invoice_date AS invoice_date, invoice.total_sale AS total_sale, IIF(invoice.payed=1, 'Yes', 'No') AS payed FROM invoice JOIN customer ON customer.customer_id = invoice.customer_id WHERE invoice.invoice_date BETWEEN :date_begin AND :date_end ORDER BY invoice.invoice_date DESC
qryInvoice.ParamByName('date_begin').AsSqlTimeStamp := dmMain.BeginDateSt; qryInvoice.ParamByName('date_end').AsSqlTimeStamp := dmMain.EndDateSt; qryInvoice.Open;
Comment , TFDUpdateSQL. |
EXECUTE PROCEDURE sp_add_invoice( NEXT VALUE FOR gen_invoice_id, :CUSTOMER_ID, :INVOICE_DATE )
EXECUTE PROCEDURE sp_edit_invoice( :INVOICE_ID, :CUSTOMER_ID, :INVOICE_DATE )
EXECUTE PROCEDURE sp_delete_invoice(:INVOICE_ID)
EXECUTE PROCEDURE sp_pay_for_inovice(:invoice_id)
if MessageDlg(' ?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then begin // trWrite.StartTransaction; try qryDeleteInvoice.ParamByName('INVOICE_ID').AsInteger := qryInvoice.FieldByName('INVOICE_ID').AsInteger; // qryDeleteInvoice.Execute; // trWrite.Commit; // qryInvoice.Refresh; except on E: Exception do begin if trWrite.Active then trWrite.Rollback; Application.ShowException(E); end; end; end;
procedure TEditInvoiceForm.edtCustomerRightButtonClick(Sender: TObject); var xSelectForm: TCustomerForm; begin xSelectForm := TCustomerForm.Create(Self); try xSelectForm.Visible := False; if xSelectForm.ShowModal = mrOK then begin FCustomerId := xSelectForm.qryCustomer.FieldByName('CUSTOMER_ID') .AsInteger; edtCustomer.Text := xSelectForm.qryCustomer.FieldByName('NAME').AsString; end; finally xSelectForm.Free; end; end;
procedure TInvoiceForm.actEditInvoiceExecute(Sender: TObject); var xEditorForm: TEditInvoiceForm; begin xEditorForm:= TEditInvoiceForm.Create(Self); try xEditorForm.OnClose := EditInvoiceEditorClose; xEditor.Caption := ' -'; xEditorForm.InvoiceId := qryInvoice.FieldByName('INVOICE_ID').AsInteger; xEditorForm.SetCustomer(qryInvoice.FieldByName('CUSTOMER_ID').AsInteger, qryInvoice.FieldByName('CUSTOMER_NAME').AsString); xEditorForm.InvoiceDate := qryInvoice.FieldByName('INVOICE_DATE').AsDateTime; xEditorForm.ShowModal; finally xEditorForm.Free; end; end;
procedure TEditInvoiceForm.SetCustomer(ACustomerId: Integer; const ACustomerName: string); begin FCustomerId := ACustomerId; edtCustomer.Text := ACustomerName; end;
procedure TInvoiceForm.EditInvoiceEditorClose(Sender: TObject; var Action: TCloseAction); var xEditorForm: TEditInvoiceForm; begin xEditorForm := TEditInvoiceForm(Sender); // OK, // . . if xEditorForm.ModalResult <> mrOK then begin Action := caFree; Exit; end; // trWrite.StartTransaction; try qryEditInvoice.ParamByName('INVOICE_ID').AsInteger := xEditorForm.InvoiceId; qryEditInvoice.ParamByName('CUSTOMER_ID').AsInteger := xEditorForm.CustomerId; qryEditInvoice.ParamByName('INVOICE_DATE').AsSqlTimeStamp := DateTimeToSQLTimeStamp(xEditorForm.InvoiceDate); qryEditInvoice.Execute(); trWrite.Commit; qryInvoice.Refresh; Action := caFree; except on E: Exception do begin if trWrite.Active then trWrite.Rollback; Application.ShowException(E); // , Action := caNone; end; end; end;
SELECT invoice_line.invoice_line_id AS invoice_line_id, invoice_line.invoice_id AS invoice_id, invoice_line.product_id AS product_id, product.name AS productname, invoice_line.quantity AS quantity, invoice_line.sale_price AS sale_price, invoice_line.quantity * invoice_line.sale_price AS total FROM invoice_line JOIN product ON product.product_id = invoice_line.product_id WHERE invoice_line.invoice_id = :invoice_id
EXECUTE PROCEDURE sp_add_invoice_line( :invoice_id, :product_id, :quantity )
EXECUTE PROCEDURE sp_edit_invoice_line( :invoice_line_id, :quantity )
EXECUTE PROCEDURE sp_delete_invoice_line( :invoice_line_id )
procedure TEditInvoiceLineForm.edtProductRightButtonClick(Sender: TObject); var xSelectForm: TGoodsForm; begin // // if FEditMode = emInvoiceLineEdit then Exit; xSelectForm := TGoodsForm.Create(Self); try xSelectForm.Visible := False; if xSelectForm.ShowModal = mrOK then begin FProductId := xSelectForm.qryGoods.FieldByName('PRODUCT_ID') .AsInteger; edtProduct.Text := xSelectForm.qryGoods.FieldByName('NAME').AsString; // edtPrice.Text := xSelectForm.qryGoods.FieldByName('PRICE').AsString; end; finally xSelectForm.Free; end; end;
procedure TInvoiceForm.actEditInvoiceLineExecute(Sender: TObject); var xEditorForm: TEditInvoiceLineForm; begin xEditorForm:= TEditInvoiceLineForm.Create(Self); try xEditorForm.OnClose := EditInvoiceLineEditorClose; xEditorForm.EditMode := emInvoiceLineEdit; xEditorForm.Caption := ' '; xEditorForm.InvoiceLineId := qryInvoiceLine.FieldByName('INVOICE_LINE_ID').AsInteger; xEditorForm.SetProduct(qryInvoiceLine.FieldByName('PRODUCT_ID').AsInteger, qryInvoiceLine.FieldByName('PRODUCTNAME').AsString, qryInvoiceLine.FieldByName('SALE_PRICE').AsCurrency); xEditorForm.Quantity := qryInvoiceLine.FieldByName('QUANTITY').AsInteger; xEditorForm.ShowModal; finally xEditorForm.Free; end; end;
procedure TEditInvoiceLineForm.SetProduct(AProductId: Integer; AProductName: string; APrice: Currency); begin FProductId := AProductId; edtProduct.Text := AProductName; edtPrice.Text := CurrToStr(APrice); end;
procedure TInvoiceForm.EditInvoiceLineEditorClose(Sender: TObject; var Action: TCloseAction); var xCustomerId: Integer; xEditorForm: TEditInvoiceLineForm; begin xEditorForm := TEditInvoiceLineForm(Sender); // OK, // . . if xEditorForm.ModalResult <> mrOK then begin Action := caFree; Exit; end; // trWrite.StartTransaction; try qryEditInvoiceLine.ParamByName('INVOICE_LINE_ID').AsInteger := xEditorForm.InvoiceLineId; qryEditInvoiceLine.ParamByName('QUANTITY').AsInteger := xEditorForm.Quantity; qryEditInvoiceLine.Execute(); trWrite.Commit; qryInvoice.Refresh; qryInvoiceLine.Refresh; Action := caFree; except on E: Exception do begin if trWrite.Active then trWrite.Rollback; Application.ShowException(E); // . Action := caNone; end; end; end;
Source: https://habr.com/ru/post/273549/
All Articles