烟酒生-

导航

纯手工编写第一个Hibernate程序

转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://legend2011.blog.51cto.com/3018495/723154 

 

 

Hibernate是目前很流行的一个对象/关系映射(Object/Relation Mapping,ORM)框架,在企业级应用开发中得到了广泛的应用。那么什么是ORM呢?

 

    IT界就是这样,总是喜欢制造一些看似高深莫测的名词出来,把我们忽悠得晕头转向,什么ORM,云计算等等不一而足。好像只有这样才能显示出IT的高贵。 不过,这可害惨了初学者,他们可能在心里犯嘀咕:这玩意儿会不会很难啊,我能不能学会啊?简言之,就是对初学者的自信心造成了一定的负面影响。

 

    对于这个问题,我们可以借用毛爷爷的一句经典言论:战略上藐视它,战术上重视它。也就是说,我们要首先树立信心,相信它也不是什么高深莫测的玩意儿,是完全可以学会的。为了达到这个目的,我们就在战术上重视它,认真学习,从而达到学会的战略目标。
    任何一项技术都是为了解决某个问题而存在的,而不是为了存在而存在。ORM无非是说:我们目前的主流程序设计语言是面向对象的,我们都喜欢对对象 (Object)进行操作;比如一个user对象,它肯定有自己的ID,姓名、年龄和性别等等属性,我们通过user对象对其属性进行操作,显得自然而方 便;然而,这个user对象必须被保存到数据库中(即所谓的持久化),才能供以后多次使用;目前主流的数据库都是关系型(Relation)的,此 user对象的ID等等属性都是数据库中的某张表的某一行的那几个列;如果我们使用传统的JDBC来访问这些属性的话,不仅繁琐(写过JDBC代码的童鞋 们都会懂的)、不直观(我们得自己想象某个列的值是某个对象的某个属性,即我们是对列进行操作而不是对对象进行操作),而且不得不重复编写大量的JDBC 代码;这样的后果就是,开发效率低,出错几率大;为了解决这个问题,Hibernate横空出世,借助于它的帮助,我们只需要在对象级别操作对象的属性 (是不是很符合人类的自然思维呢?),然后Hibernate自动帮我们把我们对对象的操作反馈到数据库中相应表的相应行中的一个或多个字段上(这其中的 JDBC代码和SQL语句它就帮我们自动生成了)。这样一来,我们是不是省了很多事呢?开发效率和质量会由此提升。
    现在,你是不是跃跃欲试,想尝试一下Hibernate的这些强大功能呢?笔者开始学习Hibernate的时候,也是这样的。但是笔者所见到的万恶的学 习资料在介绍如何编写第一个Hibernate程序的时候,不是要用到Maven,就是要用到ant等等笔者一无所知的东西!这样不是陡增我的学习难度 吗?幸好跌跌撞撞一路走来,总算度过了最黑暗的时刻。痛定思痛,为帮助今天的初学者少走一点弯路,能够尽快编写出自己的第一个Hibernate程序以提 高自信,达到战略上藐视它的目的,下面就介绍如何用纯手工的方法来编写自己的第一个Hibernate程序 (Hibernate 3.6.7 Final,下载地址:http://sourceforge.net/projects/hibernate/files/hibernate3/3.6.7.Final/)。
    在这里先申明一下,纯手工编写的目的有三:一是不需要你预先熟悉eclipse等IDE或ant等工具的用法;二是让你明白程序运行的机理,知其然并知其 所以然;三是让你知道若使用IDE,它都帮你做了什么,不要因为使用了IDE,就不明白程序是如何运行起来的了。
    好了,闲话少说,进入正题。为了编写这个程序,笔者选择使用SQL Server 2008作为我们的数据库服务器,原因是相对于其他DBMS,它是比较 简单易用的。一句话,降低难度,尽快写出我们的第一个程序。在这里我们模拟用户管理,假设每个用户都有自己的ID、姓名、年龄和性别这几个属性。我们先在 SQL Server中建立数据库mydb,然后建立表users,包括以下几个字段:ID(identity(1,1),即自增字段,从1开始,每次自 增1),name(nvarchar(10)),age(int)和gender(nvarchar(1))。接下来,我们用Hibernate写一个程 序,往这个表中添加一个用户的信息。为了编写这个程序,笔者在目录E:\DemoPrograms下新建了一个FirstHibernate目录,专门存 放本程序涉及到的所有文件。
    第一步,我们要写好一个代表用户实体的类,在这里我们写一个User类(User.java)。与数据库中的users表的字段相对应(映 射,mapping),我们为User类定义好ID等属性,以及这些属性的setter和getter方法,方便对用户实体的这些属性的操作。代码如下 (若定义类时为其声明所在的包,则放置编译后产生的class文件易出错,所以本文中所有的类都没有package声明):
 
    public class User {  
          
        private int id;  
        private String name;  
        private int age;  
        private String gender;  
          
        public int getId() {  
            return id;  
        }  
     
        public void setId(int id) {  
            this.id = id;  
        }  
     
          
        public String getName() {  
            return name;  
        }  
     
        public void setName(String name) {  
            this.name = name;  
        }  
          
        public int getAge() {  
            return age;  
        }  
     
        public void setAge(int age) {  
            this.age = age;  
        }  
     
      public String getGender()  
      {  
         return gender;  
      }  
        
      public void setGender(String gender)  
      {  
         this.gender = gender;  
      }  
          
    }  

写好后,将User.java文件保存在FirstHibernate目录下。

    第二步,为了让Hibernate知道用户对象的属性(即定义在User类中的属性)与users表的哪个字段相对应,需要我们提供一个映射文件。为此, 我们创建一个名为User.hbm.xml的文件(此文件名由类名User,加上约定俗成的后缀hbm.xml组成。其中的hbm应该是 hibernate mapping的意思吧!),放置在FirstHibernate目录下。这是一个典型的XML文件,其内容如下:
    <?xml version="1.0" encoding="gb2312"?> 
    <!--上面这行的encoding="gbk"指定本文件的编码类型。  
    这一行信息必须出现在文件的首行,即使前面有空行或注释都不行。  
    否则会报错。  
    --> 
    <!--指定相关的DTD信息,例行的信息,照抄即可。--> 
    <!DOCTYPE hibernate-mapping PUBLIC  
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 
        <!--在hibernate-mapping元素中指定映射关系--> 
        <hibernate-mapping> 
            <!--用一个class元素指定用户属性与users表字段之间的映射关系--> 
            <!--name指定代表用户的User类。本应带包名,但User类没有package  
        声明,故可省略。table指明与之映射的数据库中的表。--> 
        <class name="User" table="users"> 
               <!--  
               id元素指定用户标识属性到users表主键的映射。  
               标识属性用于唯一地标识一个用户。id元素中的name  
               的值即为User类中用于标识用户的属性名,在这里为id。  
               type指定数据类型,注意不要忘了完整的包名。  
               column指定User类的id属性与users表中的ID这个字段(主键)  
               映射。两者数据类型要一致。  
               --> 
               <id name="id" type="java.lang.Integer" column="ID"> 
                  <!--  
                generator元素指定主键生成策略。在这里设为identity。  
                指由数据库的标识列生成。还记得我们把users表的ID字段  
                设为标识(identity)列吗?这样往users表中插入一条记录时,  
                将由数据库根据自增策略为这条记录生成一个主键。  
                --> 
                <generator class="identity"/> 
             </id> 
               
             <!--  
               以下三个property元素用来指定User类的普通属性到表字段之间的映射。  
               name指定User类中的属性,column属性指定与其映射的字段。  
               如第一个property元素表示User类的age属性映射到users表的age  
               字段。由于两者名字相同,所以其实可以不指定column的值,就像后  
               两个property元素那样。  
               注意:这三个property元素都没有指定type的值,那么将由hibernate  
               自行判定类属性和表字段之间的属性转换。这在本例中会正确执行。  
            --> 
            <property name="age" column="age"/> 
            <property name="name"/> 
            <property name="gender"/> 
         </class> 
    </hibernate-mapping> 

    根据上面映射文件的id元素和property元素,hibernate就知道了该使用哪个getter和setter方法对User对象的属性进行操作。

    第三步,我们还应该告诉hibernate应该到哪里连数据库,用到的用户名和密码,以及JDBC驱动程序等信息。这就需要用到一个配置文件来保存这些信 息,这里我们采用使用较多的xml文件(另一种较少使用的是properties属性文件),其默认文件名为hibernate.cfg.xml,内容如 下。
    <?xml version="1.0" encoding="gb2312"?> 
     
    <!DOCTYPE hibernate-configuration PUBLIC  
            "-//Hibernate/Hibernate Configuration DTD 3.0//EN"  
            "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> 
     
    <!--hibernate-configuration是配置文件的根元素,配置信息都在此元素内。--> 
    <hibernate-configuration> 
     
        <!--session-factory子元素。--> 
        <session-factory> 
     
            <!-- 指定连接SQL Server 2008的驱动类。需提供相关的JAR包,可在微软官方  
            网站下载。--> 
            <property name="connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</property> 
              
            <!--连接URL--> 
            <property name="connection.url">jdbc:sqlserver://localhost;databaseName=mydb</property> 
              
            <!--用户名和密码--> 
            <property name="connection.username">sa</property> 
            <property name="connection.password">admin123</property> 
     
            <!-- 使用内置连接池,大小为1 --> 
            <property name="connection.pool_size">1</property> 
     
            <!-- 由于不同数据库之间存在差异,hibernate就需要相关的数据库方言  
             来处理这种差异性。如下指定了针对于SQL Server的数据库方言。  
             --> 
            <property name="dialect">org.hibernate.dialect.SQLServerDialect</property> 
     
            <property name="current_session_context_class">thread</property> 
     
            <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property> 
     
            <!-- 向标准输出设备输出hibernate为我们生成的SQL语句。 --> 
            <property name="show_sql">true</property> 
     
            <!--指定要用到的映射文件,就是我们刚才创建的。--> 
            <mapping resource="User.hbm.xml" /> 
              
        </session-factory> 
     
    </hibernate-configuration> 
    将此文件保存在FirstHibernate目录下。
    接下来我们该做什么呢?你肯定会疑问:到目前为止,我们写的都是JAVA程序和配置文件,怎么没见到一点hibernate框架的影子呢?是 的,hibernate框架就是提供给我们的一个个的JAR包,而到目前为止我们都还没有见到其庐山真面目。这些JAR包可在hibernate官方网站 上自由下载,本例中使用到的JAR包(其中有些是第三方的JAR包)如下:
    其中hibernate3.jar是核心包,马上要用到的接口如org.hibernate.Session和 org.hibernate.SessionFactory,以及类org.hibernate.cfg.Configuration,它们的定义都在此 包中。最后一个JAR包,即sqljdbc4.jar是微软提供的用于访问SQL Server的JDBC驱动程序,我们在配置文件 hibernate.cfg.xml中指定的驱动类,就在此JAR包中。Hibernate将使用此驱动程序,来对我们的数据库进行操作。其他的JAR包 具体有什么作用,在这里就不一一细说了。随着我们越来越多地使用hibernate,相信就会逐渐明白、熟悉。我们在FirstHibernate目录下 创建一个lib目录,用来保存这些JAR包。
    最后一步,我们写一个有main方法的JAVA类,来驱使hibernate为我们访问数据库。程序名为TestHibernate.java,放置在FirstHibernate目录下,其内容如下:
//从JAR包中引入hibernate的相关接口、类。  
import org.hibernate.Session;  
import org.hibernate.SessionFactory;  
import org.hibernate.cfg.Configuration;  
 
import java.util.Random;  
 
public class TestHibernate {  
 
      
    public static void main(String[] args) {  
                  
    //创建Configuration对象,此对象代表了我们的程序到数据库  
    //的hibernate配置。调用其configure()方法,此方法会自动  
    //搜索hibernate.cfg.xml配置文件。  
    Configuration cfg = new Configuration().configure();  
      
    //调用Configuration对象的buildSessionFactory()方法来  
    //创建会话工厂(SessionFactory)对象。  
    SessionFactory factory = cfg.buildSessionFactory();  
         
    //从会话工厂获得会话(Session)对象  
    Session session = factory.getCurrentSession();  
          
        //开始事务  
        session.beginTransaction();  
          
        User user = new User();  
        user.setName("阿汤哥");  
        user.setAge(new Random().nextInt(21) + 20);  
        user.setGender("男");  
          
        //将此用户保存到数据库中的users表。在这里hibernate将用到  
        //映射文件User.hbm.xml中的信息。直接保存user对象,是不是  
        //比我们自己分别保存姓名、年龄等信息要简单、形象多了呢?  
        //Hibernate将自动为我们生成insert语句。  
        session.save(user);  
          
        //提交事务。  
        session.getTransaction().commit();  
          
    //关闭SessionFactory  
    factory.close();  
    }  
 
      
}
    到目前为止,似乎大功告成,那让我们来编译、运行一下这个程序,看有什么效果。作为准备工作,首先确保已设置了classpath环境变量,并将当前目录 (.)添加进了此变量。可在命令行窗口下临时设置只对本命令窗口有效的classpath环境变量,并添加当前目录,命令 为:set classpath=.。以下叙述都在此设置下进行。打开命令行窗口,切换到E:\DemoPrograms\FirstHibernate 目录下,运行命令javac *java,编译所有JAVA源程序。
    经过了似乎是很漫长、焦灼的等待,我们迎来了几个错误。仔细一看,核心错误就是org.hibernate和org.hibernate.cfg这两个软 件包找不到。当然找不到了!这两个软件包的class文件在hibernate3.jar包里,而我们没有把这个jar包添加到classpath中,这 样就当然找不到了。怎么添加呢?由于这个JAR包在目录E:\DemoPrograms\FirstHibernate\lib下,因此我们可以这样写命 令:
set classpath=%classpath%;E:\DemoPrograms\FirstHibernate\lib\hibernate3.jar。
在这里%classpath%的意思是把classpath现有的值取出,再加上我们需要添加的值,它们之间要用英文的分号(;)隔开,就得到 了新的classpath。注意,hibernate3.jar必须要有,不能因为它只是把class文件打包,就认为不需要提供JAR包名JAVA能够 找到相应的class文件,JAR包也类似于一层目录。如果犯了这种错误就很难排错了。
    运行完此命令后,我们再来编译,就没有问题了。
    编译成功后,接下来就是运行了。既然编译都成功了,那么成功运行不就是顺理成章的事情吗?在运行前要保证开启了SQL Server服务,不然怎么访问数据库呢?在命令窗口中输入如下命令来运行我们的程序:java TestHibernate。
    就在你万分激动地等待自己的第一个hibernate程序正确执行的时候,程序却很不“识趣”地报出了错误信息:java.lang.NoClassDefFoundError。 
    是不是犹如被泼了一瓢冷水?别灰心,学习程序设计经常遇到这种事情。遇到了并不可怕,只要我们仔细分析错误信息,是能够找到错误原因的。这个错误信息是 说,它找不到org.dom4j.DocumentException这个类的定义。我们在程序中并没有使用这个类,那么我们可以顺理成章地猜想,应该是 Hibernate使用了这个类。这个类在JAR包dom4j-1.6.1.jar中,而这个JAR包我们已经把它放到了lib目录下了。我们现在需要做 的,就是运行类似于上一条的set命令,把此JAR包添加进classpath环境变量中,好让JAVA能够找到类 org.dom4j.DocumentException的定义。

    实际上,放置在lib目录下的每个JAR包都要用到。上面的方法解决了类org.dom4j.DocumentException的问题后,但还会有很多 Hibernate要用到的,或被Hibernate调用的其他模块要用到的类找不到。这些类都包含在剩下的那些JAR包中。读者可以自己试一下,如果现 在运行程序,它还会报错说XX类找不到,我们就又得把这个类所在的JAR包添加到classpath环境变量里。然后又会报错说YY类找不到,直到我们把 所有的JAR包都添加进去。为把这些JAR包一次性地添加到classpath中,笔者写了一个批处理文件setclasspath.bat,放置在 FirstHibernate目录下。如果你建立的目录和目录结构,以及各文件的存放位置,都与笔者完全一致(包括所在的盘,我这里是E盘),那么这个批 处理文件你就可以直接使用。其源码如下(用UltraEdit或Windows记事本等文本编辑器创建一个文本文件,然后将其后缀名改为bat,就能创建 一个批处理文件。再用文本编辑器打开就能输入想要执行的DOS命令)

    set classpath=.;E:\DemoPrograms\FirstHibernate\lib\hibernate3.jar;E:\DemoPrograms\FirstHibernate\lib\sqljdbc4.jar;E:\DemoPrograms\FirstHibernate\lib\dom4j-1.6.1.jar;E:\DemoPrograms\FirstHibernate\lib\slf4j-api-1.6.2.jar;E:\DemoPrograms\FirstHibernate\lib\slf4j-simple-1.6.2.jar;E:\DemoPrograms\FirstHibernate\lib\hibernate-jpa-2.0-api-1.0.1.Final.jar;E:\DemoPrograms\FirstHibernate\lib\cglib-2.2.jar;E:\DemoPrograms\FirstHibernate\lib\commons-collections-3.1.jar;E:\DemoPrograms\FirstHibernate\lib\javassist-3.12.0.GA.jar;E:\DemoPrograms\FirstHibernate\lib\jta-1.1.jar 

    现在再来运行程序。

    看见Hibernate打印输出的日志信息,以及为我们生成的insert语句,我们可以很高兴地确定,程序运行成功了!我们进入SQL Server数据库,就可以查询出程序插入到users表中的数据了,是不是很高兴呢?
    这个示例相当简单、原始。不过毕竟是我们纯手工编写的第一个Hibernate程序,帮助我们树立了学习的信心。遇到问题了,只要我们仔细分析错误原因, 都能找到错误原因。实在解决不了的,一般都是知识暂时积累不够。这时候可以先放一放,等到知识积累到位了自然就能解决了。不过一定不要丧失信心哦!
    坚持不懈,一定能学好程序设计!
    (点下面的“附件下载”可下载本示例的所有程序和配置文件,以及hibernate没有提供的SLF4J的JAR包和MS SQL Server JDBC驱动的JAR包。受最大下载尺寸限制,无法提供所有JAR包下载,但用到的其他JAR包hibernate都提供了。下载程序和配置文件后,直接 解压缩到E:\DemoPrograms目录下。然后把这些JAR包复制到lib目录下,即可使用了。)
     (后记:在本例中,为了降低程序编写的难度,所有的类都没有包的声明。然而在实际开发中,为了解决命名冲突的问题,通常都把类声明在某个包中。至于如何为类添加包声明,请参看笔者的下一篇博文:对“纯手工编写的第一个Hibernate程序”的改进。)

posted on 2014-06-20 14:31  烟酒生-  阅读(108)  评论(0)    收藏  举报