hibernate总结

来源:互联网 发布:苏联解体中国反应 知乎 编辑:程序博客网 时间:2024/05/16 07:09

hibernate下载

下载地址:http://sourceforge.net/

Hibernate 主配置文件详解


一、数据库基本配置信息主要包括5项:数据库方言、数据库连接字符串、数据用户名、数据库密码、驱动 具体不同数据库的配置可以参考如下:

二、其他信息主要包括3项:是否显示sql语句(showsql) 是否格式化sql语句(format_sql)及是否有Hibernate生成数据库创建脚本(DDL)

三、导入一些对象与表的映射文件

具体的配置样例如下:

 

beta1

<!DOCTYPE hibernate-configuration PUBLIC

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

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

<hibernate-configuration>

    <session-factory>

       <!-- 一、数据库信息:数据库方言(是一个类的全名)与数据库连接信息 -->

       <property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>

       <property name="connection.url">jdbc:mysql:///hibernate_20120131</property>

       <property name="connection.driver_class">com.mysql.jdbc.Driver</property>

       <property name="connection.username">root</property>

       <property name="connection.password">root</property>

       <!-- 二、其他配置 -->

       <property name="show_sql">true</property>

       <property name="format_sql">false</property>

       <!--

           create: 先删表,再建表。

           create-drop: 启动时建表,退出前删表。

           update: 如果表结构不一致,就创建或更新。

           validate: 启动时验证表结构,如果不致就抛异常。

        -->

       <property name="hibernate.hbm2ddl.auto">update</property>

       <!-- 三、导入映射配置文件

       <mapping resource="cn/itcast/a_helloworld/User.hbm.xml"/>

        -->

    </session-factory>

</hibernate-configuration>


beta2

<!--标准的XML文件的起始行,version='1.0'表明XML的版本,encoding='gb2312'表明XML文件的编码方式--> 
                <?xml version='1.0' encoding='gb2312'?> 
<!--表明解析本XML文件的DTD文档位置,DTDDocument Type Definition 的缩写,即文档类型的定义,XML解析器使用DTD文档来检查XML文件的合法性。hibernate.sourceforge.net/hibernate-configuration-3.0dtd可以在Hibernate3.1.3软件包中的src\org\hibernate目录中找到此文件--> 
<!DOCTYPE hibernate-configuration PUBLIC 
          "-//Hibernate/Hibernate Configuration DTD 3.0//EN" 
          "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> 
    <!--声明Hibernate配置文件的开始-->      
    <hibernate-configuration> 
    <!--表明以下的配置是针对session-factory配置的,SessionFactoryHibernate中的一个类,这个类主要负责保存HIbernate的配置信息,以及对Session的操作--> 
      <session-factory>    
      <!--配置数据库的驱动程序,Hibernate在连接数据库时,需要用到数据库的驱动程序--> 
          <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver </property> 
      <!--设置数据库的连接url:jdbc:mysql://localhost/hibernate,其中localhost表示mysql服务器名称,此处为本机,    hibernate是数据库名-->  
            <property name="hibernate.connection.url">jdbc:mysql://localhost/hibernate </hibernate> 
    <!--连接数据库是用户名--> 
          <property name="hibernate.connection.username">root </property> 
          <!--连接数据库是密码--> 
          <property name="hibernate.connection.password">123456 </property>        
          <!--数据库连接池的大小--> 
          <property name="hibernate.connection.pool.size">20 </property>        
        <!--是否在后台显示Hibernate用到的SQL语句,开发时设置为true,便于差错,程序运行时可以在Eclipse的控制台显示Hibernate的执行Sql语句。项目部署后可以设置为false,提高运行效率--> 
        <property name="hibernate.show_sql">true </property> 
        <!--jdbc.fetch_size是指Hibernate每次从数据库中取出并放到JDBCStatement中的记录条数。Fetch Size设的越大,读数据库的次数越少,速度越快,Fetch Size越小,读数据库的次数越多,速度越慢--> 
        <property name="jdbc.fetch_size">50 </property> 
        <!--jdbc.batch_size是指Hibernate批量插入,删除和更新时每次操作的记录数。Batch Size越大,批量操作的向数据库发送Sql的次数越少,速度就越快,同样耗用内存就越大--> 
        <property name="jdbc.batch_size">23 </property> 
        <!--jdbc.use_scrollable_resultset是否允许HibernateJDBC的可滚动的结果集。对分页的结果集。对分页时的设置非常有帮助--> 
        <property name="jdbc.use_scrollable_resultset">false </property> 
        <!--connection.useUnicode连接数据库时是否使用Unicode编码--> 
        <property name="Connection.useUnicode">true </property> 
        <!--connection.characterEncoding连接数据库时数据的传输字符集编码方式,最好设置为gbk,用gb2312有的字符不全--> 
    <property name="connection.characterEncoding">gbk </property>      
        
        <!--hibernate.dialect 只是Hibernate使用的数据库方言,就是要用Hibernate连接那种类型的数据库服务器。--> 
          <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect </property> 
        <!--指定映射文件为“hibernate/ch1/UserInfo.hbm.xml-->        
          <mapping resource="org/mxg/UserInfo.hbm.xml"> 
  </session-factory> 
  </hibernate-configuration>    
  
  
  
  <bean id="dataSource"  
  class="org.apache.commons.dbcp.BasicDataSource"  
  destroy-method="close">  
//连接驱动    
  <property name="driverClassName" value="${jdbc.driverClassName}" />  
//连接url,    
<property name="url" value="${jdbc.url}" />  
//连接用户名    
  <property name="username" value="${jdbc.username}" />  
//连接密码    
  <property name="password" value="${jdbc.password}" />  
</bean>  
  
<bean id="hbSessionFactory"  
  class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">  
  <property name="dataSource" ref="dataSource" />  
  <property name="configLocation">  
//hibernate配置文件位置    
  <value>WEB-INF/hibernate.cfg.xml </value>  
  </property>  
  <property name="configurationClass"  
  value="org.hibernate.cfg.AnnotationConfiguration" />  
  <property name="hibernateProperties">  
  <props>  
  //针对oracle数据库的方言,特定的关系数据库生成优化的SQL    
    <prop key="hibernate.dialect">  
    org.hibernate.dialect.OracleDialect    
    </prop>  
  //选择HQL解析器的实现    
    <prop key="hibernate.query.factory_class">  
    org.hibernate.hql.ast.ASTQueryTranslatorFactory    
    </prop>  
    //是否在控制台打印sql语句    
    <prop key="hibernate.show_sql">true </prop>  
    //Hibernate系统参数中hibernate.use_outer_join被打开的情况下,该参数用来允许使用outer join来载入此集合的数据。    
    <prop key="hibernate.use_outer_join">true </prop>  
  //默认打开,启用cglib反射优化。cglib是用来在Hibernate中动态生成PO字节码的,打开优化可以加快字节码构造的速度    
  <prop key="hibernate.cglib.use_reflection_optimizer">true </prop>  
  //输出格式化后的sql,更方便查看    
  <prop key="hibernate.format_sql">true </prop>  
  //useUnicode”和“characterEncoding”决定了它是否在客户端和服务器端传输过程中进行Encode,以及如何进行Encode    
  <prop key="hibernate.connection.useUnicode">true </prop>  
  //允许查询缓存个别查询仍然需要被设置为可缓存的.    
  <prop key="hibernate.cache.use_query_cache">false </prop>  
  <prop key="hibernate.default_batch_fetch_size">16 </prop>  
    //连接池的最大活动个数    
    <prop key="hibernate.dbcp.maxActive">100 </prop>  
  //当连接池中的连接已经被耗尽的时候,DBCP将怎样处理(0 = 失败,1 = 等待,2  =  增长)    
    <prop key="hibernate.dbcp.whenExhaustedAction">1 </prop>  
    //最大等待时间    
    <prop key="hibernate.dbcp.maxWait">1200 </prop>  
    //没有人用连接的时候,最大闲置的连接个数    
    <prop key="hibernate.dbcp.maxIdle">10 </prop>  
    ##以下是对prepared statement的处理,同上。    
    <prop key="hibernate.dbcp.ps.maxActive">100 </prop>  
    <prop key="hibernate.dbcp.ps.whenExhaustedAction">1 </prop>  
    <prop key="hibernate.dbcp.ps.maxWait">1200 </prop>  
    <prop key="hibernate.dbcp.ps.maxIdle">10 </prop>  
  </props>  
  </property>  
</bean>  


Hibernate工作原理

1.Hibernate是如何连接数据库  
    主要是通过hibernate.cfg.xml配置文件中的配置   
    在这个文件中定义了数据库进行连接所需要的信息,包括JDBC驱动、用户名、密码、数据库方言等,configuration类借助dom4j的XML解析器解析设置环       境,然后使用这些环境属性来生成 SessionFactory。这样这个sessionFactory生成的session就能成功获得数据库的连接。  
 

2.Hibernate是如何进行数据库写操作  
  对数据库的写操作包括保存、更新和删除,当保存一个POJO持久对象时,触发Hibernate的保存事件监听器进行处理。Hibernate通过映射文件获得对象对应数据库表名以及属性所对应的表中的列名,然后通过反射机制持久化对象(实体对象)的各个属性,最终组织成向数据库插入新对象的SQL insert语句。调用了session.save()方法后,这个对象会标识成持久化状态存放在session中,对于Hibernate来说它就是一个持久化了的对象,但这个时候Hibernate还不会真正的执行insert语句,当进行session的刷新同部或事务提交时,Hibernate会把session缓存中的所有SQL语句一起执行,对于更新、删除操作也是采用类似的机制。 然后,提交事务并事务提交成功后,这些写操作就会被永久地保存进数据库中,所以,使用session对数据库操作还依赖于Hibernate事务的处理。如果设置了二级缓存,那么这些操作会被同步到二级缓存中,Hibernate对数据库最终操作也是依赖于底层JDBC对数据库进行。  

3.Hibernate 如何从数据库中载入对象  
     
    当使用session.load()载入对象时,可以设置是否采用延迟加载,如果延迟加载,那么load返回的对象实际是CGLIB或javassist返回的代理类,它的非主键属性都是空的,这对于对象集合属性很有效。 Hibernate以此来节约内存,当真正需要读取对象时,Hibernate会先尝试从session缓存中读取,如果session缓存中数据不存在或者是脏数据并且配置了二级缓存,Hibernate尝试从二级缓存中检索数据,否则Hibernate会根据对象类型,主键等信息组织select语句到数据中读取,再把select结果组织成对象返回。  
   

