首页 > 代码库 > Hibernate技术(一)--Hibernate初步

Hibernate技术(一)--Hibernate初步

Hibernate初步

ORM:在编写程序时,处理数据采用面向对象的方式,而保存数据却以关系型数据库的方式,因此需要一种能在两者之间进行转换的机制。这种机制称为ORM,ORM保存了对象和关系型数据库表的映射信息

Hibernate框架搭建

Hibernate3JAR包引入:

antlr-2.7.6.jar

语言转换工具,实现HQL到SQL的转换

commons-collections-3.1.jar

 

dom4j-1.6.1.jar

读写XML文件

hibernate3.jar

核心类库

hibernate-jpa-2.0-api-1.0.0.Final.jar

对JPA规范的支持,事物处理

javassist-3.12.0.GA.jar

分析、编辑、创建java字节码的类库

jta-1.1.jar

 

mysql-connector-java-5.1.12-bin.jar

数据库驱动

slf4j-api-1.6.1.jar

 

Hibernate4 jar包引入:

技术分享

文件配置

Hibernate.cfg.xml

<!DOCTYPEhibernate-configuration PUBLIC

    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"

    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

<session-factory>

                       <!-- 数据库连接配置 -->

<!—数据库类型 -->

<propertyname="hibernate.connection.driver_class">com.mysql.jdbc.Driver

</property>

<!—数据库URL -->    

<propertyname="hibernate.connection.url">jdbc:mysql:///hib_demo

</property>

<!—用户名 -->

<propertyname="hibernate.connection.username">root

</property>

<!—用户名密码 -->

<propertyname="hibernate.connection.password">root

</property>

<!—配置Hibernate数据库类型  -->

<propertyname="hibernate.dialect">org.hibernate.dialect.MySQLDialect

</property>

<!—显示SQL脚本语句 -->

<propertyname="hibernate.show_sql">true

</property>

       

<!-- 加载所有映射 -->

<mappingresource="映射文件目录"/>

   

</session-factory>

</hibernate-configuration>

sf = new Configuration()

            .configure()

            .addClass(Employee.class) 

            .buildSessionFactory()//(测试时候用)会自动加载映射文件:效果等同于    <mappingresource="映射文件目录"/>

 

数据库连接参数配置

例如:

sql语言配置

#hibernate.dialectorg.hibernate.dialect.MySQLDialect

#hibernate.dialectorg.hibernate.dialect.MySQLInnoDBDialect

#hibernate.dialectorg.hibernate.dialect.MySQLMyISAMDialect

自动建表

Hibernate.properties

hibernate.hbm2ddl.auto create-drop 每次在创建sessionFactory时候执行创建表;

                                                        当调用sesisonFactory的close方法的时候,删除表!

hibernate.hbm2ddl.auto create   每次都重新建表;如果表已经存在就先删除再创建

hibernate.hbm2ddl.auto update  如果表不存在就创建;表存在就不创建;

hibernate.hbm2ddl.auto validate  (生成环境时候) 执行验证: 当映射文件的内容与数据                                                   库表结构不一样的时候就报错!

 

代码自动建表和文件配置自动建表区别

文件配置自动建表在加载配置文件时候才会建表,代码自动建表启动时候就会建表。

代码自动建表:

        // 创建配置管理类对象

        Configuration config = new Configuration();

        // 加载主配置文件

        config.configure();

        // 创建工具类对象

        SchemaExport export =new SchemaExport(config);

        // 建表

        // 第一个参数:是否在控制台打印建表语句

        // 第二个参数:是否执行脚本

        export.create(true,true);

 

对象的映射 (映射文件)

类名.hbm.xml             

<?xmlversion="1.0"?>

