首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 遊戲伺服器資料庫表存取封裝:Table/Record 反射自動化方案

     編輯:Java 相關知識

設計動機

遊戲伺服器資料表雖不多,但手寫 SQL 拼接維護成本高、易出錯。目標:自動化(減少手工勞動)與 單一化(資料/邏輯單一入口出口),且不引入重型 ORM 框架。

核心架構:Table + Record 雙類設計

1. XML 結構描述(user.xml)

<Table name="user" primaryKey="user_id" primaryField="userId">
  <Column name="username" field="username" type="2" />
  <Column name="password" field="password" type="2" />
  <Column name="salt" field="salt" type="1" />
  <Column name="reg_time" field="registerTime" type="3" />
  <Column name="last_login_time" field="lastLoginTime" type="3" />
</Table>
  • name:資料庫欄位名
  • field:Java 物件屬性名
  • type:1=int、2=String、3=Timestamp

2. Table 類:載入結構、生成 SQL

public class Table<T> {
    public static class TableField {
        public static final int TYPE_INT = 1, TYPE_STRING = 2, TYPE_TIMESTAMP = 3;
        public String columnName, fieldName;
        public int type;
    }

    private String tableName;
    private TableField primaryField;
    private List<TableField> fields = new ArrayList<>();
    private String selectSql, insertSql, updateSql, deleteSql;

    public void load(String xmlPath) { /* DOM 解析 XML 填充上述欄位 */ }

    private String generateUpdateSql() {
        StringBuilder sql = new StringBuilder("UPDATE ").append(tableName).append(" SET ");
        for (int i = 0; i < fields.size(); i++) {
            if (i > 0) sql.append(",");
            sql.append(fields.get(i).columnName).append(" = ?");
        }
        sql.append(" WHERE ").append(primaryField.columnName).append(" = ?");
        return sql.toString();
    }
    // 同理生成 select/insert/delete SQL
}

3. Record 類:反射自動綁定參數與映射結果集

public class Record<T> {
    private Table<T> table;
    private T object; // 業務物件,如 UserData

    public void setUpdateParams(PreparedStatement pst) throws Exception {
        int idx = setNormalParams(1, pst);      // 普通欄位
        setPrimaryParams(idx, pst);             // 主鍵
    }

    private int setNormalParams(int start, PreparedStatement pst) throws Exception {
        for (int i = 0; i < table.getFields().size(); i++) {
            TableField f = table.getFields().get(i);
            Object val = getFieldValue(f);
            val = toDBValue(f, val);            // 型別轉換:Long → Timestamp
            pst.setObject(start + i, val);
        }
        return start + table.getFields().size();
    }

    private Object getFieldValue(TableField f) throws Exception {
        Field field = object.getClass().getDeclaredField(f.fieldName);
        field.setAccessible(true);
        return field.get(object);
    }

    private Object toDBValue(TableField f, Object val) {
        if (f.type == TableField.TYPE_TIMESTAMP && val instanceof Long) {
            return new Timestamp((Long) val);
        }
        return val;
    }
}

4. 業務物件範例

public class UserData implements Serializable {
    public int userId;
    public String username;
    public String password;
    public String salt;
    public long registerTime;     // 對應 Timestamp
    public long lastLoginTime;
}

完整使用範例

// 單例 Table 實例
private static final Table<UserData> USER_TABLE = new Table<>();
static { USER_TABLE.load("user.xml"); }

public static boolean updateUser(UserData user) {
    try (Connection conn = DbUtil.getConnection();
         PreparedStatement pst = conn.prepareStatement(USER_TABLE.getUpdateSql())) {

        Record<UserData> record = USER_TABLE.createRecord();
        record.setObject(user);
        record.setUpdateParams(pst);

        return pst.executeUpdate() > 0;
    } catch (Exception e) {
        log.error("更新失敗", e);
        return false;
    }
}

擴展建議

  • 批次操作:新增 batchInsert(List<T>) 使用 addBatch()
  • 條件查詢:擴展 selectByCondition(Map<String, Object>) 動態構建 WHERE
  • 快取層:整合 Redis 快取熱門資料,Table 增加快取策略配置
  • 遷移工具:結合 Flyway/Liquibase 管理 XML 結構版本演進

總結

此輕量封裝以 XML 描述結構 + 反射自動映射 為核心,約 200 行代碼解決 80% CRUD 需求,無第三方依賴、編譯期無錯誤、運行期靈活擴展,極適合中小型遊戲伺服器快速迭代開發。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved