• Hibernate的Dao层通用设计


    hibernate作为一款优秀的数据库持久化框架,在现实的运用中是非常广泛的。它的出现让不熟悉sql语法的程序员能开发数据库连接层成为一种可能,但是理想与现实永远是有差距的。开发过程中如果只使用hql进行操作,并且表之间的关联配置很复杂的话,这将成为一种噩梦。还好我们伟大的hibernate支持原生的sql操作,这也大大的增加了hibernate的灵活性。下面我们探讨一下hibernate的dao层的通用设计。

    首先阐明一下显示情况中遇到的问题。假设数据库中有一张表,表名叫tbl_user,项目中对应的实体User,根据MVC的分层设计,我们会有UerDao的接口层,以及UserDaoImpl的接口实现层。对数据库最多的操作就是增删改查,我们的UserDao和UserDaoImpl中会有增删改查的方法。如果有多张表的时候,我们会发现Dao的接口层以及实现层充斥着大量的增删改查的代码,而且大部分情况这些代码都是类似的,那我们能不能将这些代码进行封装呢?答案是肯定的。

    下面进入正题,数据库使用mysql,创建一张名为tbl_user的表。

    CREATE TABLE `tbl_user` (
    `id` bigint(20) NOT NULL AUTO_INCREMENT,
    `name` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL,
    `login_name` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL,
    `pass_word` char(32) COLLATE utf8_unicode_ci DEFAULT NULL,
    `role_id` bigint(20) DEFAULT NULL,
    `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    `last_login_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
    `del_flag` enum('0','1') COLLATE utf8_unicode_ci DEFAULT '0',
    PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='用户表';

    然后我们创建一个实体User对应这张表。

    @Entity
    @Table(name = "tbl_user")
    public class User extends IdEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    // 创建时间
    private Date createTime;

    // 删除标记
    private String delFlag;

    // 最后登录时间
    private Date lastLoginTime;

    // 登录名
    private String loginName;

    // 姓名
    private String name;

    // 密码
    private String passWord;

    // 角色id
    private Long roleId;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "create_time")
    public Date getCreateTime() {
    return createTime;
    }

    public void setCreateTime(Date createTime) {
    this.createTime = createTime;
    }

    @Column(name = "del_flag")
    public String getDelFlag() {
    return delFlag;
    }

    public void setDelFlag(String delFlag) {
    this.delFlag = delFlag;
    }

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "last_login_time")
    public Date getLastLoginTime() {
    return lastLoginTime;
    }

    public void setLastLoginTime(Date lastLoginTime) {
    this.lastLoginTime = lastLoginTime;
    }

    @Column(name = "login_name")
    public String getLoginName() {
    return loginName;
    }

    public void setLoginName(String loginName) {
    this.loginName = loginName;
    }

    public String getName() {
    return name;
    }

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

    @Column(name = "pass_word")
    public String getPassWord() {
    return passWord;
    }

    public void setPassWord(String passWord) {
    this.passWord = passWord;
    }

    @Column(name = "role_id")
    public Long getRoleId() {
    return roleId;
    }

    public void setRoleId(Long roleId) {
    this.roleId = roleId;
    }

    }

    好了,下一步我们要进行要进行dao层代码的编写。但是现在不能写,我们的解决方案还没有理出来。首先我们可以肯定的是常规的CRUD代码不能写在UserDao和UserDaoImpl中了,那写在哪里呢。遇到这种情况首先想到的就是继承,如果有一个父类具有这种能力的话,再去继承这个父类,问题就迎刃而解了。好,方案确定,动手操作吧。我们先创建一个接口,命名为BaseDao,这里用到了接口泛型的概念。

    public interface BaseDao<T> {
    
    /*
    * 持久化对象的方法
    */
    public long add(T t);
    
    /*
    * 修改对象
    */
    public void update(T t);
    
    /*
    * 删除对象
    */
    public int delete(long id);
    
    /*
    * 查找所有对象的方法
    */
    public List<T> getAll();
    
    
    /*
    * 根据id查找对象
    */
    public T getById(long id);
    
    /*
    * 根据map条件查找对象
    */
    public List<T> getByMap(Map<String,Object> map);
    
    
    /*
    * 查询条数
    */
    public int countBySql(String sql);
    
    /*
    * 执行hql语句
    */
    public int executeHql(String hql);
    
    /*
    * 执行sql语句
    */
    public int executeSql(String sql);
    
    
    /*
    *根据sql语句查询map集合
    */
    public List<Map<String,Object>> getMapListBySql(String sql);
    }

    上面的顶层接口BaseDao定义了一些常用的dao层操作。我们在接口层再定义一个实体User对应的dao接口UserDao。

    public interface UserDao extends BaseDao<User> {
    
    }

    接口UserDao中什么方法都没有定义,只是去继承了BaseDao。接下来我们要编写dao实现层的代码了,首先我们编写dao实现层整个父类的BaseDaoImpl的代码,这个时候我们停一下整理一下思路。父类BaseDaoImpl要具有CRUD的操作,并且要是动态的,就是不同的操作要相应的操作不同的表,在hibernate中我们也可以说是操作不同的实体。如何实现这种想法呢?答案还是泛型,在继承父类BaseDaoImpl的时候注入自己的真实类型。下面就是BaseDaoImpl的代码, 这也是最核心的地方。

    public class BaseDaoImpl<T> implements BaseDao<T>{
    
    
    @Autowired
    private SessionFactory sessionFactory;
    
    protected Class<T> clazz;
    
    
    
    /**
    * 构造方法自动注入真实的对象类型
    */
    public BaseDaoImpl() {
    ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
    clazz = (Class<T>) type.getActualTypeArguments()[0];
    }
    
    public Session getCurrentSession() {
    return this.sessionFactory.getCurrentSession();
    }
    
    @Override
    public long add(T t){
    if (t!=null) {
    return (Long) this.getCurrentSession().save(t);
    }
    return 0;
    }
    
    
    
    @Override
    public void update(T t){
    if (t!=null) {
    this.getCurrentSession().update(t);
    }
    }
    
    
    @Override
    public int delete(long id){
    String hql = "delete "+clazz.getSimpleName()+" where id="+id;
    return this.getCurrentSession().createQuery(hql).executeUpdate();
    }
    
    
    @Override
    public List<T> getAll(){
    String hql = "from "+clazz.getSimpleName();
    return this.getCurrentSession().createQuery(hql).list();
    }
    
    
    
    @Override
    public T getById(long id){
    String hql = "from "+clazz.getSimpleName()+" where id="+id;
    return (T) this.getCurrentSession().createQuery(hql).uniqueResult();
    }
    
    
    @Override
    public List<T> getByMap(Map<String,Object> map){
    Set<String> set = map.keySet();
    if (set.size()>0) {
    List<String> list = new ArrayList<String>();
    for (String string : set) {
    list.add(string);
    }
    String hql = "from "+clazz.getSimpleName()+" where ";
    for(int i=0;i<=list.size()-1;i++){
    if (i==0) {
    hql += list.get(i)+"='"+map.get(list.get(i))+"'";
    }
    else{
    hql += " and "+list.get(i)+"='"+map.get(list.get(i))+"'";
    }
    }
    return this.getCurrentSession().createQuery(hql).list();
    }
    return null;
    }
    
    
    @Override
    public int countBySql(String sql){
    SQLQuery q = this.getCurrentSession().createSQLQuery(sql);
    return ((BigInteger) q.uniqueResult()).intValue();
    }
    @Override
    public int executeHql(String hql){
    Query q = this.getCurrentSession().createQuery(hql);
    return q.executeUpdate();
    }
    
    
    @Override
    public int executeSql(String sql){
    SQLQuery q = this.getCurrentSession().createSQLQuery(sql);
    return q.executeUpdate();
    }
    
    
    
    @Override
    public List<Map<String, Object>> getMapListBySql(String sql) {
    SQLQuery query = this.getCurrentSession().createSQLQuery(sql);
    query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
    return(List<Map<String, Object>>) query.list();
    }
    
    
    }

    BaseDao中的方法上已经写了注释,所以下面的实现类我就偷懒没有写注释了。我们重点研究其中一个方法


    我们在BaseDaoImpl中定义了一个全局变量clazz,利用BaseDaoImpl的构造方法将通过泛型传进来的真是类型的Class传到此变量中。此时,在这里可能会产生一个疑问,如果使用了spring,所有的bean管理都是由spring来完成的话,此处的构造方法会得到调用吗?答案是肯定的,spring创建bean的时候也是通过调用构造方法创建bean实例的。得到此class后我们就可以利用反射机制得到实体的真实名称进行hql的拼接。好了,我们可以去实现UserDao这个接口了。

    @Repository
    public class UserDaoImpl extends BaseDaoImpl<User> implements UserDao {
    
    }

    我们只要在实现UserDao接口的同时同时继承BaseDaoImpl这个父类,把实体的真实类型传进去就行了。虽然UserDaoImpl中一个方法都没有,实际上它已经具有父类中的所有方法了。BaseDao代码中只是示例了几个常用的方法,现实中可以进行扩充,比如分页的方法之类的,在BaseDaoImpl去实现就可以了。如果UserDaoImpl需要的方法在父类中没有,并且方法具有特殊性的话,可以在UserDao定义此方法,在UserDaoImpl中去实现就可以了。其他实体所对应的Dao层操作也是一样。


    ————————————————
    版权声明:本文为CSDN博主「小哥被占用了」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/qq_26800725/article/details/52032537

  • 相关阅读:
    最新Navicat Premium12 破解方法,亲测可用
    (转)Navicat_12安装与破解激活,注册机亲测可用
    使用ApiPost模拟发送get、post、delete、put等http请求
    模拟POST、Get 请求的工具----APIpost(中文版POSTMAN)
    推荐一款接口文档生成工具,apipost,好用
    作为后端开发者,如何更优雅、便捷的生成接口文档?
    使用apipost调试api接口并快速生成接口文档的一些小技巧,比postman更好用
    中文版postman——apipost,不试一下,你就不知道它有多香
    ApiPost如何在预执行脚本里添加请求参数?
    ApiPost的预执行脚本和后执行脚本
  • 原文地址:https://www.cnblogs.com/wuwuyong/p/14594433.html
Copyright © 2020-2023  润新知