<!DOCTYPEhibernate-mapping PUBLIC

         "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

         "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mappingpackage="cn.itcast.a_hello"  auto-import="trueHQL查询时不用指定包名/false得指定">

         <classname="实体类的名字"table="数据库表名"catalog="数据表的名字">

                   <!-- 主键 ,映射id属性必须有一个-->

                   <idname="实体类属性"type="属性的java类型"column="指定对应数据库表的主键"type=“字段类型”>

                            <generatorclass="主键生成器策略"/>

 

主键生成策略:

increment  个是由Hibernate在内存中生成主键,每次增量为1,不依赖于底层的数据库,因此所有的数据库都可以使用

identity      采用数据库生成的主键,用于为long、short、int类型生成唯一标识, Oracle 不支持自增字段.

sequence   对象标识符由底层的序列生成机制产生,要求底层数据库支持序列

native         根据底层数据库的能力,从identity、sequence、hilo中选择一个,灵活性更强。

assigned  指定主键生成策略为手动指定主键的值

uuid.hex     使用一个128-bit的UUID算法生成字符串类型的标识符

uuid.string  hibernate会算出一个16位的值插入

foreign  即把别的表的主键作为当前表的主键;

                   </id>

                   <!-- 非主键,映射 -->

                   <propertyname="实体类属性"column="指定对应数据库表的主键"></property>

            <set name="集合属性名"table="集合属性要映射到的表">

                            <key column="指定集合表的外键字段"></key>

                            <element column="address"type="元素类型,一定要指定"></element>

                    </set>

<!-- list集合映射 list-index 指定的是排序列的名称 (因为要保证list集合的有序) -->

                     <list name=".." table="..">

                             <key column=".."></key>

                             <list-index column=".."></list-index>

                             <element column=".." type="string"></element>

                     </list>

             <!-- map集合的映射-->

                     <map name=".." table="..">

                           <key column=".."></key>

                           <map-key column=".." type=".." ></map-key>

                           <element column=".." type=".." ></element>

                     </map>

    </class>

 

type   指定映射表的字段的类型,如果不指定会匹配属性的类型

                                               java类型:    必须写全名

                                               hibernate类型: 直接写类型,都是小写

<!-- 如果列名称为数据库关键字,需要用反引号或改列名。 -->

<propertyname="desc" column="`desc`"type="java.lang.String"></property>

联合/复合主键

         如果找不到合适的列作为主键,除了用id列以外,我们一般用联合主键,即多列的值作为一个主键,从而确保记录的唯一性。

<!-- 复合主键映射 -->

                   <composite-id name="keys">

                            <key-property name="userName" type="string"></key-property>

                            <key-property name="address" type="string"></key-property>

                   </composite-id>

运行流程

执行流程:

技术分享

会话工厂类SessionFactory创建

Hibernate4.0以下版本创建

public static void main(String[] args)throws Exception{

        // 1. 加载默认的hibernate.cfg.xml的配置文件

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

           // 2. 加载hbm文件  (Test.hbm.xml)

           config.addClass(cn.itcast.hibernate.api.Test.class);

           // 3. 根据配置生成表

           SchemaExport schema = new SchemaExport(config);

           schema.create(true,true);

           // 4. 构建SessionFactory对象

           SessionFactory factory = config.buildSessionFactory();

           Session session = factory.openSession();         // 5. 建立连接

           Transaction tran = session.beginTransaction(); // 6. 开启事务

           Test t = new Test();

           t.setName("test hibbernate");

           session.save(t);

           tran.commit();        // 7.提交事务

           session.close();        // 8.关闭会话

}

Session 类的方法:

取得持久化对象的方法: get() load()

持久化对象都得保存,更新和删除:save(),update(),saveOrUpdate(),delete()

开启事务: beginTransaction().

管理Session 的方法:isOpen(),flush(), clear(), evict(),close()等

Transaction 类的方法:

commit():提交相关联的session实例

rollback():撤销事务操作

wasCommitted():检查事务是否提交

Hibernate4.0以后版本创建(安全线程)

public class HibernateSessionFactory

{

         //指定Hibernate配置文件路径

