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

王朝学院·作者佚名  2016-08-27
窄屏简体版  字體: 小  |  中  |  大  |  超大  

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

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

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

下面是一个账户表的配置示例(user.xml)。

<Tablename="user"PRimaryKey="user_id"primaryField="userId"><Columnname="username"field="username"type="2"/><Columnname="passWord"field="password"type="2"/><Columnname="salt"field="salt"type="1"/><Columnname="reg_time"field="registerTime"type="3"/><Columnname="last_login_time"field="lastLoginTime"type="3"/></Table>

只定义了一个主键,有需要可对此扩充。每列name对应数据库表的列名,field对应逻辑对象的成员变量名,type对应字段的类型,比如是int、string、timestamp等,有了名字和类型,就可以使用反射方式自动get和set数据。

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

publicclassTable<T> {publicclassTableField {publicstaticfinalintTYPE_INTEGER =1;publicstaticfinalintTYPE_STRING =2;publicstaticfinalintTYPE_TIMESTAMP =3;publicString columnName ="";publicString fieldName ="";publicinttype =0;

}privateString tableName ="";privateTableField primaryField =newTableField();privateArrayList<TableField> tableFields =newArrayList<TableField>();privateString selectAllSql ="";privateString selectSql ="";privateString insertSql ="";privateString updateSql ="";privateString deleteSql ="";

...

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

privateStringgenerateUpdateSql() {

String sql ="UPDATE "+ tableName +" SET ";intsize = tableFields.size();for(intindex =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+"=?";returnsql;

}

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

publicclassRecord<T> {privateTable<T> table =null;privateT object =null;

...

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

publicclassUserDataimplementsSerializable {// 用户IDpublicintuserId =0;// 用户名publicString username ="";// 密码publicString password ="";

...

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

publicintsetPrimaryParams(intstart, PreparedStatement pst)throwsException {

Table<T>.TableFieldprimaryField = table.getPrimaryField();

Object value =getFieldValue(primaryField);

value =toDBValue(primaryField, value);

pst.setObject(start, value);returnstart +1;

}publicintsetNormalParams(intstart, PreparedStatement pst)throwsException {

ArrayList<Table<T>.TableField> normalFields = table.getNoramlFields();finalintsize = normalFields.size();for(intindex =0; index < size; ++index) {

Table<T>.TableFieldtableField = normalFields.get(index);

Object value =getFieldValue(tableField);

value =toDBValue(tableField, value);

pst.setObject(start + index, value);

}returnstart + size;

}

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

privateObjectgetFieldValue(Table<T>.TableFieldtableField)throwsException {

Field field = object.getClass().getDeclaredField(tableField.fieldName);returnfield.get(object);

}

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

privateObjecttoDBValue(Table<T>.TableFieldtableField, Object value) {if(tableField.type== TableField.TYPE_TIMESTAMP) {

value =newTimestamp((long) value);

}returnvalue;

}

以设置update SQL参数为例:

publicvoidsetUpdateParams(PreparedStatement pst)throwsException {finalintstart =setNormalParams(1, pst);setPrimaryParams(start, pst);

}

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

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

privatestaticfinalTable<UserData> udTable =newTable<UserData>();

...

udTable.load("user.xml");

...publicstaticbooleanupdateUserData(UserData userData) {booleanresult =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);

}returnresult;

}

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

公共库仓库:JMetazion

服务器示例仓库:JGameDemo

服务器架构QQ交流群:330459037

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航