Delphi和C++ Builder中的Hibernate开发(五)

Hibernate中Session接口定义了基本的CURD操作:

save()方法把Java对象保存数据库中,update()方法更新数据库中的Java对象,delete()方法把Java对象从数据库中删除,load()方法从数据库中加载Java对象,find()方法从数据库中查询Java对象;

本框架中的Session的CURD操作由TDataOperator类完成。

每一个持久类的实例在数据库中有对应的记录,并拥有一个持久化标识(identifier)。

TDataOperator类的CURD操作的实现分为两种:

第一种从SQL实现持久对象和记录的操作,方法定义如下:

从数据库中的记录加载一个持久对象:
  function Load(AType: TTableDataClass; AValue: Variant): TTableData; overload;
保存一个持久对象到数据库中:
  function Save(AObject: TTableData): Boolean;
删除一个持久对象对应的数据库表记录:
  function Delete(Aobject: TTableData): Boolean; overload;


第二种是用TDataSet的方法实现持久对象和记录的操作,方法定义如下:

从数据库中的记录加载一个持久对象:
  function TDataOperator.get(TableName: string; pkName: string;  pkValue: variant): TObject;

保存一个持久对象到数据库中:
  function TDataOperator.saveData(data: TTableData): Boolean;

删除一个持久对象对应的数据库表记录:
  TDataOperator.deleteData(data: TTableData): Boolean;

修改一个持久对象对应的数据库表记录:

function TDataOperator.updateData(data: TTableData): Boolean;

对多个持久对象的成批操作的方法定义有:

function TDataOperator.SaveList(AList: TTableDatalist): Integer;

function TDataOperator.updateAllData(dataList: TTableDatalist): Boolean;

unit UnitDataOperator;

interface

uses
  SysUtils, Windows, Messages, Classes, DB, ADODB, Variants, TypInfo, StrUtils,
  UnitDataCommand, UnitDataController, UnitAppLogger, UnitBaseTable,
  UnitBaseTableList,CnDHibernateBase;

type

  TOperateType = (otSelect, otInsert, otUpdate, otDelete);

  TDataOperator = class;

  TDataOperator = class(TPersistent)
  private
    FDataController: TDataController;
    function AddItemsToList(AList: TTableDatalist; AData: TDataSet): Integer;
    function CreateTextCommand(ASQL: string): TDataCommand;
    function Execute(ASQL: string; AObject: TTableData): Boolean;
    function GetDataSQL(AObject: TTableData;
      AOperateType: TOperateType): string;
    function GetPreparedCmd(ASQL: string; KeyNames: array of string;
      KeyValues: array of variant): TDataCommand; overload;
    function GetPreparedCmd(ASQL: string; AObject: TTableData)
      : TDataCommand; overload;
    function Open(ASQL: string; AObject: TTableData): TDataSet;
  public
    constructor Create(); reintroduce;
    destructor Destroy; override;

    class function GetSQLWhere(ANames: array of string): string;

    function CheckObjectExists(AType: TTableDataClass;
      KeyValue: variant): Boolean;

    function FindByKeyValue(AType: TTableDataClass; KeyValue: variant)
      : TTableData;

    // 加载对象
    function Load(AType: TTableDataClass; KeyNames: array of string;
      KeyValues: array of variant): TTableData; overload;
    function Load(AType: TTableDataClass; AValue: variant): TTableData;
      overload;

    function LoadAllItems(AList: TTableDatalist;
      OnlyEnabled: Boolean = false): Integer;
    function LoadItems(AList: TTableDatalist; KeyNames: array of string;
      KeyValues: array of variant): Integer;

    // 保存对象
    function Save(AObject: TTableData): Boolean;

    function Delete(AObject: TTableData): Boolean; overload;

    { get one data object }
    function get(TableName: string; pkName: string; pkValue: variant): TObject;

    { save or update }
    function saveOrUpdate(data: TTableData): Boolean;

    function saveData(data: TTableData): Boolean;

    { update }
    function updateData(data: TTableData): Boolean;

    { delete }
    function deleteData(data: TTableData): Boolean;

    { save or update all }
    function saveOrUpdateAllData(dataList: TTableDatalist): Boolean;

    function SaveList(AList: TTableDatalist): Integer;

    { update all }
    function updateAllData(dataList: TTableDatalist): Boolean;

    property DataController: TDataController read FDataController
      write FDataController;
  end;