         private static String CONFIG_FILE_LOCATION = "/hibernate.cfg.xml";

         //创建ThreadLocal对象

         private static final ThreadLocal<Session> sessionThreadLocal =new ThreadLocal<Session>();

         //创建Configuration对象

         private static Configuration configuration = new Configuration();

         //定义SessionFactory对象

         private static SessionFactory sessionFactory;

         //定义configFile属性并赋值

         private static String configFile = CONFIG_FILE_LOCATION;

         static

         {

                   try

                   {

                            //读取配置文件hibernate.cfg.xml

                            configuration.configure();

                            //生成一个注册机对象

                            ServiceRegistry serviceRegistry=new ServiceRegistryBuilder().

                                               applySettings(configuration.getProperties()).buildServiceRegistry();

               //生成注册机对象(hibernate5之后注册机对象如下创建)

               ServiceRegistry serviceRegistry =newStandardServiceRegistryBuilder()

                        .configure("org/hibernate/example/hibernate.cfg.xml").build();

 

                            //使用注册机对象serviceRegistry创建sessionFactory

                            sessionFactory = configuration.buildSessionFactory(serviceRegistry);

 

                   }

                   catch (HibernateException e)

                   {

                            e.printStackTrace();

                   }

         }

        

         //创建无参的HibernateSessionFactory构造方法

         private HibernateSessionFactory(){

         } 

        

         //获得SessionFactory对象

         public static SessionFactory getSessionFactory()

         {                

                   return sessionFactory;

         }       

        

         //重建SessionFactory

         public static void rebuildSessionFactory()

         {

                   synchronized (sessionFactory)

                   {

                            try

                            {

                                     configuration.configure(configFile);

                                     ServiceRegistry serviceRegistry=new ServiceRegistryBuilder().

                                                        applySettings(configuration.getProperties()).buildServiceRegistry();

                                     //使用注册机对象serviceRegistry创建sessionFactory

                                     sessionFactory = configuration.buildSessionFactory(serviceRegistry);

                            }

                            catch (HibernateException e)

                            {

                                     e.printStackTrace();

                            }

                   }

         }       

        

         //获得Session对象

         public static Session getSession()

         {

                   //获得ThreadLocal对象管理的Session对象

                   Session session = (Session) sessionThreadLocal.get();              

                   try

                   {

                            //判断Session对象是否已经存在或是否打开

                            if (session ==null || !session.isOpen())

                            {

                                     //如果Session对象为空或未打开,再判断sessionFactory对象是否为空

                                     if (sessionFactory ==null)

                                     {

                                               //如果sessionFactory为空,则创建SessionFactory

                                               rebuildSessionFactory();

                                     }       

                                     //如果sessionFactory不为空,则打开Session

                                     session = (sessionFactory !=null) ? sessionFactory.openSession() :null;

                                     sessionThreadLocal.set(session);

                            }

                   }

                   catch (HibernateException e)

                   {

                            e.printStackTrace();

                   }                

                   return session;

         }       

        

         //关闭Session对象

         public static void closeSession()

         {

                   Session session = (Session) sessionThreadLocal.get();

                   sessionThreadLocal.set(null);

                   try

                   {

                            if (session !=null && session.isOpen())

                            {

                                     session.close();

                            }

                   }

                   catch (HibernateException e)

                   {

                            e.printStackTrace();

                   }

         }

        

         //configFile属性的set方法

         public static void setConfigFile(String configFile)

         {

                   HibernateSessionFactory.configFile = configFile;

                   sessionFactory = null;

         }

         //configuration属性的get方法

         public static Configuration getConfiguration()

         {

                   return configuration;

         }       

}

Session创建原理

由于SessionFactory是线程安全的,因而同一个SessionFactory实例可以被多个线程共

享,即多个并发线程可以同时访问一个SessionFactory并获得Session实例。但由于Sessior

不是线程安全的,如果多个并发线程同时操作同一个Session对象,就可能出现一个线程在

