Hibernate的检索方式、级联操作、批处理

来源:互联网 发布:淘宝账号怎么快速升心 编辑:程序博客网 时间:2024/04/30 15:10

一、Hibernate的五种检索方式:

1.HQL(hibernate Query Language) 

是面向对象的查询语言, 它和 SQL 查询语言有些相似. 在 Hibernate 提供的各种检索方式中, HQL 是使用最广的一种检索方式. 它有如下功能:

l  在查询语句中设定各种查询条件

l  支持投影查询, 即仅检索出对象的部分属性

l  支持分页查询

l  支持连接查询

l  支持分组查询, 允许使用 HAVING 和 GROUP BY 关键字

l  提供内置聚集函数, 如 sum(), min() 和 max()

l  能够调用 用户定义的 SQL 函数或标准的 SQL 函数

l  支持子查询

l  支持动态绑定参数

2.OID 检索方式

 按照对象的 OID 来检索对象

1.     提供一个无参的构造器。使Hibernate可以使用Constructor.newInstance() 来实例化持久化类。

2.      提供一个标识属性(identifierproperty)。通常映射为数据库表的主键字段。如果没有该属性,一些功能将不起作用,如:Session.saveOrUpdate()。

3.      为类的持久化类的字段声明访问方法(get/set)。Hibernate对JavaBeans风格的属性实行持久化。

4.      使用非final类。在运行时生成代理是Hibernate的一个重要的功能。如果持久化类没有实现任何接口,Hibnernate使用 CGLIB 生成代理。如果使用的是 final 类,则无法生成CGLIB代理。

5.     重写eqauls()和hashCode()方法。如果需要把持久化类的实例放到Set中(当需要进行关联映射时),则应该重写这两个方法。


3.QBC 检索方式

使用 QBC(Query ByCriteria) API 来检索对象. 这种 API 封装了基于字符串形式的查询语句, 提供了更加面向对象的查询接口.它提供了更加面向对象的操作方式。

4.OGNL检索方式

就是在HQL中使用.来获取对象的属性

5.本地SQL方式

与本地数据库绑定,但是效率比较高。


二、Hibernate的级联操作:

 1.简单的介绍

cascade和inverse (Employee – Department)

l  Casade用来说明当对主对象进行某种操作时是否对其关联的从对象也作类似的操作,常用的cascade:

         none,all,save-update,delete, lock,refresh,evict,replicate,persist,

         merge,delete-orphan(one-to-many)。一般对many-to-one,many-to-many不设置级联,在<one-to-one>和<one-to-many>中设置级联。

l  inverse表“是否放弃维护关联关系”(在Java里两个对象产生关联时,对数据库表的影响),在one-to-many和many-to-many的集合定义中使用,inverse=”true”表示该对象不维护关联关系;该属性的值一般在使用有序集合时设置成false(注意hibernate的缺省值是false)。

         one-to-many维护关联关系就是更新外键。many-to-many维护关联关系就是在中间表增减记录。

         注: 配置成one-to-one的对象不维护关联关系

2.属性的解析
class元素的lazy属性设定为true,表示延迟加载,如果lazy设为false,则
表示立即加载。以下对这二点进行说明。
     立即加载:表示Hibernate在从数据库中取得数据组装好一个对象(如学生1)后,
            会立即再从数据库取得数据组装此对象所关联的对象(如学生证1)。
     延迟加载:表示Hibernate在从数据库中取得数据组装好一个对象(如学生1)后,
            不会立即再从数据库中取得数据组装此对象所关联的对象(如学生1),
            而是等到需要时,才会从数据库取得数据组装此关联对象。
<one-to-one>元素的cascade属性表明操作是否从父对象级联到被关联的对象,     它
的取得可以是以下几种:
     none:在保存,删除或修改当前对象时,不对其附属对象(关联对象)进行级联
          操作。它是默认值。
     save-update:在保存,更新当前对象时,级联保存,更新附属对象(临时对象,
          游离对象)。
     delete:在删除当前对象时,级联删除附属对象。
     all:所有情况下均进行级联操作,即包含save-update和delete操作。
     delete-orphan:删除和当前对象解除关系的附属对象。
<one-to-one>元素的fetch属性的可选值是join和select,默认是select。
当fetch属性设定为join时,表示连接抓取(Join fetching):Hibernate通过
在Select语句中使用outer join(外连接)来获得对象的关联实例或者关联集合。
当fetch属性设定为select时,表示查询抓取(Select fetching):需要另外发
送一条Select语句抓取当前对象的关联实体或集合。

3.代码练习

<set name="emps" cascade="save-update">
 <key column="depart_id"/>
  <one-to-many class="Employee"/>
</set>

<set name="students" table="taacher_student" inverse="true"><!-- table是用来指定中间表的属性 -->
<key column="teacher_id"></key><!-- 查找教师id时,链接中间表表的teacher_id -->
<many-to-many class="Student" column="student_id"></many-to-many>
</set>


