• 1.Hibernate框架核心组件 (转自冯岩)


    Hibernate框架核心组件

    Hibernate框架简述中,演示了一个简单的Hibernate应用,但并没有深入说明其中程序,在这篇中将比较详细的介绍一下Hibernate的核心组件。
    首先最关键一点就是Hibernate的配置文件,如果仔细看过我前面的笔记构建一个简单的基于MVC模式的JavaWeb ,我们可以大概知道hibernate的配置文件就相当于构建一个简单的基于MVC模式的JavaWeb 一文中的db.properties文件,当时我们在db.properties文件中设置了连接数据库的参数,因此Hibernate的配置文件作用也是如此,但它还有一个作用是要定义程序中我们用到的映射文件(***.hbm.xml)。
    例:
     1 <?xml version='1.0' encoding='GBK'?>
     2 <!DOCTYPE hibernate-configuration PUBLIC
     3           "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
     4           "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
     5 <hibernate-configuration>
     6 <session-factory>
     7     <property name="connection.username">root</property>
     8     <property name="connection.url">
     9         jdbc:mysql://localhost:3306/study
    10     </property>
    11     <property name="dialect">
    12         org.hibernate.dialect.MySQLDialect<!-- 这里是MySQL的Dialect -->
    13     </property>
    14     <property name="myeclipse.connection.profile">MySQL5.0</property>
    15     <property name="connection.password">root</property>
    16     <property name="connection.driver_class">
    17         org.gjt.mm.mysql.Driver<!-- 这里是MySQL的JDBC driver class名 -->
    18     </property>
    19     <property name="show_sql">true</property><!-- 向控制台输出SQL语句 -->
    20     
    21     
    22     <mapping resource="hibernate/PO/TRegister.hbm.xml" /><!-- 指定要用到的映射文件 -->
    23 
    24 </session-factory>
    25 
    26 </hibernate-configuration>
    另外它的持久化类其实就是一个普通的JavaBean,只不过一个持久化类对应 一个数据表。另外定义的时候 要遵循一定的规则!
    映射文件是把一个持久化类与一张表映射起来。
    例:
     1 <?xml version="1.0" encoding="GBK"?>
     2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
     3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
     4 <!-- 
     5     映射文件
     6 -->
     7 <hibernate-mapping>
     8     <class name="hibernate.PO.TRegister" table="t_register">
     9         <id name="id" type="java.lang.Integer">
    10             <column name="id" />
    11             <!-- 我在MYSQL中并没有设置该字段递增,但在Hibernate中依然可以设置为递增 -->
    12             <generator class="increment" />
    13         </id>
    14         <property name="userName" type="java.lang.String">
    15             <column name="userName" length="30" />
    16         </property>
    17         <property name="userPwd" type="java.lang.String">
    18             <column name="userPwd" length="30" />
    19         </property>
    20         <property name="sex" type="java.lang.String">
    21             <column name="sex" length="10" />
    22         </property>
    23         <property name="age" type="java.lang.Integer">
    24             <column name="age" />
    25         </property>
    26     </class>
    27 </hibernate-mapping>

    说明:
    class元素:
     用来定义一个持久化类,它的属性name和table分别为持久化的类名及对应到DB中的表名。
     id元素:
       是class的子元素。被映射的类必须声明对应数据表主键字段。大多数据有一个JavaBean 风格的属性,为一个实例包含惟一的标识。id元素定义了该属性到数据表主键字段的映射。它有name,type和column三个属性和一个generator子元素:
      generator元素 用来指定id标识的生成类的名字。它有一个属性class,用来指定一个Java类名。 该类用来为持久化类的实例生成惟一的标识。所以也叫生成器(generator)。如 果这个生成器实例需要某些配置值或者初始化参数,可用<param>元素来传递。

     所有生成器都实现org.hibernate.id.IdentifierGenerator接口。
    Hibernate提供了很多内置的生成器。下面是Hibernate内置生成器的快捷名字:
       1:increment(递增)。其为org.hibernate.id.IncrementGenerator类快 捷名字,用于为long,short或者int类型生成惟一标记。只有在没有其他进程往 同一张表中插入数据时才能使用。在集群下不要使用。
       2:identity(标识)。其为org.hibernate.id.IdentityGenerator类快捷 名字,对DB2,MySQL,SQL Server和Sybase等数据库的内置标识字段提供支持。 返回的标识符是long,short或者int类型的。
       3:sequence(序列)。其为org.hibernate.id.SequenceGenerator类的快 捷名字,为DB2,Oracle和SAP DB等数据闸的内置序列提供支持。返回类型是long, short或者int类型的。
       4:seqhilo(序列高/低位)。org.hibernate.id.SequenceHiLoGenerator 类的快捷名字,使用一个高/低位算法来高效地生成long,short或者int类型的标识符, 需要指定一个数据库sequence的名字。
       5:uuid.hex。其为org.hibernate.id.UUIDHexGenerator类的快捷名字,使用 一个128位的UUID算法生成字符串类型的标识符,在一个网络中是惟一的(使用了IP地 址)。UUID被编码为一个32位16进制的字符串,包含IP地址,JVM的启动时间(精确到 14s),系统时间和一个计算器值(在JVM中是惟一的)。
       6:assigned。其为org.hibernate.id.Assigned类的快捷名字,可让应用程序在
       执行save()方法之前为对象分配一个标识符。如果需要 为应用程序分配一个标识符(而 非由Hibernate来生成它们),可以用assigned生成器。
       7:foreign。其为org.hibernate.id.ForeignGenerator类的快捷名字。它使用了另外一个相关的对象标识符,和<one-to-one>元素一起使用。
     property元素:
      是class元素的一个子元素。它为类声明一个持久化的,JavaBean网络的属性。它有 name,colum,type和length等属性。

    Configuration类
    它负责管理Hiberante的配置信息,它主要用来加载这些配置文件。如上术的hibernate.cfg.xml文件!
    例:

    1 //加载Hibernate的配置文件
    2  Configuration config = new Configuration().configure("/hibernate/hibernate.cfg.xml");
    3  //还可以加载映射文件
    4   Configuration config = new Configuration().addFile("TRegister.hbm.xml");//方法一
    5  Configuration config = new Configuration().addClass(hibernate.PO.TRegister.class);
    6 //方法二
    7  Configuration config = new Configuration().addURL(Configuration.class.getResource("TRegister.hbm.xml"));
    8 //方法三
    9             

    SessionFactory接口
    SessionFactory负责Session实例的创建。可以通过Configuration实例创建
    例:

    1 Configuration config = new Configuration().configure("/hibernate/hibernate.cfg.xml");
    2     
    3 SessionFactory    sessionFactory = config.buildSessionFactory();

    Congifuration对象会根据当前的配置信息,生成SessionFactory对象。SessionFactory对象一旦构造完毕,即被赋予特定的配置信息,即以后配置改变不会影响到创建的SessionFactory对象。如果要把屐后的配置信息赋给SessionFactory对象,需要从新的Configuration对象生成拳的SessionFactory对象。
    SessionFactory是纯种安全的,可以被多线程调用以取得Session,而且构造SessionFactory很消耗资源,所以多数 情况下一个应用中只初始化一个SessionFactory,为不同的线程提供Session。
    当客户端发送一个请求线程时,SessionFactory生成一个Session对象来处理客户请求,如下:
     1 public static final ThreadLocal session = new ThreadLocal();
     2     
     3     public static Session currentSession() throws HibernateException
     4     {
     5         Session s = (Session)session.get();
     6         //Open a new Session,if this Thread has none yet
     7         //如果此线程还没有打开一个Session,则新建一个
     8         if(s == null || !s.isOpen())
     9         {
    10             s = sessionFactory.openSession();
    11             session.set(s);
    12         }
    13         return s;
    14     }


    Session接口
    Session是应用程序与数据库之间的一个会话,是Hibernate运作的中心,持久层操作的基础,相当于JDBC中的Connection。Session对象是通过SessionFactory创建的:
             Session      session = SessionFactory.openSession();
    一个持久化类与普通的JavaBean没有任何区别,但是它与Session关联后,就具有了持久化能力。当然,这种持久化操作是受Session控制的,即通过Session对象的装载,保存,创建或查询持久化对象。Session类的save(),delete()和load()等方法,来分别完成对持久化对象的保存,删除,修改加载等操作!Session类方法的用途可以分以下五类:
    1:取得持久化对象:get()和load()等方法。
    2:持久化对象的保存,更新和删除:save(),update()saveOrUpdate()和delete()等方法。
    3:createQuery()方法:用来从Session生成的Query对象。
    4:beginTransaction()方法:从Session对象生成一个Transaction对象。
    5:管理Session的方法:isOpen(),flush(),clear(),evict()和close()等方法,其中isOpen()方法用来检查Session是否仍然打开;flush()用来清理Session缓存,并把缓存中的SQL语句发送出去,clear()用来清除Session中的所有缓存对象evict()方法来清楚Session缓存中的某个对象;close()关闭Session。
    取得持久化对象的方法
    取得持久化对象的方法主要有get()和load(),它们通过主键id来取得PO。
    get()方法示例

     1 public void getDemo()
     2     {
     3         Session session = HibernateUtil.currentSession();//生成Session实例
     4         TRegister tr = (TRegister)session.get(TRegister.class, new Integer(1));
     5         
     6         System.out.print(tr.getUserName());
     7         /** *//**
     8          * get()方法的执行顺序如下:
     9          * a):首先通过id在session缓存中查找对象,如果存在此id的对象,直接将其返回
    10          * b):在二级缓存中查找,找到后将 其返回。
    11          * c):如果在session缓存和二级缓存中都找不到此对象,则从数据库中加载有此ID的对象
    12          * 因此get()方法并不总是导致SQL语句,只有缓存中无此数据时,才向数据库发送SQL!         
    13          */
    14     }
    load()方法示例:
     1 public void loadDemo()
     2     {
     3         Session session = HibernateUtil.currentSession();
     4         TRegister tr = (TRegister)session.load(TRegister.class, new Integer(1));
     5         System.out.println(tr.getUserName());
     6         /** *//**
     7          * 与get()的区别:
     8          * 1:在立即加载对象(当hibernate在从数据库中取得数据组装好一个对象后
     9          * 会立即再从数据库取得数据此对象所关联的对象)时,如果对象存在,
    10          * load()和get()方法没有区别,都可以取得已初始化的对象;但如果当对
    11          * 象不存在且是立即加载时,使用get()方法则返回null,而使用load()则
    12          * 抛出一个异常。因此使用load()方法时,要确认查询的主键ID一定是存在
    13          * 的,从这一点讲它没有get方便!
    14          * 2:在延迟加载对象(Hibernate从数据库中取得数据组装好一个对象后,
    15          * 不会立即再从数据库取得数据组装此对象所关联的对象,而是等到需要时,
    16          * 都会从数据库取得数据组装此对象关联的对象)时,get()方法仍然使用
    17          * 立即加载的方式发送SQL语句,并得到已初始化的对象,而load()方法则
    18          * 根本不发送SQL语句,它返回一个代理对象,直到这个对象被访问时才被
    19          * 初始化。
    20          */
    21     }

    持久化对象的保存,更新和删除方法
    save()方法:
    session的save()方法将 一个PO的属性取出放入PreparedStatement语句中,然后向数据库中插入一条记录(或多条记录,如果有级联)。
    session保存一个对象时,按如下步骤进行:
    1:根本配置文件为主键id设置的生成算法 ,为PO指定 一个ID。
    2:将 PO对象纳入session内部缓存(一个Map)内。
    3:事务提交时,清理缓存,将 新对象通过insert语句持久化到数据库中。
    如果要为新的PO强制指定一个ID,可以调用Session的重载方法save(Object obj,Serializable id)
    例:
           session.save(tRegister, new Integer(123));
    在调用save()方法时,并不立即执行SQL语句,而是等到清理完毕缓存时才执行。如果在调用save()方法后又修改了PO的属性,则Hibernate将 会发送一条insert语句和一条update语句来完成持久化操作,如下:

     1 public void saveDemo()
     2     {
     3         TRegister tr = new TRegister();
     4         tr.setId(1111);//为对象设定一个ID,但注意此ID是无效的,因为我们在配置文件中ID设置为Increment,这样将被Hibernate的increment算法生成的值覆盖
     5         tr.setAge(23);
     6         tr.setSex("男");
     7         tr.setUserName("evoll");
     8         tr.setUserPwd("efly");
     9         
    10         Session session = HibernateUtil.currentSession();
    11         Transaction tx = session.beginTransaction();
    12         session.save(tr);
    13         tr.setUserName("xuanflt");//在save后又修改了PO名字
    14         tx.commit();
    15     }

    监视上述程序运行会产生二条SQL:
    Hibernate: select max(id) from t_register
    Hibernate: insert into t_register (userName, userPwd, sex, age, id) values (?, ?, ?, ?, ?)
    Hibernate: update t_register set userName=?, userPwd=?, sex=?, age=? where id=?
    因此,最好是在对象状态稳定时再调用 save()方法,可以少执行一条update语句。
    调用save()方法将临时对象保存到数据库中,对象的临时状态将 变为持久化状态。当对象在持久化状态时,它一直位于Session缓存中,对它的任何操作在事物提交时都将同步保存到数据库中,因此,对一个已经持久化的对象调用save()或update()方法是没有意义的,如下:

     1 public void saveDemo()
     2     {
     3         TRegister tr = new TRegister();
     4         tr.setId(1111);//为对象设定一个ID,但注意此ID是无效的,因为我们在配置文件中ID设置为Increment,这样将被Hibernate的increment算法生成的值覆盖
     5         tr.setAge(23);
     6         tr.setSex("男");
     7         tr.setUserName("evoll");
     8         tr.setUserPwd("efly");
     9         
    10         Session session = HibernateUtil.currentSession();
    11         Transaction tx = session.beginTransaction();
    12         session.save(tr);
    13         tr.setUserName("xuanflt");//在save后又修改了PO名字
    14         session.save(tr);    //无效
    15         session.update(tr);    //无效
    16         tx.commit();    //提交事物
    17     }

    程序运行效果还是和上面的一样!
    update()方法:
    Session的update()方法是用来更新脱管对象的。它的用法如下:

    1 public void updateDemo()
    2     {
    3         TRegister tr = new TRegister();
    4         Session session = HibernateUtil.currentSession();
    5         Transaction tx = session.beginTransaction();
    6         tr = (TRegister)session.get(TRegister.class, new Integer(1));
    7         tr.setUserName("updated");//更新脱管对象
    8         tx.commit();
    9     }
    调用update()方法时,并不是立即发送SQL语句,对对象的更新操作将积累起来,在事物提交时由flush()清理缓存,然后发送一条SQL语句完成全部的更新操作!
    saveOrUpdate()方法:
    在实际应用中WEB程序员自言自语不会注意一个对象是脱管对象还是临时对象,而对脱管对象使用save()方法是不对的,对临时对象使用update()方法也是不对的。为了解决这个问题,便产生saveOrUpdate()方法。
    saveOrUpdate()方法首先会判断该PO是脱管对象还是临时对象,然后会调用合适的方法。
    那么saveOrUpdate()方法如何判断PO是临时对象还是 脱管对象呢?当满足下载情况之一时,Hibernate就认定它是临时对象。
    1:在映射表中为<id>设元素设置了unsaved-valu属性,并且实体对象的ID取值和unsaved-value匹配(默认为null)(注意:int和long型的ID的unsaved-value默认值为0)。
    2:在映射文件中为<version>元素设置了unsaved-value属性,并且实体对象的version取值和unsaved-value匹配(默认为null)。
    delete()方法:
    session类的delete()方法负责删除一个对象(包括持久对象和脱管对象),以下为删除一个持久对象的示例!
    1 public void deleteDemo()
    2     {
    3         TRegister tr = new TRegister();
    4         Session session = HibernateUtil.currentSession();
    5         Transaction tx = session.beginTransaction();
    6         tr = (TRegister)session.get(TRegister.class, new Integer(3));
    7         session.delete(tr);//删除持久对象
    8         tx.commit();
    9     }
    监视运行:
    Hibernate: select tregister0_.id as id0_0_, tregister0_.userName as userName0_0_, tregister0_.userPwd as userPwd0_0_, tregister0_.sex as sex0_0_, tregister0_.age as age0_0_ from t_register tregister0_ where tregister0_.id=?
    Hibernate: delete from t_register where id=?
     1   public void deleteDemo2()
     2     {    //演示删除脱管对象
     3         TRegister tr = new TRegister();
     4         Session session = HibernateUtil.currentSession();
     5         Transaction tx = session.beginTransaction();
     6         tr = (TRegister)session.get(TRegister.class, new Integer(2));
     7         tx.commit();
     8         session.close();
     9         Session session2 = HibernateUtil.currentSession();
    10         tx = session2.beginTransaction();
    11         session2.delete(tr);
    12         tx.commit();
    13         session2.close();
    14     }

    监视运行:
    Hibernate: select tregister0_.id as id0_0_, tregister0_.userName as userName0_0_, tregister0_.userPwd as userPwd0_0_, tregister0_.sex as sex0_0_, tregister0_.age as age0_0_ from t_register tregister0_ where tregister0_.id=?
    Hibernate: delete from t_register where id=?
    在上述代码中session2先把tr对象进行关联,纳入Session缓存中,然后删除。需要注意的是,在调用delete()方法时并不是发送SQL语句,而是在提交事务时,清理了缓存才发送SQL。
    使用delete()删除对象时,会有一些性能上的问题,例如从上面的监视中可以看到,当删除一个对象时,会先调用get()加载这个对象,然后调用delete()方法删除对象,所以发送了一个多余的selete SQL,所以当删除大量数据时对性能影响就比较大了。为了解决批量删除的性能问题,常用的办法是使用批量删除操作,如下:

    1 public void betchDeleteDemo()
    2     {
    3         Session session = HibernateUtil.currentSession();
    4         Transaction tx = session.beginTransaction();
    5         Query q = session.createQuery("delete from TRegister");
    6         q.executeUpdate();//删除对象
    7         tx.commit();
    8         session.close();
    9     }

    监视运行:
    Hibernate: delete from t_register
    只用了一条语句就完成了比量删除的操作。但它也有问题,批量删除后的数据还会存在缓存中,因此程序查询时可能得到脏数据(得到数据库中已不存在的数据),因此在使用批量删除时,也要经窒处世数据一致的问题。

    Query接口
    在Hibernate3.x中不再使用2.x中的find方法,而是引入了Query接口,用来执行HQL。其实在上面的例子中已经看出,Query接口可以从session对象生成;
    Query接口主要方法有三个
    setXXX()方法:用于设置HQL中问题或变量的值。
    list()方法:返回查询结果,并把结果转换成List对象。
    executeUpdate()方法:执行更新或删除名。

    setXXX()方法都有二种重载方法:
    1:setString(int position,String value):用于设置HQL中“?”的值:其中position表示?位置,而value自然就是值。如下:

    Query query = session.createQuery("from TRegister tr where tr.age>? and tr.userName like ?");//生成一个Query实例
            query.setInteger(018);//设置第一个问号的值为18
            query.setString(1"%yan%");//设置第二个问题的值为%yan%

    2:setString(String paraName,String value);用于设置HQL中“:”后跟变量的值;其中paraName代表HQL中“:”后跟变量,value为该变量设置的值。如下:

    Query query = session.createQuery("from TRegister tr where tr.age>:minAge and tr.userName like :userName");//生成一个Query实例
            query.setInteger("minAge"18);//设置变量minAge值为18
            query.setString("userName""%yan%");//设置变量userName值为%yan%

    在HQL中使用变量代替问号,然后在setXXX()方法中为该变量设值。在HQL中使用变量相比问号有以下好处:
    1:变量不依赖于它们在查询字符串中出现 的顺序。
    2:在同一个查询中可以多次使用。
    3:可读性好。
    list()方法:
    Query的list()方法用于取得查询结果,并将结果转变成一个List接口的实例。如下:

     1 public void queryDemo()
     2     {
     3         TRegister tr = new TRegister();
     4         Session session = HibernateUtil.currentSession();
     5         Query query = session.createQuery("from TRegister");
     6         java.util.List list = query.list();
     7         for(int i = 0 ; i <list.size(); i++)
     8         {
     9             tr = (TRegister)list.get(i);
    10             System.out.println(tr.getId());
    11         }
    12     }
    executeUpdate()方法:
    Query的executeUpdate()方法用于更新或删除语句。它常用于批量删除或批量更新操作,如下:
    1 Query q = session.createQuery("delete from TRegister");
    2         q.executeUpdate();//删除对象

    使用命名查询(namedQuery):
    其实我们还可以不将 HQL写在程序中,而是写入映射文件中,这样在需要的时候很方便!在映射文件中使用<query>标记,把HQL语句放入<![CDATA[]]>之中,如下:
     1 <?xml version="1.0" encoding="GBK"?>
     2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
     3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
     4 <!-- 
     5     映射文件
     6 -->
     7 <hibernate-mapping>
     8     <class name="hibernate.PO.TRegister" table="t_register">
     9         <id name="id" type="java.lang.Integer">
    10             <column name="id" />
    11             <!-- 我在MYSQL中并没有设置该字段递增,但在Hibernate中依然可以设置为递增 -->
    12             <generator class="increment" />
    13         </id>
    14         <property name="userName" type="java.lang.String">
    15             <column name="userName" length="30" />
    16         </property>
    17         <property name="userPwd" type="java.lang.String">
    18             <column name="userPwd" length="30" />
    19         </property>
    20         <property name="sex" type="java.lang.String">
    21             <column name="sex" length="10" />
    22         </property>
    23         <property name="age" type="java.lang.Integer">
    24             <column name="age" />
    25         </property>
    26     </class>    
    27     <query name = "queryUser_byAgeAndName"><!--此查询被调用的名字-->
    28     <![CDATA[from TRegister user where user.age >:minAge and user.userName like:userName]]>    
    29     </query>
    30 </hibernate-mapping>
     1 public void namedQueryDemo()
     2     {
     3         TRegister user = new TRegister();
     4         Session session = HibernateUtil.currentSession();
     5         Query query = session.getNamedQuery("queryUser_byAgeAndName");
     6         query.setInteger("minAge", 18);
     7         query.setString("userName", "%x%");
     8         List list = query.list();
     9         for(int i = 0 ; i < list.size(); i++)
    10         {
    11             user = (TRegister)list.get(i);
    12             System.out.println(user.getUserName());
    13         }
    14     }

    Trancation接口
    该接口允许应用等量齐观定义工作单元,同时又可调用JTA或JDBC执行事物管理。它的运行与Session接口相关,可调用Session的beginTransaction()方法生成一个Transaction实例。
    一个Session实例可以与多个Transaction实例相关联,但是一个特定的Session实例在任何时候必须与至少一个未提交的Transaction实例相关联。
    Transaction接口常用如下方法:
    1:commit();提交相关联的Session实例。
    2:rollback();撤销事物操作。
    3:wasCommitted();事物是否提交。

    在本篇文章最后再介绍一下我在Hibernate框架简述中的HibernateUtil类
    HibernateUtil类是Hibernate的Session管理的一个应用广泛的方案。它是使用ThreadLocal类建立的一个Session管理的辅助类。使用ThreadLocal可以有效隔离执行所使用的数据,所以避开了Session的多线程之间的数据共享问题。

  • 相关阅读:
    Programming Windows 第五版读书笔记 第三章 窗口和消息
    一个带游标的遍历数据的函数|数据库操作|软件开发
    递归删除一个文件夹下的所有文件c#代码
    杭州市市民卡办理点
    NTLDR is missing 的解决方法
    SQL Server中 将日期格式化.函数 CONVERT
    1.显示网页中的所有图片
    用sql函数获取中文字符串的全拼
    地柜便利一个文件夹下的所有文件|软件开发
    软件是什么
  • 原文地址:https://www.cnblogs.com/holly8/p/5739879.html
Copyright © 2020-2023  润新知