进行数据库操作,而另一个线程将Session对象关闭的情况,从而出现异常。如何才能保证

线程安全呢?这就要求SessionFactory能够针对不同的线程创建不同的Session对象,即需

要对Session进行有效的管理,Hibernate中使用ThreadLocal对象来维护和管理Session实例

ThreadLocal是指线程局部变量(Tread Local Variable),线程局部变量高效地为每个使用

它的线程提供单独的线程局部变量的副本。每个线程只能修改与自己相联系的副本,而不

会影响到其他进程的副本。为了实现为每个线程维护一个变量的副本, ThreadLocal类提供

了一个Map结构,其key值用来保存线程的ID, value值用来保存一个Session实例的副本。

这样,多个线程并发操作时,是在与自己绑定的Session实例副本上进行的,从而避免多个

线程在同一个Session实例上操作时可能导致的数据异常。

在 HibemateSessionFactory 类的 getSession()方法中,首先调用 ThreadLocal 类的 get()方法获得当前线程的Session对象,然后判断是否已存在该Session对象。如果该对象不存在或者该对象未打幵,再判断SessionFactory对象是否为空,如果SessionFactory对象不存在,先调用rebuildSessionFactory方法创建SessionFactory。如果存在则调用sessionFactory的openSession()方法创建Sessionsession对象。创建完成后还要调用threadLocal的set()方法为该线程保存session对象

持久化对象的状态

瞬时对象(Transient Objects)    

      使用new操作符初始化的对象不是立刻就持久化的,他们的状态是瞬时的。

      (1) 不处于Session的缓存中,也可以说,不被任何一个Session实例关联。

      (2) 在数据库中没有对应的记录。

持久化对象(Persist Objects)

      持久实例是任何具有数据库标识的实例。它有持久化管理器Session统一管理,持久实例是在事务中进行操作的———他们的状态在事务结束时同数据库进行同步。

      (1) 位于一个Session实例的缓存中,也可以说,持久化对象总是被一个Session实例关联。

      (2) 持久化对象和数据库中的相关记录对应。

      (3) Session在清理缓存时,会根据持久化对象的属性变化,来同步更新数据库。

离线对象(Detached Objects)

     Session关闭之后,持久化对象就变为离线对象。离线表示这个对象不能再与数据库保持同步,他们不再受Hibernate管理。

      (1) 不再位于Session的缓存中,也可以说,游离对象不被Session关联。

      (2) 游离对象是由持久化对象转变过来的,因此在数据库中可能还存在与它对应的记录(前提条件是没有其他程序删除了这条记录)。

技术分享

能够使hibernate的对象由瞬时态或托管态转变为持久态:

?      save()方法:将对象由瞬时态转变为持久态。

?      load()或get()方法:获得的对象的状态处于持久态。

?      find()方法:获得的List集合中的对象状态处于持久态。

?      update()、saveOrUpdate()和lock()方法:可将托管态对象转变为持久态。

能够使Hibernate的对象由持久态转变为托管态的方法:

?      close()方法:调用后,Session的缓存会被清空,缓存中所有持久态对象状态都转变为托管态。处于托管状态的对象称为游离对象,当游离对象不再被引用时,将被Java虚拟机垃圾回收机制清除。

?      evict()方法:可将Session缓存中一个指定的持久态对象删除,使其转变为托管态对象。当缓存中保存了大量处于持久态的对象时,为了节省内存空间,可以调用evict()方法删除一些持久态对象。

知识点

A.     Hibernate的运行过程如下:

1、应用程序先调用Configuration类,该类读取Hibernate配置文件及映射文件中的信息,

2、并用这些信息生成一个SessionFactory对象,

3、然后从SessionFactory对象生成一个Session对象,

