美文网首页
hibernate的DAO层封装-基础功能

hibernate的DAO层封装-基础功能

作者: 东本三月 | 来源:发表于2019-04-08 21:16 被阅读0次

1.需求/目的

  • 提供对数据库操作的常用方法
  • 隐藏实现细节,使之专注于业务的开发

2.使用环境

  • spring boot 2.0.3
  • maven
  • mysql 5.7
  • 引入
            <!-- hibernate核心包 -->
            <dependency>
              <groupId>org.hibernate</groupId>
              <artifactId>hibernate-core</artifactId>
            </dependency>

            <!-- hibernate 提供用于连接和操作数据库的entitymanager对象 -->
            <dependency>
              <groupId>org.hibernate</groupId>
              <artifactId>hibernate-entitymanager</artifactId>
            </dependency>

            <!-- 提供数据库连接池 -->
            <dependency>
              <groupId>com.alibaba</groupId>
              <artifactId>druid</artifactId>
              <version>1.1.10</version>
            </dependency>

            <!-- 用于将对象的属性封装到对象 -->
            <dependency>
                <groupId>commons-beanutils</groupId>
                <artifactId>commons-beanutils</artifactId>
                <version>1.9.3</version>
            </dependency>

3.基础方法

  • 获取数据库连接
   //通过注入的EntityManager来获取数据库操作对象session
   @PersistenceContext
   private EntityManager entityManager;
   //日志对象
   protected Logger log = LoggerFactory.getLogger(this.getClass());

    /**
     * 获取数据库操作对象,,通过session,this.entityManager.unwrap(Session.class);
    * @return org.hibernate.Session对象
    * @time 2018年9月17日09:11:26
    * @author authstr
    */
   @Override
   public Session getSession() {
           return (Session)this.entityManager.unwrap(Session.class);
     }

   /**
    *  刷新Session对象,在进行多个实体的操作时,需要调用该方法
    *  @time 2018年9月17日09:12:21
    *  @author authstr
    */
   @Override
   public  void flushSession() {
       this.getSession().flush();
       this.getSession().clear();
   }

4.提供简单增删改操作

  • 保存
 /**
      * 添加/保存一个entity
     * @param entity
     * @return 主键值
     */
    @Override
    public Serializable save(Object entity) {
           //这里可以根据一些model的设置进行特定的验证或者处理
           //比如对象是否符合model接口,设置创建时间之类的
            return this.getSession().save(entity);
       }
     
    /**
     * 添加/保存多个entity
     * @param entityList
     * @return 主键id的list
     */
    @Override
    public List<Serializable> saveList(List entityList){
        List<Serializable> res=new ArrayList<Serializable>();
        for(int i=0;i<entityList.size();i++){
            res.add(this.save(entityList.get(i)));
            //每20次刷新一下session
            if(i%20==0){
                this.flushSession();
            }
        } 
        return res;
    }
  • 更新
    /**
      * 更新/修改一个entity
     * @param entity
     * @time 2018年9月25日16:15:25
     * @author authstr
     */
    @Override
    public void update(Object entity) {
          this.getSession().update(entity);
       }

    /** 
     * 更新/修改多个entity
     * @param entityList
     * @return 修改的数量
     * @time 2018年9月25日16:16:01
     * @author authstr
     */
    @Override
    public int updateList(List entityList) {
        for(int i=0;i<entityList.size();i++){
            update(entityList.get(i));
            //每20次刷新一下session
            if(i%20==0){
                this.flushSession();
            }
        }
        return entityList.size();
    }

-删除

    /**
     * 删除一个entity对象
     * @param entity
     * @time 2018年11月13日 上午11:24:35
     * @author authstr
     */
    @Override
    public void remove(Object entity) {
        this.getSession().remove(entity);
    }

