日期:2014-05-16  浏览次数:20314 次

通过反射实现公用数据库操作方法
   最近在项目中,由于是一个web项目,使用了struts1.X和spring的框架。因为项目本身的一些结构,历史问题等原因,整个架构虽然使用了struts和spring的框架,但都使用了其最基本得技术和功能。其中和数据库的交互什么框架也没有用,使用了原生的jbdc,好在项目本身规模不是很大,单纯的jdbc还有满足,但总觉得在操作每一张表是费劲的写方法,小心的传参,拼写sql语句很费劲,后来想能不能写一个公共的类,包括最原始的增,删,改,查方法,使用时直接调用或者单独继承,不用每个业务模块都自己写自己的增,删,改,查方法。
   顺着这个思路,开始思考:操作表时无非是就操作一个一个字段,为了测试方便,要求表单数据要插入和或更新到数据库时,都先封装成和表对应的一个类(bean),然后从这个类里取值更新。而这个类得属性和数据表字段一一对应,包括命名都一样。那这样的话,就可以写一个公共的save(Object obj)方法,传进来一个bean,根据这个bean的属性名和值就可以拼接出sql语句,有了sql语句就可以使用jdbc直接操作数据库了。如何获得bean的属性名和值,就用到了java的反射技术。通过java的反射技术可以在程序中获取一个对象实例的类信息,并能获取和修改其属性的值。于是花了几个小时写出来一个公共的类,包括增,删,改,查方法,但其性能和安全性有待检验。
   现在只写出来简陋的样子,但有没有问题还不知道,项目里用不用还得测试测试再说,主要代码如下:
            // sql语句
            StringBuffer sql = new StringBuffer("insert into");
            sql.append(tableName).append("(");
            // 参数,实际上对应表字段
            StringBuffer params = new StringBuffer(" values(");
            // 字段值
            ArrayList values = new ArrayList();
     
            //解析属性信息
            Field[] fields = obj.getClass().getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                Field field = fields[i];
                // 设置安全访问权限,true可以访问私有属性
                field.setAccessible(true);
                // 属性名
                String fieldName = field.getName();
                Object value = field.get(obj);
                sql.append(fieldName + ",");
                params.append("?,");
                values.add(value);
             }
             
             sql.append(sql.deleteCharAt(sql.length() -1) + ")");
             params.append(params.deleteCharAt(params.length() -1) + ")");
             sql.append(params);
             PreparedStatement st = conn.prepareStatement(sql.toString());
             for (int i = 0; i < values.size(); i++) {
                st.setObject(i + 1,values.get(i));
             }
             // 执行
             st.executeUpdate();



在此中间遇到一个问题,在使用属性(Field)直接访问对象属性信息时,最初的bean是有继承的,所有表一些公共的字段写在一个基类bean中,然后每个表的bean继承这个基类bean。这样再对每个表的bean取其属性信息时,无法取得继承的属性,不过每个类都可以获得它的基类的一个Class类型,这样就可以通过递归的方法获取所有继承关系中的类所有的属性信息。代码如下:
   /**
     * 
     * 获取目标对象的所有属性(包括继承属性)
     * 
     * @param obj 目标对象
     * @return 所有属性(包括继承属性)
     * @see [类、类#方法、类#成员]
     */
    public Field[] getAllClassField(Object obj) {
        Field[] allfield ;
        // 如果其父类不是Object,则有基类
        if (!obj.getClass().getSuperclass().equals(Object.class)) {
            // 递归,取得其基类的属性数组
            Field[] fields = getAllClassField(obj.getClass().getSuperclass());
            // 取得自己的属性数组
            Field[] myFields = obj.getClass().getDeclaredFields();
            // 属性数组合并
            allfield = new Field[fields.length + myFields.length];
            System.arraycopy(fields, 0, allfield, 0, fields.length);
            System.arraycopy(myFields, 0, allfield, fields.length, myFields.length);
        } else {
            // 没有基类,直接返回自己的属性数组
            allfield = obj.getClass().getDeclaredFields();
        }
        
        return allfield;
    }

另外,访问属性的数据可以直接使用Field对象的set/get方法访问,也可以回调目标对象的set/get方法访问。