Low Level Database Access
- Stendhal
本文介紹了Marauroa內部如何訪問數據庫以及如何添加自己的表. Marauroa數據庫的表結構請參閱 Marauroa Database Structure. 你也許需要先看一下 High Level Database Access . 它說明了從程序代碼訪問數據庫的高級API.
數據庫抽象[編輯]
低級數據庫訪問代碼封裝在DAO類中。 只有這些類使用JDBC將SQL查詢發送到數據庫.
通過以下位置獲取DBTransaction對象來啟動事務 TransactionPool. 然後使用 DBTransaction 對象的方法與數據庫交談. 請注意,許多方法都希望將參數映射作為第二個參數. 這使您可以在SQL語句中用 [variables] 無需擔心轉義輸入參數以防止SQL注入攻擊.
但是,DBTransaction本身不執行SQL語句。 它在內部將它們轉發到DatabaseAdapter的子類。 這是一個非常小的抽象層,用於隱藏不同數據庫系統使用的SQL方言.
編寫你自己的 DAO[編輯]
編寫自己的DAO類時,請遵循以下規則
- 使用 [variables] 傳送不受信任的數據,因為它將被自動轉義以防止SQL注入攻擊
- 在DAORegistry (see next section) 中註冊你的類,而不是直接調用構造函數
- 使用 dbTransaction.getLastInsertId() 而不是 "select @@identity" 並在相關插入內容後直接調用
- 嘗試避免使用數據庫系統特定的代碼(特別注意通常支持的SQL函數)
- 為所有方法提供兩個簽名: 一個以DBTransaction作為第一個參數,另一個沒有。 第二個應該只是獲取事務本身,調用第一個,然後提交/回滾事務
擴展已有的 DAO[編輯]
DAO類永遠不能直接實例化。 相反,您應該(並且marauroa確實)使用DAORegistry。 這使您可以編寫marauroa提供的DAO的子類並進行註冊。 如果您熟悉Spring,這是一個類似的概念。 但是沒有所有大量的xml配置文件,參數注入和接口,只有一個實現.
假設您想用SomeGameCharacterDAO類將CharacterDAO子類化:
public class SomeGameCharacterDAO extends CharacterDAO {
...
您只需要簡單地註冊為
DAORegistry.get().register(CharacterDAO.class, new SomeGameCharacterDAO());
注意:在註冊調用中,第一個參數是您要替換的父類.
Adding support for another database system[編輯]
Marauroa嘗試嚴格遵循SQL標準。 但是,不可能編寫適用於所有通用數據庫系統的SQL語句,因為每個數據庫系統都有自己的方言。 幸運的是只有微小的差異. 例如 MySQL 要求 "create table" 語句以 "TYPE=InnoDB" 結束. Marauroa 使用 DatabaseAdapter 接口在隱藏這些差異.
已經為此接口提供了一個默認實現,即 AbstractDatabaseAdapter (source). And we provide a MySQLDatabaseAdapter (source) and a H2DatabaseAdapter (source), too.
請查閱這些java文件。 為其他數據庫系統添加更多適配器應該非常容易。 注意:我們對接受這些適配器並將其添加到主要代碼庫非常感興趣。
更新數據庫結構[編輯]
如果添加表和列,則最好在伺服器啟動時自動創建它們。 JDBCHelper類中的runSQLScript()方法可用於執行SQL腳本。 注意:應該使用「create table if not exists」代替簡單的「create table」,以便可以在每個伺服器啟動時執行腳本,而不必擔心表是否已經存在.
不幸的是,SQL中沒有「如果不存在則創建列」子句。 因此,如果需要將列添加到現有表中,則需要自己進行檢查.
以下示例顯示了用於將新結果列添加到passwordChange表的代碼。 在添加該列之前,僅記錄成功的密碼更改。 因此,結果列應初始化為1.
if (!transaction.doesColumnExist("passwordChange", "result")) {
transaction.execute("ALTER TABLE passwordChange ADD COLUMN (result TINYINT);", null);
transaction.execute("UPDATE passwordChange SET result=1 WHERE result IS NULL", null);
}