• 结合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编辑PDF写入文字 插入图片
    Java图片压缩
    Java base64 图片编码转换
    JAVA操作字符串
    JAVA获取文件夹下所有的文件
    IntelliJ IDEA 注释模板设置
    IntelliJ IDEA 添加junit插件
    python操作mysql数据库系列-安装MySql
    python操作mysql数据库系列-安装MySQLdb
    软件测试工程师为什么要不断提高自身技能?
  • 原文地址:https://www.cnblogs.com/huangzp1104/p/9376879.html
Copyright © 2020-2023  润新知