4.Hibernate如何进行数据库查询操作  

  Hibernate提供SQL HQL Criteria查询方式。HQL是其中运用最广泛的查询方式。用户使用session.createQuery()方法以一条HQL语句为参数创建 Query查询对象后,Hibernate会使用Anltr库把HQL语句解析成JDBC可以识别的SQL语句,如果设置了查询缓存,那么执行 Query.list()时,Hibernate会先对查询缓存进行查询,如果查询缓存不存在,再使用select语句查询数据库。
Hibernate工作原理

JAVA Hibernate工作原理及为什么要用

hibernate 简介:
hibernate是一个开源框架,它是对象关联关系映射的框架,它对JDBC做了轻量级的封装,而我们java程序员可以使用面向对象的思想来操纵数据库。
hibernate核心接口
session:负责被持久化对象CRUD操作
sessionFactory:负责初始化hibernate,创建session对象
configuration:负责配置并启动hibernate,创建SessionFactory
Transaction:负责事物相关的操作
Query和Criteria接口:负责执行各种数据库查询

hibernate工作原理:
1.通过Configuration config = new Configuration().configure();//读取并解析hibernate.cfg.xml配置文件
2.由hibernate.cfg.xml中的<mapping resource="com/xx/User.hbm.xml"/>读取并解析映射信息
3.通过SessionFactory sf = config.buildSessionFactory();//创建SessionFactory
4.Session session = sf.openSession();//打开Sesssion
5.Transaction tx = session.beginTransaction();//创建并启动事务Transation
6.persistent operate操作数据,持久化操作
7.tx.commit();//提交事务
8.关闭Session
9.关闭SesstionFactory

为什么要用hibernate:
1. 对JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码。
2. Hibernate是一个基于JDBC的主流持久化框架,是一个优秀的ORM实现。他很大程度的简化DAO层的编码工作
3. hibernate使用Java反射机制,而不是字节码增强程序来实现透明性。
4. hibernate的性能非常好,因为它是个轻量级框架。映射的灵活性很出色。它支持各种关系数据库,从一对一到多对多的各种复杂关系。
 
 Hibernate是如何延迟加载?get与load的区别

1. 对于Hibernate get方法,Hibernate会确认一下该id对应的数据是否存在,首先在session缓存中查找,然后在二级缓存中查找,还没有就查询数据库,数据 库中没有就返回null。这个相对比较简单,也没有太大的争议。主要要说明的一点就是在这个版本(bibernate3.2以上)中get方法也会查找二级缓存!

2. Hibernate load方法加载实体对象的时候,根据映射文件上类级别的lazy属性的配置(默认为true),分情况讨论: 

(1)若为true,则首先在Session缓存中查找,看看该id对应的对象是否存在,不存在则使用延迟加载,返回实体的代理类对象(该代理类为实体类的子类,由CGLIB动态生成)。等到具体使用该对象(除获取OID以外)的时候,再查询二级缓存和数据库,若仍没发现符合条件的记录,则会抛出一个ObjectNotFoundException。

(2)若为false,就跟Hibernateget方法查找顺序一样,只是最终若没发现符合条件的记录,则会抛出一个ObjectNotFoundException。

这里get和load有两个重要区别: 

如果未能发现符合条件的记录,Hibernate get方法返回null,而load方法会抛出一个ObjectNotFoundException。

load方法可返回没有加载实体数据的代 理类实例,而get方法永远返回有实体数据的对象。

(对于load和get方法返回类型:好多书中都说:“get方法永远只返回实体类”,实际上并不正 确,get方法如果在session缓存中找到了该id对应的对象,如果刚好该对象前面是被代理过的,如被load方法使用过,或者被其他关联对象延迟加 载过,那么返回的还是原先的代理对象,而不是实体类对象,如果该代理对象还没有加载实体数据(就是id以外的其他属性数据),那么它会查询二级缓存或者数 据库来加载数据,但是返回的还是代理对象,只不过已经加载了实体数据。)

总之对于get和load的根本区别,一句话,hibernate对于 load方法认为该数据在数据库中一定存在,可以放心的使用代理来延迟加载,如果在使用过程中发现了问题,只能抛异常;而对于get方 法,hibernate一定要获取到真实的数据,否则返回null。

Hibernate中怎样实现类之间的关系?(如:一对多、多对多的关系)

类与类之间的关系主要体现在表与表之间的关系进行操作,它们都市对对象进行操作,我们程序中把所有的表与类都映射在一起,它们通过配置文件中的many-to-one、one-to-many、many-to-many、

说下Hibernate的缓存机制:

<span style="font-size:12px;">Hibernate缓存的作用:    Hibernate是一个持久层框架,经常访问物理数据库,为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能。缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据Hibernate缓存分类:  Hibernate缓存包括两大类:Hibernate一级缓存和Hibernate二级缓存Hibernate一级缓存又称为“Session的缓存”,它是内置的,意思就是说,只要你使用hibernate就必须使用session缓存。由于Session对象的生命周期通常对应一个数据库事务或者一个应用事务,因此它的缓存是事务范围的缓存。在第一级缓存中,持久化类的每个实例都具有唯一的OID。 Hibernate二级缓存又称为“SessionFactory的缓存”,由于SessionFactory对象的生命周期和应用程序的整个过程对应,因此Hibernate二级缓存是进程范围或者集群范围的缓存,有可能出现并发问题,因此需要采用适当的并发访问策略,该策略为被缓存的数据提供了事务隔离级别。第二级缓存是可选的,是一个可配置的插件,在默认情况下,SessionFactory不会启用这个插件。什么样的数据适合存放到第二级缓存中?   1 很少被修改的数据   2 不是很重要的数据,允许出现偶尔并发的数据   3 不会被并发访问的数据   4 常量数据   不适合存放到第二级缓存的数据?   1经常被修改的数据   2 .绝对不允许出现并发访问的数据,如财务数据,绝对不允许出现并发   3 与其他应用共享的数据。 Hibernate查找对象如何应用缓存?当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;如果都查不到,再查询数据库,把结果按照ID放入到缓存删除、更新、增加数据的时候,同时更新缓存Hibernate管理缓存实例无论何时,我们在管理Hibernate缓存(Managing the caches)时,当你给save()、update()或saveOrUpdate()方法传递一个对象时,或使用load()、 get()、list()、iterate() 或scroll()方法获得一个对象时, 该对象都将被加入到Session的内部缓存中。 当随后flush()方法被调用时,对象的状态会和数据库取得同步。 如果你不希望此同步操作发生,或者你正处理大量对象、需要对有效管理内存时,你可以调用evict() 方法,从一级缓存中去掉这些对象及其集合。 </span>

 Hibernate的查询方式

Sql、Criteria,object comptosition
Hql:
1、 属性查询
2、 参数查询、命名参数查询
3、 关联查询
4、 分页查询
5、 统计函数

 如何优化Hibernate?
1.使用双向一对多关联,不使用单向一对多
2.灵活使用单向一对多关联
3.不用一对一,用多对一取代
4.配置对象缓存,不使用集合缓存
5.一对多集合使用Bag,多对多集合使用Set
6. 继承类使用显式多态

7. 表字段要少,表关联不要怕多,有二级缓存撑腰

 

hibernate的开发步骤:

开发步骤
    1)搭建好环境
        引入hibernate最小的jar包
        准备Hibernate.cfg.xml启动配置文件
    2)写实体类(pojo)
    3)为实体类写映射文件"User.hbm.xml"
        在hibernate.cfg.xml添加映射的实体
    4)创建库表
    5)写测试类
        获得Configuration
        创建SessionFactory
        打开Session
        开启事务
        使用session操作数据
        提交事务
        关闭资源

Hibernate 所有缓存机制详解

hibernate提供的一级缓存

hibernate是一个线程对应一个session,一个线程可以看成一个用户。也就是说session级缓存(一级缓存)只能给一个线程用,别的线程用不了,一级缓存就是和线程绑定了。

hibernate一级缓存生命周期很短,和session生命周期一样,一级缓存也称session级的缓存或事务级缓存。如果tb事务提交或回滚了,我们称session就关闭了,生命周期结束了。

缓存和连接池的区别缓存和池都是放在内存里,实现是一样的,都是为了提高性能的。但有细微的差别,池是重量级的,里面的数据是一样的,比如一个池里放100个Connection连接对象,这个100个都是一样的。缓存里的数据,每个都不一样。比如读取100条数据库记录放到缓存里,这100条记录都不一样。

缓存主要是用于查询

//同一个session中,发出两次load方法查询

Student student = (Student)session.load(Student.class, 1);

System.out.println("student.name=" + student.getName());

//不会发出查询语句,load使用缓存

student = (Student)session.load(Student.class, 1);

System.out.println("student.name=" + student.getName());

第二次查询第一次相同的数据,第二次load方法就是从缓存里取数据,不会发出sql语句到数据库里查询。

//同一个session,发出两次get方法查询

Student student = (Student)session.get(Student.class, 1);

System.out.println("student.name=" + student.getName());

//不会发出查询语句,get使用缓存

student = (Student)session.get(Student.class, 1);

System.out.println("student.name=" + student.getName());

第二次查询第一次相同的数据,第二次不会发出sql语句查询数据库,而是到缓存里取数据。

//同一个session,发出两次iterate查询实体对象

Iterator iter = session.createQuery

("from Student s where s.id<5").iterate();

while (iter.hasNext()) {

Student student = (Student)iter.next();

System.out.println(student.getName());

}

System.out.println("--------------------------------------");

//它会发出查询id的语句,但不会发出根据id查询学生的语句,因为iterate使用缓存

iter = session.createQuery("from Student s where s.id<5").iterate();

while (iter.hasNext()) {

Student student = (Student)iter.next();

System.out.println(student.getName());

}

一说到iterater查询就要立刻想起:iterater查询在没有缓存的情况下会有N+1的问题。

执行上面代码查看控制台的sql语句,第一次iterate查询会发出N+1条sql语句,第一条sql语句查询所有的id,然后根据id查询实体对象,有N个id就发出N条语句查询实体。

第二次iterate查询,却只发一条sql语句,查询所有的id,然后根据id到缓存里取实体对象,不再发sql语句到数据库里查询了。

//同一个session,发出两次iterate查询,查询普通属性

Iterator iter = session.createQuery(

"select s.name from Student s where s.id<5").iterate();

while (iter.hasNext()) {

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

System.out.println(name);

}

System.out.println("--------------------------------------");

//iterate查询普通属性,一级缓存不会缓存,所以发出查询语句

//一级缓存是缓存实体对象的

iter = session.createQuery

("select s.name from Student s where s.id<5").iterate();

while (iter.hasNext()) {

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

System.out.println(name);

}

