github地址:https://github.com/hzphzp/HeartTrace_Server

我们的服务器端数据库并没有用sqllite, 而是直接用mysql,并且用JDBC直接进行操作,故会出现比较多的sql底层代码,而且我们的数据库中一共有7张表, 如果每一个表都写对应的增加,删除, 更新和查询等操作的话,会有很庞大的代码量。

同时,在我们的设计中,服务器端的数据库功能比较单一,主要是配合同步方案进行设计的。

故,我这里选择使用Java的泛型和反射的机制来进行数据库操作和数据的同步:

数据库操作首先需要Sql语句,这里如果sql语句手写的话,就无法实现所有的表统一操作的目的了,故每一种操作的sql语句, 应该有自己生成的方法, 如下为Update类中的createSql方法代码:

  1. private static String createSql(Class<?> cls, boolean delete){
  2. String str = "";
  3. String sql;
  4. int index = 1;
  5. for(Field field : cls.getDeclaredFields()){
  6. if(index == 1){
  7. str += "username=?, ";
  8. index++;
  9. continue;
  10. }
  11. str += field.getName() + "=?, ";
  12. index++;
  13. }
  14. str = str.substring(0, str.length()-2);
  15. if(!delete) {
  16. sql = "UPDATE " + cls.getSimpleName() + " SET " + str + " WHERE username = ? AND id = ?";
  17. }
  18. else {
  19. sql = "UPDATE " + cls.getSimpleName() + "_delete" + " SET " + str + " WHERE username = ? AND id = ?";
  20. }
  21. return sql;
  22. }

有了sql语句之后, 就是往语句里面的‘?’ 里面填坑了。

这里遇到一个问题,gson将本地传输来的json解析后, Diary里面的一些外键是嵌套形式的,但是服务器端的数据库中只有响应的表的id。

这样填充的时候会出现类型错误。

为了保证操作的统一性, 我在会出现异常的语句中加入了异常的处理,如果有就进行取id操作:

  1. try {
  2. pstm.setObject(index, obj);
  3. }catch(notserializableexception nse){
  4. field = obj.getClass().getField("id");
  5. int id = field.getInt(obj);
  6. pstm.setObject(index, id);
  7. }

所以完整的填坑方法是:

  1. package Db;
  2.  
  3. import Json.dbJson.Diary;
  4.  
  5. import java.lang.reflect.Field;
  6. import java.sql.PreparedStatement;
  7. import java.sql.SQLException;
  8. import java.sql.Statement;
  9.  
  10. public class Update {
  11.  
  12. public static<T> boolean update(DatabaseAdapter adapter, T cls, String username, boolean delete){
  13. boolean flag = false;
  14. try{
  15. PreparedStatement pstm = adapter.connection.prepareStatement(createSql(cls.getClass(), delete));
  16. int index = 1;
  17. for(Field field : cls.getClass().getDeclaredFields()){
  18. if(index == 1){
  19. pstm.setString(index, username);
  20. index++;
  21. continue;
  22. }
  23. field.setAccessible(true);
  24. Object obj = field.get(cls);
  25. setObject(pstm, index, obj);
  26. index++;
  27. }
  28. pstm.setString(index, username);
  29. pstm.setObject(index+1, cls.getClass().getField("id").get(cls));
  30. int result = pstm.executeUpdate();
  31. flag = result > 0;
  32. }catch(SQLException se){
  33. // 处理 JDBC 错误
  34. se.printStackTrace();
  35. }catch(Exception e){
  36. // 处理 Class.forName 错误
  37. e.printStackTrace();
  38. }finally {
  39. return flag;
  40. }
  41. }
  42.  
  43. static void setObject(PreparedStatement pstm, int index, Object obj) throws NoSuchFieldException, IllegalAccessException, SQLException {
  44. Field field;
  45. try {
  46. pstm.setObject(index, obj);
  47. }catch(Exception nse){
  48. field = obj.getClass().getField("id");
  49. int id = field.getInt(obj);
  50. pstm.setObject(index, id);
  51. }
  52. }
  53.  
  54. private static String createSql(Class<?> cls, boolean delete){
  55. String str = "";
  56. String sql;
  57. int index = 1;
  58. for(Field field : cls.getDeclaredFields()){
  59. if(index == 1){
  60. str += "username=?, ";
  61. index++;
  62. continue;
  63. }
  64. str += field.getName() + "=?, ";
  65. index++;
  66. }
  67. str = str.substring(0, str.length()-2);
  68. if(!delete) {
  69. sql = "UPDATE " + cls.getSimpleName() + " SET " + str + " WHERE username = ? AND id = ?";
  70. }
  71. else {
  72. sql = "UPDATE " + cls.getSimpleName() + "_delete" + " SET " + str + " WHERE username = ? AND id = ?";
  73. }
  74. return sql;
  75. }
  76. }

响应的,其他几种操作各自写一个类:

接下来就是同步了,具体的同步方案,写在下一个博客中:

  1.      Gson gson = new Gson();
  2. //java.lang.reflect.Type classType = new TypeToken<Json.Sync>() {}.getType();
  3. //Json.Sync sync = gson.fromJson(content, classType);
  4. Json.Sync sync = gson.fromJson(content, Json.Sync.class);
  5. try{
  6. for(Field field : sync.getClass().getDeclaredFields()){
  7. field.setAccessible(true);
  8. Object obj = field.get(sync);
  9. Type t = field.getGenericType();
  10. if(t instanceof ParameterizedType){
  11. ParameterizedType pt = (ParameterizedType) t;
  12. Class clz = (Class) pt.getActualTypeArguments()[0];//List里面的示例的类型
  13. Class clazz = obj.getClass();//List这个类型
  14. Field anchorField = clz.getField("anchor");
  15. Field statusField = clz.getField("status");
  16. Field idField = clz.getField("id");
  17. Method sizeMethod = clazz.getDeclaredMethod("size");
  18. Method getMethod = clazz.getDeclaredMethod("get", int.class);
  19. getMethod.setAccessible(true);
  20. Method addMethod = clazz.getDeclaredMethod("add", Object.class);
  21. addMethod.setAccessible(true);
  22. int size = (Integer) sizeMethod.invoke(obj);
  23. for(int i = 0; i < size; i++){
  24. Object pattern = getMethod.invoke(obj, i);
  25. if(0 == statusField.getInt(pattern)){
  26. //新增的
  27. anchorField.set(pattern, (new Date()).getTime());
  28. Insert.insert(adapter, pattern, username, false);
  29. statusField.set(pattern, 9);
  30. addMethod.invoke(field.get(syncback), pattern);
  31. }
  32. else {
  33. //不是新增的
  34. Object patternInServer = Search.search(adapter, clz, username, idField.getInt(pattern), false);
  35. if(patternInServer == null){
  36. //没找到,在垃圾箱中找找
  37. patternInServer = Search.search(adapter, clz, username, idField.getInt(pattern), true);
  38. }
  39. if(patternInServer == null){
  40. //在服务器数据库和垃圾箱中都没找到,一定是客户端的代码写错了
  41. anchorField.set(pattern, (new Date()).getTime());
  42. statusField.set(pattern, -1);
  43. addMethod.invoke(field.get(syncback), pattern);
  44. continue;
  45. }
  46. if(anchorField.get(pattern).equals(anchorField.get(patternInServer))) {
  47. //两个数据之前已经同步好了, 直接利用status进行更新不会发生冲突
  48. if(statusField.getInt(pattern) == -1){
  49. anchorField.set(pattern, (new Date()).getTime());
  50. anchorField.set(patternInServer, anchorField.get(pattern));
  51. Delete.delete(adapter, clz, username, idField.getInt(pattern), false);
  52. Insert.insert(adapter, patternInServer, username, true);
  53. statusField.set(pattern, -1);
  54. addMethod.invoke(field.get(syncback), pattern);
  55. }
  56. else if(statusField.getInt(pattern) == 1) {
  57. anchorField.set(pattern, (new Date()).getTime());
  58. anchorField.set(patternInServer, anchorField.get(pattern));
  59. Update.update(adapter, pattern, username, false);
  60. statusField.set(pattern, 9);
  61. addMethod.invoke(field.get(syncback), pattern);
  62. }
  63. else{
  64. //不可能有这种情况,一定是客户端代码写错了
  65. anchorField.set(pattern, (new Date()).getTime());
  66. statusField.set(pattern, -1);
  67. addMethod.invoke(field.get(syncback), pattern);
  68. }
  69. }
  70. else {
  71. //表示之前本地就没有更新到最新的版本,下面的操作可能存在冲突,目前考虑冲突全部以服务器端优先
  72. if(statusField.getInt(patternInServer) == -1){
  73. //是在垃圾桶中找到这个记录的,证明之前在其他的客户端中对这一项进行了删除,这里对服务器进行更新,同时删除本地
  74. anchorField.set(pattern, (new Date()).getTime());
  75. anchorField.set(patternInServer, anchorField.get(pattern));
  76. Update.update(adapter, pattern, username, true);
  77. statusField.set(pattern, -1);
  78. addMethod.invoke(field.get(syncback), pattern);//status -1 发回本地,然本地删除
  79. }
  80. else {
  81. //仍然在服务器端的数据库中,可能出现的冲突时文本的修改,这里以服务器为主
  82. anchorField.set(pattern, (new Date()).getTime());
  83. anchorField.set(patternInServer, anchorField.get(pattern));
  84. statusField.set(patternInServer, 9);
  85. addMethod.invoke(field.get(syncback), patternInServer);
  86. }
  87. }
  88. }
  89. }
  90. }
  91. }

结合java的反射和泛型性质简化JDBC和相应的同步等服务器数据库操作代码的更多相关文章

  1. Java复习——反射和泛型的复习

    反射 Class类 一个类被类加载器加载到内存之中,占有一片区域,这个空间里的内容就是类的字节码,不同的类的字节码是不一样的,这一个个空间页可以使用类来表示,这就是Class类. 根据这个概念可知:不 ...

  2. Java通过反射读取泛型

    1.在这里有必要先提一句关于javabean,建议有一个构造器,而不是必须要写的,这是一个良好的习惯. 这样做肯定是有好处的,如果你的一个类写了带参的构造方法,而没有写空的构造方法,那么,如有有一个类 ...

  3. java 结合反射、泛型、注解获取泛型对象

    由于自己也不是特别的理解,不能做过多的解释,因为这些是问过老师做出来的,我还没有学到这里.如果有解释错误的 指出来我改正一下.见谅~(如果学到这里了,会完善) 工具类(SQLUtil)核心代码: pa ...

  4. java笔记--反射进阶之总结与详解

    一.反射进阶之动态设置类的私有域 "封装"是Java的三大特性之一,为了能更好保证其封装性,我们往往需要将域设置成私有的, 然后通过提供相对应的set和get方法来操作这个域.但是 ...

  5. 深入浅出学习hibernate框架(三):java的反射机制

    上篇博客写到了JDBC的基本操作,今天准备写一篇关于JAVA反射机制的文章,因为java的反射机制和上一篇JDBC都是Hibernate框架的基本要素.在Hibernate的运行机制中,这两块的内容正 ...

  6. Java数据库操作学习

    JDBC是java和数据库的连接,是一种规范,提供java程序与数据库的连接接口,使用户不用在意具体的数据库.JDBC类型:类型1-JDBC-ODBC桥类型2-本地API驱动类型3-网络协议驱动类型4 ...

  7. 复习java数据库操作的总结

    以前学习java数据库操作,学得那叫糊里糊涂,各种JDBC常用的类和接口根本是傻傻分不清啥是干嘛的.只是套着用用吧. 不过这次好歹清楚些了,呜呜,学习有阶段性,多次重复才有好效果,多么痛的领悟. 工程 ...

  8. 2015第22周六Java反射、泛型、容器简介

    Java的反射非常强大,传递class, 可以动态的生成该类.取得这个类的所有信息,包括里面的属性.方法以及构造函数等,甚至可以取得其父类或父接口里面的内容. obj.getClass().getDe ...

  9. 3.java的hello word.继承.泛型.反射.配置项.数据库操作.lombok

    迷迷茫茫的开始了第一步.弄个hello word.结果这第一小步也不是那么的顺利. 明明照着图敲的.可就是没有运行选项. 为此还百度了一下.也没有什么答案.最后只能老老实实的看了.结果还是粗心的问题. ...

随机推荐

  1. windows下几个方便的右键菜单

    一直在用,拷来拷去麻烦,贴这里方便自己方便大家 PSHere.reg 右键菜单打开powershell Windows Registry Editor Version 5.00 [HKEY_CLASS ...

  2. Python+USB+Vnet+FTP传输文件开发记录

    做一个Python+USB+Vnet+FTP传输文件开发记录

  3. 安装CentOS 7.4时服务器出现No Caching mode page found问题的解决方法

    2019-03-27 前提准备条件: 操作系统:centos 7.4:服务器使用U盘安装,U盘使用UltraISO制作启动盘 安装过程出现的问题: [sdb] No Caching mode page ...

  4. 四、OE 中添加对供应商名称的唯一限制

    最初的思路是利用sql_constraints 来限制重复的供应商名称,但后来想到在OE中供应商.客户乃至员工都隶属于Partner,sql_constrainst实际上是通过限制partner来限制 ...

  5. 使用nginx反向代理jenkins

    1.在Jenkins 官方网站(http://jenkins-ci.org/)下载最新版本war包.拷贝到 $TOMCAT_HOME/webapps 下(不用解压).启动tomcat服务. 2.找到n ...

  6. Javac的命令(-Xlint)

    在OptionName类中的枚举定义如下: XLINT("-Xlint"), XLINT_CUSTOM("-Xlint:"), -Xlint     Enabl ...

  7. master线程的主循环,后台循环,刷新循环,暂停循环

    InnoDB存储引擎的主要工作都是在一个单独的后台线程master thread中完成的.master thread的线程优先级别最高.其内部由几个循环(loop)组成:主循环(loop).后台循环( ...

  8. 深入redis内部--实现字符串

    redis字符串的定义和实现在Ssd.h和Ssd.c中. 1.定义 typedef char *sds; //本质是字符char的指针 2.字符串的操作 sds sdsnew(const char * ...

  9. 【转】winform程序textbox滚动条保持在最下面 内容不闪烁

    在开发winform程序时,会用到textbox控件来显示信息,当把textbox的Multiline属性改为Ture时(即多行显示状态),ScrollBars属性改为Vertical(内容过多时,显 ...

  10. Spring中使用JMS

    JMS为了Java开发人员与消息代理(message broker)交互和收发消息提供了一套标准API.而且,由于每个message broker都支持JMS,所以我们就不需要学习额外的消息API了. ...