const
{$IFDEF DB_ACCESS}
  STR_BOOL_VALUE: array [Boolean] of string = ('false', 'true');
{$ELSE}
  STR_BOOL_VALUE: array [Boolean] of string = ('0', '1');
{$ENDIF}

implementation

{
  ******************************** TDataOperator *********************************
}
function TDataOperator.AddItemsToList(AList: TTableDatalist;
  AData: TDataSet): Integer;
var
  fItem: TTableData;
begin
  with AData do
  begin
    first;
    while not Eof do
    begin
      fItem := AList.ItemType.Create(AData);
      // fDataType.Create(Pool, AData);
      fItem.IsNew := false;
      AList.Add(fItem);
      Next;
    end; // while
  end; // with
  result := AData.RecordCount;

end;

function TDataOperator.CheckObjectExists(AType: TTableDataClass;
  KeyValue: variant): Boolean;
var
  fObj: TTableData;
begin
  fObj := FindByKeyValue(AType, KeyValue);
  result := fObj <> nil;
  // if result then
  // fObj.Free;

end;

constructor TDataOperator.Create();
begin
  // FDataType := ATableDataType;
  // FValueItems := TTableDataList.Create(AOwner, FDataType);
end;

destructor TDataOperator.Destroy;
begin
end;

function TDataOperator.CreateTextCommand(ASQL: string): TDataCommand;
begin
  if FDataController = nil then
    raise Exception.CreateFmt
      ('%s.CreateTextCommand:No DataController specified.', [Classname]);
  result := TDataCommand.Create(FDataController);
  result.CommandText := ASQL;
  result.CommandType := cmdText;
end;

function TDataOperator.Delete(AObject: TTableData): Boolean;
var
  fSQL: string;
begin
  // if not DataController.Connection.InTransaction then
  DataController.Connection.BeginTrans;
  try
    fSQL := GetDataSQL(AObject, otDelete);
    result := Execute(fSQL, AObject);
    DataController.Connection.CommitTrans;
  except
    on e: Exception do
    begin
      DataController.Connection.RollbackTrans;
      AppLogger.AddLog(Self, '%s.Delete: %s', [Classname, e.Message]);
      raise;
    end;
  end; // try/except
end;

function TDataOperator.deleteData(data: TTableData): Boolean;
var
  map: ICnMap;
  hql: string;
begin
  map := getPodoProperties(data.TableName, data);
  hql := Format(DH_DELETE_RECORD, [data.TableName]) + getSearchEvent(map);
  with TADOQuery.Create(nil) do
  begin
    Connection := Self.DataController.Connection;
    SQL.Text := hql;
    try
      ExecSQL;
      result := True;
    except
      on e: Exception do
      begin
        result := false;
        raise Exception.Create(e.Message);
      end;
    end;
    Free;
  end;

end;

function TDataOperator.Execute(ASQL: string; AObject: TTableData): Boolean;
var
  fCmd: TADOCommand;
  I: Integer;
  fParamName: String;
  fValue: variant;