执行代码看控制台sql语句,第一次发出N+1条sql语句,第二次还是发出了N+1条sql语句。因为一级缓存只缓存实体对象,tb不会缓存普通属性,所以第二次还是发出sql查询语句。

//两个session,每个session发出一个load方法查询实体对象

try {

session = HibernateUtils.getSession();

session.beginTransaction();

Student student = (Student)session.load(Student.class, 1);

System.out.println("student.name=" + student.getName());

session.getTransaction().commit();

}catch(Exception e) {

e.printStackTrace();

session.getTransaction().rollback();

}finally {

HibernateUtils.closeSession(session);

}

第二个session调用load方法

try {

session = HibernateUtils.getSession();

session.beginTransaction();

Student student = (Student)session.load(Student.class, 1);

//会发出查询语句,session间不能共享一级缓存数据

//因为他会伴随着session的消亡而消亡

System.out.println("student.name=" + student.getName());

session.getTransaction().commit();

}catch(Exception e) {

e.printStackTrace();

session.getTransaction().rollback();

}finally {

HibernateUtils.closeSession(session);

}

第一个session的load方法会发出sql语句查询实体对象,第二个session的load方法也会发出sql语句查询实体对象。因为session间不能共享一级缓存的数据,所以第二个session的load方法查询相同的数据还是要到数据库中查询,因为它找不到第一个session里缓存的数据。

//同一个session,先调用save方法再调用load方法查询刚刚save的数据

Student student = new Student();

student.setName("张三");

//save方法返回实体对象的id

Serializable id = session.save(student);

student = (Student)session.load(Student.class, id);

//不会发出查询语句,因为save支持缓存

System.out.println("student.name=" + student.getName());

先save保存实体对象,再用load方法查询刚刚save的实体对象,则load方法不会发出sql语句到数据库查询的,而是到缓存里取数据,因为save方法也支持缓存。当然前提是同一个session。

//大批量的数据添加

for (int i=0; i<100; i++) {

Student student = new Student();

student.setName("张三" + i);

session.save(student);

//每20条更新一次

if (i % 20 == 0) {

session.flush();

//清除缓存的内容

session.clear();

}

}

大批量数据添加时,会造成内存溢出的,因为save方法支持缓存,每save一个对象就往缓存里放,如果对象足够多内存肯定要溢出。一般的做法是先判断一下save了多少个对象,如果save了20个对象就对缓存手动的清理缓存,这样就不会造成内存溢出。

注意:清理缓存前,要手动调用flush方法同步到数据库,否则save的对象就没有保存到数据库里。

注意:大批量数据的添加还是不要使用hibernate,这是hibernate弱项。可以使用jdbc(速度也不会太快,只是比hibernate好一点),或者使用工具产品来实现,比如oracle的Oracle SQL Loader,导入数据特别快。 



Hibernate 二级缓存

二级缓存需要sessionFactory来管理,它是进初级的缓存,所有人都可以使用,它是共享的。

二级缓存比较复杂,一般用第三方产品。hibernate提供了一个简单实现,用Hashtable做的,只能作为我们的测试使用,商用还是需要第三方产品。

使用缓存,肯定是长时间不改变的数据,如果经常变化的数据放到缓存里就没有太大意义了。因为经常变化,还是需要经常到数据库里查询,那就没有必要用缓存了。

hibernate做了一些优化,和一些第三方的缓存产品做了集成。老师采用EHCache缓存产品。

和EHCache二级缓存产品集成:EHCache的jar文件在hibernate的lib里,我们还需要设置一系列的缓存使用策略,需要一个配置文件ehcache.xml来配置。这个文件放在类路径下。

//默认配置,所有的类都遵循这个配置

<defaultCache

        //缓存里可以放10000个对象

        maxElementsInMemory="10000"

        //过不过期,如果是true就是永远不过期

        eternal="false"

        //一个对象被访问后多长时间还没有访问就失效(120秒还没有再次访问就失效)

        timeToIdleSeconds="120"

        //对象存活时间(120秒),如果设置永不过期,这个就没有必要设了

        timeToLiveSeconds="120"

        //溢出的问题,如果设成true,缓存里超过10000个对象就保存到磁盘里

        overflowToDisk="true"

        />

我们也可以对某个对象单独配置:

<cache name="com.bjpowernode.hibernate.Student"

        maxElementsInMemory="100"

        eternal="false"

        timeToIdleSeconds="10000"

        timeToLiveSeconds="10000"

        overflowToDisk="true"

        />

还需要在hibernate.cfg.xml配置文件配置缓存,让hibernate知道我们使用的是那个二级缓存。

<!-- 配置缓存提供商 -->

<property name="hibernate.cache.provider_class">

org.hibernate.cache.EhCacheProvider</property>

<!-- 启用二级缓存,这也是它的默认配置 -->

<property name="hibernate.cache.use_second_level_cache">

true</property>

启用二级缓存的配置可以不写的,因为默认就是true开启二级缓存。

必须还手动指定那些实体类的对象放到缓存里在hibernate.cfg.xml里:

//在<sessionfactory>标签里,在<mapping>标签后配置

<class-cache class="com.bjpowernode.hibernate.Student"

usage="read-only"/>

或者在实体类映射文件里:

//在<class>标签里,<id>标签前配置

<cache usage="read-only"/>

usage属性表示使用缓存的策略,一般优先使用read-only,表示如果这个数据放到缓存里了,则不允许修改,如果修改就会报错。这就要注意我们放入缓存的数据不允许修改。因为放缓存里的数据经常修改,也就没有必要放到缓存里。

使用read-only策略效率好,因为不能改缓存。但是可能会出现脏数据的问题,这个问题解决方法只能依赖缓存的超时,比如上面我们设置了超时为120秒,120后就可以对缓存里对象进行修改,而在120秒之内访问这个对象可能会查询脏数据的问题,因为我们修改对象后数据库里改变了,而缓存却不能改变,这样造成数据不同步,也就是脏数据的问题。

第二种缓存策略read-write,当持久对象发生变化,缓存里就会跟着变化,数据库中也改变了。这种方式需要加解锁,效率要比第一种慢。

还有两种策略,请看hibernate文档,最常用还是第一二种策略。

二级缓存测试代码演示:注意上面我们讲的两个session分别调用load方法查询相同的数据,第二个session的load方法还是发了sql语句到数据库查询数据,这是因为一级缓存只在当前session中共享,也就是说一级缓存不能跨session访问。

//开启二级缓存,二级缓存是进程级的缓存,可以共享

//两个session分别调用load方法查询相同的实体对象

try {

session = HibernateUtils.getSession();

session.beginTransaction();

Student student = (Student)session.load(Student.class, 1);

System.out.println("student.name=" + student.getName());

session.getTransaction().commit();

}catch(Exception e) {

e.printStackTrace();

session.getTransaction().rollback();

}finally {

HibernateUtils.closeSession(session);

}

try {

session = HibernateUtils.getSession();

session.beginTransaction();

Student student = (Student)session.load(Student.class, 1);

//不会发出查询语句,因为配置二级缓存,session可以共享二级缓存中的数据

//二级缓存是进程级的缓存

System.out.println("student.name=" + student.getName());

session.getTransaction().commit();

}catch(Exception e) {

e.printStackTrace();

session.getTransaction().rollback();

}finally {

HibernateUtils.closeSession(session);

}

如果开启了二级缓存,那么第二个session调用的load方法查询第一次查询的数据,是不会发出sql语句查询数据库的,而是去二级缓存中取数据。

//开启二级缓存

//两个session分别调用get方法查询相同的实体对象

try {

session = HibernateUtils.getSession();

session.beginTransaction();

Student student = (Student)session.get(Student.class, 1);

System.out.println("student.name=" + student.getName());

session.getTransaction().commit();

}catch(Exception e) {

e.printStackTrace();

session.getTransaction().rollback();

}finally {

HibernateUtils.closeSession(session);

}

try {

session = HibernateUtils.getSession();

session.beginTransaction();

Student student = (Student)session.get(Student.class, 1);

//不会发出查询语句,因为配置二级缓存,session可以共享二级缓存中的数据

//二级缓存是进程级的缓存

System.out.println("student.name=" + student.getName());

session.getTransaction().commit();

}catch(Exception e) {

e.printStackTrace();

session.getTransaction().rollback();

}finally {

HibernateUtils.closeSession(session);

}

注意:二级缓存必须让sessionfactory管理,让sessionfactory来清除二级缓存。sessionFactory.evict(Student.class);//清除二级缓存中所有student对象,sessionFactory.evict(Student.class,1);//清除二级缓存中id为1的student对象。

如果在第一个session调用load或get方法查询数据后,把二级缓存清除了,那么第二个session调用load或get方法查询相同的数据时,还是会发出sql语句查询数据库的,因为缓存里没有数据只能到数据库里查询。

我们查询数据后会默认自动的放到二级和一级缓存里,如果我们想查询的数据不放到缓存里,也是可以的。也就是说我们可以控制一级缓存和二级缓存的交换。

session.setCacheMode(CacheMode.IGNORE);禁止将一级缓存中的数据往二级缓存里放。

还是用上面代码测试,在第一个session调用load方法前,执行session.setCacheMode(CacheMode.IGNORE);这样load方法查询的数据不会放到二级缓存里。那么第二个session执行load方法查询相同的数据,会发出sql语句到数据库中查询,因为二级缓存里没有数据,一级缓存因为不同的session不能共享,所以只能到数据库里查询。

上面我们讲过大批量的数据添加时可能会出现溢出,解决办法是每当天就20个对象后就清理一次一级缓存。如果我们使用了二级缓存,光清理一级缓存是不够的,还要禁止一二级缓存交互,在save方法前调用session.setCacheMode(CacheMode.IGNORE)。

二级缓存也不会存放普通属性的查询数据,这和一级缓存是一样的,只存放实体对象。session级的缓存对性能的提高没有太大的意义,因为生命周期太短了。



Hibernate 查询缓存

一级缓存和二级缓存都只是存放实体对象的,如果查询实体对象的普通属性的数据,只能放到查询缓存里,查询缓存还存放查询实体对象的id。

查询缓存的生命周期不确定,当它关联的表发生修改,查询缓存的生命周期就结束。这里表的修改指的是通过hibernate修改,并不是通过数据库客户端软件登陆到数据库上修改。

hibernate的查询缓存默认是关闭的,如果要使用就要到hibernate.cfg.xml文件里配置:

<property name="hibernate.cache.use_query_cache">true</property>

并且必须在程序中手动启用查询缓存,在query接口中的setCacheable(true)方法来启用。

//关闭二级缓存,没有开启查询缓存,采用list方法查询普通属性

//同一个sessin,查询两次

List names = session.createQuery("select s.name from Student s")

.list();

