本文为大家提供一种数据库访问程序快速开发方法。在应用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==
==================================