begin
  result := false;

  if FDataController = nil then
    raise Exception.CreateFmt
      ('%s.CreateTextCommand:No DataController specified.', [Classname]);

  try
    fCmd := TADOCommand.Create(nil);
    fCmd.Connection := FDataController.Connection;
    fCmd.CommandText := ASQL;
    fCmd.CommandType := cmdText;

    if AObject <> nil then
    begin
      for I := 0 to fCmd.Parameters.Count - 1 do // Iterate
      begin
        fParamName := fCmd.Parameters[I].Name;
        try
          if AObject.FieldList.IndexOf(fParamName) > -1 then
          begin
            fValue := AObject.Values[fParamName];
            fCmd.Parameters[I].Value := fValue;
          end;
        except
          on e: Exception do
          begin
            AppLogger.AddLog(Self, 'GetPreparedCmd[ParamName=%s]: %s',
              [fParamName, e.Message]);
            raise;
          end;
        end; // try/except
      end; // for
    end;

    // fCmd := Self.GetPreparedCmd(ASQL, AObject);
    try
      fCmd.Execute;
      result := True;
    except
      on e: Exception do
      begin
        result := false;
        AppLogger.AddLog(Self, 'Execute:%s', [e.Message]);
        raise;
      end;
    end; // try/except
  finally
    FreeAndNil(fCmd);
  end;
end;

function TDataOperator.FindByKeyValue(AType: TTableDataClass; KeyValue: variant)
  : TTableData;
begin
  // result := FValueItems.FindByKeyValue(KeyValue);
  // if result = nil then
  result := Load(AType, [AType.KeyColumnName], [KeyValue]);
end;

function TDataOperator.Get(TableName: string; pkName: string;
  pkValue: variant): TObject;
var
  clazz: TClass;
  // obj               : TObject;
  obj: TObject;
  Pplst: PPropList;
  Classtypeinfo: PTypeInfo;
  classDataInfo: PTypeData;
  tempQuery: TADOQuery;
  I: Integer;
  tk: TTypeKind;
begin
  clazz := FindClass(Format(DH_CLASS_NAME, [TableName]));
  obj := clazz.NewInstance;
  tempQuery := TADOQuery.Create(nil);
  with tempQuery do
  begin
    Connection := Self.DataController.Connection;
    SQL.Text := Format(DH_GET_RECORD, [TableName, pkName, pkName]);
    Parameters.ParamValues[pkName] := pkValue;
    Open;

    Classtypeinfo := clazz.ClassInfo;
    classDataInfo := GetTypeData(Classtypeinfo);
    if classDataInfo.PropCount <> 0 then
    begin
      GetMem(Pplst, sizeof(PpropInfo) * classDataInfo.PropCount);
      try
        GetPropInfos(Classtypeinfo, Pplst);
        for I := 0 to classDataInfo.PropCount - 1 do
        begin

          tk := Pplst[I]^.PropType^.Kind;
          if tk <> tkMethod then
          begin
            // set the string properties
            if (tk = tkString) or (tk = tkLString) or (tk = tkWString) or
              (tk = tkUString) then
            begin
              SetStrProp((obj as clazz), Pplst[I]^.Name,
                FieldByName(Pplst[I]^.Name).AsString);
            end;
            // set the integer properties
            if tk = tkInteger then
            begin
              try
                SetInt64Prop((obj as clazz), Pplst[I]^.Name,
                  FieldByName(Pplst[I]^.Name).AsInteger);
              except
                SetInt64Prop((obj as clazz), Pplst[I]^.Name, 0);
              end;
            end;
            // set the float properties
            if tk = tkFloat then
            begin
              try
                SetFloatProp((obj as clazz), Pplst[I]^.Name,
                  FieldByName(Pplst[I]^.Name).AsFloat);
              except
                SetFloatProp((obj as clazz), Pplst[I]^.Name, 0);
              end;
            end;
            // set the variant properties
            if tk = tkVariant then
            begin
              SetVariantProp((obj as clazz), Pplst[I]^.Name,
                FieldByName(Pplst[I]^.Name).Value);
            end;
          end;
        end;
      finally
        FreeMem(Pplst, sizeof(PpropInfo) * classDataInfo.PropCount);
      end;
    end;
    Close;
    Free;
  end;
  // setFormulaProperties(TableName, pkName, pkValue, obj);
  result := obj;

end;

function TDataOperator.GetDataSQL(AObject: TTableData;
  AOperateType: TOperateType): string;

const
  sSQLType: array [TOperateType] of string = ('select *',
    'insert into %s (%s) values (%s)', 'update %s set %s where %s = %s%s',
    'delete');

  sFieldSplitter = ',';
  sParamPrefix = ':';
