Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 390|回复: 0

[Java基础知识]自己实现ORM

[复制链接]
  • TA的每日心情
    开心
    2021-3-12 23:18
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2014-10-1 18:04:15 | 显示全部楼层 |阅读模式
    这篇文章源自刚开发的一个小项目。项目中并未使用hibernate,但我还是要把它放在hibernate栏下。理由很简单,技术是死的,而人是活的,能熟练的使用一项技术不算什么,但能恰当的选择相应的技术,甚至自己想出办法来优雅的解决实际问题,就需要一定的积累了。而这种积累就来源自项目实践和对各种技术其实质的理解。我记得在某个论坛上某人(名字忘了)说过一句话:如果学习hibernate只是学会了怎么mapping,怎么写DAO,就只是学了皮毛,学hibernate就要从中了解到很多持久层的最佳实践。我深以为然!
      
        项目很简单,页面也不多,但页面字段较多(100多个),相互间还有一定关联。而且存入数据库类型多样,有varchar, integer和date几种。我是很希望用hibernate来实现的,但考虑到项目时间较紧,而我对hibernate的了解还是停留在理论和学习阶段(有点落后啊!),采用不熟悉的技术项目风险较大,所以还是使用普通JDBC作为持久层方案。面对这么多字段,一个个去拼SQL语句,代码臃肿,而且容易出错,也难以维护。我得对这几种类型的字段,在插入、更新和读取时分别处理,写一个private方法,传入类型和字段名,读取相应的ResultSet,是不错的方法,至少是比较优雅的实现。  

      
       
      
      什么是优雅?动态设置,避免hardcode,就是优雅;层次清晰,层次间耦合最低,就是优雅;只写一处,处处引用,就是优雅;代码精炼,避免过度设计,就是优雅;接口明确,调用简单,就是优雅;调试容易,便于测试,就是优雅。。。。。。而优雅的设计和实现,在可扩展性、可维护性、开发效率、开发成本等方面都是最好的。 Hibernate就是优雅的设计,它通过配置文件,建立实体与数据库的映射,动态的生成SQL语句,避免了对属性字段的hardcode,这就是它最本质的思想。我不使用hibernate,但一样可以借鉴它的思想,我不需要对象容器、分页查询等等高级功能,因此可以简单的实现类似ORM的功能。
      首先,定义一个配置类,将数据库字段和类型定义下来。按照常规的做法,从页面字段到对象属性到数据库都应该建立映射,这样需要生成相应的映射类。为简单起见,我不使用POJO,而是使用Map作为数据的载体,key就是数据库字段名,在页面端我也用数据库字段名作为域的名称。这样我直接通过名称建立映射,牺牲了扩展性和灵活性,但简化了操作,也无需映射类,只要一个映射Map,key是数据库字段名,value是我自己定义的字段类型。以后动态生成SQL和Map传输对象,以及从页面request动态生成Map都是基于这个配置。
      接下来,就很简单了。先处理DAO的动态生成SQL代码,下面是生成insertSQL的方法,   private static String generateInsertScoreSQL(Map m, String studentId) {

                   StringBuffer sb = new StringBuffer();

                   StringBuffer sb2 = new StringBuffer();
      
                   sb.append("insert into STUDENT_SCORE (id,");

                   sb2.append(" values("");

                   sb2.append(studentId);

                   sb2.append("",");

                  for (Iterator iter = m.keySet().iterator(); iter.hasNext();) {

                          String name = (String) iter.next();

                          String value = (String) m.get(name);

                          sb.append(name);

                          sb.append(",");

      

                          String type = (String) ScoreColumnMapping.scoreItemMap().get(name);

                         if (ScoreColumnMapping.INT.equalsIgnoreCase(type)) {

                                if (value == null || value.length() == 0) {

                                        sb2.append("null,");

                                } else {

                                        sb2.append(Integer.parseInt(value));

                                        sb2.append(",");

                                }

                         } else if (ScoreColumnMapping.STRING.equalsIgnoreCase(type)) {

                                 sb2.append(""");

                                 sb2.append(value);

                                 sb2.append("",");

                         }

                  }

                   sb.replace(sb.length() - 1, sb.length(), ")");

                   sb2.replace(sb2.length() - 1, sb2.length(), ")");

                   log.info(sb.toString() + sb2.toString());

                   return sb.toString() + sb2.toString();

            }
      

    生成updateSQL的代码:
          

        private static String generateUpdateScoreSQL(Map m, String studentId) {

                   StringBuffer sb = new StringBuffer();

      

                   sb.append("update STUDENT_SCORE set ");

      

                  for (Iterator iter = m.keySet().iterator(); iter.hasNext();) {

                          String name = (String) iter.next();

                          String value = (String) m.get(name);

                          sb.append(name);

                          sb.append("=");

      

                          String type = (String) ScoreColumnMapping.scoreItemMap().get(name);

                         if (ScoreColumnMapping.INT.equalsIgnoreCase(type)) {

                                if (value == null || value.length() == 0) {

                                        sb.append("null,");

                                } else {

                                        sb.append(Integer.parseInt(value));

                                        sb.append(",");

                               }

                         } else if (ScoreColumnMapping.STRING.equalsIgnoreCase(type)) {

                                 sb.append(""");

                                 sb.append(value);

                                 sb.append("",");

                          }

                  }

                   sb.replace(sb.length() - 1, sb.length(), "");

      

                   sb.append(" where id="");

                   sb.append(studentId);

                   sb.append(""");

      

                   log.info(sb.toString());

                   return sb.toString();

            }

      
      
    从ResultSet中生成Map对象的代码:
      
      
      

      private static void getScoreFromRs(ResultSet rs, Map m) throws SQLException {

                   String name;

                   String type;

                   for (Iterator iter = ScoreColumnMapping.scoreItemMap().keySet()

                                 .iterator(); iter.hasNext();) {

                          name = (String) iter.next();

                          type = (String) ScoreColumnMapping.scoreItemMap().get(name);

                          if (ScoreColumnMapping.INT.equalsIgnoreCase(type)) {

                                 Object value = rs.getObject(name);

                                 m.put(name, String.valueOf(value == null ? "" : value));

                         } else if (ScoreColumnMapping.STRING.equalsIgnoreCase(type)) {

                                 String value = rs.getString(name);

                                 m.put(name, value);

                          }

                   }

      

            }

      
    因为只有一个DAO采用这种方式,所以我用private方法,这可以通过重构,将其抽取到Util类中,供所有DAO使用。页面端也很简单,我做个Facade类,它获取request,并将其处理成一个Map,然后交给数据层处理(因为比较简单,省去了业务层),代码如下:
      
      

       
      
      
       
      
       Map m = new HashMap();

             for (Iterator iter = ScoreColumnMapping.scoreItemMap().keySet().iterator(); iter.hasNext();) {

                 String name = (String) iter.next();

                 String value = request.getParameter(name);

                          ……

    m.put(name, value);

    ……

       
         
      
      
    所有方法中未出现一个字段名称,全部是从配置类中动态生成。这样带来了很多好处:
    u       扩展容易,如果需增加字段,无需更改核心代码,只要修改配置文件和数据库表定义,然后页面上加加域
    u       开发效率提高,整个核心代码,几个小时搞定,然后就是处理表单域间的关系和显示逻辑,DAO层快速开发,而且基本减少出错的可能性(出错也是配置文件错)。
    u       排错容易,我定义了清晰的Exception机制和log机制,出错只记录一次,并通过log.info记录生成的SQL语句,很容易根据这些信息查出错误原因并解决。

       
         
       
    呵呵,其实很多东西并不如我们想象的那么复杂,看东西要看其本质。普元不是提出什么面向构件,xml数据总线的理论吗?它直接传xml文件?还不是用Map做载体,然后动态生成SQL语句。我这个东西扩展出去,定义完善的xml配置,然后多几层封装,然后加上些企业特性,也能出去吹吹的。
    但这也是权宜之计,以后我还是会使用hibernate,而不是把这个东西做完善。不重新发明轮子,这也是open source社区的共识。但自己做做也挺有意义的,起码能培养自己的创造力,也能对技术实质有第一手的了解,而且很开心。
    做点东西,学点技术,然后进行些反思和总结,这是我一贯的做法。也希望能对大家有所启发。
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-3 12:32 , Processed in 0.361588 second(s), 48 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表