for (int i=0; i<names.size(); i++) {

String name = (String)names.get(i);

System.out.println(name);

}

System.out.println("-----------------------------------------");

//会发出sql语句

names = session.createQuery("select s.name from Student s")

.setCacheable(true)

.list();

for (int i=0; i<names.size(); i++) {

String name = (String)names.get(i);

System.out.println(name);

}

上面代码运行,由于没有使用查询缓存,而一、二级缓存不会缓存普通属性,所以第二次查询还是会发出sql语句到数据库中查询。

现在开启查询缓存,关闭二级缓存,并且在第一次的list方法前调用setCacheable(true),并且第二次list查询前也调用这句代码,可以写出下面这样:

List names = session.createQuery("select s.name from Student s")

.setCacheable(true)

.list();

其它代码不变,运行代码后发现第二次list查询普通属性没有发出sql语句,也就是说没有到数据库中查询,而是到查询缓存中取数据。

//开启查询缓存,关闭二级缓存,采用list方法查询普通属性

//在两个session中调用list方法

try {

session = HibernateUtils.getSession();

session.beginTransaction();

List names = session.createQuery("select s.name from Student s")

.setCacheable(true)

.list();

for (int i=0; i<names.size(); i++) {

String name = (String)names.get(i);

System.out.println(name);

}

session.getTransaction().commit();

}catch(Exception e) {

e.printStackTrace();

session.getTransaction().rollback();

}finally {

HibernateUtils.closeSession(session);

}

System.out.println("----------------------------------------");

try {

session = HibernateUtils.getSession();

session.beginTransaction();

//不会发出查询语句,因为查询缓存和session的生命周期没有关系

List names = session.createQuery("select s.name from Student s")

.setCacheable(true)

.list();

for (int i=0; i<names.size(); i++) {

String name = (String)names.get(i);

System.out.println(name);

}

session.getTransaction().commit();

}catch(Exception e) {

e.printStackTrace();

session.getTransaction().rollback();

}finally {

HibernateUtils.closeSession(session);

}

运行结果是第二个session发出的list方法查询普通属性,没有发出sql语句到数据库中查询,而是到查询缓存里取数据,这说明查询缓存和session生命周期没有关系

//开启缓存,关闭二级缓存,采用iterate方法查询普通属性

//在两个session中调用iterate方法查询

运行结果是第二个session的iterate方法还是发出了sql语句查询数据库,这说明iterate迭代查询普通属性不支持查询缓存

//关闭查询缓存,关闭二级缓存,采用list方法查询实体对象

//在两个session中调用list方法查询

运行结果第一个session调用list方法查询实体对象会发出sql语句查询数据,因为关闭了二级缓存,所以第二个session调用list方法查询实体对象,还是会发出sql语句到数据库中查询。

//开启查询缓存,关闭二级缓存

//在两个session中调用list方法查询实体对象

运行结果第一个session调用list方法查询实体对象会发出sql语句查询数据库的。第二个session调用list方法查询实体对象,却发出了很多sql语句查询数据库,这跟N+1的问题是一样的,发出了N+1条sql语句。为什么会出现这样的情况呢?这是因为我们现在查询的是实体对象,查询缓存会把第一次查询的实体对象的id放到缓存里,当第二个session再次调用list方法时,它会到查询缓存里把id一个一个的拿出来,然后到相应的缓存里找(先找一级缓存找不到再找二级缓存),如果找到了就返回,如果还是没有找到,则会根据一个一个的id到数据库中查询,所以一个id就会有一条sql语句。

注意:如果配置了二级缓存,则第一次查询实体对象后,会往一级缓存和二级缓存里都存放。如果没有二级缓存,则只在一级缓存里存放。(一级缓存不能跨session共享)

//开启查询缓存,开启二级缓存

//在两个session中调用list方法查询实体对象

运行结果是第一个session调用list方法会发出sql语句到数据库里查询实体对象,因为配置了二级缓存,则实体对象会放到二级缓存里,因为配置了查询缓存,则实体对象所有的id放到了查询缓存里。第二个session调用list方法不会发出sql语句,而是到二级缓存里取数据。

查询缓存意义不大,查询缓存说白了就是存放由list方法或iterate方法查询的数据。我们在查询时很少出现完全相同条件的查询,这也就是命中率低,这样缓存里的数据总是变化的,所以说意义不大。除非是多次查询都是查询相同条件的数据,也就是说返回的结果总是一样,这样配置查询缓存才有意义。


六种方式实现hibernate查询

分别是HQL查询,对象化查询Criteria方法,动态查询DetachedCriteria,例子查询,sql查询,命名查询

           如果单纯的使用hibernate查询数据库只需要懂其中的一项就可以完成想要实现的一般功能,但是
从一个点,让我们掌握6中方法,则提供了更多选择。每一种方法都有其适用的情况与前提。

HQL查询

HQL是hibernate自己的一套查询语言,于SQL语法不同,具有跨数据库的优点。示例代码:

复制代码
static void query(String name){
  Session s=null;
  try{
   s=HibernateUtil.getSession();
   
   //from后面是对象,不是表名
   String hql="from Admin as admin where admin.aname=:name";//使用命名参数,推荐使用,易读。
   Query query=s.createQuery(hql);
   query.setString("name", name);
   
   List<Admin> list=query.list();
   
   for(Admin admin:list){
    System.out.println(admin.getAname());
   }
  }finally{
   if(s!=null)
   s.close();
  }
 }
复制代码

 

适用情况:常用方法,比较传统,类似jdbc。缺点:新的查询语言,适用面有限,仅适用于Hibernate框架。

对象化查询Criteria方法:

 

复制代码
static void cri(String name,String password){
  Session s=null;
  try{
   s=HibernateUtil.getSession();
   
   Criteria c=s.createCriteria(Admin.class);
   c.add(Restrictions.eq("aname",name));//eq是等于,gt是大于,lt是小于,or是或
   c.add(Restrictions.eq("apassword", password));
   
   List<Admin> list=c.list();
   for(Admin admin:list){
    System.out.println(admin.getAname());
   }
  }finally{
   if(s!=null)
   s.close();
  }
 }

复制代码

 

适用情况:面向对象操作,革新了以前的数据库操作方式,易读。缺点:适用面较HQL有限。

动态分离查询DetachedCriteria

 

复制代码
static List dc(DetachedCriteria dc) {

  Session s = HibernateUtil.getSession();
  Criteria c = dc.getExecutableCriteria(s);
  List rs = c.list();
  s.close();
  return rs;
 }
复制代码

 

 

复制代码
DetachedCriteria dc = DetachedCriteria.forClass(User.class);
  int id = 1;
  if (id != 0)
   dc.add(Restrictions.eq("id", id));
  Date age = new Date();
  if (age != null)
   dc.add(Restrictions.le("birthday", age));
  List users = dc(dc);
  System.out.println("离线查询返回结果:" + users);

复制代码

 

适用情况:面向对象操作,分离业务与底层,不需要字段属性摄入到Dao实现层。  缺点:适用面较HQL有限。

例子查询

复制代码
static List example(User user) {
  Session s = HibernateUtil.getSession();
  List<User> users = s.createCriteria(User.class).add(
    Example.create(user)).list();
  // List<User>
  // users2=s.createCriteria(User.class).add((Example.create(user)).ignoreCase())
  // .createCriteria("child").add((Example.create(user))).list();
  return users;
 }

复制代码

 

适用情况:面向对象操作。   缺点:适用面较HQL有限,不推荐。


sql查询

复制代码
static List sql() {

  Session s = HibernateUtil.getSession();
  Query q = s.createSQLQuery("select * from user").addEntity(User.class);
  List<User> rs = q.list();
  s.close();
  return rs;
 }

复制代码

 

适用情况:不熟悉HQL的朋友,又不打算转数据库平台的朋友,万能方法   缺点:破坏跨平台,不易维护,不面向对象。

命名查询

 

static List namedQuery(int id) {
  Session s = HibernateUtil.getSession();
  Query q = s.getNamedQuery("getUserById");
  q.setInteger("id", id);
  return q.list();
 }

 

复制代码
<?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="com.sy.vo.User" table="user" catalog="news">
    
 

    </class>
    <!-- 命名查询:定义查询条件 -->
    <query name="getUserById">
     <![CDATA[from User where id=:id]]>
    </query>
    <!-- 命名查询中使用sql,不推荐使用,影响跨数据库
    <sql-query name="getUserById2">
     <![CDATA[select * from User where ]]>
    </sql-query> -->
</hibernate-mapping>

复制代码

 适用情况:万能方法,有点像ibatis轻量级框架的操作,方便维护。  缺点:不面向对象。基于hql和sql,有一定缺陷。



