Difference between revisions of "Lazarus Database Tutorial/zh TW"

From Lazarus wiki
Jump to navigationJump to search
m (Fixed syntax highlighting; deleted category included in page template)
 
(33 intermediate revisions by 3 users not shown)
Line 1: Line 1:
 
{{Lazarus Database Tutorial}}
 
{{Lazarus Database Tutorial}}
  
 +
{{Database Programming}}
 +
= Lazarus 資料庫教學 =
 
== 導覽 ==
 
== 導覽 ==
  
本教材內容是關於如何使用 Lazarus 工作於數種資料庫環境下。
+
本教材內容是關於如何於 Lazarus 下配合數種資料庫來工作。
  
This tutorial is about getting Lazarus to work with a variety of existing databases.
+
Lazarus 支援數種非內建資料庫,而開發者仍應安裝各資料庫所對應的元件(Packages)。你可以透過程式碼或直接拉元件(components)至表單(from)來存取資料庫。資料感知 (data-aware) 元件代表資料庫欄位,並透過 TDataSource 設定指定的資料來源 (DataSource) 屬性物件來連接。資料來源代表一個資料表,是以連接至資料庫元件 (如:TPSQLDatabase, TSQLiteDataSet) DataSet 屬性所選定的。資料感知元件位於「資料控制」籤頁內。資料來源及資料庫控制元件則位於「資料存取」的籤頁內。
 
 
Lazarus 支援數種非傳統的資料庫, 而開發者仍應安裝各資料庫所對應的元件(Packages)。你可以透過程式碼或直接拉元件(components)至表單(from)來存取資料庫。資料感知(data-aware)元件代表資料庫欄位(fields), 並透過設定 DataSource 屬性指向 TDataSource 物件來連接(connected)。DataSource 代表一個表格(table), 並以 DataSet 屬性連接至一個資料庫元件(如:TPSQLDatabase, TSQLiteDataSet)。資料感知(data-aware)元件位於 "Data Controls" 頁籤。DataSource 及 資料庫控制元件則位於 "Data Access" 頁籤。
 
 
 
Lazarus 支援一些本機以外的資料庫(out-of-the-box), 然而發展者必者必須安裝必要的套件給每台電腦. 你可以存取資料庫透過程式碼或是由表單(form)來拖曳元件. The data-aware components represent fields and are connected by setting the DataSource property to point to a TDataSource. The Datasource represents a table and is connected to the database components (examples: TPSQLDatabase, TSQLiteDataSet) by setting the DataSet property. The data-aware components are located on the "Data Controls" tab. The Datasource and the database controls are located on the "Data Access" tab.
 
  
 
== Lazarus 與 MySQL ==
 
== Lazarus 與 MySQL ==
Line 17: Line 15:
  
 
=== 在文字模式中讓 MySQL 為 FPC 工作 ===
 
=== 在文字模式中讓 MySQL 為 FPC 工作 ===
在 $(fpc<b>src</b>dir)/packages/base/mysql/ 資料夾中有一個範例程式。你可以在 Lazarus 的 fpc 原始碼資料夾中找到:[環境]選單 -> [環境]選項 -> [路徑]籤頁 ->[FPC 原始碼目錄]。 mysql 的範例資料夾可能的路徑為 /usr/share/fpcsrc/packages/base/mysql/ (rpm 安裝) 或者 C:\lazarus\fpcsrc\packages\base\mysql\ (Windows)。 這個資料夾也包含了 mysql.pp, mysql_com.pp 和 mysql_version.pp。 執行這些指令碼前,你必須先建立一個 testdb 資料庫:請登入 mysql 監視器 (以根使用者登入來取得完整權限) 並執行以下 SQL 指令
+
在 $(fpc<b>src</b>dir)/packages/base/mysql/ 資料夾中有一個範例程式。你可以在 Lazarus 的 fpc 原始碼資料夾中找到:〔環境〕選單 -> 〔環境〕選項 -> 〔路徑〕籤頁 ->〔FPC 原始碼目錄〕。 mysql 的範例資料夾可能的路徑為 /usr/share/fpcsrc/packages/base/mysql/ (rpm 安裝) 或者 C:\lazarus\fpcsrc\packages\base\mysql\ (Windows)。 這個資料夾也包含了 mysql.pp, mysql_com.pp 和 mysql_version.pp。 執行這些指令碼前,你必須先建立一個 testdb 資料庫:請登入 mysql 監視器 (以根使用者登入來取得完整權限) 並執行以下 SQL 指令
 
  CREATE DATABASE testdb;
 
  CREATE DATABASE testdb;
 
接著確認所有相關使用者擁有適當的存取權限
 
接著確認所有相關使用者擁有適當的存取權限
Line 27: Line 25:
 
接著試著再執行一次,系統提示要你輸入密碼。幸運的話你應該就可以管理剛建立的資料庫了。用以下的 mysql 指令測試一下(在 mysql 監視器中):
 
接著試著再執行一次,系統提示要你輸入密碼。幸運的話你應該就可以管理剛建立的資料庫了。用以下的 mysql 指令測試一下(在 mysql 監視器中):
 
  select * from FPdev;
 
  select * from FPdev;
你可以看到一個列有 ID,使用者名稱和 Email 地址的FPC 開發者資料表了。
+
即可看到一個列有 ID,使用者名稱和 Email 地址的FPC 開發者資料表了。
  
 
現在可以執行測試程式 testdb.pp 了 (這檔也許還需要先編譯過,而且可能會在第一次編譯出錯!!)。
 
現在可以執行測試程式 testdb.pp 了 (這檔也許還需要先編譯過,而且可能會在第一次編譯出錯!!)。
Line 33: Line 31:
 
我找出這支程式可能無法連結 MySQL 的幾個原因:
 
我找出這支程式可能無法連結 MySQL 的幾個原因:
  
* My system (SuSE Linux v9.0) installs mysql v4.0.15, not the version3 for which the package was designed. 
+
* 我的系統 (SuSE Linux v9.0) 安裝的是 mysql v4.0.15,不同於安裝套件的版本 3。
  
* The program needs to have user names and passwords to get access to the database.
+
* 程式需要輸入使用者和密碼來存取資料庫。
  
