Java游戏服务器之数据库表存取封装

作者:Metazion 时间:2024-01-13 13:08:17 

项目涉及的数据库表并不多,但每个select、insert、update和delete都去手动拼接字符串,是很低效的,尤其在时常要修改结构的情况下。开发的一个目标就是自动化,即能自动实现的事情就不要手动去做;还有一个原则是单一化,即尽量保证数据或逻辑一个入口一个出口。这个需求可以使用一些开源库解决,但因为需求简单,目标明确,没有必要引入多余的第三方库。于是自己写了一个,至少满足当前需求。

数据库表的封装,核心类有两个,表(Table)和记录(Record)。首先需要一个Table类保存数据库表结构的描述,并籍此自动生成相应SQL语句。其次需要一个Record类自动设置SQL参数,并从返回结果集中自动生成逻辑对象。

table类表结构描述可以有两个来源,自动从数据库获取,或从配置表加载。这里选择从配置表加载的方式,一来实现简单,二来应用面更广。

下面是一个账户表的配置示例(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对应逻辑对象的成员变量名,type对应字段的类型,比如是int、string、timestamp等,有了名字和类型,就可以使用反射方式自动get和set数据。

Table类读取配置文件获得数据表的结构描述。


public class Table<T> {
 public class TableField {
   public static final int TYPE_INTEGER = 1;
   public static final int TYPE_STRING = 2;
   public static final int TYPE_TIMESTAMP = 3;
   public String columnName = "";
   public String fieldName = "";
   public int type = 0;
 }
 private String tableName = "";
 private TableField primaryField = new TableField();
 private ArrayList<TableField> tableFields = new ArrayList<TableField>();
 private String selectAllSql = "";
 private String selectSql = "";
 private String insertSql = "";
 private String updateSql = "";
 private String deleteSql = "";
 ...

然后生成PrepareStatement方式读写的select、insert、update和delete的预处理SQL字符串。如update:


private String generateUpdateSql() {
   String sql = "UPDATE " + tableName + " SET ";
   int size = tableFields.size();
   for (int index = 0; index < size; ++index) {
     TableField tableField = tableFields.get(index);
     String conjunction = index == 0 ? "" : ",";
     String colSql = tableField.columnName + " = ?";
     sql = sql + conjunction + colSql;
   }

sql = sql + " WHERE " + primaryField.columnName + "=?";
   return sql;
 }

Table类的功能就这么多,下面是关键的Record类,其使用反射自动存取数据。


public class Record<T> {
 private Table<T> table = null;
 private T object = null;
 ...

模板参数T即一个表记录对应的逻辑对象。在我们的示例里,即账户数据类:


public class UserData implements Serializable {
 // 用户ID
 public int userId = 0;
 // 用户名
 public String username = "";
 // 密码
 public String password = "";
 ...

有了SQL语句,要先设置参数,才能执行。主键和普通字段分开设置。


public int setPrimaryParams(int start, PreparedStatement pst) throws Exception {
   Table<T>.TableField primaryField = table.getPrimaryField();
   Object value = getFieldValue(primaryField);
   value = toDBValue(primaryField, value);
   pst.setObject(start, value);
   return start + 1;
 }
 public int setNormalParams(int start, PreparedStatement pst) throws Exception {
   ArrayList<Table<T>.TableField> normalFields = table.getNoramlFields();
   final int size = normalFields.size();
   for (int index = 0; index < size; ++index) {
     Table<T>.TableField tableField = normalFields.get(index);
     Object value = getFieldValue(tableField);
     value = toDBValue(tableField, value);
     pst.setObject(start + index, value);
   }
   return start + size;
 }

就是根据表结构描述,通过反射获取对应字段的值然后设置。


private Object getFieldValue(Table<T>.TableField tableField) throws Exception {
   Field field = object.getClass().getDeclaredField(tableField.fieldName);
   return field.get(object);
 }

toDBValue作用是将Java逻辑类型转成对应数据库类型,比如时间,在逻辑里是Long,而数据库类型是Timestamp。


private Object toDBValue(Table<T>.TableField tableField, Object value) {
   if (tableField.type == TableField.TYPE_TIMESTAMP) {
     value = new Timestamp((long) value);
   }
   return value;
 }

以设置update SQL参数为例:


public void setUpdateParams(PreparedStatement pst) throws Exception {
   final int start = setNormalParams(1, pst);
   setPrimaryParams(start, pst);
 }

之后执行该SQL语句就可以了。如果是select语句还会返回结果集(ResultSet),从结果集自动生成逻辑对象原理类似,算是一个逆过程,详细参看文末代码。

下面给出一个使用的完整示例:


private static final Table<UserData> udTable = new Table<UserData>();
...
udTable.load("user.xml");
...
public static boolean updateUserData(UserData userData) {
   boolean result = false;
   Record<UserData> record = udTable.createRecord();
   record.setObject(userData);
   PreparedStatement pst = null;
   try {
     String sql = udTable.getUpdateSql();
     pst = DbUtil.openConnection().prepareStatement(sql);
     record.setUpdateParams(pst);
     result = pst.executeUpdate() > 0;
   } catch (Exception e) {
     e.printStackTrace();
   } finally {
     DbUtil.closeConnection(null, pst);
   }
   return result;
 }

代码封装得很简易,有更多需求可据此改进。

标签:java,数据库,封装
0
投稿

猜你喜欢

  • python 列表,数组和矩阵sum的用法及区别介绍

    2022-12-16 09:01:47
  • Vue父子传递实例讲解

    2023-07-02 17:03:29
  • SQL Server利用sp_spaceused如何查看表记录存在不准确的情况

    2024-01-20 07:40:10
  • mysql5.6.8源码安装过程

    2024-01-22 02:53:41
  • 求任意自然数内的素数

    2009-10-15 12:21:00
  • 用js实现放大镜效果

    2023-09-19 18:29:29
  • golang实现跨域访问的方法

    2024-02-15 18:33:31
  • ASP调用数据库常见错误的解决

    2007-09-07 10:05:00
  • 思考如何提高交互设计水平?

    2009-12-08 12:18:00
  • Javascript操作cookie的类

    2007-08-23 09:36:00
  • PHP使用ffmpeg给视频增加字幕显示的方法

    2024-05-03 15:53:38
  • 如何解决tensorflow恢复模型的特定值时出错

    2023-12-22 14:59:36
  • Golang+Vue轻松构建Web应用的方法步骤

    2024-05-29 22:06:42
  • Jupyter notebook快速入门教程(推荐)

    2021-02-13 13:53:57
  • Python xlrd读取excel日期类型的2种方法

    2021-06-28 02:12:53
  • sql server 2005用户权限设置深入分析

    2024-01-19 12:08:21
  • Python区块链客户端类开发教程

    2023-06-18 03:05:51
  • python enumerate函数的使用方法总结

    2022-07-06 05:37:10
  • Python中的exec、eval使用实例

    2022-07-05 21:01:41
  • mysql存数组的实例代码和方法

    2024-01-23 22:45:21
  • asp之家 网络编程 m.aspxhome.com