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;
}
代码封装得很简易,有更多需求可据此改进。


猜你喜欢
- CSV 是一种简单的数据格式,通常为电子表格软件所使用。 它主要是由一系列的表格行组成,每行中单元格之间使用逗号(CSV 是 逗号分隔数值(
- 本文介绍了详解vue 模版组件的三种用法,分享给大家,具体如下:第一种//首先,别忘了引入vue.js<div id="us
- 一、form介绍我们之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来。与此同
- 案例故事:大部分带彩色屏幕的终端设备,不管是手机,车机,电视等等,都需要涉及图片的显示,作为一名专业的多媒体测试人员,我们需要一堆的规范化标
- 前言mysql默认安装的时候是有一个root用户,这个用户有最高权限,如果要开放数据库里面的数据给别人使用的话,一般是不会开放root用户给
- breakbreak可以用来立即退出循环语句(包括else)continuecontinue可以用来跳过当次循环注意:break和conti
- 本文实例讲述了Python实现的旋转数组功能算法。分享给大家供大家参考,具体如下:一、题目给定一个数组,将数组中的元素向右移动 k 个位置,
- 什么是双端队列双端队列是与队列类似的有序集合。它有一前、一后两端,元素在其中保持自己的位置。与队列不同的是,双端队列对在哪一端添加和移除元素
- 本文为大家分享了MySQL预编译功能,供大家参考,具体内容如下1、预编译的好处大家平时都使用过JDBC中的PreparedStatement
- 神经网络只是由两个或多个线性网络层叠加,并不能学到新的东西,简单地堆叠网络层,不经过非线性激活函数激活,学到的仍然是线性关系。但是加入激活函
- 实例如下:function getQueStr(url, ref) //取获参数值{ var str = url.substr(
- 昨天,系统管理员告诉我,我们一个内部应用数据库所在的磁盘空间不足了。我注意到数据库事件日志文件XXX_Data.ldf文件已经增长到了3GB
- 区别与联系: 1、get是从服务器上获取数据,post则是向服务器传送数据; 2、get将表单中数据的按照variable=value的 形
- 一、发送电子邮件Python标准库提供了smtplib,用于实现SMTP协议发送邮件。标准库还提供email模块帮助我们构建邮件格式。SMT
- RIFF file formatRIFF全称为资源互换文件格式(Resources Interchange File Format),是Wi
- <% '测试读取MySql数据库的内容strconnection="driver={mysql odbc 3.51 dri
- 前言在日常开发中 动画是必不可少的一部分 不仅能让元素直接的切换显得更加自然 同时也能极大的增强用户体验 因此 在Vue之中也提供了非常强大
- 最近帮伙计做了一个从网页抓取股票信息并把相应信息存入MySQL中的程序。使用环境:Python 2.5 for WindowsMySQLdb
- pandas是什么?是它吗?。。。。很显然pandas没有这个家伙那么可爱。。。。我们来看看pandas的官网是怎么来定义自己的:panda
- 分享几个字体设计:1.巧克力2.绿野邂逅是人间四月天3.去年花开的时候4.仲夏梦之夜5.芊芊茉莉香6.玫瑰人生7.真情告白8.遮掩锋芒