• hibernate学习笔记


    学会用20%时间和记忆去完成80%的工作!

    Hibernate中实体对象的生命周期
    l Transient-瞬间状态

    当直接使用new创建出对象,例如用User类所衍生出的对象,在还没有使用save()方法之前都是暂存对象,这些对象还没有与数据库发生任何关系,不与数据库的任何记录对应。

    l Persistent-持久状态

    当对象与数据库中的数据有对应关系,与session实例(尚未关闭)有关联。如将对象用session.save()方法存储,或用session.get()/load()方法从数据库加载数据并封装为对象,此对象进入持久状态。

    l Detached-分离状态

    当Persistent状态的对象的事务执行commit()之后或一级缓存执行了flush()操作,数据库中的对应数据也会跟着更新。如果你将session实例关闭close(),则Persistent状态的对象会成为Detached状态;而当你使用session的实例的delete()方法删除数据,Persistent状态的对象由于失去了对应的数据,成为Transient状态。

    Detached状态的对象可以使用update()方法使之与数据库中的对应数据再度发生关联,此时Detached状态的对象会变为Persistent状态。

    简要的Hibernate体系结构的概要图


    Hibernate核心接口
    l SessionFactory(org.hibernate.SessionFactory)

    针对单个数据库映射关系经过编译后的内存镜像,是线程安全的(不可变)。他是生成Sessinon的工厂,本身要用到ConnectionProvider。该对象可以在进程或集群的级别上,为那些事务之间可以重用的数据提供可循的二级缓存。

    l Session(org.hibernate.Session)

    表示应用程序与持久储存层之间交换操作的一个单线程对象,此对象生存期很短。其隐藏了JDBC连接,也是Transaction的工厂。其会吃用一个针对持久话对象的必选(第一级)缓存,在遍历对象图或跟据持久化标识查找对象时会用到。

    l 持久对象及其集合

    带有持久化状态的、具有业务功能的单线程对象,此对象生存期很短。这些对象可能是普通的JavaBean/POJO,唯一特殊的是他们正与(仅仅一个)Session相关联,一旦这个Session被关闭,这些对象就会脱离持久化状态,这样就可被应用程序的任何层自由使用(例如作表示层的数据传输对象)。

    l 瞬态(Transient)和脱管(Detached)的对象及其集合

    l 事务(Transaction(org.hibernate.Transaction))

    (可选的)应用程序用来制定原子操作单元范围的对象,它是单线程的,生命周期很短。他通过抽象将用用从底层具体的JDBC、JTA以及CORBA事务隔离开。某些情况下,一个Session之内可能包含多个Transaction对象。尽管是否使用该对象是可选的,但无论是使用底层的API还是使用Transaction对象。事务边界的开启与关闭是必不可少的。

    l ConnectionProvider(org.hibernate.connection.ConnectionProvider)

    (可选的)申城JDBC连接的工厂(同时也起到连接池的作用)。它通过抽象将应用从底层的Datasource或DriverManager隔离开。仅供开发者扩张/实现用,并不暴露给应用程序使用。

    l TransactionFactory(org.hibernate.TransactionFactory)

    (可选的)生成Transaction对象实例的工厂。仅供开发者扩展/实现用,并不暴露给应用程序使用。


    配置MyEclipse环境使用Hibernate
    hibernate3.jar是必要的,而在lib目录中还包括了许多jar文件,您可以在 Hibernate 3.0官方的参考手册 上找到这些jar的相关说明,其中必要的是 antlr、dom4j、CGLIB、asm、Commons Collections、Commons Logging、 EHCache,Hibernate底层还需要Java Transaction API,所以您还需要jta.jar,到这边为止,总共需要以下的jar文件:



    Hibernate可以运行于单机之上,也可以运行于Web应用程序之中,如果是运行于单机,则将所有用到的jar文件(包括JDBC驱动程序)设定至 CLASSPATH中,如果是运行于Web应用程序中,则将jar文件置放于WEB-INF/lib中。
    如果您还需要额外的Library,再依需求加入,例如JUnit、Proxool等等,接下来可以将etc目录下的log4j.properties复制至Hibernate项目的Classpath下,并修改一下当中的log4j.logger.org.hibernate为error,也就是只在在错误发生时显示必要的信息。

    接着设置基本的Hibernate配置文件,可以使用XML或Properties文件,这边先使用XML,文件名预设为 hibernate.cfg.xml:




    说明:这边以一个简单的单机程序来示范Hibernate的配置与功能。

    准备工作

    l MyEclipse配置如前所述

    l 建立持久化类

    l 数据库的准备工作,在MySQL中新增一个demo数据库,并建立user表格:

    CREATE TABLE user( id INT(11) NOT NULL auto_increment  PRIMARY KEY,name VARCHAR(100) NOT NULL default '',age INT);

    l 建立持久化类

    l 建立持久化类的映射文件

    l 编写Java的测试应用程序


    建立持久化类

    public class User {
        private Integer id;
        private String name;
        private Integer age;
        // 必须要有一个预设的建构方法
        // 以使得Hibernate可以使用Constructor.newInstance()建立对象
        public User() {
        }
        public Integer getId() {
            return id;
        }
        public void setId(Integer id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Integer getAge() {
            return age;
        }
        public void setAge(Integer age) {
            this.age = age;
        }
    }

    其中id是个特殊的属性,Hibernate会使用它来作为主键识别,您可以定义主键产生的方式,这是在XML映像文件中完成,为了告诉 Hibernate您所定义的User实例如何映射至数据库表格,您撰写一个XML映射文件名是User.hbm.xml,如下所示:

    建立持久化类的映射文件User.hbm.xml;


    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE hibernate-mapping
     PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
     "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

    <hibernate-mapping>

        <class name="onlyfun.caterpillar.User" table="user">

            <id name="id" column="id" type="java.lang.Integer">
                <generator class="native"/>
            </id>

            <property name="name" column="name" type="java.lang.String"/>

            <property name="age" column="age" type="java.lang.Integer"/>

        </class>

    </hibernate-mapping>


    <class>标签的name属性为所映像的对象,而table为所映像的表格;

    <id>中 column属性指定了表格字段,而 type属性指定了User实例的中的id的类型,这边type中所设定的是直接指定Java中的对象类型,Hibernate也定义有自己的映射类型,作为Java对象与SQL型态的标准对应型态(因为语言所提供的类型并不一定与数据库的类型对应),这之后会再说明。

    <id>中主键的产生方式在这边设定为"native",表示主键的生成方式由Hibernate根据数据库Dialect 的定义来决定,之后还会介绍其它主键的生成方式。


    同样的,<property>标签中的column与type都各自指明了表格中字段与对象中属性的对应。


    接着必须在Hibernate配置文件hibernate.cfg.xml中指明映射文件的位置,如下加入映射文件位置:

    修改Hibernate的配置文件hibernate.cfg.xml;

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

    <hibernate-configuration>

        <session-factory>

        ....

        <!-- 对象与数据库表格映像文件 -->
        <mapping resource=“com/cstp/User.hbm.xml"/>

        </session-factory>

    </hibernate-configuration>


    接下来编写一个测试的程序,这个程序直接以Java程序设计人员熟悉的语法方式来操作对象,而实际上也直接完成对数据库的操作,程序将会将一条数据存入表格之中:

    public class FirstHibernate {
        public static void main(String[] args) {
            // Configuration 负责管理 Hibernate 配置讯息
            Configuration config = new Configuration().configure();
            // 根据 config 建立 SessionFactory
            // SessionFactory 将用于建立 Session
            SessionFactory sessionFactory = config.buildSessionFactory();

            // 将持久化的对象
            User user = new User();
            user.setName(“hhp”);
            user.setAge(new Integer(30));

            // 开启Session,相当于开启JDBC的Connection
            Session session = sessionFactory.openSession();
            // Transaction表示一组会话操作
            Transaction tx= session.beginTransaction();
            // 将对象映像至数据库表格中储存
            session.save(user);
            tx.commit();
            session.close();
            sessionFactory.close();
     
            System.out.println(“新增记录OK!请先用MySQL观看结果!");
        }
    }


    持久性保存对象的一般步骤总结
    l 打开到数据源的一个会话

    Session snSession = factory.openSession();

    l 开始一个事务

    Transaction tTransaction = snSession.beginTransaction();

    l 创建希望持久性保存的对象,并且使用数据填充它们

    User user = new User();

    user.setName("Michael");

    user.setAge(40);

    l 保存对象到会话

    snSession.save(user);

    l 提交修改到数据源并且关闭链接

    tTransaction.commit();

    snSession.close();


    FirstHibernate.java测试程序

    如您所看到的,程序中只需要直接操作User对象,并进行Session与Transaction的相关操作,Hibernate就会自动完成对数据库的操作,您看不到任何一行JDBC或SQL的声明产生,编写好以上的各个文件之后,各文件的放置位置如下:



    着可以开始运行程序,结果如下:

    Hibernate: insert into user (name, age) values (?, ?)
    新增记录OK!请先用MySQL观看结果!


    执行结果中显示了Hibernate所实际使用的SQL,由于这个程序还没有查询功能,所以要进入MySQL中看看新增的数据,如下:

    mysql> select * from user;
    +----+-------------+------+
    | id    | name         | age  |
    +----+-------------+------+
    |  1    | caterpillar |   30   |
    +----+-------------+------+
    1 row in set (0.03 sec)


    在 第一个 Hibernate 中介绍如何使用Hibernate在不使用SQL的情况下,以Java中操作对象的习惯来插入数据至数据库中,当然储存数据之后,更重要的是如何将记录读出,Hibernate中也可以让您不写一句SQL,而以Java中操作对象的习惯来查询数据。

    Hibernate例子-条件查询

    public class SecondHibernate {

        public static void main(String[] args) {
            Configuration config = new Configuration().configure();
            SessionFactory sessionFactory = config.buildSessionFactory();
            Session session = sessionFactory.openSession();

            Criteria criteria = session.createCriteria(User.class);
            // 查询user所有字段
            List users = criteria.list();
            Iterator iterator = users.iterator();
            System.out.println("id \t name/age");
            while(iterator.hasNext()) {
                User user = (User) iterator.next();
                System.out.println(user.getId() +
                                    " \t " + user.getName() +
                                    "/" + user.getAge());
            }

    // 查询user中符合条件的字段
            criteria.add(Expression.eq("name", “hhp"));
            users = criteria.list();
            iterator = users.iterator();
            System.out.println("id \t name/age");
            while(iterator.hasNext()) {
                User user = (User) iterator.next();
                System.out.println(user.getId() +
                    " \t " + user.getName() +
                    "/" + user.getAge());
            }
     
            session.close();
            sessionFactory.close();
        }
    }

    Criteria对SQL进行封装,对于不甚了解SQL的开发人员来说,使用Criteria也可以轻易的进行各种数据的检索

    您也可以使用 Expression设定查询条件,并将之加入Criteria中对查询结果作限制,Expression.eq()表示设定符合条件的查询,

    例如 Expression.eq("name", “hhp")表示设定查询条件为"name"字段中为"caterpillar"的数据。

    先来看一下执行结果:

    Hibernate: select this_.id as id0_, this_.name as name0_0_, this_.age as age0_0_ from user this_
    id      name/age
    1      hhp/30
    3      tiantian/5
    2     zhangy/26

    Hibernate: select this_.id as id0_, this_.name as name0_0_, this_.age as age0_0_ from user this_ where this_.name=?
    id      name/age
    1       hhp/30


    Criteria是对象导向式的查询方式,让不了解SQL的开发人员也可以轻易进行各项查询,但Criteria的API目前还不是很完善,而 Hibernate鼓励的查询方式,是透过HQL(Hibernate Query Language)来进行,直接来看个实例:

    Hibernate例子-HQL查询

    public class SecondHibernate {

        public static void main(String[] args) {
            Configuration config = new Configuration().configure();
            SessionFactory sessionFactory = config.buildSessionFactory();
            Session session = sessionFactory.openSession();
     
            // 使用HQL建立查询
            Query query = session.createQuery("from User");
            List users = query.list();
            Iterator iterator = users.iterator();
            System.out.println("id \t name/age");
            while(iterator.hasNext()) {
                User user = (User) iterator.next();
                System.out.println(user.getId() +
                        " \t " + user.getName() +
                        "/" + user.getAge());
            }
     
            System.out.println();
    // 使用HQL建立查询
            query = session.createQuery("from User user where user.name like ?");
            // 设定查询参数
            query.setParameter(0, “hhp");
            users = query.list();
            iterator = users.iterator();
            System.out.println("id \t name/age");
            while(iterator.hasNext()) {
                User user = (User) iterator.next();
                System.out.println(user.getId() +
                                    " \t " + user.getName() +
                                    "/" + user.getAge());
            }
     
            session.close();
            sessionFactory.close();
        }
    }

    通过Query接口,您可以先设定查询参数,之后通过setXXX()等方法,将指定的参数值填入,而不用每次都编写完整的HQL,Query的 setParameter()方法第一个参数是指定 ? 出现的位置,从 0 开始,第二个参数则是设定查询条件。


    Hibernate.cfg.xml配置文件详解
    Hibernate可以使用XML文件或properties文件来配置SessionFactory,预设的配置文件为 hibernate.cfg.xml或hibernate.properties,XML提供较好的结构与配置方式,Hibernate建议使用XML文件进行配置。

    上节课所示范的为使用XML文件的方式,一个XML文件的例子如下:

    <hibernate-configuration>

        <session-factory>
            <!-- 显示实际操作数据库时的SQL -->
            <property name="show_sql">true</property>
            <!-- SQL方言,这边设定的是MySQL -->
            <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
            <!-- JDBC驱动程序 -->
            <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
             <!-- JDBC URL -->
            <property name="connection.url">jdbc:mysql://localhost/demo</property>
            <!-- 数据库使用者 -->
            <property name="connection.username">hhp</property>
            <!-- 数据库密码 -->
            <property name="connection.password">123456</property>
            <!-- 对象与数据库表格映像文件 -->
            <mapping resource=“com/cstp/User.hbm.xml"/>
        </session-factory>


    </hibernate-configuration>


    使用XML文件进行配置时,可以在当中指定对象与数据库表格的映像文件位置,XML配置文件的位置必须在Classpath下,使用下面的方式来读入 XML文件以配置Hibernate:

    Configuration config = new Configuration().configure();


    Configuration的实例管理Hibernate的配置信息,通常用于建立SessionFactory,例如:

    SessionFactory sessionFactory = config.buildSessionFactory();


    SessionFactory一旦建立,就被赋予当时Configuration的配置信息,之后您改变Configuration并不会影响已建立的 SessionFactory实例,如果对Configuration改动后,则要建立一个新的SessionFactory实例,新的实例中会包括新的配置信息。

    SessionFactory中包括了数据库配置及映射关系,它的建立相当复杂,所以使用时需考虑到重用已建立的SessionFactory实例, SessionFactory是被设计为「线程安全的」(Thread-safe)。


    预设的XML配置文件名称是hibernate.cfg.xml,您也可以自行指定文件,即采用程序配置方式例如:

    Configuration config = new Configuration().configure("db.cfg.xml");

    SessionFactory sf = config.buildSessionFactory();


    除了使用XML文件进行配置,您也可以使用属性文件进行配置,文件名称是hibernate.properties,一个例子如下:

    hibernate.show_sql = true
    hibernate.dialect = org.hibernate.dialect.MySQLDialect
    hibernate.connection.driver_class = com.mysql.jdbc.Driver
    hibernate.connection.url = jdbc:mysql://localhost/demo
    hibernate.connection.username = hhp
    hibernate.connection.password = 123456


    hibernate.properties的位置必须在Classpath下,由于properties文件中不包括映射文件的名称,为了要取得对象至数据库表格的映射文件,您必须在程序中如下加载:

    Configuration cfg = new Configuration()
        .addClass(com.cstp.User.class)
        .addClass(com.cstp.Item.class);

    第一个addClass()加入位于Classpath根目录下的User.hbm.xml,第二个addClass()加入Item类别的映射文件,该文件必须位于与User类别同一个目录,也就是com/cstp/Item.hbm.xml。

    注意:在Hibernate下载文件中的etc目录下,有hibernate.cfg.xml与hibernate.properties可供设定参考




    Hibernate.cfg.xml配置文件——数据库连结配置
    Hibernate的配置文件中有一部份是在设定数据库连结,例如使用XML文件进行配置:

    数据库连接池配置

    <hibernate-configuration>

     <session-factory>

     <!-- 显示实际操作数据库时的SQL -->
     <property name=“show_sql”>true</property>
     <!-- SQL方言,这边设定的是MySQL -->
     <property name=“dialect”>org.hibernate.dialect.MySQLDialect</property>
     <!-- JDBC驱动程序 -->
     <property name=“connection.driver_class”>com.mysql.jdbc.Driver</property>
     <!-- JDBC URL -->
     <property name=“connection.url”>jdbc:mysql://localhost/demo</property>
     <!-- 数据库使用者 -->
     <property name=“connection.username”>hhp</property>
     <!-- 数据库密码 -->
     <property name=“connection.password”>123456</property>

     <!设置数据库的连接池属性>
     <!-- Hibernate 预设的Connection pool -->
     <property name=“connection.pool_size”>2</property>

     <!-- 对象与数据库表格映射文件 -->
     <mapping resource=“com/cstp/User.hbm.xml"/>

     </session-factory>

    </hibernate-configuration>


    其中设定的connection.pool_size是Hibernate预设的连接池设定,通常只用于开发阶段测试之用。如果使用properties 文件的话则如下:

    hibernate.properties


    hibernate.show_sql = true
    hibernate.dialect = org.hibernate.dialect.MySQLDialect
    hibernate.connection.driver_class = com.mysql.jdbc.Driver
    hibernate.connection.url = jdbc:mysql://localhost/demo
    hibernate.connection.username = hhp
    hibernate.connection.password = 123456
    hibernate.connection.pool_size = 2


    Hibernate在数据库连接池的使用上是可选的,您可以使用C3P0连接池,例如XML的配置方式如下:

    <hibernate-configuration>

        <session-factory>

            <!-- 显示实际操作数据库时的SQL -->
            <property name="show_sql">true</property>
            <!-- SQL方言,这边设定的是MySQL -->
            <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
            <!-- JDBC驱动程序 -->
            <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
            <!-- JDBC URL -->
            <property name="connection.url">jdbc:mysql://localhost/demo</property>
            <!-- 数据库使用者 -->
            <property name="connection.username">hhp</property>
            <!-- 数据库密码 -->
            <property name="connection.password">123456</property>

            <!-- C3P0 连接池设定 -->
            <property name="c3p0.min_size">5</property>
            <property name="c3p0.max_size">20</property>
            <property name="c3p0.timeout">1800</property>
            <property name="c3p0.max_statements">50</property>
     
            <!-- 对象与数据库表格映像文件 -->
            <mapping resource=“com/cstp/User.hbm.xml"/>

        </session-factory>
    </hibernate-configuration>


    记得您的Classpath中必须包括c3p0-*.jar,属性文件hibernate.properties的配置范例如下:

    hibernate.properties


    hibernate.show_sql = true
    hibernate.dialect = org.hibernate.dialect.MySQLDialect
    hibernate.connection.driver_class = com.mysql.jdbc.Driver
    hibernate.connection.url = jdbc:mysql://localhost/demo
    hibernate.connection.username = hhp
    hibernate.connection.password = 123456
    hibernate.c3p0.min_size=5
    hibernate.c3p0.max_size=20
    hibernate.c3p0.timeout=1800
    hibernate.c3p0.max_statements=50


    您也可以使用Proxool或DBCP连接池,只要在配置文件中配置hibernate.proxool.*或hibernate.dbcp.*等相关选项,这可以在hibernate的etc目录中找hibernate.properties中的配置例子来参考,当然要记得在Classpath中加入相关的jar文件。


    如果您使用Tomcat的话,您也可以通过它提供的DBCP连接池来取得连接,您可以先参考 使用 DBCP 的文章来设定Tomcat的DBCP连接池。


    设定好容器提供的DBCP连接池之后,您只要在配置文件中加入connection.datasource属性,例如在 hibernate.cfg.xml中加入:

    <property name="connection.datasource">java:comp/env/jdbc/dbname</property>


    如果是在hibernate.properties中的话,则加入:

    hibernate.connection.datasource = java:comp/env/jdbc/dbname

    Session管理和缓存
    数据库每一次的查询都是一次不小的开销,例如连结的开启、执行查询指令,当数据库与应用服务器不在同一个服务器上时,还必须有远程调用、Socket的建立等开销,在Hibernate这样的ORM框架中,还有数据的封装等开销必须考虑进去


    缓存(Cache)是数据库在内存中的临时容器,从数据库中读取的数据在缓存中会有一份临时拷贝,当您查询某个数据时,会先在缓存中寻找是否有相对应的拷贝,如果有的话就直接返回数据,而无需连接数据库进行查询,只有在缓存中找不到数据时,才从数据库中查询数据,藉由缓存,可以提升应用程序读取数据时的效能。(需要注意在一些状态中,缓存中的数据可能与数据库中的不一致!)


    对于Hibernate这样的ORM框架来说,缓存的机制更为重要,在Hibernate中缓存分作两个层级:Session level与SessionFactory level(又称Second level缓存)。

    Session Level缓存
    这边先介绍Session level的缓存,在Hibernate中Session level缓存会在使用主键加载数据或是延迟初始(Lazy Initialization) 时作用,Session level的缓存随着Session建立时建立,而Session销毁时销毁。

    Session会维护一个Map容器,并保留与目前Session发生关系的资料,当您透过主键来加载数据时,Session会先依据所要加载的类别与所给定的主键,看看Map中是否已有数据,如果有的话就返回,若没有就对数据库进行查询,并在加载数据后在Map中维护。


    可以通过==来比较两个名称是否参考至同一个对象,以检验这个事实:

    Session session = sessionFactory.openSession();
    User user1 = (User) session.load(User.class, new Integer(1));
    User user2 = (User) session.load(User.class, new Integer(1));
    System.out.println(user1 == user2);
    session.close();

    第二次查询数据时,由于在缓存中找到数据对象,于是直接返回,这与第一次查询到的数据对象是同一个实例,所以会显示true的结果。

    可以通过evict()将某个对象从缓存中移去,例如:


    Session session = sessionFactory.openSession();


    User user1 = (User) session.load(User.class, new Integer(1));
    session.evict(user1);
    User user2 = (User) session.load(User.class, new Integer(1));


    System.out.println(user1 == user2);
    session.close();

    由于user1所参考的对象被从缓存中移去了,在下一次查询时,Session在Map容器中找不到对应的数据,于是重新查询数据库并再封装一个对象,所以user1与user2参考的是不同的对象,结果会显示false。


    也可以使用clear()清除缓存中的所有对象,例如:


    Session session = sessionFactory.openSession();


    User user1 = (User) session.load(User.class, new Integer(1));
    session.clear();
    User user2 = (User) session.load(User.class, new Integer(1));
    System.out.println(user1 == user2);
    session.close();


    同样的道理,这次也会显示false。


    Session level的缓存随着Session建立与销毁,看看下面这个程序片段:

    Session session1 = sessionFactory.openSession();


    User user1 = (User) session1.load(User.class, new new Integer(1));
    session1.close();

    Session session2 = sessionFactory.openSession();
    User user2 = (User)session2.load(User.class, new Integer(1));
    session2.close();

    System.out.println(user1 == user2);


    第一个Session在关闭后,缓存也关闭了,在第二个Session的查询中并无法用到第一个Session的缓存,两个Session阶段所查询到的并不是同一个对象,结果会显示false。


    在加载大量数据时,Session level 缓存的内容会太多,记得要自行执行clear()清除缓存或是用evict()移去不使用对象,以释放缓存所占据的资源。

    Session在使用save()储存对象时,会将要储存的对象纳入Session level缓存管理,在进行大量数据储存时,缓存中的实例大量增加,最后会导致OutOfMemoryError,可以每隔一段时间使用Session的 flush()强制储存对象,并使用clear()清除缓存,

    例如:

    Session session = sessionFactory.openSession();
    Transaction tx = session.beginTransaction();

    while(....) { // 大量加载对象时的循环示意
        ....
        session.save(someObject);
        if(count % 100 == 0) { // 每100条记录
            session.flush(); // 送入数据库
            session.clear(); // 清除缓存
        }
    }

    tx.commit();
    session.close();


    在SQL Server、Oracle等数据库中,可以在Hibernate设定文件中设定属性hibernate.jdbc.batch_size来控制每多少条数据就送至数据库,例如:

    ....
    <hibernate-configuration>
        <session-factory>
            ....
            <property name="hibernate.jdbc.batch_size">100</property>
            ....
        </session-factory>
    <hibernate-configuration>

    注意:在MySQL中则不支持这个功能


    事务管理简单说明
    事务是一组原子(Atomic)操作(一组SQL执行)的工作单元,这个工作单元中的所有原子操作在进行期间,与其它事务隔离,免于数据来源的交相更新而发生混乱,事务中的所有原子操作,要么全部执行成功,要么全部失败(即使只有一个失败,所有的原子操作也要全部撤消)。

    JDBC中的事务管理方式

    在JDBC中,可以用Connection来管理事务,可以将Connection的AutoCommit设定为false,在下达一连串的SQL语句后,自行调用Connection的commit()来送出变更,如果中间发生错误,则撤消所有的执行,例如:

    JDBC中的事务管理方式举例


    try {
        .....
        connection.setAutoCommit(false);
        .....

        // 一连串SQL操作

        connection.commit();
    } catch(Exception) {
        // 发生错误,撤消所有变更
        connection.rollback();
    }

    Hibernate本身没有事务管理功能,它依赖于JDBC或JTA的事务管理功能,预设是使用JDBC事务管理,可以在配置文件中加上 hibernate.transaction.factory_class属性来指定Transaction的工厂类别,例如:

    <hibernate-configuration>


        <session-factory>
            ....
            <!-- 设定事务管理的工厂类 -->
            <property name="hibernate.transaction.factory_class">
                org.hibernate.transaction.JDBCTransactionFactory
            </property>
     
            <!-- 对象与数据库表格映像文件 -->
            <mapping resource=“com/cstp/User.hbm.xml"/>

        </session-factory>

    </hibernate-configuration>


    基于JDBC的事务管理是最简单的方式,事实上,Hibernate基于JDBC的事务管理只是对JDBC作了个简单的封装:


    try {
        session = sessionFactory.openSession();  
        Transaction tx = session.beginTransaction();
        ....

        tx.commit();  // 必须commit才会更新数据库
    } catch(HibernateException e) {
        tx.rollback();
    }

    在一开始的openSession()取得Session时,JDBC的Connection实例之AutoCommit就被设定为false,在 beginTransaction()时,会再度检查Connection实例的AutoCommit为false,在操作过程中,最后要commit (),否则的话对数据库的操作不会有作用,如果操作过程中因发生例外,则最后commit()不会被执行,之前的操作取消,执行rollback()可撤消之前的操作,一个实际的程序如下所示:

    public class FirstHibernate {
        public static void main(String[] args) {
            Configuration config = new Configuration().configure();
            SessionFactory sessionFactory = config.buildSessionFactory();

            User user = new User();
            user.setName(“zhangy");
            user.setAge(new Integer(24));

            Session session = null;
            Transaction tx= null;
     
            try {
                session = sessionFactory.openSession();
                tx = session.beginTransaction();
                session.save(user);
                tx.commit();
            }
            catch(Exception e) {
                e.printStackTrace();
                if(tx != null) {
                    try {
                        tx.rollback();
                    }
                    catch(HibernateException ee) {
                        ee.printStackTrace();
                    }
                }
            }

    finally {
                if(session != null) {
                    try {
                        session.close();
                    }
                    catch(HibernateException e) {
                        e.printStackTrace();
                    }
                }
            }
     
            sessionFactory.close();
        }
    }

    注意: 要使用MySQL中的事务处理,必须建立事务表类型的表格,例如InnoDB的表格:


    CREATE TABLE user (
    .....
    ....
    ) TYPE = InnoDB;


    Hibernate映射文件
    Hibernate 中将对象与数据库表格映射关系连接起来的是映射文件,通常以*.hbm.xml作为文件名称,映射文件可以手工编写,或是通过工具程序从数据库表格自动生成,或是通过工具程序从Java类别自动生成。

     <hibernate-mapping>


        <!--类别名称与表格名称映像-->
        <class name=“com.cstp.User" table="user">


            <!--id与主键映射-->
            <id name="id" column="id" type="java.lang.Integer">
                <generator class="native"/>
            </id>


            <!--类别属性与表格字段的映像-->
            <property name="name" column="name" type="java.lang.String"/>
            <property name="age" column="age" type="java.lang.Integer"/>


        </class>
    </hibernate-mapping>




    映射文件中主要包括三个部份:

    类别名称与表格名称的映射、

    id属性与主键的映射、

    类别属性与表格字段的映射。

    这份映像文件对应于以下的类别与表格:

    User.java

    public class User {
        private Integer id;
        private String name;
        private Integer age;
     
        // 必须要有一个预设的建构方法
        // 以使得Hibernate可以使用Constructor.newInstance()建立对象
        public User() {}

        public Integer getId() {
            return id;
        }


            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
     
        public Integer getAge() {
            return age;
        }

        public void setAge(Integer age) {
            this.age = age;
        }
    }

    在<id>的设定上,name设定类别上的属性名,而column对应至表格字段,在type上可以设定Java类别的数据类型,但由于 Java的数据类型与数据库的数据类型并不是一对一对应的,为此Hibernate提供它自己的数据类型,作为Java数据类型与数据库数据类型的连接类型,下面的表格列出类型之间的对应:

    Java的数据类型与Hibernate的数据类型对应问题






    Hibernate的主键(标识符)生成方法
    l Hibernate自动生成主键

    l Generator生成策略说明

    l Hibernate复合主键

    *.hbm.xml中需要说明主键生成方式

    <generator>设定主键的生成方式,

    可以设定“native”表示由Hibernate自动根据Dialect选择采用 identity、hilo、sequence等作为主键生成方式,

    也可以考虑采用uuid由Hibernate根据128位UUID算法(128- bit UUID algorithm)生成16进位制数值,并编码为32位长度的字符串,

    还有其它的主键生成方式,可以参考官方手册的 Generator 说明

    数据库提供的主键生成机制

    identity-采用数据库提供的主键生成机制

    如DB2,SQL Server,MySQL,返回的是long,short,或int类型

    sequence-采用数据库提供的sequence生成机制

    如Oracle的sequence

    外部程序提供的主键生成机制

    increment-递增

    hilo-高低位算法实现的

    seqhilo

    uuid.hex-用一个128位的UUID算法生成字符串类型的主键,UUID被编码为一个32位16进制数字的字符串

    uuid.string-用一个128位的UUID算法生成字符串类型的主键,UUID被编码为一个32位16个字符长的任意ASCII字符组成的字符串

    其它主键生成机制

    native-根据底层数据库的能力选择identity,sequence,hilo任意一种

    assigned-由程序在save()之前自己分配主键

    foreign-外部引用,使用另外一个相关联的对象的标识符,和<one-to-one>一起联合使用


    Generator生成器模板

    <id

    name = "propertyName"

    type = "typeName"

    column = "columnName"

    unsaved-value="any|none|null|id_value"

    access = "field"|property|ClassName">


    <generator class = "generateClass">

    </generator>

    </id>


    Generator生成器-Hilo举例

    <id name = "id" type = "long" column = "uid" unsaved-value="0">

    <generator class = "net.sf.hibernate.id.TableHiLoGenerator">

    <param name="table"> uid_table </param>

    <param name="column"> next_hi_value_column </param>

    </generator>

    </id>


    <property

    name=“propertyName”

    column=“columnName”

    type=“typeName”

    >



    本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/welcomejzh/archive/2008/12/29/3638976.aspx
  • 相关阅读:
    POJ 1436 Horizontally Visible Segments(线段树)
    POJ 1436 Horizontally Visible Segments(线段树)
    精益项目管理的可行性分析
    精益项目管理的可行性分析
    精益项目管理的可行性分析
    精益项目管理的可行性分析
    单点登录cas常见问题(二)
    单点登录cas常见问题(二)
    蓝氏兄弟依靠板栗东山再起,意外赚回八九万元
    元旦快乐,感谢一路相伴!
  • 原文地址:https://www.cnblogs.com/pricks/p/1506698.html
Copyright © 2020-2023  润新知