5.Query对象的创建

  • 在hibernate中需要通过Query对象进行sql语句的执行
  • 当前不考虑对Hql的执行,只针对mysql的sql语句
    /**
     * 创建Query对象
     * @param sql sql语句
     * @param firstRows 起始数据行
     * @param maxRows 获取数据条数
     * @param returnType 结果集封装
     * @return Query对象
     * @time 2019年4月8日14:57:28
     * @author authstr
     */
    public Query createQuery(String sql,Integer firstRows,Integer maxRows,Class returnType){
        Assert.isTrue(StringUtils.hasText(sql),"sql语句必须存在",true);
        Assert.isTrue(returnType!=null,"返回值不能为空",true);
        NativeQuery query= this.getSession().createNativeQuery(sql);
        //设置返回值类型
        if(SqlResult.class.equals(returnType)){
            //原封不动的封装数据
            query.setResultTransformer(SqlResultTransformer.SQL_INSTANCE);
        }else{
            //将数据封装为HashMap
            query.setResultTransformer(MapResultTransformer.MAP_INSTANCE);
        }
        query.setResultTransformer(new MapResultTransformer());
        //设置开始行
        if(firstRows!=null){
            query.setFirstResult(firstRows);
        }
        //设置数据条数
        if(maxRows!=null){
            query.setMaxResults(maxRows);
        }
        return query;
    }
  • Assert之前有说明,是一个便捷抛出异常的类
  • Query对象同样使用Session创建,方法主要是定义了sql结果集的处理
  • 通过定义一个AliasedTupleSubsetResultTransformer的子类,通过Query的setResultTransformer方法,可以设置hibernate对sql结果集的处理逻辑
  • 不过这种方法使用泛型比较麻烦,因此这里我不做复杂的处理.直接将结果集封装成hashMap或者原封不动不对数据进行处理
  • sql结果集是两个数组,定义一个实体类来进行储存
/**
 * 实体类,用于储存sql语句的结果集
 * 2019年4月8日14:19:23
 * authstr
 */
public class SqlResult  {
    private String[] aliases;
    private Object[] tuple;

    public String[] getAliases() {
        return aliases;
    }

    public void setAliases(String[] aliases) {
        this.aliases = aliases;
    }

    public Object[] getTuple() {
        return tuple;
    }

    public void setTuple(Object[] tuple) {
        this.tuple = tuple;
    }
}
  • 将sql结果集原封不动封装成SqlResult对象
/**
 * hibernate的转换类,将数据库的返回值封装成SqlResult对象
 * @time 2019年4月8日15:21:27
 * @author authstr
 *
 */
public class SqlResultTransformer extends AliasedTupleSubsetResultTransformer {

    public static final SqlResultTransformer SQL_INSTANCE = new SqlResultTransformer();

    private Object readResolve() {
        return SQL_INSTANCE;
    }

    public Object transformTuple(Object[] tuple, String[] aliases) {
        SqlResult res=new SqlResult();
        res.setAliases(aliases);
        res.setTuple(tuple);
        return res;
    }

    public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength) {
        return false;
    }
}
  • 将sql结果集封装成hashMap
/**
 * hibernate的转换类,将数据库的返回值封装成map
 * @author authstr
 *
 */
public class MapResultTransformer extends AliasedTupleSubsetResultTransformer {
    public static final MapResultTransformer MAP_INSTANCE = new MapResultTransformer();

    private Object readResolve() {
        return MAP_INSTANCE;
    }

    public Object transformTuple(Object[] tuple, String[] aliases) {
        HashMap<String, Object> result = new HashMap<String, Object>(tuple.length);
        for(int i=0;i<aliases.length;i++){
            String alias = aliases[i];
            if (alias != null) {
                result.put(alias, tuple[i]);
            }
        }
        return result;
    }
    public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength) {
        return false;
    }
}

6.Query对象的参数设置

  • 为了避免sql注入,sql参数的设置都是通过预编译进行的
  • hibernate提供了两种参数设置方式
  • 一是通过"?"作为占位符的单数组参数
    如:select a.* from base_user a where name=?
    参数传递:new Object[]{"admin"}
 /**
      * 通过数组设置查询参数,适用于用”?”来定义参数位置
     * @param query 要操作的query对象
     * @param values 数组,要替换成的值
     * @time 2018年9月25日11:27:58
     * @author authstr
     */
    public Query setQueryParameters(Query query, Object[] values) {
        //数组为空.直接返回
        if (!ObjectUtils.isExist(values)) return query;
        for(int i=0;i<values.length;i++){
            query.setParameter(i + 1, values[i]);
        }
        return query;
    }

这种方式比较方便快捷,缺点是参数较多时,sql语句参数的阅读与修改比较麻烦

  • 二是通过":"+参数名称 的方式,使用双数组设置参数
    如:select a.* from base_user a where name= :username
    参数传递: new String[]{"username"},new Object[]{"admin"}