var
  I: Integer;
  fNames, fValues: string;
  fname: string;

begin
  result := EmptyStr;
  case AOperateType of //
    otSelect, otDelete:
      begin
        result := Format('%s from %s where %s = %s%s',
          [sSQLType[AOperateType], AObject.TableName, AObject.KeyColumnName,
          sParamPrefix, AObject.KeyColumnName]);
        if (AOperateType = otSelect) and (AObject.OrderByList <> EmptyStr) then
          result := result + ' order by ' + AObject.OrderByList;
      end;
    otInsert:
      begin
        for I := 0 to AObject.FieldList.Count - 1 do // Iterate
        begin
          fname := AObject.FieldList[I];

          if SameText(AObject.KeyColumnName, fname) and AObject.UseUniqueID and
            AObject.AutoKeyValue then
            Continue;

          fNames := fNames + sFieldSplitter + fname;

        end; // for
        fValues := StringReplace(fNames, sFieldSplitter,
          sFieldSplitter + sParamPrefix, [rfReplaceAll]);
        fNames := Copy(fNames, 2, Length(fNames));
        fValues := Copy(fValues, 2, Length(fValues));
        result := Format(sSQLType[AOperateType], [AObject.TableName, fNames,
          fValues]);
      end;
    otUpdate:
      begin
        for I := 0 to AObject.FieldList.Count - 1 do // Iterate
        begin
          fname := AObject.FieldList[I];
          if SameText(COL_UNIQUEID, fname) or SameText(AObject.KeyColumnName,
            fname) then
            Continue;
          fNames := fNames + sFieldSplitter + fname + ' = ' +
            sParamPrefix + fname;
        end; // for
        fNames := Copy(fNames, 2, Length(fNames));
        result := Format(sSQLType[AOperateType], [AObject.TableName, fNames,
          AObject.KeyColumnName, sParamPrefix, AObject.KeyColumnName]);
      end;
  end; // case
end;

function TDataOperator.GetPreparedCmd(ASQL: string; KeyNames: array of string;
  KeyValues: array of variant): TDataCommand;
var
  fParam: TParameter;
  I: Integer;
begin
  result := CreateTextCommand(ASQL);
  for I := Low(KeyNames) to High(KeyNames) do // Iterate
  begin
    fParam := result.Parameters.FindParam(KeyNames[I]);
    if fParam <> nil then
    begin
      fParam.Value := KeyValues[I];
    end;
  end; // for
end;

function TDataOperator.GetPreparedCmd(ASQL: string; AObject: TTableData)
  : TDataCommand;
var
  I: Integer;
  fParamName: string;
  fValue: variant;
begin
  // TODO: Prepared the database command and fill the paramlist.
  result := CreateTextCommand(ASQL);

  if FDataController = nil then
    raise Exception.CreateFmt
      ('%s.CreateTextCommand:No DataController specified.', [Classname]);
  result := TDataCommand.Create(FDataController);
  result.CommandText := ASQL;
  result.CommandType := cmdText;

  if AObject <> nil then
  begin
    for I := 0 to result.Parameters.Count - 1 do // Iterate
    begin
      fParamName := result.Parameters[I].Name;
      try
        if AObject.FieldList.IndexOf(fParamName) > -1 then
        begin
          fValue := AObject.Values[fParamName];
          result.Parameters[I].Value := fValue;
        end;
      except
        on e: Exception do
        begin
          AppLogger.AddLog(Self, 'GetPreparedCmd[ParamName=%s]: %s',
            [fParamName, e.Message]);
          raise;
        end;
      end; // try/except
    end; // for
  end;
end;

class function TDataOperator.GetSQLWhere(ANames: array of string): string;
var
  I: Integer;
begin
  result := EmptyStr;
  for I := Low(ANames) to High(ANames) do // Iterate
  begin
    result := result + Format(' AND %s = :%s', [ANames[I], ANames[I]]);
  end; // for
  result := Copy(result, 5, Length(result));