hibernate注解
  1. 声明实体Bean  
  2.   
  3. @Entity  
  4. public class Flight implements Serializable {  
  5.   Long id;  
  6.   @Id  
  7.   public Long getId() { return id; }  
  8.   public void setId(Long id) { this.id = id; }  
  9. }  
  10.   
  11. @Entity 注解将一个类声明为实体 Bean, @Id 注解声明了该实体Bean的标识属性。  
  12.   
  13. Hibernate 可以对类的属性或者方法进行注解。属性对应field类别,方法的 getXxx()对应property类别。  
  14.   
  15. 定义表  
  16.   
  17. 通过 @Table 为实体Bean指定对应数据库表,目录和schema的名字。  
  18.   
  19. @Entity  
  20. @Table(name="tbl_sky")  
  21. public class Sky implements Serializable {  
  22.   
  23. ...  
  24.   
  25. @Table 注解包含一个schema和一个catelog 属性,使用@UniqueConstraints 可以定义表的唯一约束。  
  26.   
  27. @Table(name="tbl_sky",  
  28.   uniqueConstraints = {@UniqueConstraint(columnNames={"month""day"})}  
  29. )  
  30.   
  31. 上述代码在  "month" 和 "day" 两个 field 上加上 unique constrainst.  
  32.   
  33. @Version 注解用于支持乐观锁版本控制。  
  34.   
  35. @Entity  
  36. public class Flight implements Serializable {  
  37.    ...  
  38.    @Version  
  39.    @Column(name="OPTLOCK")  
  40.    public Integer getVersion() { ... }  
  41. }  
  42.   
  43. version属性映射到 "OPTLOCK" 列,entity manager 使用这个字段来检测冲突。 一般可以用 数字 或者 timestamp 类型来支持 version.  
  44.   
  45. 实体Bean中所有非static 非 transient 属性都可以被持久化,除非用@Transient注解。  
  46.   
  47. 默认情况下,所有属性都用 @Basic 注解。  
  48.   
  49. public transient int counter; //transient property  
  50.   
  51. private String firstname; //persistent property  
  52. @Transient  
  53. String getLengthInMeter() { ... } //transient property  
  54. String getName() {... } // persistent property  
  55. @Basic  
  56. int getLength() { ... } // persistent property  
  57. @Basic(fetch = FetchType.LAZY)  
  58. String getDetailedComment() { ... } // persistent property  
  59. @Temporal(TemporalType.TIME)  
  60. java.util.Date getDepartureTime() { ... } // persistent property  
  61. @Enumerated(EnumType.STRING)  
  62. Starred getNote() { ... } //enum persisted as String in database  
  63.   
  64. 上述代码中 counter, lengthInMeter 属性将忽略不被持久化,而 firstname, name, length 被定义为可持久化和可获取的。   
  65.   
  66. @TemporalType.(DATE,TIME,TIMESTAMP) 分别Map java.sql.(Date, Time, Timestamp).  
  67.   
  68. @Lob 注解属性将被持久化为 Blog 或 Clob 类型。具体的java.sql.Clob, Character[], char[] 和 java.lang.String 将被持久化为 Clob 类型. java.sql.Blob, Byte[], byte[] 和 serializable type 将被持久化为 Blob 类型。  
  69.   
  70. @Lob  
  71. public String getFullText() {  
  72.    return fullText;  // clob type  
  73. }  
  74.   
  75.   
  76. @Lob  
  77. public byte[] getFullCode() {  
  78.   return fullCode;  // blog type  
  79. }  
  80.   
  81. @Column 注解将属性映射到列。  
  82.   
  83. @Entity  
  84. public class Flight implements Serializable {  
  85.    ...  
  86.    @Column(updatable = false, name = "flight_name", nullable = false, length=50)  
  87.    public String getName() { ... }  
  88.   
  89. 定义 name 属性映射到 flight_name column, not null, can't update, length equal 50  
  90.   
  91. @Column(  
  92.    name="columnName"; (1) 列名  
  93.    boolean unique() default false; (2)    是否在该列上设置唯一约束  
  94.    boolean nullable() default true; (3)   列可空?  
  95.    boolean insertable() default true; (4) 该列是否作为生成 insert语句的一个列  
  96.    boolean updatable() default true; (5)  该列是否作为生成 update语句的一个列  
  97.    String columnDefinition() default ""; (6)  默认值  
  98.    String table() default ""; (7)             定义对应的表(deault 是主表)  
  99.    int length() default 255; (8)              列长度  
  100.    int precision() default 0// decimal precision (9)  decimal精度  
  101.    int scale() default 0// decimal scale        (10)  decimal长度  
  102.   
  103. 嵌入式对象(又称组件)也就是别的对象定义的属性  
  104.   
  105. 组件类必须在类一级定义 @Embeddable 注解。在特定的实体关联属性上使用 @Embeddable 和 @AttributeOverride 注解可以覆盖该属性对应的嵌入式对象的列映射。  
  106.   
  107. @Entity  
  108. public class Person implements Serializable {  
  109.    // Persistent component using defaults  
  110.    Address homeAddress;  
  111.    @Embedded  
  112.    @AttributeOverrides( {  
  113.       @AttributeOverride(name="iso2", column = @Column(name="bornIso2") ),  
  114.       @AttributeOverride(name="name", column = @Column(name="bornCountryName") )  
  115.    } )  
  116.    Country bornIn;  
  117.    ...  
  118. }  
  119.   
  120. @Embeddable  
  121. public class Address implements Serializable {  
  122.    String city;  
  123.    Country nationality; //no overriding here  
  124. }  
  125.   
  126. @Embeddable  
  127. public class Country implements Serializable {  
  128.    private String iso2;  
  129.    @Column(name="countryName"private String name;  
  130.    public String getIso2() { return iso2; }  
  131.    public void setIso2(String iso2) { this.iso2 = iso2; }  
  132.    public String getName() { return name; }  
  133.    public void setName(String name) { this.name = name; }  
  134.    ...  
  135. }  
  136.   
  137. Person 类定义了 Address 和  Country 对象,具体两个类实现见上。  
  138.   
  139. 无注解属性默认值:  
  140.   
  141. ? 属性为简单类型,则映射为 @Basic  
  142.   
  143. ? 属性对应的类型定义了 @Embeddable 注解,则映射为 @Embedded  
  144.   
  145. ? 属性对应的类型实现了Serializable,则属性被映射为@Basic并在一个列中保存该对象的serialized版本。  
  146.   
  147. ? 属性的类型为 java.sql.Clob or java.sql.Blob, 则映射到 @Lob 对应的类型。  
  148.   
  149. 映射主键属性  
  150.   
  151. @Id 注解可将实体Bean中某个属性定义为主键,使用@GenerateValue注解可以定义该标识符的生成策略。  
  152.   
  153. ? AUTO -  可以是 identity column, sequence 或者 table 类型,取决于不同底层的数据库  
  154. ? TABLE - 使用table保存id值  
  155. ? IDENTITY - identity column  
  156. ? SEQUENCE - seque  
  157.   
  158. nce   
  159.   
  160. @Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_STORE")  
  161. public Integer getId() { ... }  
  162.   
  163. @Id @GeneratedValue(strategy=GenerationType.IDENTITY)  
  164. public Long getId() { ... }  
  165.   
  166. AUTO 生成器,适用与可移值的应用,多个@Id可以共享同一个 identifier生成器,只要把generator属性设成相同的值就可以。通过@SequenceGenerator 和 @TableGenerator 可以配置不同的 identifier 生成器。   
  167.   
  168. <table-generator name="EMP_GEN"  
  169.      table="GENERATOR_TABLE"  
  170.      pk-column-name="key"  
  171.      value-column-name="hi"  
  172.      pk-column-value="EMP"  
  173.      allocation-size="20"/>  
  174. //and the annotation equivalent  
  175. @javax.persistence.TableGenerator(  
  176.      name="EMP_GEN",  
  177.      table="GENERATOR_TABLE",  
  178.      pkColumnName = "key",  
  179.      valueColumnName = "hi"  
  180.      pkColumnValue="EMP",  
  181.      allocationSize=20  
  182. )  
  183. <sequence-generator name="SEQ_GEN"  
  184.      sequence-name="my_sequence"  
  185.      allocation-size="20"/>  
  186. //and the annotation equivalent  
  187. @javax.persistence.SequenceGenerator(  
  188.      name="SEQ_GEN",  
  189.      sequenceName="my_sequence",  
  190.      allocationSize=20  
  191. )  
  192.   
  193. The next example shows the definition of a sequence generator in a class scope:  
  194.   
  195. @Entity  
  196. @javax.persistence.SequenceGenerator(  
  197.     name="SEQ_STORE",  
  198.     sequenceName="my_sequence"  
  199. )  
  200. public class Store implements Serializable {  
  201.    private Long id;  
  202.    @Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_STORE")  
  203.    public Long getId() { return id; }  
  204. }  
  205.   
  206. Store类使用名为my_sequence的sequence,并且SEQ_STORE生成器对于其他类是不可见的。  
  207.   
  208. 通过下面语法,你可以定义组合键。  
  209.   
  210. ? 将组件类注解为 @Embeddable, 并将组件的属性注解为 @Id  
  211. ? 将组件的属性注解为 @EmbeddedId  
  212. ? 将类注解为 @IdClass,并将该实体中所有主键的属性都注解为 @Id  
  213.   
  214. @Entity  
  215. @IdClass(FootballerPk.class)  
  216. public class Footballer {  
  217.   //part of the id key  
  218.   @Id public String getFirstname() {  
  219.     return firstname;  
  220.   }  
  221.   public void setFirstname(String firstname) {  
  222.      this.firstname = firstname;  
  223.   }  
  224.   //part of the id key  
  225.   @Id public String getLastname() {  
  226.     return lastname;  
  227.   }  
  228.   public void setLastname(String lastname) {  
  229.     this.lastname = lastname;  
  230.   }  
  231.   public String getClub() {  
  232.     return club;  
  233.   }  
  234.   public void setClub(String club) {  
  235.    this.club = club;  
  236.   }  
  237.   //appropriate equals() and hashCode() implementation  
  238. }  
  239.   
  240. @Embeddable  
  241. public class FootballerPk implements Serializable {  
  242.   //same name and type as in Footballer  
  243.   public String getFirstname() {  
  244.     return firstname;  
  245.   }  
  246.   public void setFirstname(String firstname) {  
  247.     this.firstname = firstname;  
  248.   }  
  249.   //same name and type as in Footballer  
  250.   public String getLastname() {  
  251.     return lastname;  
  252.   }  
  253.   public void setLastname(String lastname) {  
  254.    this.lastname = lastname;  
  255.   }  
  256.   //appropriate equals() and hashCode() implementation  
  257. }  
  258.   
  259. @Entity  
  260. @AssociationOverride( name="id.channel", joinColumns = @JoinColumn(name="chan_id") )  
  261. public class TvMagazin {  
  262.    @EmbeddedId public TvMagazinPk id;  
  263.    @Temporal(TemporalType.TIME) Date time;  
  264. }  
  265.   
  266.   
  267. @Embeddable  
  268. public class TvMagazinPk implements Serializable {  
  269.    @ManyToOne  
  270.    public Channel channel;  
  271.    public String name;  
  272.    @ManyToOne  
  273.    public Presenter presenter;  
  274. }  
  275.   
  276. 映射继承关系  
  277.   
  278. EJB支持3种类型的继承。  
  279.   
  280. ? Table per Class Strategy: the <union-class> element in Hibernate 每个类一张表  
  281. ? Single Table per Class Hierarchy Strategy: the <subclass> element in Hibernate 每个类层次结构一张表  
  282. ? Joined Subclass Strategy: the <joined-subclass> element in Hibernate 连接的子类策略  
  283.   
  284. @Inheritance 注解来定义所选的之类策略。  
  285.   
  286. 每个类一张表  
  287.   
  288. @Entity  
  289. @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)  
  290. public class Flight implements Serializable {  
  291.   
  292. 有缺点,如多态查询或关联。Hibernate 使用 SQL Union 查询来实现这种策略。 这种策略支持双向的一对多关联,但不支持 IDENTIFY 生成器策略,因为ID必须在多个表间共享。一旦使用就不能使用AUTO和IDENTIFY生成器。  
  293.   
  294. 每个类层次结构一张表  
  295.   
  296. @Entity  
  297. @Inheritance(strategy=InheritanceType.SINGLE_TABLE)  
  298. @DiscriminatorColumn(  
  299.     name="planetype",  
  300.     discriminatorType=DiscriminatorType.STRING  
  301. )  
  302. @DiscriminatorValue("Plane")  
  303. public class Plane { ... }  
  304.   
  305. @Entity  
  306. @DiscriminatorValue("A320")  
  307. public class A320 extends Plane { ... }  
  308.   
  309. 整个层次结构中的所有父类和子类属性都映射到同一个表中,他们的实例通过一个辨别符列(discriminator)来区分。  
  310.   
  311. Plane 是父类。@DiscriminatorColumn 注解定义了辨别符列。对于继承层次结构中的每个类, @DiscriminatorValue 注解指定了用来辨别该类的值。 辨别符列名字默认为 DTYPE,其默认值为实体名。其类型为DiscriminatorType.STRING。  
  312.   
  313. 连接的子类  
  314.   
  315. @Entity  
  316. @Inheritance(strategy=InheritanceType.JOINED)  
  317. public class Boat implements Serializable { ... }  
  318.   
  319.   
  320. @Entity  
  321. public class Ferry extends Boat { ... }  
  322.   
  323. @Entity  
  324. @PrimaryKeyJoinColumn(name="BOAT_ID")  
  325. public class AmericaCupClass extends Boat { ... }  
  326.   
  327. 以上所有实体使用 JOINED 策略 Ferry和Boat class使用同名的主键关联(eg: Boat.id = Ferry.id), AmericaCupClass 和 Boat 关联的条件为 Boat.id = AmericaCupClass.BOAT_ID.  
  328.   
  329. 从父类继承的属性  
  330.   
  331. @MappedSuperclass  
  332. public class BaseEntity {  
  333.   @Basic  
  334.   @Temporal(TemporalType.TIMESTAMP)  
  335.   public Date getLastUpdate() { ... }  
  336.   public String getLastUpdater() { ... }  
  337.   ...  
  338. }  
  339.   
  340.   
  341. @Entity class Order extends BaseEntity {  
  342.   @Id public Integer getId() { ... }  
  343.   ...  
  344. }  
  345.   
  346. 继承父类的一些属性,但不用父类作为映射实体,这时候需要 @MappedSuperclass 注解。 上述实体映射到数据库中的时候对应 Order 实体Bean, 其具有 id, lastUpdate, lastUpdater 三个属性。如果没有@MappedSuperclass 注解,则父类中属性忽略,这是 Order 实体 Bean 只有 id 一个属性。  
  347.   
  348. 映射实体Bean的关联关系  
  349.   
  350. 一对一  
  351.   
  352. 使用 @OneToOne 注解可以建立实体Bean之间的一对一关系。一对一关系有3种情况。  
  353.   
  354. 关联的实体都共享同样的主键。  
  355.   
  356. @Entity  
  357. public class Body {  
  358.   @Id  
  359.   public Long getId() { return id; }  
  360.   @OneToOne(cascade = CascadeType.ALL)  
  361.   @PrimaryKeyJoinColumn  
  362.   public Heart getHeart() {  
  363.      return heart;  
  364.   }  
  365.   ...  
  366. }  
  367.   
  368. @Entity  
  369. public class Heart {  
  370.   @Id  
  371.   public Long getId() { ...}  
  372. }  
  373.   
  374. 通过@PrimaryKeyJoinColumn 注解定义了一对一的关联关系。  
  375.   
  376.   
  377. 多对一  
  378.   
  379. 使用 @ManyToOne 注解定义多对一关系。  
  380.   
  381. @Entity()  
  382. public class Flight implements Serializable {  
  383.   @ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} )  
  384.   @JoinColumn(name="COMP_ID")  
  385.   public Company getCompany() {  
  386.     return company;  
  387.   }  
  388.   ...  
  389. }  
  390.   
  391. 其中@JoinColumn 注解是可选的,关键字段默认值和一对一关联的情况相似。列名为:主题的关联属性名 + 下划线 + 被关联端的主键列名。本例中为company_id,因为关联的属性是company, Company的主键为 id.  
  392.   
  393. @ManyToOne 注解有个targetEntity属性,该参数定义了目标实体名。通常不需要定义,大部分情况为默认值。但下面这种情况则需要 targetEntity 定义(使用接口作为返回值,而不是常用的实体)。  
  394.   
  395. @Entity()  
  396. public class Flight implements Serializable {  
  397.    @ManyToOne(cascade=   {CascadeType.PERSIST,CascadeType.MERGE},targetEntity= CompanyImpl.class)  
  398.    @JoinColumn(name="COMP_ID")  
  399.    public Company getCompany() {  
  400.      return company;  
  401.    }  
  402.    ...  
  403. }  
  404.   
  405.   
  406. public interface Company {  
  407.    ...  
  408.   
  409. 多对一也可以通过关联表的方式来映射,通过 @JoinTable 注解可定义关联表。该关联表包含指回实体的外键(通过@JoinTable.joinColumns)以及指向目标实体表的外键(通过@JoinTable.inverseJoinColumns).  
  410.   
  411. @Entity()  
  412. public class Flight implements Serializable {  
  413.   
  414.    @ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} )  
  415.    @JoinTable(name="Flight_Company",  
  416.        joinColumns = @JoinColumn(name="FLIGHT_ID"),  
  417.        inverseJoinColumns = @JoinColumn(name="COMP_ID")  
  418.    )  
  419.    public Company getCompany() {  
  420.        return company;  
  421.    }  
  422.    ...  
  423. }  
  424.   
  425. 集合类型  
  426.   
  427.   
  428.   
  429.  一对多  
  430.   
  431. @OneToMany 注解可定义一对多关联。一对多关联可以是双向的。  
  432.   
  433. 双向  
  434.   
  435. 规范中多对一端几乎总是双向关联中的主体(owner)端,而一对多的关联注解为 @OneToMany(mappedBy=)  
  436.   
  437. @Entity  
  438. public class Troop {  
  439.   @OneToMany(mappedBy="troop")  
  440.   public Set<Soldier> getSoldiers() {  
  441.   ...  
  442. }  
  443.   
  444.   
  445. @Entity  
  446. public class Soldier {  
  447.   @ManyToOne  
  448.   @JoinColumn(name="troop_fk")  
  449.   public Troop getTroop() {  
  450.   ...  
  451.   }  
  452.   
  453. Troop 通过troop属性和Soldier建立了一对多的双向关联。在 mappedBy 端不必也不能定义任何物理映射。  
  454.   
  455. 单向  
  456.   
  457. @Entity  
  458. public class Customer implements Serializable {  
  459.    @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)  
  460.    @JoinColumn(name="CUST_ID")  
  461.    public Set<Ticket> getTickets() {  
  462.       ...  
  463.    }  
  464.   
  465. @Entity  
  466. public class Ticket implements Serializable {  
  467.    ... //no bidir  
  468. }  
  469.   
  470. 一般通过连接表来实现这种关联,可以通过@JoinColumn注解来描述这种单向关联关系。上例 Customer 通过 CUST_ID 列和 Ticket 建立了单向关联关系。  
  471.   
  472. 通过关联表来处理单向关联  
  473.   
  474. @Entity  
  475. public class Trainer {  
  476.   @OneToMany  
  477.   @JoinTable(  
  478.      name="TrainedMonkeys",  
  479.      joinColumns = @JoinColumn( name="trainer_id"),  
  480.      inverseJoinColumns = @JoinColumn( name="monkey_id")  
  481.   )  
  482.   public Set<Monkey> getTrainedMonkeys() {  
  483.      ...  
  484.   }  
  485.   
  486.   
  487. @Entity  
  488. public class Monkey {  
  489.   ... //no bidir  
  490. }  
  491.   
  492. 通过关联表来处理单向一对多关系是首选,这种关联通过 @JoinTable 注 解来进行描述。上例子中 Trainer 通过TrainedMonkeys表和Monkey建立了单向关联关系。其中外键trainer_id关联到 Trainer(joinColumns)而外键monkey_id关联到Monkey(inverseJoinColumns).  
  493.   
  494. 默认处理机制  
  495.   
  496. 通过连接表来建立单向一对多关联不需要描述任何物理映射,表名由一下3个部分组成,主表(owner table)表名 + 下划线 + 从表(the other side table)表名。指向主表的外键名:主表表名+下划线+主表主键列名 指向从表的外键定义为唯一约束,用来表示一对多的关联关系。  
  497.   
  498. @Entity  
  499. public class Trainer {  
  500.   @OneToMany  
  501.   public Set<Tiger> getTrainedTigers() {  
  502.   ...  
  503. }  
  504.   
  505.   
  506. @Entity  
  507. public class Tiger {  
  508.   ... //no bidir  
  509. }  
  510.   
  511. 上述例子中 Trainer 和 Tiger 通过 Trainer_Tiger 连接表建立单向关联关系。其中外键 trainer_id 关联到 Trainer表,而外键 trainedTigers_id 关联到 Tiger 表。  
  512.   
  513. 多对多  
  514.   
  515. 通过 @ManyToMany 注解定义多对多关系,同时通过 @JoinTable 注解描述关联表和关联条件。其中一端定义为 owner, 另一段定义为 inverse(对关联表进行更新操作,这段被忽略)。  
  516.   
  517. @Entity  
  518. public class Employer implements Serializable {  
  519.   @ManyToMany(  
  520.     targetEntity=org.hibernate.test.metadata.manytomany.Employee.class,  
  521.     cascade={CascadeType.PERSIST, CascadeType.MERGE}  
  522.   )  
  523.   @JoinTable(  
  524.     name="EMPLOYER_EMPLOYEE",  
  525.     joinColumns=@JoinColumn(name="EMPER_ID"),  
  526.     inverseJoinColumns=@JoinColumn(name="EMPEE_ID")  
  527.   )  
  528.   public Collection getEmployees() {  
  529.     return employees;  
  530.   }  
  531.   ...  
  532. }  
  533.   
  534. @Entity  
  535. public class Employee implements Serializable {  
  536.   @ManyToMany(  
  537.     cascade = {CascadeType.PERSIST, CascadeType.MERGE},  
  538.     mappedBy = "employees",  
  539.     targetEntity = Employer.class  
  540.   )  
  541.   public Collection getEmployers() {  
  542.     return employers;  
  543.   }  
  544. }  
  545.   
  546. 默认值:  
  547.   
  548. 关联表名:主表表名 + 下划线 + 从表表名;关联表到主表的外键:主表表名 + 下划线 + 主表中主键列名;关联表到从表的外键名:主表中用于关联的属性名 + 下划线 + 从表的主键列名。  
  549.   
  550. 用 cascading 实现传播持久化(Transitive persistence)  
  551.   
  552. cascade 属性接受值为 CascadeType 数组,其类型如下:  
  553.   
  554. ? CascadeType.PERSIST: cascades the persist (create) operation to associated entities persist() is called or if the entity is managed 如果一个实体是受管状态,或者当 persist() 函数被调用时,触发级联创建(create)操作。  
  555.   
  556.   
  557. CascadeType.MERGE: cascades the merge operation to associated entities if merge() is called or if the entity is managed 如果一个实体是受管状态,或者当 merge() 函数被调用时,触发级联合并(merge)操作。  
  558.   
  559.   
  560. CascadeType.REMOVE: cascades the remove operation to associated entities if delete() is called 当 delete() 函数被调用时,触发级联删除(remove)操作。  
  561.   
  562.   
  563. CascadeType.REFRESH: cascades the refresh operation to associated entities if refresh() is called  当 refresh() 函数被调用时,出发级联更新(refresh)操作。  
  564.   
  565.   
  566. CascadeType.ALL: all of the above  以上全部  
  567.   
  568. 映射二级列表  
  569.   
  570. 使用类一级的 @SecondaryTable 和 @SecondaryTables 注解可以实现单个实体到多个表的映射。使用 @Column 或者 @JoinColumn 注解中的 table 参数可以指定某个列所属的特定表。  
  571.   
  572. @Entity  
  573. @Table(name="MainCat")  
  574. @SecondaryTables({  
  575.     @SecondaryTable(name="Cat1", pkJoinColumns={  
  576.            @PrimaryKeyJoinColumn(name="cat_id", referencedColumnName="id")}),  
  577.     @SecondaryTable(name="Cat2", uniqueConstraints={  
  578.            @UniqueConstraint(columnNames={"storyPart2"})})  
  579.  })  
  580. public class Cat implements Serializable {  
  581.   private Integer id;  
  582.   private String name;  
  583.   
  584.   private String storyPart1;  
  585.   private String storyPart2;  
  586.   @Id @GeneratedValue  
  587.   public Integer getId() {  
  588.     return id;  
  589.   }  
  590.   public String getName() {  
  591.     return name;  
  592.   }  
  593.   @Column(table="Cat1")  
  594.   public String getStoryPart1() {  
  595.     return storyPart1;  
  596.   }  
  597.   @Column(table="Cat2")  
  598.   public String getStoryPart2() {  
  599.     return storyPart2;  
  600.   }  
  601.   
  602. 上 述例子中, name 保存在 MainCat 表中,storyPart1保存在 Cat1 表中,storyPart2 保存在 Cat2 表 中。 Cat1 表通过外键 cat_id 和 MainCat 表关联, Cat2 表通过 id 列和 MainCat 表关联。对 storyPart2 列还定义了唯一约束。  
  603.   
  604. 映射查询  
  605.   
  606. 使用注解可以映射 EJBQL/HQL 查询,@NamedQuery 和 @NamedQueries 是可以使用在类级别或者JPA的XML文件中的注解。  
  607.   
  608. <entity-mappings>  
  609.  <named-query name="plane.getAll">  
  610.   <query>select p from Plane p</query>  
  611.  </named-query>  
  612.  ...  
  613. </entity-mappings>  
  614. ...  
  615. @Entity  
  616. @NamedQuery(name="night.moreRecentThan", query="select n from Night n where n.date >= :date")  
  617. public class Night {  
  618.  ...  
  619. }  
  620. public class MyDao {  
  621.  doStuff() {  
  622.    Query q = s.getNamedQuery("night.moreRecentThan");  
  623.    q.setDate( "date", aMonthAgo );  
  624.    List results = q.list();  
  625.    ...  
  626.  }  
  627.  ...  
  628. }  
  629.   
  630. 可以通过定义 QueryHint 数组的 hints 属性为查询提供一些 hint 信息。下图是一些 Hibernate hints:  
  631.   
  632.   
  633.   
  634. 映射本地化查询  
  635.   
  636. 通过@SqlResultSetMapping 注解来描述 SQL 的 resultset 结构。如果定义多个结果集映射,则用 @SqlResultSetMappings。  
  637.   
  638. @NamedNativeQuery(name="night&area", query="select night.id nid, night.night_duration, "  
  639.      + " night.night_date, area.id aid, night.area_id, area.name "  
  640.      + "from Night night, Area area where night.area_id = area.id", resultSetMapping="joinMapping")  
  641.   
  642. @SqlResultSetMapping( name="joinMapping", entities={  
  643.   @EntityResult(entityClass=org.hibernate.test.annotations.query.Night.class, fields = {  
  644.    @FieldResult(name="id", column="nid"),  
  645.    @FieldResult(name="duration", column="night_duration"),  
  646.    @FieldResult(name="date", column="night_date"),  
  647.    @FieldResult(name="area", column="area_id"),  
  648.    discriminatorColumn="disc"  
  649.   }),  
  650.     
  651.   @EntityResult(entityClass=org.hibernate.test.annotations.query.Area.class, fields = {  
  652.    @FieldResult(name="id", column="aid"),  
  653.    @FieldResult(name="name", column="name")  
  654.   })  
  655.  }  
  656. )  
  657.   
  658. 上面的例子,名为“night&area”的查询和 "joinMapping"结果集映射对应,该映射返回两个实体,分别为 Night 和 Area, 其中每个属性都和一个列关联,列名通过查询获取。  
  659.   
  660. @Entity  
  661. @SqlResultSetMapping(name="implicit",   
  662.   entities=@EntityResult(  
  663.     entityClass=org.hibernate.test.annotations.@NamedNativeQuery(  
  664.       name="implicitSample", query="select * from SpaceShip",   
  665.       resultSetMapping="implicit")  
  666. public class SpaceShip {  
  667.  private String name;  
  668.  private String model;  
  669.  private double speed;  
  670.  @Id  
  671.  public String getName() {  
  672.   return name;  
  673.  }  
  674.  public void setName(String name) {  
  675.   this.name = name;  
  676.  }  
  677.  @Column(name="model_txt")  
  678.  public String getModel() {  
  679.   return model;  
  680.  }  
  681.  public void setModel(String model) {  
  682.   this.model = model;  
  683.  }  
  684.  public double getSpeed() {  
  685.   return speed;  
  686.  }  
  687.  public void setSpeed(double speed) {  
  688.   this.speed = speed;  
  689.  }  
  690. }  
  691.   
  692. 上例中 model1 属性绑定到 model_txt 列,如果和相关实体关联设计到组合主键,那么应该使用 @FieldResult 注解来定义每个外键列。@FieldResult的名字组成:定义这种关系的属性名字 + "." + 主键名或主键列或主键属性。  
  693.   
  694. @Entity  
  695. @SqlResultSetMapping(name="compositekey",  
  696.  entities=@EntityResult(entityClass=org.hibernate.test.annotations.query.SpaceShip.class,  
  697.   fields = {  
  698.    @FieldResult(name="name", column = "name"),  
  699.    @FieldResult(name="model", column = "model"),  
  700.    @FieldResult(name="speed", column = "speed"),  
  701.    @FieldResult(name="captain.firstname", column = "firstn"),  
  702.    @FieldResult(name="captain.lastname", column = "lastn"),  
  703.    @FieldResult(name="dimensions.length", column = "length"),  
  704.    @FieldResult(name="dimensions.width", column = "width")  
  705.   }),  
  706.  columns = { @ColumnResult(name = "surface"),  
  707.    
  708. @ColumnResult(name = "volume") } )  
  709.  @NamedNativeQuery(name="compositekey",  
  710.  query="select name, model, speed, lname as lastn, fname as firstn, length, width, length * width as resultSetMapping="compositekey")  
  711. })  
  712.   
  713. 如果查询返回的是单个实体,或者打算用系统默认的映射,这种情况下可以不使用 resultSetMapping,而使用resultClass属性,例如:  
  714.   
  715. @NamedNativeQuery(name="implicitSample", query="select * from SpaceShip",  
  716.                                             resultClass=SpaceShip.class)  
  717. public class SpaceShip {  
  718.   
  719. Hibernate 独有的注解扩展  
  720.   
  721. Hibernate 提供了与其自身特性想吻合的注解,org.hibernate.annotations package包含了这些注解。  
  722.   
  723. 实体  
  724.   
  725. org.hibernate.annotations.Entity 定义了  Hibernate 实体需要的信息。  
  726.  ? mutable: whether this entity is mutable or not  此实体是否可变  
  727. ? dynamicInsert: allow dynamic SQL for inserts   用动态SQL新增  
  728. ? dynamicUpdate: allow dynamic SQL for updates   用动态SQL更新  
  729. ? selectBeforeUpdate: Specifies that Hibernate should never perform an SQL UPDATE unless it is certain that an object is actually modified. 指明Hibernate从不运行SQL Update,除非能确定对象已经被修改  
  730. ? polymorphism: whether the entity polymorphism is of PolymorphismType.IMPLICIT (default) or PolymorphismType.EXPLICIT 指出实体多态是 PolymorphismType.IMPLICIT(默认)还是PolymorphismType.EXPLICIT   
  731. ? optimisticLock: optimistic locking strategy (OptimisticLockType.VERSION, OptimisticLockType.NONE, OptimisticLockType.DIRTY or OptimisticLockType.ALL) 乐 观锁策略  
  732.   
  733. 标识符  
  734.   
  735. @org.hibernate.annotations.GenericGenerator和@org.hibernate.annotations.GenericGenerators允许你定义hibernate特有的标识符。  
  736.   
  737. @Id @GeneratedValue(generator="system-uuid")  
  738. @GenericGenerator(name="system-uuid", strategy = "uuid")  
  739. public String getId() {  
  740. @Id @GeneratedValue(generator="hibseq")  
  741. @GenericGenerator(name="hibseq", strategy = "seqhilo",  
  742.    parameters = {  
  743.      @Parameter(name="max_lo", value = "5"),  
  744.      @Parameter(name="sequence", value="heybabyhey")  
  745.    }  
  746. )  
  747. public Integer getId() {  
  748.   
  749. 新例子  
  750.   
  751. @GenericGenerators(  
  752.  {  
  753.   @GenericGenerator(  
  754.    name="hibseq",  
  755.    strategy = "seqhilo",  
  756.    parameters = {  
  757.     @Parameter(name="max_lo", value = "5"),  
  758.     @Parameter(name="sequence", value="heybabyhey")  
  759.    }  
  760.   ),  
  761.   @GenericGenerator(...)  
  762.  }  
  763. )  
  764.   
  765. 自然ID  
  766.   
  767. 用 @NaturalId 注解标识  
  768.   
  769. 公式  
  770.   
  771. 让数据库而不是JVM进行计算。  
  772.   
  773. @Formula("obj_length * obj_height * obj_width")  
  774. public long getObjectVolume()  
  775.   
  776. 索引  
  777.   
  778. 通过在列属性(property)上使用@Index注解,可以指定特定列的索引,columnNames属性(attribute)将随之被忽略。  
  779.   
  780. @Column(secondaryTable="Cat1")  
  781. @Index(name="story1index")  
  782. public String getStoryPart1() {  
  783.   return storyPart1;  
  784. }  
  785.   
  786. 辨别符  
  787.   
  788. @Entity  
  789. @DiscriminatorFormula("case when forest_type is null then 0 else forest_type end")  
  790. public class Forest { ... }  
  791.   
  792. 过滤 查询 ...  
  793. 其中一个实体通过外键关联到另一个实体的主键。注:一对一,则外键必须为唯一约束。  
  794.   
  795. @Entity  
  796. public class Customer implements Serializable {  
  797.    @OneToOne(cascade = CascadeType.ALL)  
  798.    @JoinColumn(name="passport_fk")  
  799.    public Passport getPassport() {  
  800.    ...  
  801. }  
  802.   
  803.   
  804. @Entity  
  805. public class Passport implements Serializable {  
  806.    @OneToOne(mappedBy = "passport")  
  807.    public Customer getOwner() {  
  808.    ...  
  809. }  
  810.   
  811. 通过@JoinColumn注解定义一对一的关联关系。如果没有@JoinColumn注解,则系统自动处理,在主表中将创建连接列,列名为:主题的关联属性名 + 下划线 + 被关联端的主键列名。上例为 passport_id, 因为Customer 中关联属性为 passport, Passport 的主键为 id.  
  812.   
  813.   
  814. 通过关联表来保存两个实体之间的关联关系。注:一对一,则关联表每个外键都必须是唯一约束。  
  815.   
  816. @Entity  
  817. public class Customer implements Serializable {  
  818.    @OneToOne(cascade = CascadeType.ALL)  
  819.    @JoinTable(name = "CustomerPassports",  
  820.         joinColumns = @JoinColumn(name="customer_fk"),  
  821.         inverseJoinColumns = @JoinColumn(name="passport_fk")  
  822.    )  
  823.    public Passport getPassport() {  
  824.    ...  
  825. }  
  826.   
  827.   
  828. @Entity public class Passport implements Serializable {  
  829.    @OneToOne(mappedBy = "passport")  
  830.    public Customer getOwner() {  
  831.    ...  
  832. }  
  833.   
  834. Customer 通 过 CustomerPassports 关联表和 Passport 关联。该关联表通过 passport_fk 外键指向 Passport 表, 该信心定义为 inverseJoinColumns 的属性值。 通过 customer_fk 外键指向 Customer 表,该信息定义 为 joinColumns 属性值。 



hibernate分页查询的实现
method1
在mysql中新建数据好USER表,字段有3个,分别是id、username、password,贴上脚本仅供参考

    create table `ding`.`user`(
        `id` int default '' not null,
       `username` varchar(30),
       `password` varchar(30),
        primary key (`id`)
    );

切换到数据库透视图,新建一个mysql连接。

建一个web工程,然后右键选择myeclipse,选择add hibernate........ ,把hibernate的jar加入到工程中,选择的时候注意jar包要放在lib中,可以检查下,其中有一下会选择刚新建的mysql连接,加完以后就会看到SRC下多了一个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">

<!-- Generated by MyEclipse Hibernate Tools.                   -->
<hibernate-configuration>

    <session-factory>
        <property name="connection.username">root</property>
        <property name="connection.url">
            jdbc:mysql://localhost:3306/ding
        </property>
        <property name="dialect">
            org.hibernate.dialect.MySQLDialect
        </property>
        <property name="myeclipse.connection.profile">mysql</property>
        <property name="connection.password">ding</property>
        <property name="connection.driver_class">
            com.mysql.jdbc.Driver
        </property>

    </session-factory>

</hibernate-configuration>

切换到数据库透视图,启动那个连接找到建的USER表,右键选择Hibernate Reverse Engineering...,在弹出的框里面点击 Java src folder 一行最右侧的 Browser.. 按钮来选择源码目录,这里我选择自己工程src 目录,并把下面的 Java package 右侧的输入框中输入自己想要的包名,我这里是test,接着选中下面的复选框,我选前面两个:
[x] Hibernate mapping file (*.hbm.xml) for each database table
[x] Java Data Object (POJO <> DB Table)
第2项下面的复选create abstract class我这里不选,点击next,再在ID generator选择increment(自增长),点完成。

返回j2ee透视图,会看到在test包下生成了User.java和User.hbm.xml文件,如下:
public class User implements java.io.Serializable {

    private Integer id;

    private String username;

    private String password;


    public Integer getId() {
        return this.id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return this.username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return this.password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

}


<?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">
<!-- 
    Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
    <class name="test.User" table="user">
        <id name="id" type="java.lang.Integer">
            <column name="id" />
            <generator class="increment" />
        </id>
        <property name="username" type="java.lang.String">
            <column name="username" length="30" />
        </property>
        <property name="password" type="java.lang.String">
            <column name="password" length="30" />
        </property>
    </class>
</hibernate-mapping>

同时在原先的hibernate.cfg.xml中加入了  <mapping resource="test/User.hbm.xml" />,最终的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">

<!-- Generated by MyEclipse Hibernate Tools.                   -->
<hibernate-configuration>

    <session-factory>
        <property name="connection.username">root</property>
        <property name="connection.url">
            jdbc:mysql://localhost:3306/ding
        </property>
        <property name="dialect">
            org.hibernate.dialect.MySQLDialect
        </property>
        <property name="myeclipse.connection.profile">mysql</property>
        <property name="connection.password">ding</property>
        <property name="connection.driver_class">
            com.mysql.jdbc.Driver
        </property>
        <mapping resource="test/User.hbm.xml" />

    </session-factory>

</hibernate-configuration>

写个Test测试类

public class Test {

    /**  
     *   @Description 方法实现功能描述  
     *   @param args
     *   void
     *   @throws  抛出异常说明
     */
    public static void main(String[] args) {
        
      SessionFactory sf = new Configuration().configure().buildSessionFactory();
      Session session = sf.openSession();
      String hql = "from User";
      int curpage = 1;//当前页 
      int pagesize = 2;//每页显示数
      //以上两项有一项为0则查出所有记录
      List list = findOnePage(session, hql, curpage, pagesize);
      for(Iterator i = list.iterator();i.hasNext();){
          User user = (User) i.next();
          System.out.println("username==="+user.getUsername());
      }
    }

    public static List findOnePage(Session session,String strHQL, int offset, int pagesize){
        List lst = null;
        try {
            Query query = session.createQuery(strHQL);
            if (offset != 0 && pagesize != 0) {
                query.setFirstResult((offset - 1) * pagesize);
                query.setMaxResults(pagesize);
            }
            lst = query.list();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return lst;
    }

}

测试下就可以看到效果了
   int curpage = 1;//当前页 
   int pagesize = 2;//每页显示数
可以改这两个参数试试


method2
a,将查询的条件保存到session中,或用其它方式,只要能保证翻页时能再取出就可以了b,数据库需要count一次总记录数,并取出结果集c,自己写个PageUtil类,目的是根据总记录数能算出有多少页、上一页下一页都是什么,就够了,jsp页面根据这个pageutil类的信息来写翻页的导航,1234页之类的
每次根据不页码的信息(这个页码的信息应该是通过get方式得来,比如?page=3这样的参数)和以前存的查询条件,从数据库中再取出对应的结果集,应该是通过hibernate设置一下起始记录数、最大记录数



hibernate常用类
Configuration接口      Configuration接口的作用是对Hibernate进行配置,以及对它进行启动。在Hibernate的启动过程中,Configuration类的实例首先定位映射文档的位置,读取这些配置,然后创建一个SessionFactory对象。
      
SessionFactory 接口      这里用到了一个设计模式――工厂模式,用户程序从工厂类SessionFactory中取得Session的实例。SessionFactory并不是轻量级的,一个项目通常只需要一个 SessionFactory就够了,但是当你的项目要操作多个数据库时,那你必须为每个数据库指定一个SessionFactory。        
SessionFactory在Hibernate中实际起到了一个缓冲区的作用,它缓冲了Hibernate自动生成的SQL语句和一些其它的映射数据,还缓冲了一些将来有可能重复利用的数据。
      
Session接口     Session接口对于Hibernate 开发人员来说是一个最重要的接口。然而在Hibernate中,实例化的Session是一个轻量级的类,创建和销毁它都不会占用很多资源。这在实际项目中确实很重要,因为在客户程序中,可能会不断地创建以及销毁Session对象,如果Session的开销太大,会给系统带来不良影响。但值得注意的是 Session对象是非线程安全的,因此在你的设计中,最好是一个线程只创建一个Session对象。 

Hibernate使用Session来存取,它是持久化管理器接口,代表与数据库之间的一次操作。然而为了操作持久化数据,首先要获得SessionFactory对象,SessionFactory负责一个数据库,是Session的工厂,对于一个XML配置文件,SessionFactory对象Configuration对象负责创建。这里的net.sf.hibernate.cfg.Configuration的一个实例代表了应用程序中所有的Java类到关系数据库的映射的集合,这些映射是从一些XML映射文件中编译得到的。可以得到一个Configuration的实例,直接实例化即可,当所有的映射都被Configuration解析之后,应用程序为了得到Session实例,必须先得到它的工厂,这些工厂应该是应用程序的所有线程所共享的。当然Hibernate并不禁止程序实例化多个SessionFactory,这在使用不止一个数据库时候就很有用。
      
Transaction 接口     Transaction接口是一个可选的API,你可以选择不使用这个接口,取而代之的是Hibernate的设计者自己写的底层事务处理代码。 Transaction接口是对实际事务实现的一个抽象,这些实现包括JDBC的事务、JTA中的UserTransaction、甚至可以是CORBA 事务。之所以这样设计是能让开发者能够使用一个统一事务的操作界面,使得自己的项目可以在不同的环境和容器之间方便地移值。  
    
Query和Criteria接口     Query接口让你方便地对数据库及持久对象进行查询,它可以有两种表达方式:HQL语言或本地数据库的SQL语句。Query经常被用来绑定查询参数、限制查询记录数量,并最终执行查询操作。     Criteria接口与Query接口非常类似,它允许你创建并执行面向对象的标准化查询。       值得注意的是Query接口也是轻量级的,它不能在Session之外使用。
        
Callback 接口         当一些有用的事件发生时――例如持久对象的载入、存储、删除时,Callback接口会通知Hibernate去接收一个通知消息。一般而言,Callback接口在用户程序中并不是必须的,但你要在你的项目中创建审计日志时,你可能会用到它。


0 0
原创粉丝点击