Low Level Database Access

来自gamedev
115.192.141.63留言2020年4月27日 (一) 15:49的版本
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)
跳转到导航 跳转到搜索



Database
Marauroa
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);
}

{{#breadcrumbs: Marauroa | Internals | 低级数据库访问 }}