Есть приложение, есть база данных. Приложение используют несколько пользователей с разных компьютеров.
На каждом компьютере есть мелкий файлик с настройкой подключения, а остальные настройки программы я бы очень хотел хранить в базе данных.
Может, кто-нибудь подскажет красивое решение для хранения определенной структуры в БД ? Учитывая то, что структура может и поменяться.
Здравствуйте, Rius, Вы писали:
R>попробуйте хранить xml-документ с настройками в текстовом поле неопределённой длины
А потом при добавлении какого-то свойства дописывать парсер?
Сам, если честно, остановился имеено на этом способе, но надеюсь на существование более красивого решения.
Здравствуйте, silentroach, Вы писали:
S>Здравствуйте, Rius, Вы писали:
R>>попробуйте хранить xml-документ с настройками в текстовом поле неопределённой длины
S>А потом при добавлении какого-то свойства дописывать парсер?
ага, только чтобы парсер при отсутсвии параметра в xml инициализировал соответствующую настройку по умолчанию...
Здравствуйте, Rius, Вы писали:
R>ага, только чтобы парсер при отсутсвии параметра в xml инициализировал соответствующую настройку по умолчанию...
ну вот это как раз мне и кажется не очень удобным
Вы, наверно издеваетесь — любая бд предназначена для хранения структурированой инф-ии, и мне странно видеть предложения хранить в бд структуру типа xml
вполне удобно хранить настройки в таблице со структурой вида "имя-значение",
уже не говоря о том, что можно сделать таблицу _нужной_ структуры.
Здравствуйте, __Azeroth, Вы писали:
__A>Вы, наверно издеваетесь — любая бд предназначена для хранения структурированой инф-ии, и мне странно видеть предложения хранить в бд структуру типа xml __A>вполне удобно хранить настройки в таблице со структурой вида "имя-значение", __A>уже не говоря о том, что можно сделать таблицу _нужной_ структуры.
замечательно! а если в "настройки" входит список значений или N-мерный массив, что тогда? ограничивать программу возможностями БД?
Здравствуйте, __Azeroth, Вы писали:
__A>Вы, наверно издеваетесь — любая бд предназначена для хранения структурированой инф-ии, и мне странно видеть предложения хранить в бд структуру типа xml __A>вполне удобно хранить настройки в таблице со структурой вида "имя-значение", __A>уже не говоря о том, что можно сделать таблицу _нужной_ структуры.
ну, честно говоря, это уже второй вопрос. главное то, как их получить, имена и параметры.
чтобы и с самими данными можно было удобно работать и обновлять эту структуру было возможно без изменения кода для обновления данных в БД.
например, есть у меня какой-нить класс конфигурации
type
TConfig = class
private
FColor: TColor;
public
property Color: TColor read FColor write FColor default $000000;
end;
могу я как-нить в рантайме пробежаться по свойствам экземпляра этого класса без ручного указания свойств?
а потом взять значения откуда-то и расставить все по своим местам?
а если значения нет, то бралось бы дефолтное.
т.е. мне нужно что-то наподобие TWriter'а и TReader'а что-ли, я даже и не знаю. только таких, чтобы могли записывать все это и считывать из базы.
что-то я не задумывался над этим, погляжу как они работают пойду =)
Если кому будет интересно, вот пример, который у меня получился.
unit tlConfig;
interface
uses
Classes, RClientDataSet, TypInfo, ADODB;
type
TString128 = string[128];
{$M+}
TConfig = class(TPersistent)
private
cdsConfig: TRClientDataSet;
FCompanyName: TString128;
protected
procedure WriteProperty(PropInfo: PPropInfo);
procedure GetProperty(PropInfo: PPropInfo);
public
constructor Create(const AConnection: TADOConnection);
destructor Destroy; override;
procedure LoadConfig;
procedure SaveConfig;
published
property CompanyName: TString128 read FCompanyName write FCompanyName;
end;
{$M-}var
Config: TConfig;
implementation
uses
SysUtils;
{ TConfig }constructor TConfig.Create(const AConnection: TADOConnection);
begin
cdsConfig := TRClientDataSet.Create(nil);
cdsConfig.StoredProc.ProcedureName := 'up_LoadConfig';
cdsConfig.StoredProc.Parameters.Clear;
cdsConfig.TableName := 'config';
cdsConfig.Connection := AConnection;
cdsConfig.KeyField := 'param';
end;
destructor TConfig.Destroy;
begin
if Assigned(cdsConfig) then
cdsConfig.Free;
inherited;
end;
procedure TConfig.GetProperty(PropInfo: PPropInfo);
var
Value: string;
begin
Value := '';
if cdsConfig.Locate('param', PropInfo^.Name, []) then
begin
Value := cdsConfig.FieldByName('value').AsString;
if Value <> ''then
SetPropValue(Self, PropInfo, Value);
end;
end;
procedure TConfig.LoadConfig;
var
i, Count: integer;
bChanged, bFinded: boolean;
PropInfo: PPropInfo;
PropList: PPropList;
begin
cdsConfig.Open;
try
Count := GetTypeData(ClassInfo)^.PropCount;
if Count > 0 then
begin
GetMem(PropList, Count * SizeOf(Pointer));
try
GetPropInfos(Config.ClassInfo, PropList);
for i := 0 to Count - 1 do
begin
PropInfo := PropList^[i];
if PropInfo = nil then
break;
GetProperty(PropInfo);
end;
finally
FreeMem(PropList, Count * SizeOf(Pointer));
end;
end;
// проверяем на несуществующие свойства в базе
bChanged := false;
cdsConfig.First;
while not cdsConfig.EOF do
begin
bFinded := false;
for i := 0 to Count - 1 do
if PropList^[i].Name = cdsConfig.FieldByName('param').AsString then
begin
bFinded := true;
break;
end;
if not bFinded then
begin
cdsConfig.Delete;
bChanged := true;
end;
cdsConfig.Next;
end;
if bChanged then
cdsConfig.ApplyUpdates(0);
// ---finally
cdsConfig.Active := false;
end;
end;
procedure TConfig.SaveConfig;
var
i, Count: integer;
PropInfo: PPropInfo;
PropList: PPropList;
begin
cdsConfig.Open;
try
Count := GetTypeData(ClassInfo)^.PropCount;
if Count > 0 then
begin
GetMem(PropList, Count * SizeOf(Pointer));
try
GetPropInfos(Config.ClassInfo, PropList);
for i := 0 to Count - 1 do
begin
PropInfo := PropList^[i];
if PropInfo = nil then
Break;
WriteProperty(PropInfo);
end;
finally
FreeMem(PropList, Count * SizeOf(Pointer));
end;
end;
finally
cdsConfig.Close;
end;
end;
procedure TConfig.WriteProperty(PropInfo: PPropInfo);
var
Value: string;
bChanged: boolean;
begin
Value := '';
bChanged := false;
case PropInfo^.PropType^.Kind of
tkInteger, tkChar, tkWChar, tkEnumeration, tkSet:
Value := IntToStr(GetOrdProp(Self, PropInfo));
tkFloat:
Value := FloatToStr(GetFloatProp(Self, PropInfo));
tkString, tkLString, tkWString:
Value := GetWideStrProp(Self, PropInfo);
end;
if cdsConfig.Locate('param', PropInfo^.Name, []) then
begin
if cdsConfig.FieldByName('value').AsString <> Value then
begin
if Value = ''then
cdsConfig.Delete
else
begin
cdsConfig.Edit;
try
cdsConfig.FieldByName('value').AsString := Value;
finally
cdsConfig.Post;
end;
end;
bChanged := true;
end;
end else
begin
if Value <> ''then
begin
cdsConfig.Append;
try
cdsConfig.FieldByName('param').AsString := PropInfo^.Name;
cdsConfig.FieldByName('value').AsString := Value;
finally
cdsConfig.Post;
end;
bChanged := true;
end;
end;
if bChanged then
cdsConfig.ApplyUpdates(0);
end;
end.
посмотри на функции BinaryToObject и ObjectToBinary. Они создают DFM структуру объекта в текстовом виде и соотвественно парсят ее теми же методами, которые очень трудно выкинуть из использования даже при всем желании. Зачем создавать велосипед еще раз и дублировать то, что разработчики вложили изначально. Кроме того, один блок теста намного проще и быстрее прочитать из BLOB, чем собирать кучу значений из таблиц, с последующей модификацией отдельного свойства (как советовали отдельные товарисчи).
Здравствуйте, svd71, Вы писали:
S>посмотри на функции BinaryToObject и ObjectToBinary. Они создают DFM структуру объекта в текстовом виде и соотвественно парсят ее теми же методами, которые очень трудно выкинуть из использования даже при всем желании. Зачем создавать велосипед еще раз и дублировать то, что разработчики вложили изначально. Кроме того, один блок теста намного проще и быстрее прочитать из BLOB, чем собирать кучу значений из таблиц, с последующей модификацией отдельного свойства (как советовали отдельные товарисчи).
В чем достоинство моего метода — если изменился один параметр, в базе обновится одна мелкая запись, а если делать по-твоему, то обновится одна большая запись (если настройек много, а их у меня будет действительно много).
В общем, и у того, и у этого способа свои плюсы и минусы