/**
     * 设置query对象的一个参数
     * @param query 要操作的query对象
     * @param paramName 要替换的sql语句中的占位字符名称,如 :temp
     * @param value 要替换成的值
     * @return 被操作的query对象
     * @time 2018年9月17日09:44:24
     * @author authstr
     */
    private Query setKeyValue(Query query, String paramName, Object value) {
        //如果值是集合类型的对象,如,map,set,list,向下转型并设置
        //setParameterList方法无法使用Object作为参数,必须转成正确的类型
        if (value instanceof Collection) {
            query.setParameterList(paramName, (Collection)value);
        } else if (value instanceof Object[]) {
            //如果是数组,也向下转型
            query.setParameterList(paramName, (Object[])value);
        } else {
            query.setParameter(paramName, value);
        }
        return query;
    }
    /**
     * 通过两个数组设置query对象的参数,适用于用”:”+参数名称来定义参数位置
     * @param query 要操作的query对象
     * @param paramNames 数组,要替换的sql语句中的占位字符名称,如 :temp
     * @param values 数组,要替换成的值
     * @return 被操作的query对象
     * @time 2018年9月17日10:37:32
     * @author authstr
     */
    public Query setQueryParameters(Query query,String[] paramNames,Object[] values){
        //字段名称为空,视为使用”?”来定义参数位置
        if(!ObjectUtils.isArrayExist(paramNames)){
            return setQueryParameters(query,values);
        }
        //参数长度验证
        Assert.isTrue(paramNames.length==values.length, 
                "参数长度不一致,parm["+paramNames.length+"],value["+values.length+"]",true);
        for(int i=0;i<paramNames.length;i++){
            setKeyValue(query, paramNames[i], values[i]);
        }
        return query;
    }
  • 一般来说,实际使用时,很少用两个数组来进行":"+参数名称 类型的设置,而是使用Map来保存参数
    /**
     * 通过Map设置query对象的参数
     * @param query query 要操作的query对象
     * @param kv key表示要替换的sql语句中的占位字符名称,如 :temp,value表示要替换成的值
     * @return 被操作的query对象
     * @time 2018年9月17日11:12:24
     * @author authstr
     */
    public Query setQueryParameters(Query query,Map<String, Object> kv ){
        //判断map是否存在
        if(!CollectionUtils.isMapExist(kv)){
            return  query;
        }
        for (Map.Entry<String, Object> m : kv.entrySet()) {//遍历map
             this.setKeyValue(query, m.getKey(), m.getValue());
         }
        return query;
    };

