Sign in Join
Wiki copytemporary

Lazarus Database Tutorial/zh TW: what changed

Revision 2 to 3. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11# Lazarus 資料庫教學
22
33## 導覽
44
55本教材內容是關於如何於 Lazarus 下配合數種資料庫來工作。
66
77Lazarus 支援數種非內建資料庫,而開發者仍應安裝各資料庫所對應的元件(Packages)。你可以透過程式碼或直接拉元件(components)至表單(from)來存取資料庫。資料感知 (data-aware) 元件代表資料庫欄位,並透過 TDataSource 設定指定的資料來源 (DataSource) 屬性物件來連接。資料來源代表一個資料表,是以連接至資料庫元件 (如:TPSQLDatabase, TSQLiteDataSet) 的 DataSet 屬性所選定的。資料感知元件位於「資料控制」籤頁內。資料來源及資料庫控制元件則位於「資料存取」的籤頁內。
88
99## Lazarus 與 MySQL
1010
1111### 讓 MySQL 在 Linux 或 Windows 中動起來
1212
1313照著 MySQL 使用手冊裡的指示,讓 mysqld 程序於背景執行。對於所有潛在的主機來源 (包含本機 'localhost',本機的名稱和其他透過網路連線的主機),所有的用戶 (包含根使用者,mysql,你自己和其他所有會用到的人) 都要設定使用權限。也要儘可能的達到安全性要求,所有使用者,包含根使用者都要有一個密碼。接著使用手冊上的範例方法來測試確定所有使用者在系統上的權限都安全可靠。
1414
1515### 在文字模式中讓 MySQL 為 FPC 工作
1616
1717在 \$(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 指令
1818
19-`CREATE DATABASE testdb;`
19+```text
20+CREATE DATABASE testdb;
21+```
2022
2123接著確認所有相關使用者擁有適當的存取權限
2224
23-`GRANT ALL ON testdb TO johnny-user IDENTIFIED BY 'johnnyspassword'; `
25+```text
26+GRANT ALL ON testdb TO johnny-user IDENTIFIED BY 'johnnyspassword';
27+```
2428
2529現在你應試著去執行一個叫 mkdb 的指令碼:
2630
27-`sh ./mkdb`
31+```text
32+sh ./mkdb
33+```
2834
2935這個指令應該會失敗, 因為系統不會允許匿名者存取資料庫。所以必須修改這個指令碼讓 mysql 讀取以下這行:
3036
31-`mysql -u root -p  ${1-testdb} << EOF >/dev/null`
37+```text
38+mysql -u root -p ${1-testdb} << EOF >/dev/null
39+```
3240
3341接著試著再執行一次,系統提示要你輸入密碼。幸運的話你應該就可以管理剛建立的資料庫了。用以下的 mysql 指令測試一下(在 mysql 監視器中):
3442
35-`select * from FPdev;`
43+```text
44+select * from FPdev;
45+```
3646
3747即可看到一個列有 ID,使用者名稱和 Email 地址的FPC 開發者資料表了。
3848
3949現在可以執行測試程式 testdb.pp 了 (這檔也許還需要先編譯過,而且可能會在第一次編譯出錯!!)。
4050
4151我找出這支程式可能無法連結 MySQL 的幾個原因:
4252
4353- 我的系統 (SuSE Linux v9.0) 安裝的是 mysql v4.0.15,不同於安裝套件的版本 3。
4454
4555- 程式需要輸入使用者和密碼來存取資料庫。
4656
4757- 編譯器需要知道哪裡可以找到 MySQL 的函式庫 (**如果你並沒有安裝 MYSQL 的開發函式庫,那現在快裝!**)
4858
4959我將 testdb.pp 另存成 trydb.pp,然後做修改 - 這樣代表原來那支還是可以隨後透過 CVS 更新。 我另外也將在 mysql/ver40/ 子目錄下找到的那支另存成 mysql_v4.pp,mysql_com_v4.pp 和 mysql_version_v4.pp,記得你單元裡的程式也要改成適當的檔名。在 trydb.pp 裡我變更 uses 敘述句為
5060
51-`uses mysql_v4`
61+``` pascal
62+uses mysql_v4
63+```
5264
5365mysql_v4.pp 的敘述句改為
5466
55-`uses mysql_com_v4`
67+``` pascal
68+uses mysql_com_v4
69+```
5670
5771另外為要使用的函式庫加入一行到 /etc/fpc.cfg 裡:
5872
59-`-Fl/lib;/usr/lib`
73+```text
74+-Fl/lib;/usr/lib
75+```
6076
6177接下來的動作也許不必要做,因為你在安裝開發函式庫的時候應該會自動幫你建立連結,但檢查一下總是無仿。 進行前需要先找到我的 MYSQL 用戶端函式庫是放在 /usr/lib 目錄下面的哪裡,我的狀況是先下 shell 指令:
6278
63-`ln -s libmysqlclient.so.12.0.0 lmysqlclient`
79+```text
80+ln -s libmysqlclient.so.12.0.0 lmysqlclient
81+```
6482
6583建立一個軟連結讓 FPC 可以找的到函式庫。為了更準確一些我再建立一個連結
6684
67-`ln -s libmysqlclient.so.12.0.0 mysqlclient`
85+```text
86+ln -s libmysqlclient.so.12.0.0 mysqlclient
87+```
6888
6989然後放一個相同的連結在不同的其他目錄裡:不是必需,視請況而定! 然後有些使用者也許會需要增加如下的連結:
7090
71-`ln -s libmysqlclient.so.12.0.0 libmysqlclient.so`
91+```text
92+ln -s libmysqlclient.so.12.0.0 libmysqlclient.so
93+```
7294
7395我修改 trydb.pp 的內容,含括進使用者連線資訊,從上到下計的內容有主機,使用者與密碼:
7496
75-`const`\
76-`  host : Pchar= 'localhost';`\
77-`  user : Pchar= 'myusername';`\
78-`  passwd: Pchar = 'mypassword';`
97+``` pascal
98+const
99+ host : Pchar= 'localhost';
100+ user : Pchar= 'myusername';
101+ passwd: Pchar = 'mypassword';
102+```
79103
80104我還發現我忽叫 mysql_connect() 還是無法連上 MySQL,但使用 mysql_real_connect() ,加上一堆參數後就可以了。未來還會更複雜,參數的數目似乎從 3 版(七個)換到 4 版(八個)還有變化。在使用 mysql_real_connect 之前我還得先使用 mysql_init(),但這在原始的 mysql.pp 裡並找不到,但出現於 mysql_v4.pp。
81105
82106所以連線的程式碼將會是現在這樣:
83107
84-`{加一些額外的變數}`\
85-`var`\
86-`  alloc : PMYSQL;`\
87-` `\
88-`{主程式片段}`\
89-` `\
90-`begin`\
91-` if paramcount=1 then`\
92-`   begin`\
93-`   Dummy:=Paramstr(1)+#0;`\
94-`   DataBase:=@Dummy[1];`\
95-`   end;`\
96-` `\
97-`Writeln ('分派空間...');`\
98-` alloc := mysql_init(PMYSQL(@qmysql));`\
99-` Write ('連線到 MySQL...');`\
100-` sock :=  mysql_real_connect(alloc, host, user, passwd, database, 0, nil, 0);`\
101-` if sock=Nil then`\
102-`   begin`\
103-`   Writeln (stderr,'無法連線到 MySQL.');`\
104-`   Writeln (stderr, '錯誤是:', mysql_error(@qmysql));`\
105-`   halt(1);`\
106-`   end;`\
107-` Writeln ('完成。');`\
108-` Writeln ('連線資料:');`\
109-`{$ifdef Unix}`\
110-` writeln ('Mysql_port      : ',mysql_port);`\
111-` writeln ('Mysql_unix_port : ',mysql_unix_port);`\
112-`{$endif}`\
113-` writeln ('Host info       : ',mysql_get_host_info(sock));`\
114-` writeln ('Server info     : ',mysql_stat(sock));`\
115-` writeln ('Client info     : ',mysql_get_client_info);`\
116-` `\
117-` Writeln ('選擇資料庫 ',DataBase,'...');`\
118-` if mysql_select_db(sock,DataBase) < 0 then`\
119-`   begin`\
120-`   Writeln (stderr,'無法選取資料庫 ',Database);`\
121-`   Writeln (stderr,mysql_error(sock));`\
122-`   halt (1);`\
123-`   end;`\
124-`{... 其他就如原來 testdb.pp 裡的內容}`
108+``` pascal
109+{加一些額外的變數}
110+var
111+ alloc : PMYSQL;
112+
113+{主程式片段}
114+
115+begin
116+ if paramcount=1 then
117+ begin
118+ Dummy:=Paramstr(1)+#0;
119+ DataBase:=@Dummy[1];
120+ end;
121+
122+Writeln ('分派空間...');
123+ alloc := mysql_init(PMYSQL(@qmysql));
124+ Write ('連線到 MySQL...');
125+ sock := mysql_real_connect(alloc, host, user, passwd, database, 0, nil, 0);
126+ if sock=Nil then
127+ begin
128+ Writeln (stderr,'無法連線到 MySQL.');
129+ Writeln (stderr, '錯誤是:', mysql_error(@qmysql));
130+ halt(1);
131+ end;
132+ Writeln ('完成。');
133+ Writeln ('連線資料:');
134+{$ifdef Unix}
135+ writeln ('Mysql_port : ',mysql_port);
136+ writeln ('Mysql_unix_port : ',mysql_unix_port);
137+{$endif}
138+ writeln ('Host info : ',mysql_get_host_info(sock));
139+ writeln ('Server info : ',mysql_stat(sock));
140+ writeln ('Client info : ',mysql_get_client_info);
141+
142+ Writeln ('選擇資料庫 ',DataBase,'...');
143+ if mysql_select_db(sock,DataBase) < 0 then
144+ begin
145+ Writeln (stderr,'無法選取資料庫 ',Database);
146+ Writeln (stderr,mysql_error(sock));
147+ halt (1);
148+ end;
149+{... 其他就如原來 testdb.pp 裡的內容}
150+```
125151
126152現在 - 準備好開始編譯 trydb.pp 了嗎?
127153
128-` fpc trydb`
154+```text
155+ fpc trydb
156+```
129157
130158成功!現在執行:
131159
132-` ./trydb`
160+```text
161+ ./trydb
162+```
133163
134164哇!我讀取到 FPC 開發者的清單了!
135165
136166想要更精進的話:將使用者連線資訊的部份與 MySQL 的指令做成互動的模式,使用變數而不使用常數,如此可以在你退出 MySQL 前輸入多個 SQL 指令:參見[[Lazarus Database Tutorial/TryDbpp|所有程式一覽表]],當使用者資訊是從主控台模式輸入取得時,程式將要利用迴圈,讓 SQL 的指令可以重覆輸入 (可以不用分號做結尾),然後再將回應顯示出來,直到使用者鍵入 'quit' 為止。
137167
138168參見[[Lazarus Database Tutorial/SampleListing|主控台範例一覽表]]。
139169
140170### 在 Lazarus 的應用程式裡連線到 MySQL
141171
142172此教學將示範 Lazarus 如何連線到 MySQL 資料庫,並執行一段簡單的查詢,僅止使用 Lazarus 基本的元件;它並沒有用到任何資料感知元件,但已可想像連線到資料庫其介面的原理。
143173
144174建立一個新的 Lazarus 專案:
145175
146-`〔專案〕->〔新專案〕->〔應用程式〕`
176+```text
177+〔專案〕->〔新專案〕->〔應用程式〕
178+```
147179
148180將會自動產生出一份表單。
149181
150182先把表單拉大到約半個畫面的大小,然後將表單的標題改名為 'TryMySQL'。
151183
152184從標準元件的籤頁裡放置三個編輯方框到表單的左上,然後緊接著其下各放一個標籤。將標籤的標題改為「主機」(還有 HostLabel,HostEdit),「使用者名稱」 (還有 UserLabel,UserEdit) 和「密碼」 (還有 PasswordLabel 和 PasswordEdit)。或是你可以使用附加籤頁裡的標籤編輯元件來取代。
153185
154186選取密碼編輯的方框,找到 PasswordChar 這個內容:將其改為 * 或是其他字元,這樣當你鍵入你的密碼時你的畫面就會重覆顯示你這個字元來取代你真正的密碼。然後確定 Text 內容裡的值皆是空的。
155187
156188然後現在再放置一個編輯方框和標籤到你表單的右上方。改變標籤成「輸入 SQL 命令」,並命名為 CommandEdit。
157189
158190表單上再放三個按鈕:兩個在左邊,編框框的下面,另一個在右邊,命令框的下面。
159191
160192將左邊的按鈕標記為「連結到資料庫」(ConnectButton) 與「離開」(ExitButton),而右邊的那個記為「送出查尋」(QueryButton)。
161193
162194再放一個大的記事框在右下,標名為「結果」(ResultMemo),把送下能利用的空間都填滿給它。找到它捲軸條(ScrollBars)的內容,選為 ssAutoBoth,這樣捲軸條就可以為出現的文字自動調整空間。然後文字換行 (WordWrap) 的內容設為真 (True)。
163195
164196再放一個狀態列 (從共通控制項籤頁裡) 在表單的下面,然後將其單純的文字 (SimpleText) 內容裡設為 'TryMySQL'。
165197
166198擷圖將顯示表單長這個樣子:[Mysql 範例擷圖](http://lazarus-ccr.sourceforge.net/kbdata/trymysqldb.png)
167199
168200現在我們要寫一些事件處理器。
169201
170202這三個編輯框是用來輸入主機名稱,使用者名稱和密碼用的。當這三個都已輸入滿足條件後,將按下連線的按鈕。因此這按鈕 OnCLick 在事件處理器裡就是將在處理上述文字模式的 FPC 程式裡做的事情。
171203
172204而資料庫回傳的訊息,現在就不能用 Pascal 的 write 和 writeln 這兩個敘述句來負責了:取而代之的是所有回應都將轉成字串然後丟到記事框裡。鑑於 Pascal 的 write 和 writeln 敘述句能「即時」地將資料型態轉換,因為在記事框要輸出的文字,在處理時也要能不含糊地都轉換成字串才送出,所以字元變數就要用 StrPass 轉換成字串變數,而數字就要用 IntToStr 去轉換。
173205
174206在記事框裡顯示字串時將用
175207
176-`procedure ShowString (S : string);`\
177-`(* display a string in a Memo box *)`\
178-`begin`\
179-`       trymysqlForm1.ResultsMemo.Lines.Add (S)`\
180-`end;`
208+``` pascal
209+procedure ShowString (S : string);
210+(* display a string in a Memo box *)
211+begin
212+ trymysqlForm1.ResultsMemo.Lines.Add (S)
213+end;
214+```
181215
182216連線按鈕的事件處理器將會變成:
183217
184-`procedure TtrymysqlForm1.ConnectButtonClick(Sender: TObject);`\
185-`(* 使用主表單裡文字框裡輸入的使用者訊息來連線到 MySQL *)`\
186-`var strg: string;`\
187-` `\
188-`begin`\
189-` `\
190-` dummy1 :=  trymysqlForm1.HostEdit.text+#0;`\
191-` host := @dummy1[1];`\
192-` dummy2 := trymysqlForm1.UserEdit.text+#0;`\
193-` user := @dummy2[1] ;`\
194-` dummy3 := trymysqlForm1.PasswdEdit.text+#0;`\
195-` passwd := @dummy3[1] ;`
218+``` pascal
219+procedure TtrymysqlForm1.ConnectButtonClick(Sender: TObject);
220+(* 使用主表單裡文字框裡輸入的使用者訊息來連線到 MySQL *)
221+var strg: string;
222+
223+begin
224+
225+ dummy1 := trymysqlForm1.HostEdit.text+#0;
226+ host := @dummy1[1];
227+ dummy2 := trymysqlForm1.UserEdit.text+#0;
228+ user := @dummy2[1] ;
229+ dummy3 := trymysqlForm1.PasswdEdit.text+#0;
230+ passwd := @dummy3[1] ;
231+```
196232
197-` alloc := mysql_init(PMYSQL(@qmysql));`\
198-` sock :=  mysql_real_connect(alloc, host, user, passwd, database, 0, nil, 0);`\
199-` if sock=Nil then`\
200-`   begin`\
201-`     strg :='無法連線到 MySQL.'; showstring (strg);`\
202-`     Strg :='錯誤是:'+ StrPas(mysql_error(@qmysql)); showstring (strg);`\
203-`  end`\
204-`   else`\
205-`   begin`\
206-`     trymysqlForm1.statusBar1.simpletext := 'Connected to MySQL';`\
207-`     strg := '現在選擇資料庫:' + database; showstring (strg);`\
208-`{$ifdef Unix}`\
209-`     strg :='Mysql_port      : '+ IntToStr(mysql_port); showstring (strg);`\
210-`     strg :='Mysql_unix_port : ' + StrPas(mysql_unix_port); showstring (strg);`\
211-`{$endif}`\
212-`     Strg :='Host info       : ' + StrPas(mysql_get_host_info(sock));`\
213-`     showstring (strg);`\
214-`     Strg :='Server info     : ' + StrPas(mysql_stat(sock)); showstring (strg);`\
215-`     Strg :='Client info     : ' + Strpas(mysql_get_client_info);  showstring (strg);`\
216-` `\
217-`     trymysqlForm1.statusbar1.simpletext := '選取資料庫 ' + DataBase +' 中...';`\
218-` if mysql_select_db(sock,DataBase) < 0 then`\
219-` begin`\
220-`   strg :='無法選取資料庫 '+ Database; ShowString (strg);`\
221-`   Strg := mysql_error(sock); ShowString (strg);`\
222-` end`\
223-` end;`\
224-`end;`
233+``` pascal
234+ alloc := mysql_init(PMYSQL(@qmysql));
235+ sock := mysql_real_connect(alloc, host, user, passwd, database, 0, nil, 0);
236+ if sock=Nil then
237+ begin
238+ strg :='無法連線到 MySQL.'; showstring (strg);
239+ Strg :='錯誤是:'+ StrPas(mysql_error(@qmysql)); showstring (strg);
240+ end
241+ else
242+ begin
243+ trymysqlForm1.statusBar1.simpletext := 'Connected to MySQL';
244+ strg := '現在選擇資料庫:' + database; showstring (strg);
245+{$ifdef Unix}
246+ strg :='Mysql_port : '+ IntToStr(mysql_port); showstring (strg);
247+ strg :='Mysql_unix_port : ' + StrPas(mysql_unix_port); showstring (strg);
248+{$endif}
249+ Strg :='Host info : ' + StrPas(mysql_get_host_info(sock));
250+ showstring (strg);
251+ Strg :='Server info : ' + StrPas(mysql_stat(sock)); showstring (strg);
252+ Strg :='Client info : ' + Strpas(mysql_get_client_info); showstring (strg);
253+
254+ trymysqlForm1.statusbar1.simpletext := '選取資料庫 ' + DataBase +' 中...';
255+ if mysql_select_db(sock,DataBase) < 0 then
256+ begin
257+ strg :='無法選取資料庫 '+ Database; ShowString (strg);
258+ Strg := mysql_error(sock); ShowString (strg);
259+ end
260+ end;
261+end;
262+```
225263
226264右方的文字框是用來輸入 SQL 語法的,不用在結尾加分號;當你輸入內容滿足語法條件是,按下送出查詢 (SendQuery) 按鈕,查詢即進行處理,然後結果都會寫進記事框 (ResultBox) 內。
227265
228266而送出查詢按鈕在事件處理器裡要做的事也跟先前在 FPC 文字模式裡寫的一樣,但除了在字串回傳後要丟上記事框內之前,其型態轉換不得含糊。
229267
230268這和文字模式的 FPC 程式唯一不一樣的地方在於當一但偵測到錯誤狀態時,程式不會中斷,MySQL 也不會關閉,取而代之的是事件處理又回到主表單,等待新的查詢再重新送出。程式只有在按下「離開」按鈕後,才會關閉 MySQL 的連線與整個應用程式。 送出查詢 (SendQuery) 按鈕的程式碼如下:
231269
232-`procedure TtrymysqlForm1.QueryButtonClick(Sender: TObject);`\
233-`var`\
234-` dumquery, strg: string;`\
235-`begin`\
236-`     dumquery := TrymysqlForm1.CommandEdit.text;`\
237-`     dumquery := dumquery+#0;`\
238-`     query := @dumquery[1];`\
239-`     trymysqlForm1.statusbar1.simpletext := '執行查詢中:'+ dumQuery +'...';`\
240-`     strg := '執行查詢中:' + dumQuery; showstring (strg);`\
241-`     if (mysql_query(sock,Query) < 0) then`\
242-`     begin`\
243-`       Strg :='查詢失敗 '+ StrPas(mysql_error(sock)); showstring (strg);`\
244-`     end`\
245-`     else`\
246-`     begin`\
247-`       recbuf := mysql_store_result(sock);`\
248-`       if RecBuf=Nil then`\
249-`       begin`\
250-`         Strg :='查詢回傳的內容為空。'; showstring (strg);`\
251-`       end`\
252-`       else`\
253-`       begin`\
254-`         strg :='回傳資料筆數:' + IntToStr(mysql_num_rows (recbuf));`\
255-`         Showstring (strg);`\
256-`         Strg :='欄位記錄數:' + IntToStr(mysql_num_fields(recbuf));`\
257-`         showstring (strg);`\
258-`         rowbuf := mysql_fetch_row(recbuf);`\
259-`         while (rowbuf <>nil) do`\
260-`         begin`\
261-`              Strg :='(Id: '+ rowbuf[0]+', Name: ' + rowbuf[1]+ ', Email : ' +`\
262-`               rowbuf[2] +')';`\
263-`              showstring (strg);`\
264-`              rowbuf := mysql_fetch_row(recbuf);`\
265-`         end;`\
266-`       end;`\
267-`     end;`\
268-`end;`
270+``` pascal
271+procedure TtrymysqlForm1.QueryButtonClick(Sender: TObject);
272+var
273+ dumquery, strg: string;
274+begin
275+ dumquery := TrymysqlForm1.CommandEdit.text;
276+ dumquery := dumquery+#0;
277+ query := @dumquery[1];
278+ trymysqlForm1.statusbar1.simpletext := '執行查詢中:'+ dumQuery +'...';
279+ strg := '執行查詢中:' + dumQuery; showstring (strg);
280+ if (mysql_query(sock,Query) < 0) then
281+ begin
282+ Strg :='查詢失敗 '+ StrPas(mysql_error(sock)); showstring (strg);
283+ end
284+ else
285+ begin
286+ recbuf := mysql_store_result(sock);
287+ if RecBuf=Nil then
288+ begin
289+ Strg :='查詢回傳的內容為空。'; showstring (strg);
290+ end
291+ else
292+ begin
293+ strg :='回傳資料筆數:' + IntToStr(mysql_num_rows (recbuf));
294+ Showstring (strg);
295+ Strg :='欄位記錄數:' + IntToStr(mysql_num_fields(recbuf));
296+ showstring (strg);
297+ rowbuf := mysql_fetch_row(recbuf);
298+ while (rowbuf <>nil) do
299+ begin
300+ Strg :='(Id: '+ rowbuf[0]+', Name: ' + rowbuf[1]+ ', Email : ' +
301+ rowbuf[2] +')';
302+ showstring (strg);
303+ rowbuf := mysql_fetch_row(recbuf);
304+ end;
305+ end;
306+ end;
307+end;
308+```
269309
270310儲存你的專案,然後按〔執行〕-\>〔執行〕
271311
272312#### 下載 MYSQL 原始碼
273313
274314所有程式的一覽表都可以在這裡取得:[範例程式碼](http://lazarus-ccr.sourceforge.net/kbdata/mysqldemo.tar.gz)
275315
276316## 使用 TMySQL50Connection 元件的簡單 MySQL 示範
277317
278318這程式碼是一個簡單快速的示範 (在 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 兩個按鈕來切換觀看。
279319
280320 unit unt_db;
281321 //範例來源:
282322 //http://www.lazarus.freepascal.org/index.php?name=PNphpBB2&file=viewtopic&t=5761
283323 //來自論壇的 tpglemur
284324 {$mode objfpc}{$H+}
285325 interface
286326 uses
287327 Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs,
288328 mysql50conn, sqldb, StdCtrls;
289329 type
290330 { TForm1 }
291331 TForm1 = class(TForm)
292332 btnTest: TButton;
293333 btnNext: TButton;
294334 btnFirst: TButton;
295335 btnNew: TButton;
296336 edtPersonID: TEdit;
297337 edtSurname: TEdit;
298338 edtDOB: TEdit;
299339 Memo1: TMemo;
300340 procedure btnFirstClick(Sender: TObject);
301341 procedure btnNewClick(Sender: TObject);
302342 procedure btnNextClick(Sender: TObject);
303343 procedure btnTestClick(Sender: TObject);
304344 private
305345 { private declarations }
306346 conn : TMySQL50Connection;
307347 query : TSQLQuery;
308348 transaction : TSQLTransaction;
309349 procedure Display;
310350 public
311351 { public declarations }
312352 end;
313353 var
314354 Form1: TForm1;
315355 implementation
316356 { TForm1 }
317357 procedure TForm1.btnTestClick(Sender: TObject);
318358 var
319359 S: String;
320360 begin
321361 conn := TMySQL50Connection.Create(nil);
322362 query := TSQLQuery.Create(nil);
323363 transaction := TSQLTransaction.Create(nil);
324364 try
325365 try
326366 conn.HostName := '127.0.0.1';
327367 conn.UserName := 'root';
328368 conn.Password := '';
329369 conn.DatabaseName := 'test1';
330370 conn.Connected := True;
331371 conn.Transaction := transaction;
332372 query.DataBase := conn;
333373 //query.ParseSQL := true; //該行不必須,這是預設的方法
334374 //query.ReadOnly := false; //該行不必須,這是預設的方法
335375 query.SQL.Text := 'select * from tperson';
336376 query.Open;
337377
338378 query.Last;
339379 S := IntToStr(query.RecordCount) + #13#10;
340380 query.First;
341381
342382 while not query.EOF do
343383 begin
344384 S := S + query.FieldByName('surname').AsString + #13#10;
345385 query.Next;
346386 end;
347387 finally
348388 //query.Free;
349389 //conn.Free;
350390 end;
351391 except
352392 on E: Exception do
353393 ShowMessage(E.message);
354394 end;
355395 Memo1.Text:= S;
356396 end;
357397
358398 procedure TForm1.Display;
359399 begin
360400 edtPersonID.Text := query.FieldByName('personid').AsString;
361401 edtSurname.Text := query.FieldByName('surname').AsString;
362402 edtDOB.Text := query.FieldByName('dob').AsString;
363403 end;
364404
365405 procedure TForm1.btnFirstClick(Sender: TObject);
366406 begin
367407 query.First;
368408 Display;
369409 end;
370410
371411 procedure TForm1.btnNewClick(Sender: TObject);
372412 begin
373413 query.Append;
374414 query.FieldValues['personid'] := edtPersonID.Text;
375415 query.FieldValues['surname'] := edtSurname.Text;
376416 query.FieldValues['dob'] := edtDOB.Text;
377417 query.Post;
378418 query.ApplyUpdates; //套用更新
379419 //transaction.Commit; //該行不必須
380420 end;
381421
382422 procedure TForm1.btnNextClick(Sender: TObject);
383423 begin
384424 query.Next;
385425 Display;
386426 end;
387427
388428 initialization
389429 {$I unt_db.lrs}
390430 end.
391431
392432這是使用 TMySQL50Connection,TSQLQuery,TSQLTransaction,TDatasource 與 TDBGrid 元件的另一種片版本,可將他們直接拉到表單上:
393433
394434 unit unt_mysql2;
395435 {$mode objfpc}{$H+}
396436 interface
397437 uses
398438 Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs,
399439 mysql50conn, sqldb, StdCtrls, db, DBGrids, DbCtrls;
400440 type
401441 { TForm1 }
402442 TForm1 = class(TForm)
403443 btnConnect: TButton;
404444 btnSave: TButton;
405445 btnNext: TButton;
406446 btnPrior: TButton;
407447 Datasource1: TDatasource;
408448 DBGrid1: TDBGrid;
409449 Memo1: TMemo;
410450 MySQL50Connection1: TMySQL50Connection;
411451 SQLQuery1: TSQLQuery;
412452 SQLTransaction1: TSQLTransaction;
413453 procedure btnConnectClick(Sender: TObject);
414454 procedure btnNextClick(Sender: TObject);
415455 procedure btnPriorClick(Sender: TObject);
416456 procedure btnSaveClick(Sender: TObject);
417457 procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
418458 procedure FormCreate(Sender: TObject);
419459 private
420460 { private declarations }
421461 public
422462 { public declarations }
423463 end;
424464 var
425465 Form1: TForm1;
426466 implementation
427467 { TForm1 }
428468
429469 procedure TForm1.FormCreate(Sender: TObject);
430470 begin
431471 //設定元件內容:
432472 //(可以從物件檢視器做到)
433473 MySQL50Connection1.HostName := '127.0.0.1';
434474 MySQL50Connection1.UserName := 'root';
435475 MySQL50Connection1.Password := '';
436476 MySQL50Connection1.DatabaseName := 'test1';
437477 MySQL50Connection1.Transaction := SQLTransaction1;
438478 //SQLQuery1.ParseSQL := true; //該行不必須,這是預設值
439479 //SQLQuery1.ReadOnly := false; //該行不必須,這是預設值
440480 SQLQuery1.SQL.Text := 'select * from tperson';
441481 SQLQuery1.Transaction := SQLTransaction1;
442482 SQLQuery1.UpdateMode := upWhereChanged;
443483 Datasource1.Dataset := SQLQuery1;
444484 DBGrid1.DataSource := Datasource1;;
445485 end;
446486
447487 procedure TForm1.btnConnectClick(Sender: TObject);
448488 var
449489 S : string;
450490 begin
451491 try
452492 MySQL50Connection1.Connected := true;
453493 SQLQuery1.Open;
454494
455495 //測試一下是否都 OK:
456496 SQLQuery1.Last;
457497 S := IntToStr(SQLQuery1.RecordCount) + #13#10;
458498 SQLQuery1.First;
459499 while not SQLQuery1.EOF do
460500 begin
461501 S := S + SQLQuery1.FieldByName('surname').AsString + #13#10;
462502 SQLQuery1.Next;
463503 end;
464504 except
465505 on E: Exception do
466506 ShowMessage(E.message);
467507 end;
468508 Memo1.Text:= S;
469509 end;
470510
471511 procedure TForm1.btnNextClick(Sender: TObject);
472512 begin
473513 SQLQuery1.Next;
474514 end;
475515
476516 procedure TForm1.btnPriorClick(Sender: TObject);
477517 begin
478518 SQLQuery1.Prior;
479519 end;
480520
481521 procedure TForm1.btnSaveClick(Sender: TObject);
482522 begin
483523 SQLQuery1.ApplyUpdates;
484524 end;
485525
486526 procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
487527 begin
488528 //必要的,或是在關閉時取得 EDatabase 錯誤:
489529 MySQL50Connection1.Connected := false;
490530 end;
491531
492532 initialization
493533 {$I unt_mysql2.lrs}
494534 end.
495535
496536## Lazarus 和 PostgreSQL
497537
498538這個教學很簡短,告訴大家如何在 Lazarus 0.9.12 或更新的版本下使用 TPQConnection 連線到 PostgreSQL 資料庫,本地端或遠端都行。
499539
500540在正確地安裝後,進行下列步驟:
501541
502542- 從 SQLdb 籤頁裡放入 PQConnection
503543- 從 SQLdb 籤頁裡放入 SQLQuery
504544- 從 SQLdb 籤頁裡放入 SQLTransaction
505545- 從資料存取 (DataAccess) 籤頁裡放入資料來源 (DataSource)
506546- 從資料控制 (DataControl) 籤頁裡放入 DBGrid
507547
508548- 在 PQConnection 裡填入:
509549 - 每個 SQLTransaction 物件的交易內容
510550 - 資料庫名稱
511551 - 主機名稱
512552 - 使用者名稱 + 密碼
513553
514554- 檢查 SQLTransaction 已自動改為指向 PQConnection
515555
516556- 在 SQLQuery 內填入:
517557 - 每個物件的交易內容
518558 - 每個物件的資料庫內容
519559 - SQL (像是 'select * from anytable' 這樣的句子)
520560
521561- 在資料庫來源物件內照著 SQLQuery 物件填入 DataSet 的內容
522562
523563- 在 DBGrid 內依資料庫來源物件填入資料來源
524564
525565將所有的物件都調為「已連線;connected」和「啟動;active」,DBGrid 應該還要填入設定好的時間。 TDBText 和 TDBEdit 看起來已經在運作,但(對我來說)他們只負責顯示資料。
526566
527567要變更資料庫內容,需要使用下列程式碼來呼叫資料庫引擎:
528568
529-` try`\
530-`   sql:= 'UPDATE table SET setting=1';`\
531-`   PQDataBase.Connected:=True;`\
532-`   PQDataBase.ExecuteDirect('開始作業;');`\
533-`   PQDataBase.ExecuteDirect(sql);`\
534-`   PQDataBase.ExecuteDirect('交付作業');`\
535-`   PQDataBase.Connected:=False;`\
536-` except`\
537-`   on E : EDatabaseError do`\
538-`     MemoLog.Append('資料庫錯誤:'+sql+chr(13)+chr(10)+E.ClassName+chr(13)+chr(10)+E.Message);`\
539-`   on E : Exception do`\
540-`     MemoLog.Append('錯誤'+sql+chr(13)+chr(10)+E.ClassName+chr(13)+chr(10)+E.Message);`\
541-` end;`
569+``` pascal
570+ try
571+ sql:= 'UPDATE table SET setting=1';
572+ PQDataBase.Connected:=True;
573+ PQDataBase.ExecuteDirect('開始作業;');
574+ PQDataBase.ExecuteDirect(sql);
575+ PQDataBase.ExecuteDirect('交付作業');
576+ PQDataBase.Connected:=False;
577+ except
578+ on E : EDatabaseError do
579+ MemoLog.Append('資料庫錯誤:'+sql+chr(13)+chr(10)+E.ClassName+chr(13)+chr(10)+E.Message);
580+ on E : Exception do
581+ MemoLog.Append('錯誤'+sql+chr(13)+chr(10)+E.ClassName+chr(13)+chr(10)+E.Message);
582+ end;
583+```
542584
543585- 備註:
544586 - 在 Windows 環境下測試,Lazarus 0.9.12 + PgSQL 8.3.1
545587 - Linux 環境下部份測試,Lazarus 0.9.12 and PgSQL 8.0.x
546588
547589- 安裝與錯誤:
548590 - 在測試版本 Lazarus .12下,欄位型態為「文字」與「數字」時會有程式錯誤
549591 - 在使用固定長度字元,整數與 8 位元浮點數時沒有問題
550592 - 有時候有些愚蠢的錯誤需要你重啟 Lazarus ...
551593 - 某些錯誤發生後,其交易仍在進行,而必須手動去關閉。
552594 - Lazarus 一些更新會讓交易在交付之前都無法看見
553595 - 內建的除錯器看起來也有些錯誤 (至少在 Windows 下時) - 有時候執行到 IDE 之外的會需要協助找尋錯誤
554596 - 在 Linux 下某些錯誤訊息只會出現在主控台下 -- 用主控台模式下指令來執行程式,有時候你會因此而得到一些額外的除錯訊息。
555597 - 錯誤:「無法載入 PostgreSQL 用戶端,libq.so 是否已安裝?」
556598 - 在 PostgreSQL 安裝時加入可以搜尋到 libpq* 的路徑。
557599 - 在 Linux 下,加入 libpq.so 檔案的路徑到你的函式庫 /etc/fpc.cfg 檔案裡,例如:-Fl/usr/local/pgsql/lib
558600 - 在 Windows 下,將這些函式庫路徑加入到環境變數 Path 裡,或是專案的目錄裡
559601 - 在 Windows 下,我將 C:\Program Files\PostgreSQL\8.1\bin 目錄下的 DLL 檔案都複製到另一個 PATH 變數裡的目錄。
560602 - 或是加入 postgres\bin 目錄到路徑裡
561603
562604## Lazarus 與 SQLite
563605
564606by Luiz Americo
565607
566608請至 [sqlite4fpc homepage](http://www.geocities.com/camara_luiz/) 取得 API reference 與更多的教材。
567609
568610### 前言
569611
570612TSqliteDataset 與 TSqlite3Dataset 繼承自 TDataset, 個自分別可存取 2.8.x 與 3.x.x sqlite 資料庫。 以下是使用Sqlite主要的優缺點:
571613
572614優點:
573615
574616- 靈活: 程式設計師可以選擇使用或不使用SQL 語言,允許他們使用SQL/sqlite容許的簡單的表格配置或是任何複雜的配置
575617- 自動更新資料庫: 不需要手動更新資料庫使用SQL 述?, 單一方法來照顧它
576618- 快速: 它的快取資料是放在記憶體, 使的瀏覽資料集是相當的快速
577619- 沒有伺服器的安裝/設定: 只是包裝在sqlite 動態函式庫
578620
579621缺點:
580622
581623- 要求外部的檔案 (sqlite 函式庫)
582624
583625### 需求
584626
585627- sqlite2 資料庫:
586628 - fpc 2.0.0
587629 - Lazarus 0.9.10
588630 - sqlite 執行期函式庫 2.8.15 或以上 (從 www.sqlite.org 獲得)
589631
590632- sqlite3 資料庫:
591633 - fpc 2.0.2
592634 - Lazarus 0.9.11 (svn 修訂版 8443 或以上)
593635 - sqlite 執行期函式庫 3.2.1 或以上 (從 www.sqlite.org 獲得)
594636
595637**開始一個 lazarus 專案前, 請先確認以下事項:**
596638
597639- SQLite 資料庫有在系統路徑 PATH 變數裡,或其它可執行的目錄
598640- 在 Linux 下,將 cmem 第一個放入主程式的 uses 語法內
599641 - 在 Debian,Ubuntu 和其他 Debian-like 發佈版本裡,為了要建置 Lazarus IDE 你得先安裝 libsqlite-dev/libsqlite3-dev 套件,而不僅只有 sqlite/sqlite3 (對 OpenSuSe 也一樣)
600642
601643### 使用方法 (基本)
602644
603645安裝 /components/sqlite 資料夾中的套件 (安裝教材[[Install Packages|在這裡]])
604646
605647於設計模式下設定以下屬性
606648
607649- FileName: sqlite 檔案的路徑 \[必要\]
608650- TableName: sql 語法中使用的表格(table)名稱 \[必要\]
609651- Sql: 一個 SQL 的 select 語法 \[非必要\]
610652
611653**建立一個資料表 (Dataset)**
612654
613655雙按元件圖示, 或點選右鍵選單中的「建立資料表」。便會出現一個簡單的表格編輯器。
614656
615-` 以下是 TSqliteDataset 與 TSqlite3Dataset 所支援的所有欄位格式:`
657+```text
658+ 以下是 TSqliteDataset 與 TSqlite3Dataset 所支援的所有欄位格式:
659+```
616660
617-` Integer`\
618-` AutoInc`\
619-` String`\
620-` Memo`\
621-` Bool `\
622-` Float`\
623-` Word`\
624-` DateTime`\
625-` Date`\
626-` Time`\
627-` LargeInt`\
628-` Currency`\
629-` `
661+```text
662+ Integer
663+ AutoInc
664+ String
665+ Memo
666+ Bool
667+ Float
668+ Word
669+ DateTime
670+ Date
671+ Time
672+ LargeInt
673+ Currency
674+```
630675
631676**取得資料**
632677
633678建立資料表後, 或者在已有的資料表。以 Open 方法開啟 dataset。 如果 SQL 屬性沒有設定內容, 則所有的資料與欄位都會被傳回, 效果與以下指令相同:
634679
635-` SQL:='Select * from TABLENAME'; `
680+``` pascal
681+ SQL:='Select * from TABLENAME';
682+```
636683
637684**為資料檔案套用更新**
638685
639686使用 ApplyUpdates 函式前,dataset 須包含至少一個主鍵欄位填入需求 (必須為唯一且非 NULL 值)
640687
641688這可以由兩種方式來達成:
642689
643690- 設定 PrimaryKey 屬性為主鍵欄位的名稱
644691- 新增一個 AutoInc 欄位(這比較簡單, 因為 TSqliteDataSet 會自行將此欄位當成主鍵)
645692
646693以上兩個條件設定好其中一個後, 只要呼叫以下指令
647694
648-` ApplyUpdates;`
695+```text
696+ ApplyUpdates;
697+```
649698
650699備註一:若兩個條件都有設定, 則欄位會使用 PrimaryKey 來更新資料
651700
652701備註二: 若 PrimaryKey 被指定的欄位不是主鍵, 則呼叫 ApplyUpdates 可能會造成資料的遺失, 所以請確定所選擇的欄位值是唯一且非 NULL 值。
653702
654703### 註記
655704
656705- 雖然此程式已經測試過 10000 筆的資料都運行的很好,TSqliteDataset 將所有的資料都留在記憶體裡,所以請記得只取出你所需要的資料就好 (特別是針對記事欄位)。
657706- 同一個資料檔案 (檔案的內容) 可以負責多個資料表/資料集合
658707- 數個資料集合 (不同的欄位合併後) 可以建立一個暫存的資料表
659708- SQL 允許你用 WHERE 的語法來篩選你要取出的資料,重覆開啟再關閉同一資料集合 (或是呼叫重新擷取資料 - RefetchData - 的方法)。但條件就是欄位的數量要一定
660709- 像使用一些複合的 SQL 敘述句,如 aliases,joins 同時檢視多個資料表 (請記得這麼做就是都分配在同一支資料檔案裡),但在 ApplyUpdates 的情況裡卻行不通。如果有人想要使用這種複合的查詢而又要套用更新到資料檔案,請寄信給我,我會告訴你一些絕竅
661710- 當為不是從 TSqliteDataset 建立的 sqlite2.x 資料檔妹設定檔案名稱時,雖然檔案可以開啟,但有些欄位可能無法正確地偵測到,會被當成是字串欄位。
662711
663712可以在 fpc/fcl/db/sqlite CVS 目錄下找到一個通用的範例
664713
665714Luiz Americo pascalive(at)bol(dot)com(dot)br
666715
667716## Lazarus 與 MSSQL
668717
669718需要啟用 Zeoslib (最新版修訂版),請見此頁下方的連結。
670719
671720## Lazarus and Interbase / Firebird
672721
673722請參看[[Install Packages|安裝套件]]。該頁為解釋如何連線到 IB 或 FB 主機的簡短範例。
674723
675724也可以使用最新的 Zeoslib 的版本 (取自 cvs)。
676725
677726### FBLib Firebird 函式庫
678727
679728\[<http://fblib.altervista.org/%7CFBLib%5D是一個不含資料感知元件的開放原始碼函式庫,使用> Borland Delphi,Freepascal 與 Lazarus 都可以直接對 Firebird 關聯資料庫做存取。
680729
681730現有的特點包括:
682731
683732- 直接對 Firebird 1.0.x 1.5.x Classic 或 SuperServer 版本做存取
684733- 跨平台工作 (Win32,Gnu/Linux,FreeBSD)
685734- 自動選取 'fbclient' 或 'gds32' 用戶端函式庫
686735- 支援參數查詢
687736- 支援 SQL 同源語 1/3
688737- LGPL 認證通過
689738- 解析前置檔頭資料
690739- 單一程式腳本解析
691740- EXE 末端僅只使用 100-150 KB
692741- 支援 BLOB 欄位
693742- SQL 腳本直接支援資料與 HTML 轉換
694743- 伺服器管理 (備份,還原,gfix...)
695744- 事件警告器
696745
697746你可以從 [FBLib's website](http://fblib.altervista.org/) 下載文件。
698747
699748## Lazarus and dBase
700749
701750Tony Maro
702751
703752You might also want to visit the beginnings of the [[Lazarus Tdbf Tutorial|TDbf Tutorial page]]
704753
705754FPC includes a simple database component that is similar in function to the Delphi TTable component called "TDbf" ([TDbf Website](http://tdbf.sourceforge.net/)) 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).
706755
707756The 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.
708757
709758It'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.
710759
711760Real 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.
712761
713762Attempting 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.
714763
715764For 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".
716765
717766In your code, insert the following:
718767
719-`   Dbf1.FilePathFull := '/path/to/my/database';`\
720-`   Dbf1.TableName := 'dvds';`\
721-`   With Dbf1.FieldDefs do begin`\
722-`       Add('Name', ftString, 80, True);`\
723-`       Add('Description', ftMemo, 0, False);`\
724-`       Add('Rating', ftString, 5, False);`\
725-`   end;`\
726-`   Dbf1.CreateTable;`
768+``` pascal
769+ Dbf1.FilePathFull := '/path/to/my/database';
770+ Dbf1.TableName := 'dvds';
771+ With Dbf1.FieldDefs do begin
772+ Add('Name', ftString, 80, True);
773+ Add('Description', ftMemo, 0, False);
774+ Add('Rating', ftString, 5, False);
775+ end;
776+ Dbf1.CreateTable;
777+```
727778
728779When 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.
729780
730781Adding 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:
731782
732-`   Dbf1.Exclusive := True;`\
733-`   Dbf1.Open;`\
734-`   Dbf1.AddIndex('dvdsname','Name',[ixPrimary, ixUnique, ixCaseInsensitive]);`\
735-`   Dbf1.AddIndex('rating.ndx', 'Rating', [ixCaseInsensitive]);`\
736-`   Dbf1.Close;`
783+``` pascal
784+ Dbf1.Exclusive := True;
785+ Dbf1.Open;
786+ Dbf1.AddIndex('dvdsname','Name',[ixPrimary, ixUnique, ixCaseInsensitive]);
787+ Dbf1.AddIndex('rating.ndx', 'Rating', [ixCaseInsensitive]);
788+ Dbf1.Close;
789+```
737790
738791The 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.
739792
740793I 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!
741794
742795### Searching and Displaying a data set
743796
744797Simon Batty
745798
746799In 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
747800
748-`   Dbf1.FilePathFull := '/home/somelocatio/database_location/'; // path to the database directory`\
749-`   Dbf1.TableName := 'books.dbase';                             // database file (including extension)`\
750-`   DbF1.Open;`\
751-`   memo1.Clear;                                                 // clear the memo box`
801+``` pascal
802+ Dbf1.FilePathFull := '/home/somelocatio/database_location/'; // path to the database directory
803+ Dbf1.TableName := 'books.dbase'; // database file (including extension)
804+ DbF1.Open;
805+ memo1.Clear; // clear the memo box
806+```
752807
753-`   Dbf1.FilterOptions := [foCaseInsensitive];`\
754-`   Df1.Filter := 'AU=' + QuotedStr('anauthor');         // AU is the field name containing the authors`
808+``` pascal
809+ Dbf1.FilterOptions := [foCaseInsensitive];
810+ Df1.Filter := 'AU=' + QuotedStr('anauthor'); // AU is the field name containing the authors
811+```
755812
756-`   Dbf1.Filtered := true;       // This selects the filtered set`\
757-`   Dbf1.First;                  // moves the the first filtered data`\
758-`   while not dbf1.EOF do        // prints the titles that match the author to the memo box`\
759-`   begin`\
760-`       memo1.Append(Dbf1.FieldByName('TI').AsString); // TI is the field name for titles`\
761-`       dbf1.next;                                     // use .next here NOT .findnext!`\
762-`   end;`\
763-`   Dbf1.Close;   `
813+``` pascal
814+ Dbf1.Filtered := true; // This selects the filtered set
815+ Dbf1.First; // moves the the first filtered data
816+ while not dbf1.EOF do // prints the titles that match the author to the memo box
817+ begin
818+ memo1.Append(Dbf1.FieldByName('TI').AsString); // TI is the field name for titles
819+ dbf1.next; // use .next here NOT .findnext!
820+ end;
821+ Dbf1.Close;
822+```
764823
765824Note 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.
766825
767826This 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.
768827
769828## Using TSdfDataset and TFixedDataset
770829
771830TSdfDataset 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.
772831
773832To start with this formats, a initial database file should be created. The format is very simple, so use a text editor to do this.
774833
775834Bellow 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:
776835
777836 ID,NAMEEN,NAMEPT,HEIGHT,WIDTH,PINS,DRAWINGCODE
778837 1,resistor,resistor,1,1,1,LINE
779838 2,capacitor,capacitor,1,1,1,LINE
780839 3,transistor npn,transistor npn
781840
782841And 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.
783842
784843 Name = 15 chars; Surname = 15 chars; Tell = 10 chars; e_mail = 20 chars;
785844 Piet Pompies piet@pompies.net
786845
787846### Using the datasets directly
788847
789848Sometimes 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:
790849
791850- The lines in the database can have a maximum size of about 300. A fix is being researched.
792851- It is necessary to add the field definitions. Some datasets are able to fill this information alone from the database file
793852- One should set FirstLineAsSchema to true, to indicate that the first line includes the field names and positions
794853- 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.
795854
796855``` pascal
797856constructor TComponentsDatabase.Create;
798857begin
799858 inherited Create;
800859
801860 FDataset := TSdfDataset.Create(nil);
802861 FDataset.FileName := vConfigurations.ComponentsDBFile;
803862
804863 // Not necessary with TSdfDataset
805864// FDataset.TableName := STR_DB_COMPONENTS_TABLE;
806865// FDataset.PrimaryKey := STR_DB_COMPONENTS_ID;
807866
808867 // Adds field definitions
809868 FDataset.FieldDefs.Add('ID', ftString);
810869 FDataset.FieldDefs.Add('NAMEEN', ftString);
811870 FDataset.FieldDefs.Add('NAMEPT', ftString);
812871 FDataset.FieldDefs.Add('HEIGHT', ftString);
813872 FDataset.FieldDefs.Add('WIDTH', ftString);
814873 FDataset.FieldDefs.Add('PINS', ftString);
815874 FDataset.FieldDefs.Add('DRAWINGCODE', ftString);
816875
817876 // Necessary for TSdfDataset
818877 FDataset.Delimiter := ',';
819878 FDataset.FirstLineAsSchema := True;
820879
821880 FDataset.Active := True;
822881
823882 // Sets the initial record
824883 CurrentRecNo := 1;
825884 FDataset.First;
826885end;
827886```
828887
829888When 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
830889
831890``` pascal
832891{@@
833892 Moves to the desired record using TDataset.Next and TDataset.Prior
834893 This avoids using TDataset.RecNo which doesn't navigate reliably in any dataset.
835894
836895 @param AID Indicates the record number. The first record has number 1
837896}
838897procedure TComponentsDatabase.GoToRec(AID: Integer);
839898begin
840899 // We are before the desired record, move forward
841900 if CurrentRecNo < AID then
842901 begin
843902 while (not FDataset.EOF) and (CurrentRecNo < AID) do
844903 begin
845904 FDataset.Next;
846905 FDataset.CursorPosChanged;
847906 Inc(CurrentRecNo);
848907 end;
849908 end
850909 // We are after the desired record, move back
851910 else if CurrentRecNo > AID then
852911 begin
853912 while (CurrentRecNo >= 1) and (CurrentRecNo > AID) do
854913 begin
855914 FDataset.Prior;
856915 FDataset.CursorPosChanged;
857916 Dec(CurrentRecNo);
858917 end;
859918 end;
860919end;
861920```
862921
863922### 使用資料感知控制元件
864923
865924## 相關連結
866925
867926- [[Databases|在 Lazarus 下利用 DB-unit 建立資料庫應用程式]]\
868927- [[MySQLDatabases|使用 MySQL 建立資料庫應用程式]]\
869928- [[Zeos tutorial|使用 Zeos 元件建立資料庫應用程式]]
870929- [Pascal 資料物件集](http://pdo.sourceforge.net) 是為一資料庫 API,可在 FPC 下與 Delphi 下配合 MySQL 原始函式庫版 4.1 與 5.0,Firebird SQL 1.5 與 2.0 使用。這是來自 PHP PDO 類別的啟發。
871930
872931## 貢獻與變更
873932
874933本頁是從 epikwiki [version](http://lazarus-ccr.sourceforge.net/index.php?wiki=LazarusDatabase) 轉換過來的。
875934
876935---
877936*From the Free Pascal wiki, page "Lazarus Database Tutorial/zh TW", as it stood on 2020-02-18 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history