三、Hibernate的批处理:

我们在用数据库时,如果可以批量插入或者更新数据,可以很大地提高使用数据库的性能。

1.撰写代码

首先你必须要撰写或者修改你的批处理代码。比如你的目标是通过Hibernate一次保存50个Customer对象,那你必须每save满50个对象,就flush一次,如下:

Transaction tx = session.beginTransaction();for(Customer customer : customers) {  session.save(customer );  if(++i % 50 == 0) {    session.flush();    session.clear();  }}tx.commit();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

这是因为Hibernate会把新插入的数据都保存在缓存(persistence context cache)中。因为缓存大小的限制,所以隔一定数量就要flush,否则可能会碰上OutMemorException.

2.修改配置文件

Hibernate官方指导文档建议,为了最好的性能,应该把hibernate.jdbc.batch_size设置成和代码中procedure batch一样的size,在hibernate.cfg.xml中加上:

<property name="hibernate.jdbc.batch_size">50</property>
  • 1
  • 1

这里还有一点要注意的是,记得关掉batch操作的二级缓存。否则的话,每个对象被保存时,都会同时保存到二级缓存,这是一个不必要的浪费。

3.修改标识符生成器(id generator)

上面两个步骤看起来应该大功告成了,其实离成功还得远。首先,Hibernate明确说明了这样一个事实:

Hibernate silently disables JDBC batch inserts if your entity is mapped with an identity identifier generator; many JDBC drivers don’t support batching in that case
  • 1
  • 1

也就是说,如果你用的是identity的标识符生成器,那Hibernate的批处理就不灵了,所以我们必须得换一个,下面告诉你如何用MultipleHiLoPerTableGenerator。这个标识符生成器由Hibernate按照HiLo算法来生成id,它从数据库的特定表的字段长获取high值。至于什么是Hilo算法,可以参考这篇文章(https://vladmihalcea.com/2014/06/23/the-hilo-algorithm/)。在你的Customer.hbm.xml里,加上下面的代码:

<id name="id" type="java.lang.Integer">      <column name="id"/>      <generator class="org.hibernate.id.MultipleHiLoPerTableGenerator">        <param name="table">identityGenerator</param>        <param name="primary_key_column">sequence_name</param>        <param name="value_column">next_hi_value</param>        <param name="primary_key_value">TableCustomer</param>        <param name="max_lo">10000</param>      </generator>    </id>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

hi值存放在identityGenerator表的next_hi_value字段中:

CREATE TABLE IdentityGenerator (    id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,    sequence_name VARCHAR(36) NOT NULL,    next_hi_value INTEGER NOT NULL)ENGINE= InnoDB DEFAULT CHARSET=latin1;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

4.批处理到块处理

到这一步位置,batch操作已经完成了,但是你可能发现性能没有太多的提高,如果你看一眼MySQL的log,你会发现数据实际上还是一条条插入数据库的。事实上,我们之前做的事情,只是把50条insert语句打包成了一个batch,发给了数据库,而数据库并没有把这50条insert变成下面这个我们期望的样子:

INSERT INTO Customer () VALUES (), (), (), (), ()
  • 1
  • 1

这被称作bulk insert的操作通常依赖于数据库connector provider。Hibernate依赖的mysql-connector-Java提供了rewriteBatchedStatements这个配置参数来帮助我们把batch inserts转成bulk inserts。再次修改你的hibernate.cfg.xml如下:

<property name="hibernate.connection.url">jdbc:mysql://local:3306/database?rewriteBatchedStatements=true</property>
  • 1
  • 1

5.查看数据库log

最后,你还可以查看下mysql的log,来确定这会是不是真的bulk insert了

sudo tail -f  /var/log/mysql/mysql.log
  • 1
  • 1

mysqllog的打开方法如下:

mysql> show global variables like '%general%';     mysql> SET GLOBAL general_log = 'ON';   // 打开     mysql> SET GLOBAL general_log = 'OFF'; // 关闭  
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

6.一个常见的flush时抛出的异常

如果你跟我一样不幸运,session.flush()时可能会看到以下异常:

org.hibernate.jdbc.BatchedTooManyRowsAffectedException: Batch update returned unexpected row count from update [0]; actual row count: 10; expected: 1
  • 1
  • 1

我在stackoverflow上找到了两种解决方法:

  • 检查你的mysql-connector-java版本,把mysql-connector-java降级到5.1.24,或者升级到更新的版本(参考:http://stackoverflow.com/questions/23663557/hibernate-mysql-rewritebatchedstatements-true)
  • 在org.hibernate.jdbc这个源码包中,拷贝Exceptations.java这个文件到你的工程里(不要改变包名和相对路径),然后在你拷贝的这个Exceptions.java中,注释掉checkBatched方法中抛出BatchedTooManyRowsAffectedException的相关代码

    我是用第二种方法解决的。



原创粉丝点击