Hibernate万能数据库访问程序及其存在问题

本文为大家提供一种数据库访问程序快速开发方法。在应用Hibernate框架开发数据库访问程序的时候,通常需要建立DAO层,针对各个service服务实现不同的数据库访问,开发较为繁琐,本文利用反射技术针对数据库访问的常用功能,开发了具有一定通用性的数据库访问程序,能够实现绝大部分数据库操作,其他数据库操作框架,可根据此思路做类似开发,供大家参考讨论。

开门见山

功能需求

这里只介绍读数据库功能的开发,增删改功能可参考读程序进行拓展。
需求:根据指定查询条件(数据条目限制、字段精确查询条件、字段模糊查询条件、排序)读取符合条件的数据。

开发思路

建立查询条件类,并利用反射技术及Hibernate框架自带的Criteria类,实现此功能。

实现代码

查询类:


    public class SortAndFilterBean {
        private int begin = 0;
        private int end = 0;
        private String value = "{}";
        private String valueFuzz = "{}";
        private String name = "";
        private String type = "";

        public int getBegin() {
            return begin;
        }

        public void setBegin(int begin) {
            this.begin = begin;
        }

        public int getEnd() {
            return end;
        }

        public void setEnd(int end) {
            this.end = end;
        }

        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }

        public String getValueFuzz() {
            return valueFuzz;
        }

        public void setValueFuzz(String valueFuzz) {
            this.valueFuzz = valueFuzz;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }
    }

功能实现:


    public Map getObjectsByCondition(SortAndFilterBean sortAndFilterBean, Class clazz) {
        Map<String, Object> map = new HashedMap();
        try {
            Criteria criteria = currentSession().createCriteria(clazz);
            //添加精确查找条件
            criteria = CriteriaUtil.addFilterCondition(criteria,clazz,sortAndFilterBean.getValue());
            //添加模糊查找条件
            criteria = CriteriaUtil.addFilterConditionFuzz(criteria,sortAndFilterBean.getValueFuzz());
            //添加排序条件
            if (sortAndFilterBean.getType().equalsIgnoreCase("up")){
                criteria.addOrder(Order.asc(sortAndFilterBean.getName()));
            }else {
                criteria.addOrder(Order.desc(sortAndFilterBean.getName()));
            }
            //指定起始位置及结果最大数
            criteria = criteria.setFirstResult(sortAndFilterBean.getBegin() - 1);
            criteria = criteria.setMaxResults(sortAndFilterBean.getEnd() -sortAndFilterBean.getBegin() + 1);
            List list = criteria.list();
            //复位起始位置及结果最大数,用于获取所有满足条件的结果数目
            criteria = criteria.setFirstResult(0);
            criteria = criteria.setMaxResults(-1);
            Integer rowTotalCount = Integer.valueOf(criteria.setProjection(Projections.rowCount()).uniqueResult().toString());
            map.put("list",list);
            map.put("count",rowTotalCount);
            map.put("status",0);
            return map;
        } catch (DataAccessResourceFailureException e) {
            e.printStackTrace();
        } catch (HibernateException e) {
            e.printStackTrace();
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }
        map.put("status",-1);
        return map;
    }

查询实现:


    public class CriteriaUtil {
        /**
         * 为数据查询添加精确过滤条件
         * @param criteria  查询对象
         * @param clazz     所需查询的对象类
         * @param json      搜索内容,json格式字符串
         * @return      增加过滤条件的查询对象
         */
        public static Criteria addFilterCondition(Criteria criteria, Class clazz, String json){
            //将 json 转换为对应 class 以便获得其属性数据类型
            Object object = JSONObject.toBean(JSONObject.fromObject(json),clazz);

            //获取 json 字符串中所有需要搜索的字段
            JSONObject jsonObject = JSONObject.fromObject(json);
            Iterator iterator = jsonObject.keys();
            while (iterator.hasNext()){
                String key = iterator.next().toString();
                //利用反射技术,使用 bean 的 getter 方法获取搜索内容
                String getter = "get" + key.substring(0,1).toUpperCase() + key.substring(1);
                Object value = null;
                try {
                    Method methodGet = clazz.getMethod(getter);
                    value = methodGet.invoke(object);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }

                //添加索引条件
                if(value != null && !value.toString().equals("")){
                    criteria = criteria.add(Restrictions.eq(key,value));
                }
            }
            return criteria;
        }

        /**
         * 为数据库查询增加模糊过滤条件(忽略大小写),只针对 String 类型条目
         * @param criteria  查询对象
         * @param json      搜索内容,json 格式字符串
         * @return      增加搜索条件的查询对象
         */
        public static Criteria addFilterConditionFuzz(Criteria criteria, String json){
            JSONObject jsonObject = JSONObject.fromObject(json);
            Iterator iterator = jsonObject.keys();
            while (iterator.hasNext()){
                String key = iterator.next().toString();
                Object value = jsonObject.get(key);
                //添加索引条件
                if(value != null && value.getClass().equals(String.class) && !value.toString().equals("")){
                    criteria = criteria.add( Restrictions.like(key, "%"+value.toString()+"%").ignoreCase());
                }
            }
            return criteria;
        }
    }

进一步讨论

存在问题

1.在service层依然需要针对每一个服务添加数据库事务注释,稍微会有点麻烦,可以考虑使用统一命名规则,同时使用aop进行数据库事务注入,减小工作量;
2.由于是通用规则,如果限制规则没有处理好,攻击者可以通过修改请求url及自制json字符串,进而篡改不应该被修改的字段。建议不要在用户及权限管理等关键地方应用此方法;
3.使用反射技术的时候,涉及到拆装箱的内容要仔细处理,不然容易出现类型不匹配的情况。

关于@Transactional注解的讨论

问题1: @Transactional注解放在service层还是dao层?
@Transactional注解的主要作用是绑定数据库会话,和数据库事务回滚密切相关。

事务的实质就是要你操作数据库时从一而终,要么全部完成,要么回滚撤销所有操作。

在dao层的方法对数据库的操作通常来说是简单的,没业务逻辑性的,所以对于这种加了一个事务也可以。

但在service层,里面的方法通常是包含一些复杂逻辑的,一个方法可能就调用了dao的多个方法,所以就必须做到事务管理,要么service方法里面的dao方法全部执行,要么全部撤销。这样才能保证数据库数据的正确性。

所以可以得到如下结论:

  • 针对单一数据库,并且需要读写的情况,配置在service层会比较科学一些,一旦失败,整个方法回滚;
  • 针对同一方法中需要访问不同数据库的情况,可以配置在dao层了,或者反向注入操作另一个数据库的service;
  • 如果针对只需要读的数据库,事务注解直接配置在dao层,可以使代码更为简洁。

同样,可以根据这个思路将一些需要经常使用到的数据库操作,写成通用方法,可以节省开发时间、便于维护,并且能够有效地防止sql注入问题。

==================================
==疑问?帮助?批评?欢迎评论 | QQ:593159978==
==================================

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值