end;

function TDataOperator.Load(AType: TTableDataClass; AValue: variant)
  : TTableData;
begin
  result := Load(AType, [AType.KeyColumnName], [AValue]);
  if result = nil then
  begin
    AppLogger.AddLog(Self, '%s.Load: Not found [%s=%s]',
      [AType.Classname, AType.KeyColumnName, VarToStr(AValue)]);
  end;

end;

function TDataOperator.Load(AType: TTableDataClass; KeyNames: array of string;
  KeyValues: array of variant): TTableData;
var
  fList: TTableDatalist;
begin
  result := nil;
  fList := TTableDatalist.Create(AType);
  try
    if LoadItems(fList, KeyNames, KeyValues) > 0 then
    begin
      result := fList.Items[0] as TTableData;
      // result := fList.Extract(fList.Items[0]) as TTableData;
      if fList.Count > 1 then
      begin
        AppLogger.AddLog(Self, '%s.Load: [Count=%d]More than one row found!',
          [Classname, fList.Count]);
      end;
    end;
  finally
    fList.Free;
  end;
  // result := FDataType.Create(Pool);
  // fSQL := GetDataSQL(result, otSelect);
  // fCmd := GetPreparedCmd(fSQL, KeyNames, KeyValues);


end;

function TDataOperator.LoadAllItems(AList: TTableDatalist;
  OnlyEnabled: Boolean): Integer;
var
  fSQL: string;
  fData: TDataSet;
begin
  // load all items into AList, return the count.
  fSQL := Format('select * from %s', [AList.ItemType.TableName]);
  if OnlyEnabled and AList.ItemType.PropertyExists('Enabled') then
    fSQL := fSQL + ' where Enabled = ' + STR_BOOL_VALUE[True];
  if AList.ItemType.OrderByList <> EmptyStr then
    fSQL := fSQL + ' order by ' + AList.ItemType.OrderByList;
  fData := Open(fSQL, nil);
  try
    result := AddItemsToList(AList, fData);
  finally
    // FreeAndNil(fData);
  end;

end;

function TDataOperator.LoadItems(AList: TTableDatalist;
  KeyNames: array of string; KeyValues: array of variant): Integer;
var
  fCmd: TDataCommand;
  fSQL: string;
  fData: TDataSet;
  fWhere: string;
begin
  // load all items into AList, return the count.
  result := 0;
  fWhere := GetSQLWhere(KeyNames);
  fSQL := Format('select * from %s where %s', [AList.ItemType.TableName,
    fWhere]);
  if AList.ItemType.OrderByList <> EmptyStr then
    fSQL := fSQL + ' order by ' + AList.ItemType.OrderByList;

  try
    try
      fCmd := GetPreparedCmd(fSQL, KeyNames, KeyValues);
      fData := fCmd.Open;
      result := AddItemsToList(AList, fData);
    except
      on e: Exception do
      begin
        AppLogger.AddLog(Self, 'LoadItems:%s', [e.Message]);
        raise ;
      end;
    end; // try/except
  finally
    // FreeAndNil(fData);
    FreeAndNil(fCmd);
  end;

end;

function TDataOperator.Open(ASQL: string; AObject: TTableData): TDataSet;
var
  fCmd: TDataCommand;
begin
  fCmd := GetPreparedCmd(ASQL, AObject);
  try
    result := fCmd.Open;
  except
    on e: Exception do
    begin
      AppLogger.AddLog(Self, 'Open:%s', [e.Message]);
      raise;
    end;
  end; // try/except
end;

function TDataOperator.Save(AObject: TTableData): Boolean;
var
  fSQL: string;
  FIsNew: Boolean;