4、并用Session对象生成Transaction对象;

    A、可通过Session对象的get(),load(),save(),update(),delete()

      和saveOrUpdate()等方法对PO进行加载、保存、更新、删除、

      等操作;

    B、在查询的情况下,可通过Session对象生成一个Query对象,然后

      利用Query对象执行查询操作;如果没有异常,Transaction对象将

      提交这些操作到数据库中

B.      session的get()和load()有什么区别?

    get()如果没有找到持久化类返回null,有可能导致空指针异常。

   load()如果没有找到持久化类直接抛出异常。

   get()是直接加载数据,load()是延迟加载,当使用被加载数据的时候才发送SQL。

简而言之:Hibernate对于load()认为数据库一定存在,因此可以放心的使用代理进行延迟加载,如果使用中发现了问题,那么只能抛出异常。而对于get方法一定要获取真实的数据,否则返回null。

DataType dataType1 = (DataType) session.load(DataType.class,new Long(1));

DataType dataType2 = (DataType) session.load(DataType.class,new Long(1));

System.out.println(dataType1);   // 延迟加载,需要使用才发送SQL语句

System.out.println(dataType2);   // 从一级缓存中获取持久化对象

System.out.println(dataType1 == dataType2);

session.getTransaction().commit();

C.     lazy 值

       true   使用懒加载 

       false   关闭懒加载  

       extra   (在集合数据懒加载时候提升效率)

       在真正使用数据的时候才向数据库发送查询的sql;

       如果调用集合的size()/isEmpty()方法,只是统计,不真正查询数据!

D.    Query对象在获取表的所有的数据的时候,使用list()和 iterator()有什么  

区别?

ü  编写代码的方式不同list()和iterator()

ü  底层发送的SQL语句不同

ü  list()直接一次性获取到所有持久化类的对象,iterator()先获取的是所有的数据的id值。当真正的遍历使用数据的时候再发送select语句。因此该方法一定要处于session会话中。

ü  list发送的查询语句只有1条。Iterator发送多条查询语句,因此iterator的效率低下。懒汉式(iterator)   饿汉式(list)

Hibernate  Api

|-- Configuration       配置管理类对象

       config.configure();    加载主配置文件的方法(hibernate.cfg.xml)

                                          默认加载src/hibernate.cfg.xml

       config.configure(“cn/config/hibernate.cfg.xml”);加载指定路径下指定名称的主配                                                                                      置文件

       config.buildSessionFactory();      创建session的工厂对象

 

|-- SessionFactory     session的工厂(或者说代表了这个hibernate.cfg.xml配置文件)

       sf.openSession();   创建一个sesison对象

       sf.getCurrentSession();  创建session或取出session对象

 

|--Session      session对象维护了一个连接(Connection), 代表了与数据库连接的会话。Hibernate最重要的对象: 只用使用hibernate与数据库操作,都用到这个对象

session.beginTransaction(); 开启一个事务; hibernate要求所有的与数据库的操作必须有事务的环境,否则报错!

 

更新:

       session.save(obj);   保存一个对象

       session.update(emp);  更新一个对象

session.saveOrUpdate(emp); 保存或者更新的方法:

                                                 à没有设置主键,执行保存;

                                                 à有设置主键,执行更新操作;

                                                 à如果设置主键不存在报错!

使用Session对象的save方法,虽然可以完成对象的持久化操作,但有时候会出现问题,如一个对象己经被持久化了,此时如果再次调用saveO方法,将会出现异常。

使用saveOrUpdateO方法可以很好地解决这一问题,因为它会自动判断该对象是否已经持久化,如果已经持久化,将执行更新操作,否则执行添加操作。如果标识(主键)的生成策略是自增型的,则使用Session对象的save()和saveOrUpdate〇方法是完全相同的。

提示: Session的save方法必须在事务环境中完成,并需使用commit方法提交事务,记录才能成功添加到数据表中。

 

      

 

主键查询:

       session.get(Employee.class, 1);    主键查询

session.load(Employee.class, 1);   主键查询 (支持懒加载)


Hibernate技术(一)--Hibernate初步