* The compiler needs to know where to find the mysql libraries (IF YOU HAVEN'T INSTALLED THE MYSQL DEVELOPMENT LIBRARIES, DO SO NOW!)
+
* 編譯器需要知道哪裡可以找到 MySQL 的函式庫 ('''如果你並沒有安裝 MYSQL 的開發函式庫,那現在快裝!''')
  
I created a copy of testdb.pp called trydb.pp, rather than editing the original - this means that the original files still get fixed in subsequent CVS updates.
+
我將 testdb.pp 另存成 trydb.pp,然後做修改 - 這樣代表原來那支還是可以隨後透過 CVS 更新。
I also copied the files found in the subdirectory mysql/ver40/ into the main mysql/ subdirectory, renaming them mysql_v4.pp, mysql_com_v4.pp and mysql_version_v4.pp, being sure to rename the units within each file correspondingly.  I changed the uses statement in trydb.pp to
+
我另外也將在 mysql/ver40/ 子目錄下找到的那支另存成 mysql_v4.pp,mysql_com_v4.pp mysql_version_v4.pp,記得你單元裡的程式也要改成適當的檔名。在 trydb.pp 裡我變更 uses 敘述句為
 
  uses mysql_v4
 
  uses mysql_v4
and the statement in mysql_v4.pp to
+
mysql_v4.pp 的敘述句改為
  
 
  uses mysql_com_v4
 
  uses mysql_com_v4
  
I added a line to /etc/fpc.cfg to point to my libraries:
+
另外為要使用的函式庫加入一行到 /etc/fpc.cfg 裡:
 
  -Fl/lib;/usr/lib
 
  -Fl/lib;/usr/lib
The following step might not be necessary if the devel-libraries are installed as the links will be created for you, but it never hurts to check.
+
接下來的動作也許不必要做,因為你在安裝開發函式庫的時候應該會自動幫你建立連結,但檢查一下總是無仿。
I had to find the real name of the mysqlclint library in the /usr/lib directory and in my case I had to issue the shell command:
+
進行前需要先找到我的 MYSQL 用戶端函式庫是放在 /usr/lib 目錄下面的哪裡,我的狀況是先下 shell 指令:
 
  ln -s libmysqlclient.so.12.0.0 lmysqlclient
 
  ln -s libmysqlclient.so.12.0.0 lmysqlclient
to make a symbolic link allowing FPC to find the library.  For good measure I also created the link
+
建立一個軟連結讓 FPC 可以找的到函式庫。為了更準確一些我再建立一個連結
 
  ln -s libmysqlclient.so.12.0.0 mysqlclient
 
  ln -s libmysqlclient.so.12.0.0 mysqlclient
and placed similar links in various other directories:  not strictly necessary, but just in case ...!
+
然後放一個相同的連結在不同的其他目錄裡:不是必需,視請況而定!
Some users might need to add the following link:
+
然後有些使用者也許會需要增加如下的連結:
 
  ln -s libmysqlclient.so.12.0.0 libmysqlclient.so
 
  ln -s libmysqlclient.so.12.0.0 libmysqlclient.so
  
I modified trydb.pp to include user details, initially by adding host, user and password as constants:
+
我修改 trydb.pp 的內容,含括進使用者連線資訊,從上到下計的內容有主機,使用者與密碼:
  
 
  const
 
  const
Line 64: Line 62:
 
   passwd: Pchar = 'mypassword';
 
   passwd: Pchar = 'mypassword';
  
I also found that I couldn't connect to mysql using the mysql_connect() call, but had to use mysql_real_connect() which has many more parameters.  To complicate things further, the number of parameters seems to have changed between version3 (where there are seven) and version4 (where there are eight).  Before using mysql_real_connect I had to use mysql_init() which is not found in the original mysql.pp but is found in mysql_v4.pp.
+
我還發現我忽叫 mysql_connect() 還是無法連上 MySQL,但使用 mysql_real_connect() ,加上一堆參數後就可以了。未來還會更複雜,參數的數目似乎從 3 版(七個)換到 4 版(八個)還有變化。在使用 mysql_real_connect 之前我還得先使用 mysql_init(),但這在原始的 mysql.pp 裡並找不到,但出現於 mysql_v4.pp。
  
So the code for connection to the database is now:
+
所以連線的程式碼將會是現在這樣:
  
  { a few extra variables}
+
  {加一些額外的變數}
 
  var
 
  var
 
   alloc : PMYSQL;
 
   alloc : PMYSQL;
 
    
 
    
  {main program fragment}
+
  {主程式片段}
 
    
 
    
 
  begin
 
  begin
Line 81: Line 79:
 
     end;
 
     end;
 
    
 
    
  Writeln ('Allocating Space...');
+
  Writeln ('分派空間...');
 
   alloc := mysql_init(PMYSQL(@qmysql));
 
   alloc := mysql_init(PMYSQL(@qmysql));
   Write ('Connecting to MySQL...');
+
   Write ('連線到 MySQL...');
 
   sock :=  mysql_real_connect(alloc, host, user, passwd, database, 0, nil, 0);
 
   sock :=  mysql_real_connect(alloc, host, user, passwd, database, 0, nil, 0);
 
   if sock=Nil then
 
   if sock=Nil then
 
     begin
 
     begin
     Writeln (stderr,'Couldn''t connect to MySQL.');
+
     Writeln (stderr,'無法連線到 MySQL.');
     Writeln (stderr, 'Error was: ', mysql_error(@qmysql));
+
     Writeln (stderr, '錯誤是:', mysql_error(@qmysql));
 
     halt(1);
 
     halt(1);
 
     end;
 
     end;
   Writeln ('Done.');
+
   Writeln ('完成。');
   Writeln ('Connection data:');
+
   Writeln ('連線資料:');
 
  {$ifdef Unix}
 
  {$ifdef Unix}
 
   writeln ('Mysql_port      : ',mysql_port);
 
   writeln ('Mysql_port      : ',mysql_port);
Line 101: Line 99:
 
   writeln ('Client info    : ',mysql_get_client_info);
 
   writeln ('Client info    : ',mysql_get_client_info);
 
    
 
    
   Writeln ('Selecting Database ',DataBase,'...');
+
   Writeln ('選擇資料庫 ',DataBase,'...');
 
   if mysql_select_db(sock,DataBase) < 0 then
 
   if mysql_select_db(sock,DataBase) < 0 then
 
     begin
 
     begin
     Writeln (stderr,'Couldn''t select database ',Database);
+
     Writeln (stderr,'無法選取資料庫 ',Database);
 
     Writeln (stderr,mysql_error(sock));
 
     Writeln (stderr,mysql_error(sock));
 
     halt (1);
 
     halt (1);
 
     end;
 
     end;
  {... as original contents of testdb.pp}
+
  {... 其他就如原來 testdb.pp 裡的內容}
  
  
Now - ready to start compiling trydb.pp?
+
現在 - 準備好開始編譯 trydb.pp 了嗎?
 
   fpc trydb
 
   fpc trydb
success!  Now run it:
+
成功!現在執行:
 
   ./trydb
 
   ./trydb
whoopee!  I got the listing of the FPC developers!
+
哇!我讀取到 FPC 開發者的清單了!
  
A few extra refinements:  make the entry of user details and the mysql commands interactive, using variables rather than constants, and allow several SQL commands to be entered, until we issue the quit command: see the [[Lazarus Database Tutorial/TryDbpp|full program listing]], where user details are entered from the console, and the program goes into a loop where SQL commands are entered from the console (without the terminal semicolon) and the responses are printed out, until 'quit' is entered from the keyboard.
+
想要更精進的話:將使用者連線資訊的部份與 MySQL 的指令做成互動的模式,使用變數而不使用常數,如此可以在你退出 MySQL 前輸入多個 SQL 指令:參見[[Lazarus Database Tutorial/TryDbpp|所有程式一覽表]],當使用者資訊是從主控台模式輸入取得時,程式將要利用迴圈,讓 SQL 的指令可以重覆輸入 (可以不用分號做結尾),然後再將回應顯示出來,直到使用者鍵入 'quit' 為止。
  
See [[Lazarus Database Tutorial/SampleListing|Sample Console Listing]].
+
參見[[Lazarus Database Tutorial/SampleListing|主控台範例一覽表]]
  
===Connecting to MySQL from a Lazarus Application ===
+
=== 在 Lazarus 的應用程式裡連線到 MySQL ===
  
This tutorial shows how to connect Lazarus to the MySQL database, and execute simple queries, using only the basic Lazarus components; it uses no Data Aware components, but illustrates the principles of interfacing with the database.
+
此教學將示範 Lazarus 如何連線到 MySQL 資料庫,並執行一段簡單的查詢,僅止使用 Lazarus 基本的元件;它並沒有用到任何資料感知元件,但已可想像連線到資料庫其介面的原理。
  
Create a new project in Lazarus:
+
建立一個新的 Lazarus 專案:
  Project -> New Project -> Application
+
  〔專案〕->〔新專案〕->〔應用程式〕
A new automatically generated Form will appear.
+
將會自動產生出一份表單。
  
Enlarge the form to fill about half of the screen, then re-name the form and its caption to 'TryMySQL'.
+
先把表單拉大到約半個畫面的大小,然後將表單的標題改名為 'TryMySQL'
  
From the Standard Component tab place three Edit Boxes on the upper left side of the Form, and immediately above each box place a label.  Change the names and captions to 'Host' (and HostLLabel,HostEdit), 'UserName' (and UserLabel, UserEdit) and 'Password' (with PasswdLabel and PasswdEdit).  Alternatively you could use LabelledEdit components from the Additional tab.
+
從標準元件的籤頁裡放置三個編輯方框到表單的左上,然後緊接著其下各放一個標籤。將標籤的標題改為「主機」(還有 HostLabel,HostEdit),「使用者名稱」 (還有 UserLabel,UserEdit) 和「密碼」 (還有 PasswordLabel 和 PasswordEdit)。或是你可以使用附加籤頁裡的標籤編輯元件來取代。
  
Select the Passwd Edit box and find the PasswordChar property:  change this to * or some other character, so that when you type in a password the characters do not appear on your screen but are echoed by a series of *s.  Make sure that the Text property of each edit box is blank.
+
選取密碼編輯的方框,找到 PasswordChar 這個內容:將其改為 * 或是其他字元,這樣當你鍵入你的密碼時你的畫面就會重覆顯示你這個字元來取代你真正的密碼。然後確定 Text 內容裡的值皆是空的。
  
Now place another Edit box and label at the top of the right side of your form.  Change the label to 'Enter SQL Command' and name it CommandEdit.
+
然後現在再放置一個編輯方框和標籤到你表單的右上方。改變標籤成「輸入 SQL 命令」,並命名為 CommandEdit。
  
Place three Buttons on the form:  two on the left under the Edit boxes, and one on the right under the command box.
+
表單上再放三個按鈕:兩個在左邊,編框框的下面,另一個在右邊,命令框的下面。
  
Label the buttons on the left 'Connect to Database' (ConnectButton)and 'Exit' (ExitButton) and the one on the right 'Send Query' (QueryButton).
+
將左邊的按鈕標記為「連結到資料庫」(ConnectButton) 與「離開」(ExitButton),而右邊的那個記為「送出查尋」(QueryButton)
  
Place a large Memo Box labelled and named 'Results' (ResultMemo) on the lower right, to fill most of the available space. Find its ScrollBars property and select ssAutoBoth so that scroll bars appear automatically if text fills the space.  Make the WordWrap property True.
+
再放一個大的記事框在右下,標名為「結果」(ResultMemo),把送下能利用的空間都填滿給它。找到它捲軸條(ScrollBars)的內容,選為 ssAutoBoth,這樣捲軸條就可以為出現的文字自動調整空間。然後文字換行 (WordWrap) 的內容設為真 (True)。
  
Place a Status Bar (from the Common Controls tab) at the bottom of the Form, and make its SimpleText property 'TryMySQL'.
+
再放一個狀態列 (從共通控制項籤頁裡) 在表單的下面,然後將其單純的文字 (SimpleText) 內容裡設為 'TryMySQL'
  
A screenshot of the Form can be seen here: [http://lazarus-ccr.sourceforge.net/kbdata/trymysqldb.png Mysql Example Screenshot]
+
擷圖將顯示表單長這個樣子:[http://lazarus-ccr.sourceforge.net/kbdata/trymysqldb.png Mysql 範例擷圖]
  
Now we need to write some event handlers.
+
現在我們要寫一些事件處理器。
  
The three Edit boxes on the left are for entry of hostname, username and password.  When these have been entered satisfactorily, the Connect Button is clicked.  The OnCLick event handler for this button is based on part of the text-mode FPC program above.
+
這三個編輯框是用來輸入主機名稱,使用者名稱和密碼用的。當這三個都已輸入滿足條件後,將按下連線的按鈕。因此這按鈕 OnCLick 在事件處理器裡就是將在處理上述文字模式的 FPC 程式裡做的事情。
  
The responses from the database cannot now be written using the Pascal write or writeln statements: rather, the replies have to be converted into strings and displayed in the Memo box.  Whereas the Pascal write and writeln statements are capable of performing a lot of type conversion 'on the fly', the use of a memo box for text output necessitates the explicit conversion of data types to the correct form of string, so Pchar variables have to be converted to strings using StrPas, and integers have to be converted with IntToStr
+
而資料庫回傳的訊息,現在就不能用 Pascal write writeln 這兩個敘述句來負責了:取而代之的是所有回應都將轉成字串然後丟到記事框裡。鑑於 Pascal write writeln 敘述句能「即時」地將資料型態轉換,因為在記事框要輸出的文字,在處理時也要能不含糊地都轉換成字串才送出,所以字元變數就要用 StrPass 轉換成字串變數,而數字就要用 IntToStr 去轉換。
  
Strings are displayed in the Memo box using
+
在記事框裡顯示字串時將用
  
 
  procedure ShowString (S : string);
 
  procedure ShowString (S : string);
Line 161: Line 159:
 
  end;
 
  end;
  
The ConnectButton event handler thus becomes:
+
連線按鈕的事件處理器將會變成:
  
 
  procedure TtrymysqlForm1.ConnectButtonClick(Sender: TObject);
 
  procedure TtrymysqlForm1.ConnectButtonClick(Sender: TObject);
  (* Connect to MySQL using user data from Text entry boxes on Main Form *)
+
  (* 使用主表單裡文字框裡輸入的使用者訊息來連線到 MySQL *)
 
  var strg: string;
 
  var strg: string;
 
    
 
    
Line 180: Line 178:
 
   if sock=Nil then
 
   if sock=Nil then
 
     begin
 
     begin
       strg :='Couldn''t connect to MySQL.'; showstring (strg);
+
       strg :='無法連線到 MySQL.'; showstring (strg);
       Strg :='Error was: '+ StrPas(mysql_error(@qmysql)); showstring (strg);
+
       Strg :='錯誤是:'+ StrPas(mysql_error(@qmysql)); showstring (strg);
 
   end
 
   end
 
     else
 
     else
 
     begin
 
     begin
 
       trymysqlForm1.statusBar1.simpletext := 'Connected to MySQL';
 
       trymysqlForm1.statusBar1.simpletext := 'Connected to MySQL';
       strg := 'Now choosing database : ' + database; showstring (strg);
+
       strg := '現在選擇資料庫:' + database; showstring (strg);
 
  {$ifdef Unix}
 
  {$ifdef Unix}
 
       strg :='Mysql_port      : '+ IntToStr(mysql_port); showstring (strg);
 
       strg :='Mysql_port      : '+ IntToStr(mysql_port); showstring (strg);
Line 196: Line 194:
 
       Strg :='Client info    : ' + Strpas(mysql_get_client_info);  showstring (strg);
 
       Strg :='Client info    : ' + Strpas(mysql_get_client_info);  showstring (strg);
 
    
 
    
       trymysqlForm1.statusbar1.simpletext := 'Selecting Database ' + DataBase +'...';
+
       trymysqlForm1.statusbar1.simpletext := '選取資料庫 ' + DataBase +' ...';
 
   if mysql_select_db(sock,DataBase) < 0 then
 
   if mysql_select_db(sock,DataBase) < 0 then
 
   begin
 
   begin
     strg :='Couldn''t select database '+ Database; ShowString (strg);
+
     strg :='無法選取資料庫 '+ Database; ShowString (strg);
 
     Strg := mysql_error(sock); ShowString (strg);
 
     Strg := mysql_error(sock); ShowString (strg);
 
   end
 
   end
Line 206: Line 204:
  
  
The Text Box on the right allows entry of a SQL statement, without a terminal semicolon;  when you are satisfied with its content or syntax, the SendQuery button is pressed, and the query is processed, with results being written in the ResultsMemo box.
+
右方的文字框是用來輸入 SQL 語法的,不用在結尾加分號;當你輸入內容滿足語法條件是,按下送出查詢 (SendQuery) 按鈕,查詢即進行處理,然後結果都會寫進記事框 (ResultBox) 內。
 
 
The SendQuery event handler is again based on the FPC text-mode version, except that once again explicit type-conversion has to be done before strings are displayed in the box.
 
  
A difference from the text-mode FPC program is that if an error condition is detected, the program does not halt and MySQL is not closed;  instead, control is returned to the main form and an opportunity is given to correct the entry before the command is re-submitted.  The application finally exits (with closure of MySQL) when the Exit Button is clicked.
+
而送出查詢按鈕在事件處理器裡要做的事也跟先前在 FPC 文字模式裡寫的一樣,但除了在字串回傳後要丟上記事框內之前,其型態轉換不得含糊。
  
The code for SendQuery follows:
+
這和文字模式的 FPC 程式唯一不一樣的地方在於當一但偵測到錯誤狀態時,程式不會中斷,MySQL 也不會關閉,取而代之的是事件處理又回到主表單,等待新的查詢再重新送出。程式只有在按下「離開」按鈕後,才會關閉 MySQL 的連線與整個應用程式。
 +
送出查詢 (SendQuery) 按鈕的程式碼如下:
  
 
  procedure TtrymysqlForm1.QueryButtonClick(Sender: TObject);
 
  procedure TtrymysqlForm1.QueryButtonClick(Sender: TObject);
Line 221: Line 218:
 
       dumquery := dumquery+#0;
 
       dumquery := dumquery+#0;
 
       query := @dumquery[1];
 
       query := @dumquery[1];
       trymysqlForm1.statusbar1.simpletext := 'Executing query : '+ dumQuery +'...';
+
       trymysqlForm1.statusbar1.simpletext := '執行查詢中:'+ dumQuery +'...';
       strg := 'Executing query : ' + dumQuery; showstring (strg);
+
       strg := '執行查詢中:' + dumQuery; showstring (strg);
 
       if (mysql_query(sock,Query) < 0) then
 
       if (mysql_query(sock,Query) < 0) then
 
       begin
 
       begin
         Strg :='Query failed '+ StrPas(mysql_error(sock)); showstring (strg);
+
         Strg :='查詢失敗 '+ StrPas(mysql_error(sock)); showstring (strg);
 
       end
 
       end
 
       else
 
       else
Line 232: Line 229:
 
         if RecBuf=Nil then
 
         if RecBuf=Nil then
 
         begin
 
         begin
           Strg :='Query returned nil result.'; showstring (strg);
+
           Strg :='查詢回傳的內容為空。'; showstring (strg);
 
         end
 
         end
 
         else
 
         else
 
         begin
 
         begin
           strg :='Number of records returned  : ' + IntToStr(mysql_num_rows (recbuf));
+
           strg :='回傳資料筆數:' + IntToStr(mysql_num_rows (recbuf));
 
           Showstring (strg);
 
           Showstring (strg);
           Strg :='Number of fields per record : ' + IntToStr(mysql_num_fields(recbuf));
+
           Strg :='欄位記錄數:' + IntToStr(mysql_num_fields(recbuf));
 
           showstring (strg);
 
           showstring (strg);
 
           rowbuf := mysql_fetch_row(recbuf);
 
           rowbuf := mysql_fetch_row(recbuf);
Line 253: Line 250:
  
  
Save your Project, and press Run -> Run
+
儲存你的專案,然後按〔執行〕->〔執行〕
  
==== Download MYSQL Source Code ====
+
==== 下載 MYSQL 原始碼 ====
A full listing of the  program is available here [http://lazarus-ccr.sourceforge.net/kbdata/mysqldemo.tar.gz Sample Source Code]
+
所有程式的一覽表都可以在這裡取得:[http://lazarus-ccr.sourceforge.net/kbdata/mysqldemo.tar.gz 範例程式碼]
  
== Simple MySQL Demo Using the TMySQL50Connection Component ==
+
== 使用 TMySQL50Connection 元件的簡單 MySQL 示範 ==
  
Here is code that functions as a quick demo to get up and running simply (tested on Win XP with Lighty2Go). libmysql.dll was put in the project and lazarus.exe directories (available from a directory in Lighty2Go). There is no requirement to place any components on the form other than the three edit boxes, a memo box and a few buttons. You need to add mysql50conn and sqldb to the uses statement. The Lazarus component directory must be rw for the programmer. The mysql dbms here has a user 'root' with no password, and a database test1 with table tPerson which has three fields: personid (int), surname (varchar(40)) and dob (datetime). phpMyAdmin (in Lighty2Go) was used to create the db, table and fields and insert some sample data. Note that dates in phpMyAdmin should be entered YYYY-MM-DD, though the program created below will accept dates in the usual formats. The button btnTest must be clicked first as it creates the connection with the dbms. Note the line that applies updates - without this the changed or new data will not be written back to the db though they will be in memory and can be viewed using btnFirst and btnNext.
+
這程式碼是一個簡單快速的示範 (Win XP 下使用 Lighty2Go 執行)。libmysql.dll 放入 Lazarus 的專案,同樣置於與 lazarus.exe 相同的目錄裡 (可在 Lighty2Go 目錄下取得)。這裡在你的表單裡,除了三個編輯框,一個記事框和幾個按鈕外你什麼元件也不用再加進去。你先必須在 uses 敘述句裡加入 mysql50conn sqldb 這兩項。Lazarus 放元件的目錄對於程式設計師的權限是可讀取和寫入 (rw)。MySQL dbm 使用者預設為 'root' 並沒有密碼,在一個名為 test1 的資料庫裡與名為 tPerson 的表格裡有三個欄位:personid (int),surname (varchar(40)) dob (datetime)。使用 phpMyAdmin (Lighty2Go ) 來建立資料庫,表格與欄位,然後插入一些測試資料。注意到在 phpMyAdmin 裡的日期應打成 年年年年-月月-日日,不過我們下面建立的程式可以輸入一般格式的日期。在連線之前要先按 btnTest 按鈕,請注意每一行內容都可以套用更新-若不這麼做則這些資料不會再寫回資料庫裡,只是存放在記憶體內,用 btnFirst btnNext 兩個按鈕來切換觀看。
 
<pre>
 
<pre>
 
unit unt_db;
 
unit unt_db;
//Example based on:
+
//範例來源:
 
//http://www.lazarus.freepascal.org/index.php?name=PNphpBB2&file=viewtopic&t=5761
 
//http://www.lazarus.freepascal.org/index.php?name=PNphpBB2&file=viewtopic&t=5761
//from tpglemur on that forum
+
//來自論壇的 tpglemur
 
{$mode objfpc}{$H+}
 
{$mode objfpc}{$H+}
 
interface
 
interface
Line 315: Line 312:
 
       conn.Transaction := transaction;
 
       conn.Transaction := transaction;
 
       query.DataBase := conn;
 
       query.DataBase := conn;
       //query.ParseSQL := true; //line not needed - this is the default anyway
+
       //query.ParseSQL := true; //該行不必須,這是預設的方法
       //query.ReadOnly := false; //line not needed - this is the default anyway
+
       //query.ReadOnly := false; //該行不必須,這是預設的方法
 
       query.SQL.Text := 'select * from tperson';
 
       query.SQL.Text := 'select * from tperson';
 
       query.Open;
 
       query.Open;
Line 360: Line 357:
 
   query.FieldValues['dob'] := edtDOB.Text;
 
   query.FieldValues['dob'] := edtDOB.Text;
 
   query.Post;   
 
   query.Post;   
   query.ApplyUpdates; //to apply update
+
   query.ApplyUpdates; //套用更新
   //transaction.Commit; //line not needed
+
   //transaction.Commit; //該行不必須
 
end;
 
end;
  
Line 375: Line 372:
 
</pre>
 
</pre>
  
Here is a version using the TMySQL50Connection, TSQLQuery, TSQLTransaction, TDatasource and TDBGrid components that have been placed on the form:
+
這是使用 TMySQL50Connection,TSQLQuery,TSQLTransaction,TDatasource 與 TDBGrid 元件的另一種片版本,可將他們直接拉到表單上:
 
<pre>
 
<pre>
 
unit unt_mysql2;
 
unit unt_mysql2;
Line 414: Line 411:
 
procedure TForm1.FormCreate(Sender: TObject);
 
procedure TForm1.FormCreate(Sender: TObject);
 
begin
 
begin
   //Set properties of components:
+
   //設定元件內容:
   //(could be done in the Object Inspector)
+
   //(可以從物件檢視器做到)
 
   MySQL50Connection1.HostName := '127.0.0.1';
 
   MySQL50Connection1.HostName := '127.0.0.1';
 
   MySQL50Connection1.UserName := 'root';
 
   MySQL50Connection1.UserName := 'root';
Line 421: Line 418:
 
   MySQL50Connection1.DatabaseName := 'test1';
 
   MySQL50Connection1.DatabaseName := 'test1';
 
   MySQL50Connection1.Transaction := SQLTransaction1;
 
   MySQL50Connection1.Transaction := SQLTransaction1;
   //SQLQuery1.ParseSQL := true; //line not needed - this is the default
+
   //SQLQuery1.ParseSQL := true; //該行不必須,這是預設值
   //SQLQuery1.ReadOnly := false; //line not needed - this is the default
+
   //SQLQuery1.ReadOnly := false; //該行不必須,這是預設值
 
   SQLQuery1.SQL.Text := 'select * from tperson';
 
   SQLQuery1.SQL.Text := 'select * from tperson';
 
   SQLQuery1.Transaction := SQLTransaction1;
 
   SQLQuery1.Transaction := SQLTransaction1;
Line 438: Line 435:
 
     SQLQuery1.Open;
 
     SQLQuery1.Open;
  
     //Tests to see if all is OK:
+
     //測試一下是否都 OK:
 
     SQLQuery1.Last;
 
     SQLQuery1.Last;
 
     S := IntToStr(SQLQuery1.RecordCount) + #13#10;
 
     S := IntToStr(SQLQuery1.RecordCount) + #13#10;
Line 471: Line 468:
 
procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
 
procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
 
begin
 
begin
   //Required or get EDatabase error on close:
+
   //必要的,或是在關閉時取得 EDatabase 錯誤:
 
   MySQL50Connection1.Connected := false;
 
   MySQL50Connection1.Connected := false;
 
end;
 
end;
Line 480: Line 477:
 
</pre>
 
</pre>
  
== Lazarus and PostgreSQL ==
+
== Lazarus PostgreSQL ==
  
This is a very short tutorial to get Lazarus 0.9.12 or later to connect to a PostgreSQL database, local or remote, using TPQConnection.
+
這個教學很簡短,告訴大家如何在 Lazarus 0.9.12 或更新的版本下使用 TPQConnection 連線到 PostgreSQL 資料庫,本地端或遠端都行。
  
After correct install, follow these steps:
+
在正確地安裝後,進行下列步驟:
  
* Place a PQConnection   from the SQLdb tab
+
* 從 SQLdb 籤頁裡放入 PQConnection
* Place a SQLQuery       from the SQLdb tab
+
* 從 SQLdb 籤頁裡放入 SQLQuery
* Place a SQLTransaction from the SQLdb tab
+
* 從 SQLdb 籤頁裡放入 SQLTransaction
* Place a DataSource     from the DataAccess tab
+
* 從資料存取 (DataAccess) 籤頁裡放入資料來源 (DataSource)
* Place a DBGrid         from the DataControls tab
+
* 從資料控制 (DataControl) 籤頁裡放入 DBGrid
  
* In the PQConnection fill in:
+
* PQConnection 裡填入:
** transaction property with the respective SQLTransaction object
+
** 每個 SQLTransaction 物件的交易內容
** Database name
+
** 資料庫名稱
** HostName
+
** 主機名稱
** UserName + password
+
** 使用者名稱 + 密碼
  
* Check that the SQLTransaction was automatically changed to point to the PQConnection
+
* 檢查 SQLTransaction 已自動改為指向 PQConnection
  
* In the SQLQuery fill in:
+
* SQLQuery 內填入:
** transaction property with the respective object
+
** 每個物件的交易內容
** database property with respective object
+
** 每個物件的資料庫內容
** SQL (something like 'select * from anytable')
+
** SQL (像是 'select * from anytable' 這樣的句子)
  
* In the DataSource object fill in the DataSet property with the SQLQuery object
+
* 在資料庫來源物件內照著 SQLQuery 物件填入 DataSet 的內容
  
* In the DBGrid fill in the datasource as the DataSource Object
+
* DBGrid 內依資料庫來源物件填入資料來源
  
Turn everything to connected and active and the DBGrid should be filled in design time.
+
將所有的物件都調為「已連線;connected」和「啟動;active」,DBGrid 應該還要填入設定好的時間。
TDBText and TDBEdit seem to work but (for me) they only _show_ _data_.
+
TDBText TDBEdit 看起來已經在運作,但(對我來說)他們只負責顯示資料。
  
To change contents in the database, I called the DB Engine direct with the following code:
+
要變更資料庫內容,需要使用下列程式碼來呼叫資料庫引擎:
 
   try
 
   try
 
     sql:= 'UPDATE table SET setting=1';
 
     sql:= 'UPDATE table SET setting=1';
 
     PQDataBase.Connected:=True;
 
     PQDataBase.Connected:=True;
     PQDataBase.ExecuteDirect('Begin Work;');
+
     PQDataBase.ExecuteDirect('開始作業;');
 
     PQDataBase.ExecuteDirect(sql);
 
     PQDataBase.ExecuteDirect(sql);
     PQDataBase.ExecuteDirect('Commit Work;');
+
     PQDataBase.ExecuteDirect('交付作業');
 
     PQDataBase.Connected:=False;
 
     PQDataBase.Connected:=False;
 
   except
 
   except
 
     on E : EDatabaseError do
 
     on E : EDatabaseError do
       MemoLog.Append('DB ERROR:'+sql+chr(13)+chr(10)+E.ClassName+chr(13)+chr(10)+E.Message);
+
       MemoLog.Append('資料庫錯誤:'+sql+chr(13)+chr(10)+E.ClassName+chr(13)+chr(10)+E.Message);
 
     on E : Exception do
 
     on E : Exception do
       MemoLog.Append('ERROR:'+sql+chr(13)+chr(10)+E.ClassName+chr(13)+chr(10)+E.Message);
+
       MemoLog.Append('錯誤'+sql+chr(13)+chr(10)+E.ClassName+chr(13)+chr(10)+E.Message);
 
   end;
 
   end;
  
  
* Notes:
+
* 備註:
** Tested on windows, Lazarus 0.9.12 + PgSQL 8.3.1
+
** 在 Windows 環境下測試,Lazarus 0.9.12 + PgSQL 8.3.1
** Some tests in linux, Lazarus 0.9.12 and PgSQL 8.0.x
+
** Linux 環境下部份測試,Lazarus 0.9.12 and PgSQL 8.0.x
  
  
* Instalation and errors:
+
* 安裝與錯誤:
** In the tested version of Lazarus .12, fields of type "text" and "numeric" have bugs
+
** 在測試版本 Lazarus .12下,欄位型態為「文字」與「數字」時會有程式錯誤
** I used with no problems char fixed size, int and float8
+
** 在使用固定長度字元,整數與 8 位元浮點數時沒有問題
** Sometimes restarting Lazarus solves stupid errors...
+
** 有時候有些愚蠢的錯誤需要你重啟 Lazarus ...
** After some errors, the transactions remain active and should be deactivated mannually
+
** 某些錯誤發生後,其交易仍在進行,而必須手動去關閉。
** Changes made in Lazarus are of course not visible until transaction commited
+
** Lazarus 一些更新會讓交易在交付之前都無法看見
** The integrated debugger seems buggy (at least in windows) - sometimes running outside of the IDE may help to find errors
+
** 內建的除錯器看起來也有些錯誤 (至少在 Windows 下時) - 有時候執行到 IDE 之外的會需要協助找尋錯誤
** In linux certain error messages are printed in the console -- run your program in the command line, sometimes there is some extra useful debugging info
+
** 在 Linux 下某些錯誤訊息只會出現在主控台下 -- 用主控台模式下指令來執行程式,有時候你會因此而得到一些額外的除錯訊息。
** Error: "Can not load Postgresql client. Is it installed (libpq.so) ?"
+
** 錯誤:「無法載入 PostgreSQL 用戶端,libq.so 是否已安裝?」
*** Add the path to seach libpq* from the PostgreSQL installation.
+
*** 在 PostgreSQL 安裝時加入可以搜尋到 libpq* 的路徑。
*** In linux add the path to the libpq.so file to the libraries section in your /etc/fpc.cfg file. For example : -Fl/usr/local/pgsql/lib
+
*** 在 Linux 下,加入 libpq.so 檔案的路徑到你的函式庫 /etc/fpc.cfg 檔案裡,例如:-Fl/usr/local/pgsql/lib
***  In windows, add these libs anywhere in the Path environment variable or project dir
+
***  在 Windows 下,將這些函式庫路徑加入到環境變數 Path 裡,或是專案的目錄裡
***  I windows, I copied all the DLLs in my C:\Program Files\PostgreSQL\8.1\bin dir to another dir in the PATH  
+
***  在 Windows 下,我將 C:\Program Files\PostgreSQL\8.1\bin 目錄下的 DLL 檔案都複製到另一個 PATH 變數裡的目錄。
*** Or add this postgres\bin dir to the path
+
*** 或是加入 postgres\bin 目錄到路徑裡
  
 
== Lazarus 與 SQLite ==
 
== Lazarus 與 SQLite ==
  
by Luiz Américo
+
by Luiz Americo
  
 
請至 [http://www.geocities.com/camara_luiz/ sqlite4fpc homepage] 取得 API reference 與更多的教材。
 
請至 [http://www.geocities.com/camara_luiz/ sqlite4fpc homepage] 取得 API reference 與更多的教材。
Line 561: Line 558:
  
 
* 靈活: 程式設計師可以選擇使用或不使用SQL 語言,允許他們使用SQL/sqlite容許的簡單的表格配置或是任何複雜的配置
 
* 靈活: 程式設計師可以選擇使用或不使用SQL 語言,允許他們使用SQL/sqlite容許的簡單的表格配置或是任何複雜的配置
* 自動更新資料庫: 不需要手動更新資料庫使用SQL 述敍, 單一方法來照顧它
+
* 自動更新資料庫: 不需要手動更新資料庫使用SQL 述?, 單一方法來照顧它
 
* 快速: 它的快取資料是放在記憶體, 使的瀏覽資料集是相當的快速
 
* 快速: 它的快取資料是放在記憶體, 使的瀏覽資料集是相當的快速
 
* 沒有伺服器的安裝/設定: 只是包裝在sqlite 動態函式庫
 
* 沒有伺服器的安裝/設定: 只是包裝在sqlite 動態函式庫
Line 571: Line 568:
 
===需求===
 
===需求===
  
* For sqlite2 databases:
+
* sqlite2 資料庫:
 
** fpc 2.0.0
 
** fpc 2.0.0
 
** Lazarus 0.9.10
 
** Lazarus 0.9.10
** sqlite runtime library 2.8.15 or above (get from www.sqlite.org)
+
** sqlite 執行期函式庫 2.8.15 或以上 (www.sqlite.org 獲得)
  
* For sqlite3 databases:
+
* sqlite3 資料庫:
 
** fpc 2.0.2
 
** fpc 2.0.2
** Lazarus 0.9.11 (svn revision 8443 or above)
+
** Lazarus 0.9.11 (svn 修訂版 8443 或以上)
** sqlite runtime library 3.2.1 or above (get from www.sqlite.org)
+
** sqlite 執行期函式庫 3.2.1 或以上 (www.sqlite.org 獲得)
  
'''開始一個 lazarus 專案前, 請先確認以下事項:'''
+
'''開始一個 lazarus 專案前, 請先確認以下事項:'''
* the sqlite library is on the system PATH or in the executable directory
+
* SQLite 資料庫有在系統路徑 PATH 變數裡,或其它可執行的目錄
* under Linux, put cmem as the first unit in uses clause of the main program
+
* Linux 下,將 cmem 第一個放入主程式的 uses 語法內
** In Debian, Ubuntu and other Debian-like distros, in order to build Lazarus IDE you must install the packages libsqlite-dev/libsqlite3-dev, not only sqlite/sqlite3 (Also applies to OpenSuSe)
+
** 在 Debian,Ubuntu 和其他 Debian-like 發佈版本裡,為了要建置 Lazarus IDE 你得先安裝 libsqlite-dev/libsqlite3-dev 套件,而不僅只有 sqlite/sqlite3 (OpenSuSe 也一樣)
  
 
===使用方法 (基本)===
 
===使用方法 (基本)===
  
安裝  /components/sqlite 資料夾中的 package (安裝教材 [[Install_Packages|here]])
+
安裝  /components/sqlite 資料夾中的套件 (安裝教材[[Install_Packages|在這裡]])
  
 
於設計模式下設定以下屬性
 
於設計模式下設定以下屬性
Line 596: Line 593:
 
* Sql: 一個 SQL 的 select 語法 [非必要]  
 
* Sql: 一個 SQL 的 select 語法 [非必要]  
  
'''建立一個 Table (Dataset)'''
+
'''建立一個資料表 (Dataset)'''
  
雙按元件圖示, 或點選右鍵選單中的 'Create Table'。便會出現一個簡單的表格編輯器。
+
雙按元件圖示, 或點選右鍵選單中的「建立資料表」。便會出現一個簡單的表格編輯器。
  
 
   以下是 TSqliteDataset 與 TSqlite3Dataset 所支援的所有欄位格式:
 
   以下是 TSqliteDataset 與 TSqlite3Dataset 所支援的所有欄位格式:
Line 617: Line 614:
 
'''取得資料'''
 
'''取得資料'''
  
建立 Table 後, 或者在已有的 Table。以 Open 方法開啟 dataset。
+
建立資料表後, 或者在已有的資料表。以 Open 方法開啟 dataset。
 
如果 SQL 屬性沒有設定內容, 則所有的資料與欄位都會被傳回, 效果與以下指令相同:
 
如果 SQL 屬性沒有設定內容, 則所有的資料與欄位都會被傳回, 效果與以下指令相同:
  
 
   SQL:='Select * from TABLENAME';  
 
   SQL:='Select * from TABLENAME';  
  
'''修改資料 Applying changes to the underlying datafile'''
+
'''為資料檔案套用更新'''
  
使用 ApplyUpdates 函式前, dataset 須包含至少一個主鍵欄位填入需求(必須為唯一且非 Null )
+
使用 ApplyUpdates 函式前,dataset 須包含至少一個主鍵欄位填入需求 (必須為唯一且非 NULL 值)
 
 
To use the ApplyUpdates function, the dataset must contain at least one field that fills the requirements for a Primary Key (values must be UNIQUE and not NULL)
 
  
 
這可以由兩種方式來達成:
 
這可以由兩種方式來達成:
 
It's possible to do that in two ways:
 
  
 
* 設定 PrimaryKey 屬性為主鍵欄位的名稱
 
* 設定 PrimaryKey 屬性為主鍵欄位的名稱
* Set PrimaryKey property to the name of a Primary Key field
 
 
* 新增一個 AutoInc 欄位(這比較簡單, 因為 TSqliteDataSet 會自行將此欄位當成主鍵)
 
* 新增一個 AutoInc 欄位(這比較簡單, 因為 TSqliteDataSet 會自行將此欄位當成主鍵)
* Add an AutoInc field (This is easier since the TSqliteDataSet automatically handles it as a Primary Key)
 
  
 
以上兩個條件設定好其中一個後, 只要呼叫以下指令
 
以上兩個條件設定好其中一個後, 只要呼叫以下指令
 
If one of the two conditions is set then just call
 
 
    
 
    
 
   ApplyUpdates;
 
   ApplyUpdates;
  
備註1: 若兩個條件都有設定, 則欄位會使用 PrimaryKey 來更新資料
+
備註一:若兩個條件都有設定, 則欄位會使用 PrimaryKey 來更新資料
PS1: If both conditions are set, the field corresponding to PrimaryKey is used to apply the updates.
 
  
備註2: 若 PrimaryKey 被指定的欄位不是主鍵, 則呼叫 ApplyUpdates 可能會造成資料的遺失, 所以請確定所選擇的欄位值是唯一且非 Null 值。
+
備註二: 若 PrimaryKey 被指定的欄位不是主鍵, 則呼叫 ApplyUpdates 可能會造成資料的遺失, 所以請確定所選擇的欄位值是唯一且非 NULL 值。
PS2: Setting PrimaryKey to a field that is not a Primary Key will lead to loss of data if ApplyUpdates is called, so ensure that the chosen field contains not Null and Unique values before using it.
 
  
===Remarks===
+
===註記===
  
* Although it has been tested with 10000 records and worked fine, TSqliteDataset keeps all the data in memory, so remember to retrieve only the necessary data (principally with Memo Fields).
+
* 雖然此程式已經測試過 10000 筆的資料都運行的很好,TSqliteDataset 將所有的資料都留在記憶體裡,所以請記得只取出你所需要的資料就好 (特別是針對記事欄位)
* The same datafile (Filename property) can host several tables/datasets
+
* 同一個資料檔案 (檔案的內容) 可以負責多個資料表/資料集合
* Several datasets (different combinations of fields) can be created using the same table simultaneously
+
* 數個資料集合 (不同的欄位合併後) 可以建立一個暫存的資料表
* It's possible to filter the data using WHERE statements in the sql, closing and reopening the dataset (or calling RefetchData method). But in this case, the order and number of fields must remain the same
+
* SQL 允許你用 WHERE 的語法來篩選你要取出的資料,重覆開啟再關閉同一資料集合 (或是呼叫重新擷取資料 - RefetchData - 的方法)。但條件就是欄位的數量要一定
* It's also possible to use complex SQL statements using aliases, joins, views in multiple tables (remember that they must reside in the same datafile), but in this case ApplyUpdates won't work. If someone wants to use complex queries and to apply the updates to the datafile, mail me and I will give some hints how to do that
+
* 像使用一些複合的 SQL 敘述句,如 aliases,joins 同時檢視多個資料表 (請記得這麼做就是都分配在同一支資料檔案裡),但在 ApplyUpdates 的情況裡卻行不通。如果有人想要使用這種複合的查詢而又要套用更新到資料檔案,請寄信給我,我會告訴你一些絕竅
* Setting filename to a sqlite2.x datafile not created by TSqliteDataset and opening it is allowed but some fields won't have the correct field type detected. These will be treated as string fields.
+
* 當為不是從 TSqliteDataset 建立的 sqlite2.x 資料檔妹設定檔案名稱時,雖然檔案可以開啟,但有些欄位可能無法正確地偵測到,會被當成是字串欄位。
  
Generic examples can be found at fpc/fcl/db/sqlite CVS directory
+
可以在 fpc/fcl/db/sqlite CVS 目錄下找到一個通用的範例
  
Luiz Américo
+
Luiz Americo
 
pascalive(at)bol(dot)com(dot)br
 
pascalive(at)bol(dot)com(dot)br
  
== Lazarus and MSSQL ==
+
== Lazarus MSSQL ==
  
It is working with Zeoslib (latest cvs), see the links on bottom of page.
+
需要啟用 Zeoslib (最新版修訂版),請見此頁下方的連結。
  
 
== Lazarus and Interbase / Firebird ==
 
== Lazarus and Interbase / Firebird ==
  
請看 [[Install Packages|Install Packages]]. 在這一頁是一個小的範例解釋如何連線到一個 IB 或 FB 主機.
+
請參看[[Install Packages|安裝套件]]。該頁為解釋如何連線到 IB 或 FB 主機的簡短範例。
  
也可以工作使用最後Zeoslib的版本 (取自 cvs).
+
也可以使用最新的 Zeoslib 的版本 (取自 cvs)
  
===FBLib Firebird Library===
+
===FBLib Firebird 函式庫===
  
[http://fblib.altervista.org/|FBLib]是一個開放原始碼函式庫沒有資料 is an open Source Library No Data Aware for direct access to Firebird Relational Database from Borland Delphi / Kylix, Freepascal and Lazarus.
+
[http://fblib.altervista.org/|FBLib]是一個不含資料感知元件的開放原始碼函式庫,使用 Borland Delphi,Freepascal 與 Lazarus 都可以直接對 Firebird 關聯資料庫做存取。
  
Current Features include:
+
現有的特點包括:
  
* Direct Access to Firebird 1.0.x 1.5.x Classic or SuperServer
+
* 直接對 Firebird 1.0.x 1.5.x Classic SuperServer 版本做存取
* Multiplatform [Win32,Gnu/Linux,FreeBSD)
+
* 跨平台工作 (Win32,Gnu/Linux,FreeBSD)
* Automatic select client library 'fbclient' or 'gds32'
+
* 自動選取  'fbclient' 'gds32' 用戶端函式庫
* Query with params
+
* 支援參數查詢
* Support SQL Dialect 1/3
+
* 支援 SQL 同源語 1/3
* LGPL License agreement
+
* LGPL 認證通過
* Extract Metadata
+
* 解析前置檔頭資料
* Simple Script Parser
+
* 單一程式腳本解析
* Only 100-150 KB added into final EXE
+
* EXE 末端僅只使用 100-150 KB
* Support BLOB Fields
+
* 支援 BLOB 欄位
* Export Data to HTML SQL Script
+
* SQL 腳本直接支援資料與 HTML 轉換
* Service manager (backup,restore,gfix...)
+
* 伺服器管理 (備份,還原,gfix...)
* Events Alerter
+
* 事件警告器
  
You can download documentation on [http://fblib.altervista.org/ FBLib's website].
+
你可以從 [http://fblib.altervista.org/ FBLib's website] 下載文件。
  
 
== Lazarus and dBase ==
 
== Lazarus and dBase ==
Line 802: Line 789:
 
* The Delimiter property holds the separator for the fields. It will not be possible to use this char in strings in the database. Similarly it will not be possible to have lineendings in the database because they mark the change between records. It's possible to overcome this by substituting the needed comma or line ending with another not often used char, like # for example. So that when showing the data on screen all # chars could be converted to line endings and the inverse when storing data back to the database. The ReplaceString routine is useful here.
 
* The Delimiter property holds the separator for the fields. It will not be possible to use this char in strings in the database. Similarly it will not be possible to have lineendings in the database because they mark the change between records. It's possible to overcome this by substituting the needed comma or line ending with another not often used char, like # for example. So that when showing the data on screen all # chars could be converted to line endings and the inverse when storing data back to the database. The ReplaceString routine is useful here.
  
<delphi>
+
<syntaxhighlight lang=pascal>
 
constructor TComponentsDatabase.Create;
 
constructor TComponentsDatabase.Create;
 
begin
 
begin
Line 833: Line 820:
 
   FDataset.First;
 
   FDataset.First;
 
end;
 
end;
</delphi>
+
</syntaxhighlight>
  
 
When using TSdfDataset directly be aware that RecNo, although it is implemented, does not work as a way to move through the dataset whether reading or writing records. The standard navigation routines like First, Next, Prior and Last work as expected, so you need to use them rather than RecNo.
 
When using TSdfDataset directly be aware that RecNo, although it is implemented, does not work as a way to move through the dataset whether reading or writing records. The standard navigation routines like First, Next, Prior and Last work as expected, so you need to use them rather than RecNo.
 
If you are used to using absolute record numbers to navigate around a database you can implement your own version of RecNo. Declare a global longint variable called CurrentRecNo which will hold the current RecNo value. Remember that this variable will have the same convention as RecNo, so the first record has number 1 (it is not zero-based). After activating the database initialize the database to the first record with TSdfDataset.First and set CurrentRecNo := 1
 
If you are used to using absolute record numbers to navigate around a database you can implement your own version of RecNo. Declare a global longint variable called CurrentRecNo which will hold the current RecNo value. Remember that this variable will have the same convention as RecNo, so the first record has number 1 (it is not zero-based). After activating the database initialize the database to the first record with TSdfDataset.First and set CurrentRecNo := 1
  
<delphi>
+
<syntaxhighlight lang=pascal>
 
{@@
 
{@@
 
   Moves to the desired record using TDataset.Next and TDataset.Prior
 
   Moves to the desired record using TDataset.Next and TDataset.Prior
Line 868: Line 855:
 
   end;
 
   end;
 
end;
 
end;
</delphi>
+
</syntaxhighlight>
 
 
=== Using with data-aware controls ===
 
  
== Related Links ==
+
=== 使用資料感知控制元件 ===
*[[Databases|Creating Databases Applications with Lazarus using the DB-unit]]<br>
 
*[[MySQLDatabases|Creating a database application using MySQL]]<br>
 
*[[Zeos_tutorial|Creating a database application using the Zeos components]]
 
*[http://pdo.sourceforge.net Pascal Data Objects] is a database API that worked for both FPC and Delphi and utilises native MySQL libraries for version 4.1 and 5.0 and Firebird SQL 1.5, and 2.0.  It's inspired by PHP's PDO class.
 
  
==Contributors and Changes==
+
== 相關連結 ==
This page has been converted from the epikwiki [http://lazarus-ccr.sourceforge.net/index.php?wiki=LazarusDatabase version].
+
*[[Databases|在 Lazarus 下利用 DB-unit 建立資料庫應用程式]]<br>
 +
*[[MySQLDatabases|使用 MySQL 建立資料庫應用程式]]<br>
 +
*[[Zeos_tutorial|使用 Zeos 元件建立資料庫應用程式]]
 +
*[http://pdo.sourceforge.net Pascal 資料物件集] 是為一資料庫 API,可在 FPC 下與 Delphi 下配合 MySQL 原始函式庫版 4.1 與 5.0,Firebird SQL 1.5 與 2.0 使用。這是來自 PHP PDO 類別的啟發。
  
----
+
== 貢獻與變更 ==
[[Category:zh]]
+
本頁是從 epikwiki [http://lazarus-ccr.sourceforge.net/index.php?wiki=LazarusDatabase version] 轉換過來的。

Latest revision as of 23:48, 18 February 2020

Deutsch (de) English (en) español (es) français (fr) Bahasa Indonesia (id) italiano (it) 日本語 (ja) Nederlands (nl) português (pt) русский (ru) 中文(中国大陆)‎ (zh_CN) 中文(台灣)‎ (zh_TW)

Databases portal

References:

Tutorials/practical articles:

Databases

Advantage - MySQL - MSSQL - Postgres - Interbase - Firebird - Oracle - ODBC - Paradox - SQLite - dBASE - MS Access - Zeos

Lazarus 資料庫教學

導覽

本教材內容是關於如何於 Lazarus 下配合數種資料庫來工作。

Lazarus 支援數種非內建資料庫,而開發者仍應安裝各資料庫所對應的元件(Packages)。你可以透過程式碼或直接拉元件(components)至表單(from)來存取資料庫。資料感知 (data-aware) 元件代表資料庫欄位,並透過 TDataSource 設定指定的資料來源 (DataSource) 屬性物件來連接。資料來源代表一個資料表,是以連接至資料庫元件 (如:TPSQLDatabase, TSQLiteDataSet) 的 DataSet 屬性所選定的。資料感知元件位於「資料控制」籤頁內。資料來源及資料庫控制元件則位於「資料存取」的籤頁內。

Lazarus 與 MySQL

讓 MySQL 在 Linux 或 Windows 中動起來

照著 MySQL 使用手冊裡的指示,讓 mysqld 程序於背景執行。對於所有潛在的主機來源 (包含本機 'localhost',本機的名稱和其他透過網路連線的主機),所有的用戶 (包含根使用者,mysql,你自己和其他所有會用到的人) 都要設定使用權限。也要儘可能的達到安全性要求,所有使用者,包含根使用者都要有一個密碼。接著使用手冊上的範例方法來測試確定所有使用者在系統上的權限都安全可靠。

在文字模式中讓 MySQL 為 FPC 工作

在 $(fpcsrcdir)/packages/base/mysql/ 資料夾中有一個範例程式。你可以在 Lazarus 的 fpc 原始碼資料夾中找到:〔環境〕選單 -> 〔環境〕選項 -> 〔路徑〕籤頁 ->〔FPC 原始碼目錄〕。 mysql 的範例資料夾可能的路徑為 /usr/share/fpcsrc/packages/base/mysql/ (rpm 安裝) 或者 C:\lazarus\fpcsrc\packages\base\mysql\ (Windows)。 這個資料夾也包含了 mysql.pp, mysql_com.pp 和 mysql_version.pp。 執行這些指令碼前,你必須先建立一個 testdb 資料庫:請登入 mysql 監視器 (以根使用者登入來取得完整權限) 並執行以下 SQL 指令

CREATE DATABASE testdb;

接著確認所有相關使用者擁有適當的存取權限

GRANT ALL ON testdb TO johnny-user IDENTIFIED BY 'johnnyspassword'; 

現在你應試著去執行一個叫 mkdb 的指令碼:

sh ./mkdb

這個指令應該會失敗, 因為系統不會允許匿名者存取資料庫。所以必須修改這個指令碼讓 mysql 讀取以下這行:

mysql -u root -p  ${1-testdb} << EOF >/dev/null

接著試著再執行一次,系統提示要你輸入密碼。幸運的話你應該就可以管理剛建立的資料庫了。用以下的 mysql 指令測試一下(在 mysql 監視器中):

select * from FPdev;

即可看到一個列有 ID,使用者名稱和 Email 地址的FPC 開發者資料表了。

現在可以執行測試程式 testdb.pp 了 (這檔也許還需要先編譯過,而且可能會在第一次編譯出錯!!)。

我找出這支程式可能無法連結 MySQL 的幾個原因:

  • 我的系統 (SuSE Linux v9.0) 安裝的是 mysql v4.0.15,不同於安裝套件的版本 3。
  • 程式需要輸入使用者和密碼來存取資料庫。
  • 編譯器需要知道哪裡可以找到 MySQL 的函式庫 (如果你並沒有安裝 MYSQL 的開發函式庫,那現在快裝!)

我將 testdb.pp 另存成 trydb.pp,然後做修改 - 這樣代表原來那支還是可以隨後透過 CVS 更新。 我另外也將在 mysql/ver40/ 子目錄下找到的那支另存成 mysql_v4.pp,mysql_com_v4.pp 和 mysql_version_v4.pp,記得你單元裡的程式也要改成適當的檔名。在 trydb.pp 裡我變更 uses 敘述句為

uses mysql_v4

mysql_v4.pp 的敘述句改為

uses mysql_com_v4

另外為要使用的函式庫加入一行到 /etc/fpc.cfg 裡:

-Fl/lib;/usr/lib

接下來的動作也許不必要做,因為你在安裝開發函式庫的時候應該會自動幫你建立連結,但檢查一下總是無仿。 進行前需要先找到我的 MYSQL 用戶端函式庫是放在 /usr/lib 目錄下面的哪裡,我的狀況是先下 shell 指令:

ln -s libmysqlclient.so.12.0.0 lmysqlclient

建立一個軟連結讓 FPC 可以找的到函式庫。為了更準確一些我再建立一個連結

ln -s libmysqlclient.so.12.0.0 mysqlclient

然後放一個相同的連結在不同的其他目錄裡:不是必需,視請況而定! 然後有些使用者也許會需要增加如下的連結:

ln -s libmysqlclient.so.12.0.0 libmysqlclient.so

我修改 trydb.pp 的內容,含括進使用者連線資訊,從上到下計的內容有主機,使用者與密碼:

const
  host : Pchar= 'localhost';
  user : Pchar= 'myusername';
  passwd: Pchar = 'mypassword';

我還發現我忽叫 mysql_connect() 還是無法連上 MySQL,但使用 mysql_real_connect() ,加上一堆參數後就可以了。未來還會更複雜,參數的數目似乎從 3 版(七個)換到 4 版(八個)還有變化。在使用 mysql_real_connect 之前我還得先使用 mysql_init(),但這在原始的 mysql.pp 裡並找不到,但出現於 mysql_v4.pp。

所以連線的程式碼將會是現在這樣:

{加一些額外的變數}
var
  alloc : PMYSQL;
 
{主程式片段}
 
begin
 if paramcount=1 then
   begin
   Dummy:=Paramstr(1)+#0;
   DataBase:=@Dummy[1];
   end;
 
Writeln ('分派空間...');
 alloc := mysql_init(PMYSQL(@qmysql));
 Write ('連線到 MySQL...');
 sock :=  mysql_real_connect(alloc, host, user, passwd, database, 0, nil, 0);
 if sock=Nil then
   begin
   Writeln (stderr,'無法連線到 MySQL.');
   Writeln (stderr, '錯誤是:', mysql_error(@qmysql));
   halt(1);
   end;
 Writeln ('完成。');
 Writeln ('連線資料:');
{$ifdef Unix}
 writeln ('Mysql_port      : ',mysql_port);
 writeln ('Mysql_unix_port : ',mysql_unix_port);
{$endif}
 writeln ('Host info       : ',mysql_get_host_info(sock));
 writeln ('Server info     : ',mysql_stat(sock));
 writeln ('Client info     : ',mysql_get_client_info);
 
 Writeln ('選擇資料庫 ',DataBase,'...');
 if mysql_select_db(sock,DataBase) < 0 then
   begin
   Writeln (stderr,'無法選取資料庫 ',Database);
   Writeln (stderr,mysql_error(sock));
   halt (1);
   end;
{... 其他就如原來 testdb.pp 裡的內容}


現在 - 準備好開始編譯 trydb.pp 了嗎?

 fpc trydb

成功!現在執行:

 ./trydb

哇!我讀取到 FPC 開發者的清單了!

想要更精進的話:將使用者連線資訊的部份與 MySQL 的指令做成互動的模式,使用變數而不使用常數,如此可以在你退出 MySQL 前輸入多個 SQL 指令:參見所有程式一覽表,當使用者資訊是從主控台模式輸入取得時,程式將要利用迴圈,讓 SQL 的指令可以重覆輸入 (可以不用分號做結尾),然後再將回應顯示出來,直到使用者鍵入 'quit' 為止。

參見主控台範例一覽表

在 Lazarus 的應用程式裡連線到 MySQL

此教學將示範 Lazarus 如何連線到 MySQL 資料庫,並執行一段簡單的查詢,僅止使用 Lazarus 基本的元件;它並沒有用到任何資料感知元件,但已可想像連線到資料庫其介面的原理。

建立一個新的 Lazarus 專案:

〔專案〕->〔新專案〕->〔應用程式〕

將會自動產生出一份表單。

先把表單拉大到約半個畫面的大小,然後將表單的標題改名為 'TryMySQL'。

從標準元件的籤頁裡放置三個編輯方框到表單的左上,然後緊接著其下各放一個標籤。將標籤的標題改為「主機」(還有 HostLabel,HostEdit),「使用者名稱」 (還有 UserLabel,UserEdit) 和「密碼」 (還有 PasswordLabel 和 PasswordEdit)。或是你可以使用附加籤頁裡的標籤編輯元件來取代。

選取密碼編輯的方框,找到 PasswordChar 這個內容:將其改為 * 或是其他字元,這樣當你鍵入你的密碼時你的畫面就會重覆顯示你這個字元來取代你真正的密碼。然後確定 Text 內容裡的值皆是空的。

然後現在再放置一個編輯方框和標籤到你表單的右上方。改變標籤成「輸入 SQL 命令」,並命名為 CommandEdit。

表單上再放三個按鈕:兩個在左邊,編框框的下面,另一個在右邊,命令框的下面。

將左邊的按鈕標記為「連結到資料庫」(ConnectButton) 與「離開」(ExitButton),而右邊的那個記為「送出查尋」(QueryButton)。

再放一個大的記事框在右下,標名為「結果」(ResultMemo),把送下能利用的空間都填滿給它。找到它捲軸條(ScrollBars)的內容,選為 ssAutoBoth,這樣捲軸條就可以為出現的文字自動調整空間。然後文字換行 (WordWrap) 的內容設為真 (True)。

再放一個狀態列 (從共通控制項籤頁裡) 在表單的下面,然後將其單純的文字 (SimpleText) 內容裡設為 'TryMySQL'。

擷圖將顯示表單長這個樣子:Mysql 範例擷圖

現在我們要寫一些事件處理器。

這三個編輯框是用來輸入主機名稱,使用者名稱和密碼用的。當這三個都已輸入滿足條件後,將按下連線的按鈕。因此這按鈕 OnCLick 在事件處理器裡就是將在處理上述文字模式的 FPC 程式裡做的事情。

而資料庫回傳的訊息,現在就不能用 Pascal 的 write 和 writeln 這兩個敘述句來負責了:取而代之的是所有回應都將轉成字串然後丟到記事框裡。鑑於 Pascal 的 write 和 writeln 敘述句能「即時」地將資料型態轉換,因為在記事框要輸出的文字,在處理時也要能不含糊地都轉換成字串才送出,所以字元變數就要用 StrPass 轉換成字串變數,而數字就要用 IntToStr 去轉換。

在記事框裡顯示字串時將用

procedure ShowString (S : string);
(* display a string in a Memo box *)
begin
       trymysqlForm1.ResultsMemo.Lines.Add (S)
end;

連線按鈕的事件處理器將會變成:

procedure TtrymysqlForm1.ConnectButtonClick(Sender: TObject);
(* 使用主表單裡文字框裡輸入的使用者訊息來連線到 MySQL *)
var strg: string;
 
begin
 
 dummy1 :=  trymysqlForm1.HostEdit.text+#0;
 host := @dummy1[1];
 dummy2 := trymysqlForm1.UserEdit.text+#0;
 user := @dummy2[1] ;
 dummy3 := trymysqlForm1.PasswdEdit.text+#0;
 passwd := @dummy3[1] ;
 alloc := mysql_init(PMYSQL(@qmysql));
 sock :=  mysql_real_connect(alloc, host, user, passwd, database, 0, nil, 0);
 if sock=Nil then
   begin
     strg :='無法連線到 MySQL.'; showstring (strg);
     Strg :='錯誤是:'+ StrPas(mysql_error(@qmysql)); showstring (strg);
  end
   else
   begin
     trymysqlForm1.statusBar1.simpletext := 'Connected to MySQL';
     strg := '現在選擇資料庫:' + database; showstring (strg);
{$ifdef Unix}
     strg :='Mysql_port      : '+ IntToStr(mysql_port); showstring (strg);
     strg :='Mysql_unix_port : ' + StrPas(mysql_unix_port); showstring (strg);
{$endif}
     Strg :='Host info       : ' + StrPas(mysql_get_host_info(sock));
     showstring (strg);
     Strg :='Server info     : ' + StrPas(mysql_stat(sock)); showstring (strg);
     Strg :='Client info     : ' + Strpas(mysql_get_client_info);  showstring (strg);
 
     trymysqlForm1.statusbar1.simpletext := '選取資料庫 ' + DataBase +' 中...';
 if mysql_select_db(sock,DataBase) < 0 then
 begin
   strg :='無法選取資料庫 '+ Database; ShowString (strg);
   Strg := mysql_error(sock); ShowString (strg);
 end
 end;
end;


右方的文字框是用來輸入 SQL 語法的,不用在結尾加分號;當你輸入內容滿足語法條件是,按下送出查詢 (SendQuery) 按鈕,查詢即進行處理,然後結果都會寫進記事框 (ResultBox) 內。

而送出查詢按鈕在事件處理器裡要做的事也跟先前在 FPC 文字模式裡寫的一樣,但除了在字串回傳後要丟上記事框內之前,其型態轉換不得含糊。

這和文字模式的 FPC 程式唯一不一樣的地方在於當一但偵測到錯誤狀態時,程式不會中斷,MySQL 也不會關閉,取而代之的是事件處理又回到主表單,等待新的查詢再重新送出。程式只有在按下「離開」按鈕後,才會關閉 MySQL 的連線與整個應用程式。 送出查詢 (SendQuery) 按鈕的程式碼如下:

procedure TtrymysqlForm1.QueryButtonClick(Sender: TObject);
var
 dumquery, strg: string;
begin
     dumquery := TrymysqlForm1.CommandEdit.text;
     dumquery := dumquery+#0;
     query := @dumquery[1];
     trymysqlForm1.statusbar1.simpletext := '執行查詢中:'+ dumQuery +'...';
     strg := '執行查詢中:' + dumQuery; showstring (strg);
     if (mysql_query(sock,Query) < 0) then
     begin
       Strg :='查詢失敗 '+ StrPas(mysql_error(sock)); showstring (strg);
     end
     else
     begin
       recbuf := mysql_store_result(sock);
       if RecBuf=Nil then
       begin
         Strg :='查詢回傳的內容為空。'; showstring (strg);
       end
       else
       begin
         strg :='回傳資料筆數:' + IntToStr(mysql_num_rows (recbuf));
         Showstring (strg);
         Strg :='欄位記錄數:' + IntToStr(mysql_num_fields(recbuf));
         showstring (strg);
         rowbuf := mysql_fetch_row(recbuf);
         while (rowbuf <>nil) do
         begin
              Strg :='(Id: '+ rowbuf[0]+', Name: ' + rowbuf[1]+ ', Email : ' +
               rowbuf[2] +')';
              showstring (strg);
              rowbuf := mysql_fetch_row(recbuf);
         end;
       end;
     end;
end;


儲存你的專案,然後按〔執行〕->〔執行〕

下載 MYSQL 原始碼

所有程式的一覽表都可以在這裡取得:範例程式碼

使用 TMySQL50Connection 元件的簡單 MySQL 示範

這程式碼是一個簡單快速的示範 (在 Win XP 下使用 Lighty2Go 執行)。libmysql.dll 放入 Lazarus 的專案,同樣置於與 lazarus.exe 相同的目錄裡 (可在 Lighty2Go 目錄下取得)。這裡在你的表單裡,除了三個編輯框,一個記事框和幾個按鈕外你什麼元件也不用再加進去。你先必須在 uses 敘述句裡加入 mysql50conn 和 sqldb 這兩項。Lazarus 放元件的目錄對於程式設計師的權限是可讀取和寫入 (rw)。MySQL dbm 使用者預設為 'root' 並沒有密碼,在一個名為 test1 的資料庫裡與名為 tPerson 的表格裡有三個欄位:personid (int),surname (varchar(40)) 與 dob (datetime)。使用 phpMyAdmin (Lighty2Go 裡) 來建立資料庫,表格與欄位,然後插入一些測試資料。注意到在 phpMyAdmin 裡的日期應打成 年年年年-月月-日日,不過我們下面建立的程式可以輸入一般格式的日期。在連線之前要先按 btnTest 按鈕,請注意每一行內容都可以套用更新-若不這麼做則這些資料不會再寫回資料庫裡,只是存放在記憶體內,用 btnFirst 和 btnNext 兩個按鈕來切換觀看。

unit unt_db;
//範例來源:
//http://www.lazarus.freepascal.org/index.php?name=PNphpBB2&file=viewtopic&t=5761
//來自論壇的 tpglemur
{$mode objfpc}{$H+}
interface
uses
  Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs,
  mysql50conn, sqldb, StdCtrls;
type
  { TForm1 }
  TForm1 = class(TForm)
    btnTest: TButton;
    btnNext: TButton;
    btnFirst: TButton;
    btnNew: TButton;
    edtPersonID: TEdit;
    edtSurname: TEdit;
    edtDOB: TEdit;
    Memo1: TMemo;
    procedure btnFirstClick(Sender: TObject);
    procedure btnNewClick(Sender: TObject);
    procedure btnNextClick(Sender: TObject);
    procedure btnTestClick(Sender: TObject);
  private
    { private declarations }
    conn : TMySQL50Connection;
    query : TSQLQuery;
    transaction : TSQLTransaction;
    procedure Display;
  public
    { public declarations }
  end;
var
  Form1: TForm1;
implementation
{ TForm1 }
procedure TForm1.btnTestClick(Sender: TObject);
var
  S: String;
begin
  conn := TMySQL50Connection.Create(nil);
  query := TSQLQuery.Create(nil);
  transaction := TSQLTransaction.Create(nil);
  try
    try
      conn.HostName := '127.0.0.1';
      conn.UserName := 'root';
      conn.Password := '';
      conn.DatabaseName := 'test1';
      conn.Connected := True;
      conn.Transaction := transaction;
      query.DataBase := conn;
      //query.ParseSQL := true; //該行不必須,這是預設的方法
      //query.ReadOnly := false; //該行不必須,這是預設的方法
      query.SQL.Text := 'select * from tperson';
      query.Open;

      query.Last;
      S := IntToStr(query.RecordCount) + #13#10;
      query.First;

      while not query.EOF do
      begin
        S := S + query.FieldByName('surname').AsString + #13#10;
        query.Next;
      end;
    finally
      //query.Free;
      //conn.Free;
    end;
  except
    on E: Exception do
      ShowMessage(E.message);
  end;
  Memo1.Text:= S;
end;

procedure TForm1.Display;
begin
  edtPersonID.Text := query.FieldByName('personid').AsString;
  edtSurname.Text := query.FieldByName('surname').AsString;
  edtDOB.Text := query.FieldByName('dob').AsString;
end;

procedure TForm1.btnFirstClick(Sender: TObject);
begin
  query.First;
  Display;
end;

procedure TForm1.btnNewClick(Sender: TObject);
begin
  query.Append;
  query.FieldValues['personid'] := edtPersonID.Text;
  query.FieldValues['surname'] := edtSurname.Text;
  query.FieldValues['dob'] := edtDOB.Text;
  query.Post;  
  query.ApplyUpdates; //套用更新
  //transaction.Commit; //該行不必須
end;

procedure TForm1.btnNextClick(Sender: TObject);
begin
  query.Next;
  Display;
end;

initialization
  {$I unt_db.lrs}
end.

這是使用 TMySQL50Connection,TSQLQuery,TSQLTransaction,TDatasource 與 TDBGrid 元件的另一種片版本,可將他們直接拉到表單上:

unit unt_mysql2;
{$mode objfpc}{$H+}
interface
uses
  Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs,
  mysql50conn, sqldb, StdCtrls, db, DBGrids, DbCtrls;
type
  { TForm1 }
  TForm1 = class(TForm)
    btnConnect: TButton;
    btnSave: TButton;
    btnNext: TButton;
    btnPrior: TButton;
    Datasource1: TDatasource;
    DBGrid1: TDBGrid;
    Memo1: TMemo;
    MySQL50Connection1: TMySQL50Connection;
    SQLQuery1: TSQLQuery;
    SQLTransaction1: TSQLTransaction;
    procedure btnConnectClick(Sender: TObject);
    procedure btnNextClick(Sender: TObject);
    procedure btnPriorClick(Sender: TObject);
    procedure btnSaveClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
    procedure FormCreate(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end; 
var
  Form1: TForm1; 
implementation
{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
  //設定元件內容:
  //(可以從物件檢視器做到)
  MySQL50Connection1.HostName := '127.0.0.1';
  MySQL50Connection1.UserName := 'root';
  MySQL50Connection1.Password := '';
  MySQL50Connection1.DatabaseName := 'test1';
  MySQL50Connection1.Transaction := SQLTransaction1;
  //SQLQuery1.ParseSQL := true; //該行不必須,這是預設值
  //SQLQuery1.ReadOnly := false; //該行不必須,這是預設值
  SQLQuery1.SQL.Text := 'select * from tperson';
  SQLQuery1.Transaction := SQLTransaction1;
  SQLQuery1.UpdateMode := upWhereChanged;
  Datasource1.Dataset := SQLQuery1;
  DBGrid1.DataSource := Datasource1;;
end;

procedure TForm1.btnConnectClick(Sender: TObject);
var
 S : string;
begin
  try
    MySQL50Connection1.Connected := true;
    SQLQuery1.Open;

    //測試一下是否都 OK:
    SQLQuery1.Last;
    S := IntToStr(SQLQuery1.RecordCount) + #13#10;
    SQLQuery1.First;
    while not SQLQuery1.EOF do
    begin
      S := S + SQLQuery1.FieldByName('surname').AsString + #13#10;
      SQLQuery1.Next;
    end;
  except
    on E: Exception do
      ShowMessage(E.message);
  end;
  Memo1.Text:= S;
end;

procedure TForm1.btnNextClick(Sender: TObject);
begin
  SQLQuery1.Next;
end;

procedure TForm1.btnPriorClick(Sender: TObject);
begin
  SQLQuery1.Prior;
end;

procedure TForm1.btnSaveClick(Sender: TObject);
begin
  SQLQuery1.ApplyUpdates;
end;

procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
  //必要的,或是在關閉時取得 EDatabase 錯誤:
  MySQL50Connection1.Connected := false;
end;

initialization
  {$I unt_mysql2.lrs}
end.

Lazarus 和 PostgreSQL

這個教學很簡短,告訴大家如何在 Lazarus 0.9.12 或更新的版本下使用 TPQConnection 連線到 PostgreSQL 資料庫,本地端或遠端都行。

在正確地安裝後,進行下列步驟:

  • 從 SQLdb 籤頁裡放入 PQConnection
  • 從 SQLdb 籤頁裡放入 SQLQuery
  • 從 SQLdb 籤頁裡放入 SQLTransaction
  • 從資料存取 (DataAccess) 籤頁裡放入資料來源 (DataSource)
  • 從資料控制 (DataControl) 籤頁裡放入 DBGrid
  • 在 PQConnection 裡填入:
    • 每個 SQLTransaction 物件的交易內容
    • 資料庫名稱
    • 主機名稱
    • 使用者名稱 + 密碼
  • 檢查 SQLTransaction 已自動改為指向 PQConnection
  • 在 SQLQuery 內填入:
    • 每個物件的交易內容
    • 每個物件的資料庫內容
    • SQL (像是 'select * from anytable' 這樣的句子)
  • 在資料庫來源物件內照著 SQLQuery 物件填入 DataSet 的內容
  • 在 DBGrid 內依資料庫來源物件填入資料來源

將所有的物件都調為「已連線;connected」和「啟動;active」,DBGrid 應該還要填入設定好的時間。 TDBText 和 TDBEdit 看起來已經在運作,但(對我來說)他們只負責顯示資料。

要變更資料庫內容,需要使用下列程式碼來呼叫資料庫引擎:

 try
   sql:= 'UPDATE table SET setting=1';
   PQDataBase.Connected:=True;
   PQDataBase.ExecuteDirect('開始作業;');
   PQDataBase.ExecuteDirect(sql);
   PQDataBase.ExecuteDirect('交付作業');
   PQDataBase.Connected:=False;
 except
   on E : EDatabaseError do
     MemoLog.Append('資料庫錯誤:'+sql+chr(13)+chr(10)+E.ClassName+chr(13)+chr(10)+E.Message);
   on E : Exception do
     MemoLog.Append('錯誤'+sql+chr(13)+chr(10)+E.ClassName+chr(13)+chr(10)+E.Message);
 end;


  • 備註:
    • 在 Windows 環境下測試,Lazarus 0.9.12 + PgSQL 8.3.1
    • Linux 環境下部份測試,Lazarus 0.9.12 and PgSQL 8.0.x


  • 安裝與錯誤:
    • 在測試版本 Lazarus .12下,欄位型態為「文字」與「數字」時會有程式錯誤
    • 在使用固定長度字元,整數與 8 位元浮點數時沒有問題
    • 有時候有些愚蠢的錯誤需要你重啟 Lazarus ...
    • 某些錯誤發生後,其交易仍在進行,而必須手動去關閉。
    • Lazarus 一些更新會讓交易在交付之前都無法看見
    • 內建的除錯器看起來也有些錯誤 (至少在 Windows 下時) - 有時候執行到 IDE 之外的會需要協助找尋錯誤
    • 在 Linux 下某些錯誤訊息只會出現在主控台下 -- 用主控台模式下指令來執行程式,有時候你會因此而得到一些額外的除錯訊息。
    • 錯誤:「無法載入 PostgreSQL 用戶端,libq.so 是否已安裝?」
      • 在 PostgreSQL 安裝時加入可以搜尋到 libpq* 的路徑。
      • 在 Linux 下,加入 libpq.so 檔案的路徑到你的函式庫 /etc/fpc.cfg 檔案裡,例如:-Fl/usr/local/pgsql/lib
      • 在 Windows 下,將這些函式庫路徑加入到環境變數 Path 裡,或是專案的目錄裡
      • 在 Windows 下,我將 C:\Program Files\PostgreSQL\8.1\bin 目錄下的 DLL 檔案都複製到另一個 PATH 變數裡的目錄。
      • 或是加入 postgres\bin 目錄到路徑裡

Lazarus 與 SQLite

by Luiz Americo

請至 sqlite4fpc homepage 取得 API reference 與更多的教材。

前言

TSqliteDataset 與 TSqlite3Dataset 繼承自 TDataset, 個自分別可存取 2.8.x 與 3.x.x sqlite 資料庫。 以下是使用Sqlite主要的優缺點:

優點:

  • 靈活: 程式設計師可以選擇使用或不使用SQL 語言,允許他們使用SQL/sqlite容許的簡單的表格配置或是任何複雜的配置
  • 自動更新資料庫: 不需要手動更新資料庫使用SQL 述?, 單一方法來照顧它
  • 快速: 它的快取資料是放在記憶體, 使的瀏覽資料集是相當的快速
  • 沒有伺服器的安裝/設定: 只是包裝在sqlite 動態函式庫

缺點:

  • 要求外部的檔案 (sqlite 函式庫)

需求

  • sqlite2 資料庫:
    • fpc 2.0.0
    • Lazarus 0.9.10
    • sqlite 執行期函式庫 2.8.15 或以上 (從 www.sqlite.org 獲得)
  • sqlite3 資料庫:
    • fpc 2.0.2
    • Lazarus 0.9.11 (svn 修訂版 8443 或以上)
    • sqlite 執行期函式庫 3.2.1 或以上 (從 www.sqlite.org 獲得)

開始一個 lazarus 專案前, 請先確認以下事項:

  • SQLite 資料庫有在系統路徑 PATH 變數裡,或其它可執行的目錄
  • 在 Linux 下,將 cmem 第一個放入主程式的 uses 語法內
    • 在 Debian,Ubuntu 和其他 Debian-like 發佈版本裡,為了要建置 Lazarus IDE 你得先安裝 libsqlite-dev/libsqlite3-dev 套件,而不僅只有 sqlite/sqlite3 (對 OpenSuSe 也一樣)

使用方法 (基本)

安裝 /components/sqlite 資料夾中的套件 (安裝教材在這裡)

於設計模式下設定以下屬性

  • FileName: sqlite 檔案的路徑 [必要]
  • TableName: sql 語法中使用的表格(table)名稱 [必要]
  • Sql: 一個 SQL 的 select 語法 [非必要]

建立一個資料表 (Dataset)

雙按元件圖示, 或點選右鍵選單中的「建立資料表」。便會出現一個簡單的表格編輯器。

 以下是 TSqliteDataset 與 TSqlite3Dataset 所支援的所有欄位格式:
 Integer
 AutoInc
 String
 Memo
 Bool 
 Float
 Word
 DateTime
 Date
 Time
 LargeInt
 Currency
 

取得資料

建立資料表後, 或者在已有的資料表。以 Open 方法開啟 dataset。 如果 SQL 屬性沒有設定內容, 則所有的資料與欄位都會被傳回, 效果與以下指令相同:

 SQL:='Select * from TABLENAME'; 

為資料檔案套用更新

使用 ApplyUpdates 函式前,dataset 須包含至少一個主鍵欄位填入需求 (必須為唯一且非 NULL 值)

這可以由兩種方式來達成:

  • 設定 PrimaryKey 屬性為主鍵欄位的名稱
  • 新增一個 AutoInc 欄位(這比較簡單, 因為 TSqliteDataSet 會自行將此欄位當成主鍵)

以上兩個條件設定好其中一個後, 只要呼叫以下指令

 ApplyUpdates;

備註一:若兩個條件都有設定, 則欄位會使用 PrimaryKey 來更新資料

備註二: 若 PrimaryKey 被指定的欄位不是主鍵, 則呼叫 ApplyUpdates 可能會造成資料的遺失, 所以請確定所選擇的欄位值是唯一且非 NULL 值。

註記

  • 雖然此程式已經測試過 10000 筆的資料都運行的很好,TSqliteDataset 將所有的資料都留在記憶體裡,所以請記得只取出你所需要的資料就好 (特別是針對記事欄位)。
  • 同一個資料檔案 (檔案的內容) 可以負責多個資料表/資料集合
  • 數個資料集合 (不同的欄位合併後) 可以建立一個暫存的資料表
  • SQL 允許你用 WHERE 的語法來篩選你要取出的資料,重覆開啟再關閉同一資料集合 (或是呼叫重新擷取資料 - RefetchData - 的方法)。但條件就是欄位的數量要一定
  • 像使用一些複合的 SQL 敘述句,如 aliases,joins 同時檢視多個資料表 (請記得這麼做就是都分配在同一支資料檔案裡),但在 ApplyUpdates 的情況裡卻行不通。如果有人想要使用這種複合的查詢而又要套用更新到資料檔案,請寄信給我,我會告訴你一些絕竅
  • 當為不是從 TSqliteDataset 建立的 sqlite2.x 資料檔妹設定檔案名稱時,雖然檔案可以開啟,但有些欄位可能無法正確地偵測到,會被當成是字串欄位。

可以在 fpc/fcl/db/sqlite CVS 目錄下找到一個通用的範例

Luiz Americo pascalive(at)bol(dot)com(dot)br

Lazarus 與 MSSQL

需要啟用 Zeoslib (最新版修訂版),請見此頁下方的連結。

Lazarus and Interbase / Firebird

請參看安裝套件。該頁為解釋如何連線到 IB 或 FB 主機的簡短範例。

也可以使用最新的 Zeoslib 的版本 (取自 cvs)。

FBLib Firebird 函式庫

[1]是一個不含資料感知元件的開放原始碼函式庫,使用 Borland Delphi,Freepascal 與 Lazarus 都可以直接對 Firebird 關聯資料庫做存取。

現有的特點包括:

  • 直接對 Firebird 1.0.x 1.5.x Classic 或 SuperServer 版本做存取
  • 跨平台工作 (Win32,Gnu/Linux,FreeBSD)
  • 自動選取 'fbclient' 或 'gds32' 用戶端函式庫
  • 支援參數查詢
  • 支援 SQL 同源語 1/3
  • LGPL 認證通過
  • 解析前置檔頭資料
  • 單一程式腳本解析
  • EXE 末端僅只使用 100-150 KB
  • 支援 BLOB 欄位
  • SQL 腳本直接支援資料與 HTML 轉換
  • 伺服器管理 (備份,還原,gfix...)
  • 事件警告器

你可以從 FBLib's website 下載文件。

Lazarus and dBase

Tony Maro

You might also want to visit the beginnings of the TDbf Tutorial page

FPC includes a simple database component that is similar in function to the Delphi TTable component called "TDbf" (TDbf Website) that supports a very basic subset of features for dBase files. It is not installed by default, so you will first need to install the Lazarus package from the "lazarus/components/tdbf" directory and rebuild your Lazarus IDE. It will then appear next to the TDatasource in your component palette (Data Access tab).

The TDbf component has an advantage over other database components in that it doesn't require any sort of runtime database engine. However it's not the best option for large database applications.

It's very easy to use. Simply drop a TDbf on your form, set the runtime path to the directory that your database files will be in, set the table name, and link it to your TDatasource component.

Real functionality requires a bit more effort, however. If a table doesn't already exist, you'll need to create it programmatically, unless there's a compatible table designer I'm not familiar with. Note: Current version of OpenOffice (2.0x) contains OpenOffice Base, which can create dbf files in a somewhat user-friendly way.

Attempting to open a nonexistent table will generate an error. Tables can be created programmatically through the component after the runtime path and table name are set.

For instance, to create a table called "dvds" to store your dvd collection you would drop it on your form, set the runtime path, and set the table name to "dvds". The resulting file will be called "dvds.dbf".

In your code, insert the following:

   Dbf1.FilePathFull := '/path/to/my/database';
   Dbf1.TableName := 'dvds';
   With Dbf1.FieldDefs do begin
       Add('Name', ftString, 80, True);
       Add('Description', ftMemo, 0, False);
       Add('Rating', ftString, 5, False);
   end;
   Dbf1.CreateTable;

When this code is run, your DVD collection table will be created. After that, all data aware components linked through the TDatasource to this component will allow easy access to the data.

Adding an index is a little different from your typical TTable. It must be done after the database is open. You use the same method also to rebuild the indices. For instance:

   Dbf1.Exclusive := True;
   Dbf1.Open;
   Dbf1.AddIndex('dvdsname','Name',[ixPrimary, ixUnique, ixCaseInsensitive]);
   Dbf1.AddIndex('rating.ndx', 'Rating', [ixCaseInsensitive]);
   Dbf1.Close;

The first (primary) index will be a file called "dvdsname.mdx" and the second will be a file named "rating.ndx" so in a multiple table database you must be careful not to use the same file name again.

I will try to add a more detailed example at a later date, but hopefully this will get those old Delphi programmers up and running with databases in Lazarus!


Searching and Displaying a data set

Simon Batty

In this example I wanted to search a database of books for all the titles an author has listed and then display the list in a memo box


   Dbf1.FilePathFull := '/home/somelocatio/database_location/'; // path to the database directory
   Dbf1.TableName := 'books.dbase';                             // database file (including extension)
   DbF1.Open;
   memo1.Clear;                                                 // clear the memo box
   Dbf1.FilterOptions := [foCaseInsensitive];
   Df1.Filter := 'AU=' + QuotedStr('anauthor');         // AU is the field name containing the authors
   Dbf1.Filtered := true;       // This selects the filtered set
   Dbf1.First;                  // moves the the first filtered data
   while not dbf1.EOF do        // prints the titles that match the author to the memo box
   begin
       memo1.Append(Dbf1.FieldByName('TI').AsString); // TI is the field name for titles
       dbf1.next;                                     // use .next here NOT .findnext!
   end;
   Dbf1.Close;   

Note that you can use Ddf1.findfirst to get the first record in the filtered set, then use Dbf1.next to move though the data. I found that using Dbf1.Findnext just causes the program to hang.

This database was generated using TurboBD that came with the Kylix 1. I cannot get TurboBD tables to work with Lazarus, however you can download a command line tool from TurboDB's website that allows you to convert TurboDB table to other formats.

Using TSdfDataset and TFixedDataset

TSdfDataset and TFixedDataset are two simple datasets which offer a very simple textual storage format. These datasets are very convenient for small databases, because they are fully implemented as an object pascal unit, and thus require no external libraries, and because their textual format allows them to be easely edited with a text editor.

To start with this formats, a initial database file should be created. The format is very simple, so use a text editor to do this.

Bellow is a sample database for TSdfDataset. Note that the first line has the names of the fields and that we are using commas as separators:

ID,NAMEEN,NAMEPT,HEIGHT,WIDTH,PINS,DRAWINGCODE
1,resistor,resistor,1,1,1,LINE
2,capacitor,capacitor,1,1,1,LINE
3,transistor npn,transistor npn

And here is an example database for using with TFixedDataset. Each record occupies a fixed amount of space, and if the field is smaller then it, spaces should be used to fill the remaining size.

Name = 15 chars; Surname = 15 chars; Tell = 10 chars; e_mail = 20 chars;
Piet           Pompies                  piet@pompies.net    

Using the datasets directly

Sometimes it is useful to create the dataset and work with it completely in code, and the following code will do exactly this. Note some peculiarities of TSdfDataset/TFixedDataset:

  • The lines in the database can have a maximum size of about 300. A fix is being researched.
  • It is necessary to add the field definitions. Some datasets are able to fill this information alone from the database file
  • One should set FirstLineAsSchema to true, to indicate that the first line includes the field names and positions
  • The Delimiter property holds the separator for the fields. It will not be possible to use this char in strings in the database. Similarly it will not be possible to have lineendings in the database because they mark the change between records. It's possible to overcome this by substituting the needed comma or line ending with another not often used char, like # for example. So that when showing the data on screen all # chars could be converted to line endings and the inverse when storing data back to the database. The ReplaceString routine is useful here.
constructor TComponentsDatabase.Create;
begin
  inherited Create;

  FDataset := TSdfDataset.Create(nil);
  FDataset.FileName := vConfigurations.ComponentsDBFile;

  // Not necessary with TSdfDataset
//  FDataset.TableName := STR_DB_COMPONENTS_TABLE;
//  FDataset.PrimaryKey := STR_DB_COMPONENTS_ID;

  // Adds field definitions
  FDataset.FieldDefs.Add('ID', ftString);
  FDataset.FieldDefs.Add('NAMEEN', ftString);
  FDataset.FieldDefs.Add('NAMEPT', ftString);
  FDataset.FieldDefs.Add('HEIGHT', ftString);
  FDataset.FieldDefs.Add('WIDTH', ftString);
  FDataset.FieldDefs.Add('PINS', ftString);
  FDataset.FieldDefs.Add('DRAWINGCODE', ftString);

  // Necessary for TSdfDataset
  FDataset.Delimiter := ',';
  FDataset.FirstLineAsSchema := True;

  FDataset.Active := True;

  // Sets the initial record
  CurrentRecNo := 1;
  FDataset.First;
end;

When using TSdfDataset directly be aware that RecNo, although it is implemented, does not work as a way to move through the dataset whether reading or writing records. The standard navigation routines like First, Next, Prior and Last work as expected, so you need to use them rather than RecNo. If you are used to using absolute record numbers to navigate around a database you can implement your own version of RecNo. Declare a global longint variable called CurrentRecNo which will hold the current RecNo value. Remember that this variable will have the same convention as RecNo, so the first record has number 1 (it is not zero-based). After activating the database initialize the database to the first record with TSdfDataset.First and set CurrentRecNo := 1

{@@
  Moves to the desired record using TDataset.Next and TDataset.Prior
  This avoids using TDataset.RecNo which doesn't navigate reliably in any dataset.

  @param AID Indicates the record number. The first record has number 1
}
procedure TComponentsDatabase.GoToRec(AID: Integer);
begin
  // We are before the desired record, move forward
  if CurrentRecNo < AID then
  begin
    while (not FDataset.EOF) and (CurrentRecNo < AID) do
    begin
      FDataset.Next;
      FDataset.CursorPosChanged;
      Inc(CurrentRecNo);
    end;
  end
  // We are after the desired record, move back
  else if CurrentRecNo > AID  then
  begin
    while (CurrentRecNo >= 1) and (CurrentRecNo > AID) do
    begin
      FDataset.Prior;
      FDataset.CursorPosChanged;
      Dec(CurrentRecNo);
    end;
  end;
end;

使用資料感知控制元件

相關連結

貢獻與變更

本頁是從 epikwiki version 轉換過來的。