begin
  if AObject.DeleteFlag then
  begin
    result := Delete(AObject);
    Exit;
  end;
  try
    if not DataController.Connected then
      DataController.OpenConnection;

    DataController.Connection.BeginTrans;

    if AObject.IsNew then
      fSQL := GetDataSQL(AObject, otInsert)
    else
      fSQL := GetDataSQL(AObject, otUpdate);

    result := Execute(fSQL, AObject);

    AObject.IsNew := false;

    AObject.Modified := false;

    AObject.DeleteFlag := false;

    DataController.Connection.CommitTrans;
  except
    on e: Exception do
    begin
      DataController.Connection.RollbackTrans;
      AppLogger.AddLog(Self, 'Save: %s', [e.Message]);
      raise;
    end;
  end; // try/except
end;

function TDataOperator.saveData(data: TTableData): Boolean;
var
  map: ICnMap;
  I: Integer;
  hql: string;
begin
  map := getPodoProperties(data.TableName, data);
  hql := Format(DH_SELECT, [data.TableName]);
  with TADOQuery.Create(nil) do
  begin
    Connection := Self.DataController.Connection;;
    SQL.Text := hql;
    Open;
    Append;
    // 写入数据
    for I := 0 to map.size - 1 do
    begin
      FieldByName(map.getTable(I).hashName).Value := map.getTable(I).hashValue;
    end;
    try
      Post;
      result := True;
    except
      result := false;
    end;
    Free;
  end;

end;

function TDataOperator.SaveList(AList: TTableDatalist): Integer;
var
  I: Integer;
begin
  result := 0;
  for I := 0 to AList.Count - 1 do // Iterate
  begin
    if AList[I].DeleteFlag then
      Delete(AList[I])
    else if (AList[I].Modified or AList[I].IsNew) and Save(AList[I]) then
      Inc(result);
  end; // for

end;

function TDataOperator.saveOrUpdate(data: TTableData): Boolean;
var
  map: ICnMap;
  I: Integer;
  hql: string;
begin
  map := getPodoProperties(data.TableName, data);
  hql := Format(DH_ID_GENERATOR, [data.TableName, data.KeyColumnName,
    map.get(data.KeyColumnName)]);
  with TADOQuery.Create(nil) do
  begin
    Connection := Self.DataController.Connection;
    SQL.Text := hql;
    Open;
    if RecordCount = 0 then
      Append
    else
      Edit;
    for I := 0 to map.size - 1 do
    begin
      // ShowMessage(map.getTable(i).hashName+':'+string(map.getTable(i).hashValue));
      FieldByName(map.getTable(I).hashName).Value := map.getTable(I).hashValue;
    end;
    try
      Post;
      result := True;
    except
      result := false;
    end;
    Free;
  end;
end;

function TDataOperator.saveOrUpdateAllData(dataList: TTableDatalist): Boolean;
var
  I: Integer;
begin
  result := True;
  for I := 0 to dataList.Count - 1 do
  begin
    if not saveOrUpdate(dataList.Items[I]) then
      result := false;
  end;

end;

function TDataOperator.updateAllData(dataList: TTableDatalist): Boolean;
var
  I: Integer;
begin
  result := True;
  for I := 0 to dataList.Count - 1 do
  begin
    if not updateData(dataList.Items[I]) then
      result := false;
  end;

end;

function TDataOperator.updateData(data: TTableData): Boolean;
var
  map: ICnMap;
  hql: string;
  I: Integer;
begin
  map := getPodoProperties(data.TableName, data);
  hql := Format(DH_ID_GENERATOR, [data.TableName, data.KeyColumnName,
    map.get(data.KeyColumnName)]);
  with TADOQuery.Create(nil) do
  begin
    Connection := Self.DataController.Connection;
    SQL.Text := hql;
    Open;
    if RecordCount = 0 then
    begin
      result := false;
      Free;
      Exit;
    end;
    Edit;
    for I := 0 to map.size - 1 do
    begin
      FieldByName(map.getTable(I).hashName).Value := map.getTable(I).hashValue;
    end;
    try
      Post;
      result := True;
    except
      result := false;
    end;
    Free;
  end;

end;

end.

 

posted @ 2012-02-26 13:33  一块走  阅读(1786)  评论(0)    收藏  举报