Перейти к содержанию
Fire Monkey от А до Я
  • 0

Работа с БД в класс


Aleks133

Вопрос

Здравствуйте,

Подскажите как правильно организовать работу с БД в классах.Есть БД(Sqlite), для работы использую FireDac.

Например структура БД состоит из 3-х таблиц.Сейчас у меня для каждой таблицы свой(боюсь конфликтов в программе) FDQuery.

Основной Unit разросся, поэтому хочу весь функционал перевести в отдельный класс.С классами только начал знакомиться.

Начал писать так:

unit WorkBase;

interface

uses classes, Sysutils, FireDAC.Stan.Intf, FireDAC.Stan.Option,
  FireDAC.Stan.Error,
  FireDAC.UI.Intf, FireDAC.Phys.Intf, FireDAC.Stan.Def, FireDAC.Stan.Pool,
  FireDAC.Stan.Async, FireDAC.Phys, FireDAC.FMXUI.Wait, FireDAC.Stan.Param,
  FireDAC.DatS, FireDAC.DApt.Intf, FireDAC.DApt, Data.DB, FireDAC.Comp.DataSet,
  FireDAC.Comp.Client, FireDAC.Phys.SQLite, FireDAC.Phys.SQLiteDef,
  FireDAC.Stan.ExprFuncs, system.Variants;

type
  TDBWork = class
  private
    Bcon: TFDConnection;
    BQ: TFDQuery;
    procedure beforeconnection(sender: TObject);
    function GetCountExe: integer;
  public
    procedure SetAccaunts(fname, token: string);    
    function GetAccauntsList: TStringList;
    constructor create;
    destructor free; // override;
    property Count: integer read GetCountExe;
  end;

implementation

{ TDBWork }

procedure TDBWork.beforeconnection(sender: TObject);
begin
  Bcon.Params.Values['Database'] := ExtractFilePath(ParamStr(0)) + 'base.db';
end;

constructor TDBWork.create;
begin
  Bcon := TFDConnection.create(nil);
  Bcon.BeforeConnect := beforeconnection;
  Bcon.Connected;
  Bcon.LoginPrompt := false;
  Bcon.Params.Values['LockingMode'] := 'Normal';
  Bcon.Params.Values['DriverID'] := 'SQlite';
  BQ := TFDQuery.create(nil);
  BQ.Connection := Bcon;
end;

destructor TDBWork.free;
begin
  Bcon.free;
  BQ.free;
  // inherited;
end;

function TDBWork.GetCountExe: integer;
begin
  BQ.Close;
  BQ.SQL.Text := 'select max(id) from exe';
  BQ.OpenOrExecute;  
  result := BQ.RowsAffected;
end;

function TDBWork.GetAccauntsList: TStringList;
begin
  result := TStringList.create;
  with BQ do
  begin
    Close;
    Open('select * from accaunt');
    First;
    while not BQ.Eof do
    begin
      result.Add(BQ.FieldByName('name').AsString);
      next;
    end;
  end;
end;

// добавление аккаунта
procedure TDBWork.SetAccaunts(fname: string; fFirstname: string);
var
  rez: variant;
begin
  try
    with BQ do
    begin
      Open('select * from accaunt');
      rez := Lookup('name', fname, 'name');
      if vartype(rez) = varnull then
      begin
        Close;
        SQL.Text := 'insert into accaunt(name,Firstname) values(:n,:t)';
        ParamByName('n').AsString := fname;
        ParamByName('t').AsString := fFirstname;
        ExecSQL;
      end;
    end;
  except

  end;
end;
end.

Дальше мне нужно добавить новую функцию для работы со второй  таблицей.Вот только теперь думаю, добавлять в constructor еще один query или можно с одим работать.Например для записи в лог.Вдруг гдето в программе обе функции будут использовать этот один query одновременно, например в основном потоке и дополнительном.Или этого быть не может из-за того что компоненты FDConnection и FDQuery создаются динамически и при использовании класса я каждый раз создаю новый экземпляр? 

procedure TForm1.Button35Click(Sender: TObject);
var
wb1:TDBWork;
list:TStringList;
s:string;
begin
wb1:=TDBWork.create;
list:=TStringList.Create;
list:=wb1.GetAccauntsList;
showmessage(list.Text);
wb1.free;
list.Free;
end;

ps Сильно не ругайте, только учусь.

Ссылка на комментарий

Рекомендуемые сообщения

  • 0

Хорошей практикой является следующая концепция.

Есть модель данных (МД) - набор классов, описывающий все, что происходит в вашей программе, и хранящий все нужные данные. 

