结合java的反射和泛型性质简化JDBC和相应的同步等服务器数据库操作代码
github地址:https://github.com/hzphzp/HeartTrace_Server
我们的服务器端数据库并没有用sqllite, 而是直接用mysql,并且用JDBC直接进行操作,故会出现比较多的sql底层代码,而且我们的数据库中一共有7张表, 如果每一个表都写对应的增加,删除, 更新和查询等操作的话,会有很庞大的代码量。
同时,在我们的设计中,服务器端的数据库功能比较单一,主要是配合同步方案进行设计的。
故,我这里选择使用Java的泛型和反射的机制来进行数据库操作和数据的同步:
数据库操作首先需要Sql语句,这里如果sql语句手写的话,就无法实现所有的表统一操作的目的了,故每一种操作的sql语句, 应该有自己生成的方法, 如下为Update类中的createSql方法代码:
private static String createSql(Class<?> cls, boolean delete){
String str = "";
String sql;
int index = 1;
for(Field field : cls.getDeclaredFields()){
if(index == 1){
str += "username=?, ";
index++;
continue;
}
str += field.getName() + "=?, ";
index++;
}
str = str.substring(0, str.length()-2);
if(!delete) {
sql = "UPDATE " + cls.getSimpleName() + " SET " + str + " WHERE username = ? AND id = ?";
}
else {
sql = "UPDATE " + cls.getSimpleName() + "_delete" + " SET " + str + " WHERE username = ? AND id = ?";
}
return sql;
}
有了sql语句之后, 就是往语句里面的‘?’ 里面填坑了。
这里遇到一个问题,gson将本地传输来的json解析后, Diary里面的一些外键是嵌套形式的,但是服务器端的数据库中只有响应的表的id。
这样填充的时候会出现类型错误。
为了保证操作的统一性, 我在会出现异常的语句中加入了异常的处理,如果有就进行取id操作:
try {
pstm.setObject(index, obj);
}catch(notserializableexception nse){
field = obj.getClass().getField("id");
int id = field.getInt(obj);
pstm.setObject(index, id);
}
所以完整的填坑方法是:
package Db; import Json.dbJson.Diary; import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement; public class Update { public static<T> boolean update(DatabaseAdapter adapter, T cls, String username, boolean delete){
boolean flag = false;
try{
PreparedStatement pstm = adapter.connection.prepareStatement(createSql(cls.getClass(), delete));
int index = 1;
for(Field field : cls.getClass().getDeclaredFields()){
if(index == 1){
pstm.setString(index, username);
index++;
continue;
}
field.setAccessible(true);
Object obj = field.get(cls);
setObject(pstm, index, obj);
index++;
}
pstm.setString(index, username);
pstm.setObject(index+1, cls.getClass().getField("id").get(cls));
int result = pstm.executeUpdate();
flag = result > 0;
}catch(SQLException se){
// 处理 JDBC 错误
se.printStackTrace();
}catch(Exception e){
// 处理 Class.forName 错误
e.printStackTrace();
}finally {
return flag;
}
} static void setObject(PreparedStatement pstm, int index, Object obj) throws NoSuchFieldException, IllegalAccessException, SQLException {
Field field;
try {
pstm.setObject(index, obj);
}catch(Exception nse){
field = obj.getClass().getField("id");
int id = field.getInt(obj);
pstm.setObject(index, id);
}
} private static String createSql(Class<?> cls, boolean delete){
String str = "";
String sql;
int index = 1;
for(Field field : cls.getDeclaredFields()){
if(index == 1){
str += "username=?, ";
index++;
continue;
}
str += field.getName() + "=?, ";
index++;
}
str = str.substring(0, str.length()-2);
if(!delete) {
sql = "UPDATE " + cls.getSimpleName() + " SET " + str + " WHERE username = ? AND id = ?";
}
else {
sql = "UPDATE " + cls.getSimpleName() + "_delete" + " SET " + str + " WHERE username = ? AND id = ?";
}
return sql;
}
}
响应的,其他几种操作各自写一个类:
接下来就是同步了,具体的同步方案,写在下一个博客中:
Gson gson = new Gson();
//java.lang.reflect.Type classType = new TypeToken<Json.Sync>() {}.getType();
//Json.Sync sync = gson.fromJson(content, classType);
Json.Sync sync = gson.fromJson(content, Json.Sync.class);
try{
for(Field field : sync.getClass().getDeclaredFields()){
field.setAccessible(true);
Object obj = field.get(sync);
Type t = field.getGenericType();
if(t instanceof ParameterizedType){
ParameterizedType pt = (ParameterizedType) t;
Class clz = (Class) pt.getActualTypeArguments()[0];//List里面的示例的类型
Class clazz = obj.getClass();//List这个类型
Field anchorField = clz.getField("anchor");
Field statusField = clz.getField("status");
Field idField = clz.getField("id");
Method sizeMethod = clazz.getDeclaredMethod("size");
Method getMethod = clazz.getDeclaredMethod("get", int.class);
getMethod.setAccessible(true);
Method addMethod = clazz.getDeclaredMethod("add", Object.class);
addMethod.setAccessible(true);
int size = (Integer) sizeMethod.invoke(obj);
for(int i = 0; i < size; i++){
Object pattern = getMethod.invoke(obj, i);
if(0 == statusField.getInt(pattern)){
//新增的
anchorField.set(pattern, (new Date()).getTime());
Insert.insert(adapter, pattern, username, false);
statusField.set(pattern, 9);
addMethod.invoke(field.get(syncback), pattern);
}
else {
//不是新增的
Object patternInServer = Search.search(adapter, clz, username, idField.getInt(pattern), false);
if(patternInServer == null){
//没找到,在垃圾箱中找找
patternInServer = Search.search(adapter, clz, username, idField.getInt(pattern), true);
}
if(patternInServer == null){
//在服务器数据库和垃圾箱中都没找到,一定是客户端的代码写错了
anchorField.set(pattern, (new Date()).getTime());
statusField.set(pattern, -1);
addMethod.invoke(field.get(syncback), pattern);
continue;
}
if(anchorField.get(pattern).equals(anchorField.get(patternInServer))) {
//两个数据之前已经同步好了, 直接利用status进行更新不会发生冲突
if(statusField.getInt(pattern) == -1){
anchorField.set(pattern, (new Date()).getTime());
anchorField.set(patternInServer, anchorField.get(pattern));
Delete.delete(adapter, clz, username, idField.getInt(pattern), false);
Insert.insert(adapter, patternInServer, username, true);
statusField.set(pattern, -1);
addMethod.invoke(field.get(syncback), pattern);
}
else if(statusField.getInt(pattern) == 1) {
anchorField.set(pattern, (new Date()).getTime());
anchorField.set(patternInServer, anchorField.get(pattern));
Update.update(adapter, pattern, username, false);
statusField.set(pattern, 9);
addMethod.invoke(field.get(syncback), pattern);
}
else{
//不可能有这种情况,一定是客户端代码写错了
anchorField.set(pattern, (new Date()).getTime());
statusField.set(pattern, -1);
addMethod.invoke(field.get(syncback), pattern);
}
}
else {
//表示之前本地就没有更新到最新的版本,下面的操作可能存在冲突,目前考虑冲突全部以服务器端优先
if(statusField.getInt(patternInServer) == -1){
//是在垃圾桶中找到这个记录的,证明之前在其他的客户端中对这一项进行了删除,这里对服务器进行更新,同时删除本地
anchorField.set(pattern, (new Date()).getTime());
anchorField.set(patternInServer, anchorField.get(pattern));
Update.update(adapter, pattern, username, true);
statusField.set(pattern, -1);
addMethod.invoke(field.get(syncback), pattern);//status -1 发回本地,然本地删除
}
else {
//仍然在服务器端的数据库中,可能出现的冲突时文本的修改,这里以服务器为主
anchorField.set(pattern, (new Date()).getTime());
anchorField.set(patternInServer, anchorField.get(pattern));
statusField.set(patternInServer, 9);
addMethod.invoke(field.get(syncback), patternInServer);
}
}
}
}
}
}
结合java的反射和泛型性质简化JDBC和相应的同步等服务器数据库操作代码的更多相关文章
- Java复习——反射和泛型的复习
反射 Class类 一个类被类加载器加载到内存之中,占有一片区域,这个空间里的内容就是类的字节码,不同的类的字节码是不一样的,这一个个空间页可以使用类来表示,这就是Class类. 根据这个概念可知:不 ...
- Java通过反射读取泛型
1.在这里有必要先提一句关于javabean,建议有一个构造器,而不是必须要写的,这是一个良好的习惯. 这样做肯定是有好处的,如果你的一个类写了带参的构造方法,而没有写空的构造方法,那么,如有有一个类 ...
- java 结合反射、泛型、注解获取泛型对象
由于自己也不是特别的理解,不能做过多的解释,因为这些是问过老师做出来的,我还没有学到这里.如果有解释错误的 指出来我改正一下.见谅~(如果学到这里了,会完善) 工具类(SQLUtil)核心代码: pa ...
- java笔记--反射进阶之总结与详解
一.反射进阶之动态设置类的私有域 "封装"是Java的三大特性之一,为了能更好保证其封装性,我们往往需要将域设置成私有的, 然后通过提供相对应的set和get方法来操作这个域.但是 ...
- 深入浅出学习hibernate框架(三):java的反射机制
上篇博客写到了JDBC的基本操作,今天准备写一篇关于JAVA反射机制的文章,因为java的反射机制和上一篇JDBC都是Hibernate框架的基本要素.在Hibernate的运行机制中,这两块的内容正 ...
- Java数据库操作学习
JDBC是java和数据库的连接,是一种规范,提供java程序与数据库的连接接口,使用户不用在意具体的数据库.JDBC类型:类型1-JDBC-ODBC桥类型2-本地API驱动类型3-网络协议驱动类型4 ...
- 复习java数据库操作的总结
以前学习java数据库操作,学得那叫糊里糊涂,各种JDBC常用的类和接口根本是傻傻分不清啥是干嘛的.只是套着用用吧. 不过这次好歹清楚些了,呜呜,学习有阶段性,多次重复才有好效果,多么痛的领悟. 工程 ...
- 2015第22周六Java反射、泛型、容器简介
Java的反射非常强大,传递class, 可以动态的生成该类.取得这个类的所有信息,包括里面的属性.方法以及构造函数等,甚至可以取得其父类或父接口里面的内容. obj.getClass().getDe ...
- 3.java的hello word.继承.泛型.反射.配置项.数据库操作.lombok
迷迷茫茫的开始了第一步.弄个hello word.结果这第一小步也不是那么的顺利. 明明照着图敲的.可就是没有运行选项. 为此还百度了一下.也没有什么答案.最后只能老老实实的看了.结果还是粗心的问题. ...
随机推荐
- 剑指offer——面试题30:包含min函数的栈
#include"iostream" #include"stdio.h" using namespace std; ; ; template<typena ...
- redux进阶 --- 中间件和异步操作
你为什么需要异步操作? https://stackoverflow.com/questions/34570758/why-do-we-need-middleware-for-async-flow-in ...
- 话说C语言的关键字volatile
最近搞NVMe驱动需求分析,对volatile这个单词实在是再熟悉不过了. 而在C语言中,有一个关键字就叫做volatile, 其字面意思是"挥发性的, 不稳定的,可改变的". 那 ...
- js禁止微信浏览器下拉显示黑底查看网址,不影响内部Scroll
开发项目跑在微信浏览器经常会遇到一个问题,微信浏览器下拉的时候会出现自带的黑色底色(显示网址)如下图: 网上好多js禁止操作的做法禁止了内部Scroll,导致页面不能滚动,上拉加载失效,例如这种做法: ...
- 解读MySQL的慢日志
完整的慢日志格式一般如下: # Time: :: # User@Host: db_user[db_database] @ localhost [] # Query_time: Rows_examine ...
- “System.OutOfMemoryException”类型的异常在 mscorlib.dll 中发生,但未在用户代码中进行处理
“System.OutOfMemoryException”类型的异常在 mscorlib.dll 中发生,但未在用户代码中进行处理 这个原因肯定不是因为程序内部的逻辑错误,或者别的什么情况. 想想,肯 ...
- 从java到web前端再到php,一路走来的小总结
java的学习: 初学者对Java的学习,上来的感觉都是比较难,感觉java的东西很多,如此多的类和接口.有时还弄不懂为啥实例化出一个int空数组为什么数组中默认都是0,实例化一个空字符串数组时(St ...
- <深入理解JavaScript>学习笔记(3)_全面解析Module模式
简介 Module模式是JavaScript编程中一个非常通用的模式,一般情况下,大家都知道基本用法,本文尝试着给大家更多该模式的高级使用方式. 首先我们来看看Module模式的基本特征: 模块化,可 ...
- java 基础 --- 动态代理和静态代理
问题 : 代理的应用场景是什么 动态代理的底层原理是什么,为什么只能继承接口 概述 代理模式是设计模式的一种,简单地说就是调用代理类的方法实际就是调用真实类的方法.这种模式在AOP (切面编程)中非 ...
- Docker学习之基本概念
Docker学习之基本概念 作为一个后端noder,不了解docker有点说不过去,这节开始,学习一些docker层面的东西. 什么是docker Docker最初是dotCloud公司创始人Solo ...