7.执行sql进行查询数据

  • sql的执行有4步
    1.创建Query对象
    2.设置Query参数
    3.获取Query查询结果
    4.封装查询结果
    /**
     * 执行查询语句,获取返回值(执行方法)
     * @param sql 查询sql语句
     * @param fields sql参数 属性项数组
     * @param values sql参数 属性值数组
     * @param firstRows 查询起始行数
     * @param maxRows 查询数据条数
     * @param returnType 结果集封装的类型
     * @param <T> 结果集类型
     * @return 查询结果
     * @time 2019年4月5日11:23:53
     * @author authstr
     */
    public  <T> List<T> getBySQL(String sql,String[] fields,Object[] values,Integer firstRows,Integer maxRows,Class<T> returnType){
        //获取query对象
        Query query=createQuery(sql,firstRows,maxRows,returnType);
        //设置参数
        setQueryParameters(query,fields,values);
        List li=query.list();
        List res = converListToModel(returnType, li);
        return res;
    }
  • 对于结果集的封装,使用的是apache.commons.beanutils包.
    /**
     * 将map的集合,转换为指定对象的集合
     * @param clazz 要转换为的对象类型
     * @param list 原数据
     * @return 转换后的数据
     * @time 2018年10月15日 上午11:35:53
     * @author authstr
     */
    public <T> List converListToModel(Class<T> clazz, List<Map> list){
        Assert.isTrue(clazz!=null,"返回类型不能为空",true);
        //如果返回值是SqlResult或是Map,不进行处理
        if(SqlResult.class.equals(clazz)||HashMap.class.equals(clazz) || Map.class.equals(clazz)){
            return list;
        }
        List<T> res=new ArrayList(list.size());
        //检查集合
        if(!ObjectUtils.isExist(list)){
            return res;
        }
        //遍历转换
        for(Map map:list){
            try {
                T bean = clazz.newInstance();
                BeanUtils.populate(bean, map);
                res.add(bean);
            } catch (Exception e) {
                log.error("无法正确的将sql结果集的某条数据转换为["+clazz.getName()+"]类型的数据;位置:AbstractDao-converListToModel;异常原因:"+e.getMessage());
            }
        }
    
  • 为了调用查看时方便,除了getBySQL主执行方法,还有一些参数跳转的方法(主要进行方法的跳转,不实际执行)


    参数跳转
    /**
     * 使用 属性+值 的参数方式,执行查询语句,获取返回值(参数跳转)
     * @param sql 查询sql语句
     * @param kv sql参数,键为属性项数组,值为 属性值数组
     * @param firstRows 查询起始行数
     * @param maxRows 查询数据条数
     * @param returnType 结果集封装的类型
     * @param <T> 结果集类型
     * @return 查询结果
     * @time 2019年4月5日11:27:12
     * @author authstr
     */
    public  <T> List<T> getByMapSQL(String sql,Map<String,Object> kv,Integer firstRows,Integer maxRows,Class<T> returnType){
        String[] fields=new String[kv.size()];
        Object[] values=new Object[kv.size()];
        int i=0;
        //遍历Map
        for (Entry<String, Object> entry : kv.entrySet()) {
            fields[i]= entry.getKey();
            values[i]= entry.getValue();
            i++;
        }
        return getBySQL(sql,fields,values,firstRows,maxRows,returnType);
    }
/**
     * 使用 参数值 的参数方式,执行查询语句,获取返回值(参数跳转)
     * @param sql 查询sql语句
     * @param values
     * @param firstRows 查询起始行数
     * @param maxRows 查询数据条数
     * @param returnType 结果集封装的类型
     * @param <T> 结果集类型
     * @return 查询结果
     * @return
     * @time 2019年4月5日16:53:36
     * @author authstr
     */
    public  <T> List<T> getByValuesSql(String sql,Object[] values,Integer firstRows,Integer maxRows,Class<T> returnType){
        return getBySQL(sql,null,values,firstRows,maxRows,returnType);
    }
    /**
     * 执行查询语句,获取返回值(参数跳转)
     * @param sql 查询sql语句
     * @param fields sql参数 属性项数组
     * @param values sql参数 属性值数组
     * @param returnType 结果集封装的类型
     * @param <T> 结果集类型
     * @return 查询结果
     * @time 2019年4月5日17:18:59
     * @author authstr
     */
    public  <T> List<T> getBySQL(String sql,String[] fields,Object[] values,Class<T> returnType){
        return getBySQL(sql,fields,values,null,null,returnType);
    }
  • 理论上,getByMapSQL方法使用setQueryParameters(Query query,Map<String, Object> kv )方法进行设置参数比当前进行参数分解后使用setQueryParameters(Query query,String[] paramNames,Object[] values)更高效些.不过这样会写两份执行sql的代码,当前代码还是以可维护性为重,效率在需要的时候针对性修改.

8.执行sql进行修改数据

  • 除开查询,有时候也要执行一些删除或者更新的sql语句
  • 相对于查询,执行的步骤比较少
    /**
     * 执行sql语句,对数据库进行操作(map参数)
     * @param sql sql语句
     * @param kv sql参数
     * @return 影响行数
     * @time 2018年11月13日 上午11:37:44
     * @author authstr
     */
    public int executeSQl(String sql,Map<String,Object> kv){
        NativeQuery query=getSession().createNativeQuery(sql);
        setQueryParameters(query, kv);
        return query.executeUpdate();
    }

    /**
     * 执行sql语句,对数据库进行操作(value参数)
     * @param sql sql语句
     * @param value sql参数
     * @return 影响行数
     * @time 2018年11月13日 上午11:39:14
     * @author authstr
     */
    public int executeSQl(String sql,Object[] value){
        NativeQuery query=getSession().createNativeQuery(sql);
        setQueryParameters(query, value);
        return query.executeUpdate();
    }

相关文章

  • hibernate的DAO层封装-基础功能

    1.需求/目的 提供对数据库操作的常用方法 隐藏实现细节,使之专注于业务的开发 2.使用环境 spring boo...

  • hibernate的DAO层封装-扩展功能

    1.说明 以[hibernate的dao层封装-基础功能]所说明的方法为基础 某些需求不太确定,根据实际需要进行调...

  • hibernate的DAO层封装-分页

    1. 说明 上接[hibernate的dao层封装-基础功能] 2.设计思路 查询操作基本都要经过三步1.调用者发...

  • 什么是SSH框架(spring+) 一.Hibernate(控制dao层):全自动化,对jdbc的封装,是一个标准...

  • spring 笔记1

    ssh struts:web层,ValueStack值栈,拦截器 hibernate mydatis: dao层 ...

  • 工程结构

    三大层: DAO层: DAO层:DAO层主要是做数据持久层的工作,负责与数据库进行联络的一些任务都封装在此,DAO...

  • Hibernate学习(环境搭建)

    JavaEE三层结构 web层Struts2框架 service层Spring框架 dao层hibernate M...

  • Hibernate框架

    Hibernate:基于持久层的框架(数据访问层使用) Service业务逻辑层 Dao 数据访问层 ORM(Ob...

  • ssm介绍

    持久层:DAO层(mapper) DAO层:DAO层主要是做数据持久层的工作,负责与数据库进行联络的一些任务都封装...

  • hibernate使用jpa对于基础dao的封装

    一、dao的接口 二、dao的实现

网友评论

      本文标题:hibernate的DAO层封装-基础功能

      本文链接:https://www.haomeiwen.com/subject/vkstiqtx.html