А есть отдельный модуль, который отвечает за загрузку и сохранение этой МД. В нем класс-"загрузчик". Чтобы вам было удобнее, можно в качестве такого модуля сделать Datamodule, в котором будут все нужные все компоненты и методы.

При этом становится не суть важно, откуда идет загрузка, хоть из интернета, хоть из БД.

Вопросов с выделением этих процессов в отдельные потоки не будет, только нужно помнить только одно - один поток - один Datamodule. Т.е. создавать их надо динамически.

создали объект модели данных, создали datamodule, вызвали некий метод для загрузки данных в объект. 

Хорошим тоном будет не создавать/уничтожать Datamodule каждый раз заново, потому что процесс подключения к БД довольно длительный по сравнению с простым запросом в БД. Поэтому можно создать какой-то пул(массив, список) уже созданных DM  и брать одну штуку из него для совершения операции, и возвращать обратно по ее окончанию.

Получается, что каждый поток будет иметь свой Datamodule и следовательно свой FDConnection и свой FDQuery для каждой операции.

Надеюсь, я ответил на ваш вопрос

Ссылка на комментарий
  • 0

Спасибо большое за оперативный ответ и за отклик в принципе!!!

Правильно ли я понимаю:

1.Создаем datamodule(Tdm)->кидаем на него нужные компоненты

1.1. Или Создаем datamodule(Tdm)->создаем в нем constructor create для создания нужных компонентов динамически?

2.В своих классах используем этот дата-модуль так?

uses UnitDM

type TMyclass=class(TObject)
private
...
public
function getdata:string
end;

type TNewDM = class(Tdm)

implementation

...
function TMyclass.getdata:string;
var
NewDM1:TNewDM;
begin
NewDM1:=TNewDM.Create;
Connection.params.value['base']:='путь к бд';
Query.sql.text:='запрос';
result:=Query.fields[1].asstring;
NewDM1.free;
end;
...

  А в основном юните использовать так:

...
implementation
uses MyClass;
....
var
s:string;
MyClass1:TMyclass;
 begin
  MyClass1:=TMyclass.create;
  s:=MyClass1.getdata;
  MyClass1.free;
 end;

 

4 часа назад, krapotkin сказал:

Поэтому можно создать какой-то пул(массив, список) уже созданных DM  и брать одну штуку из него для совершения операции, и возвращать обратно по ее окончанию.

Это я вообще не понял как реализовать. Т.е нужно  в TMyClass создать массив что-то вроде того? 

...
type TMyclass=class(TObject)
 private
   ArrDm: array [0 .. 2] of TDm;
 public
   function getdata:string
end;

implementation
...

constructor TMyClass.create;
var i:integer;
begin
  for i:=0 to 2 do
    ArrDm[i]:=TDm.create;
end;

а потом для в реализации класса чередовать их или для каждой таблицы использовать свой элемент массива?

 

Изменено пользователем Aleks133
Ссылка на комментарий
  • 0

Dm самый обычный только создавать его автоматически не нужно как и все формы приложения кроме главной. 

Про массив все верно. Только вместо него лучше взять tthreadlist. 

Можно сделать так. Поток стартует, смотрит, нет ли уже готового дм, если есть, забирает его из списка, если нет, создаёт. По окончании работы кладёт дм в список.

Ссылка на комментарий
  • 0
6 минут назад, krapotkin сказал:

Dm самый обычный только создавать его автоматически не нужно как и все формы приложения кроме главной. 

т.е. создаю новый unit->описываю в нем Tdatamodule и создаю все компоненты динамически.верно?

8 минут назад, krapotkin сказал:

Про массив все верно. Только вместо него лучше взять tthreadlist. 

Я еще нашел на другом форуме Вы предлагали использовать TObjectList, он подойдет?(Вроде не сложен в использовании)

  

Ссылка на комментарий
  • 0
20 часов назад, Aleks133 сказал:

создаю все компоненты динамически

нет. динамически создавать надо только сам dm. он создаст свои компоненты настолько же динамически как и вы )

20 часов назад, Aleks133 сказал:

использовать TObjectList

подойдет. НО. использовать его надо через синхронизацию. Создать cs:TCriticalSection. Каждый поток перед доступом к списку делает cs.enter, забирает оттуда dm, делает cs.leave. Т.е. в один момент времени только один поток может работать со списком

Эта же функциональность просто встроена в TThreadList. Чем пользоваться - по сути все равно

Ссылка на комментарий

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить на вопрос...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...