阳光男孩

Never give up!

Entries Tagged ‘Hibernate’

优化Hibernate性能的七点建议

0顶一下1、针对Oracle数据库而言,Fetch Size 是设定JDBC的Statement读取数据的时候每次从数据库中取出的记录条数,一般设置为30、50、100。Oracle数据库的JDBC驱动默认的Fetch Size=15,设置Fetch Size设置为:30、50,性能会有明显提升,如果继续增大,超出100,性能提升不明显,反而会消耗内存。 即在Hibernate配制文件中进行配制: < property name=”hibernateProperties”> < pro...[阅读全文]

0
顶一下

1、针对Oracle数据库而言,Fetch Size 是设定JDBC的Statement读取数据的时候每次从数据库中取出的记录条数,一般设置为30、50、100。Oracle数据库的JDBC驱动默认的Fetch Size=15,设置Fetch Size设置为:30、50,性能会有明显提升,如果继续增大,超出100,性能提升不明显,反而会消耗内存。
即在Hibernate配制文件中进行配制:
< property name=”hibernateProperties”>
< props>
< prop key=”hibernate.dialect”>
org.hibernate.dialect.Oracle9Dialect
< /prop>
< prop key=”hibernate.show_sql”>false< /prop>
< !– Create/update the database tables automatically when the JVM starts up
< prop key=”hibernate.hbm2ddl.auto”>update< /prop> –>
< !– Turn batching off for better error messages under PostgreSQL
< prop key=”hibernate.jdbc.batch_size”>100< /prop> –>
< prop key=”hibernate.jdbc.batch_size”>50< /prop>
< /props>< /property>

     < property name=”hibernateProperties”>  < props>

< prop key=”hibernate.dialect”>

org.hibernate.dialect.Oracle9Dialect

< /prop>

< prop key=”hibernate.show_sql”>false< /prop>

< !– Create/update the database tables automatically when the JVM starts up

< prop key=”hibernate.hbm2ddl.auto”>update< /prop> –>

< !– Turn batching off for better error messages under PostgreSQL

< prop key=”hibernate.jdbc.batch_size”>100< /prop> –>

< prop key=”hibernate.jdbc.batch_size”>50< /prop>

< /props>< /property>

3、不要把所有的责任推在hibernate上,对代码进行重构,减少对数据库的操作,尽量避免在数据库查询时使用in操作,以及避免递归查询操作,代码质量、系统设计的合理性决定系统性能的高低。
4、 对大数据量查询时,慎用list()或者iterator()返回查询结果,   (1). 使用List()返回结果时,Hibernate会所有查询结果初始化为持久化对象,结果集较大时,会占用很多的处理时间。   (2). 而使用iterator()返回结果时,在每次调用iterator.next()返回对象并使用对象时,Hibernate才调用查询将对应的对象初始化,对于大数据量时,每调用一次查询都会花费较多的时间。当结果集较大,但是含有较大量相同的数据,或者结果集不是全部都会使用时,使用iterator()才有优势。
5、在一对多、多对一的关系中,使用延迟加载机制,会使不少的对象在使用时方会初始化,这样可使得节省内存空间以及减少数据库的负荷,而且若PO中的集合没有被使用时,就可减少互数据库的交互从而减少处理时间。
6、对含有关联的PO(持久化对象)时,若default-cascade=”all”或者 “save-update”,新增PO时,请注意对PO中的集合的赋值操作,因为有可能使得多执行一次update操作。
7、 对于大数据量新增、修改、删除操作或者是对大数据量的查询,与数据库的交互次数是决定处理时间的最重要因素,减少交互的次数是提升效率的最好途径,所以在开发过程中,请将show_sql设置为true,深入了解Hibernate的处理过程,尝试不同的方式,可以使得效率提升。尽可能对每个页面的显示,对数据库的操作减少到100—-150条以内。越少越好。   以上是在进行Struts+hibernate+spring进行项目开发中,对hibernate性能优化的几点心得。

Comments (1)

别让Hibernate偷走了您的身份

0顶一下别让Hibernate偷走了您的身份,企业级Java应用程序常常把数据在Java对象和相关数据库之间来回移动。从手工编写SQL代码到诸如Hibernate这样成熟的对象关系映射(ORM)解决方案,有很多种方法可以实现这个过程。 无论采用什么样的技术,一旦开始将Java对象持久存储到数据库中,身份将成为一个复杂且难以管理的课题。可能出现的情况是:您实例化了两个不同的对象,而它们却代表数据库中的同一...[阅读全文]

0
顶一下

别让Hibernate偷走了您的身份,企业级Java应用程序常常把数据在Java对象和相关数据库之间来回移动。从手工编写SQL代码到诸如Hibernate这样成熟的对象关系映射(ORM)解决方案,有很多种方法可以实现这个过程。

无论采用什么样的技术,一旦开始将Java对象持久存储到数据库中,身份将成为一个复杂且难以管理的课题。可能出现的情况是:您实例化了两个不同的对象,而它们却代表数据库中的同一行。为了解决这个问题,您可能采取的措施是在持久性对象中实现equals()和hashCode(),可是要恰当地实现这两个方法比乍看之下要有技巧一些。让问题更糟糕的是,那些传统的思路(包括Hibernate官方文档所提倡的)对于新的项目并不一定能提出最实用的解决方案。

对象身份在虚拟机(VM)中和在数据库中的差异是问题滋生的温床。在虚拟机中,您并不会得到对象的ID,您只是简单地持有对象的直接引用。而在幕后,虚拟机确实给每个对象指派了一个8字节大小的ID,这个ID才是对象的真实引用。当您将对象持久存储到数据库中的时候,问题开始产生了。假定您创建了一个Person对象并将它存入数据库(我们可以叫它person1)。而您的其他某段代码从数据库中读取了这个Person对象的数据,并将它实例化为另一个新的Person对象(我们可以叫它Person2)。现在您的内存中有了两个映射到数据库中同一行的对象。一个对象引用只能指向它们的其中一个,可是我们需要一种方法来表示这两个对象实际上表示着同一个实体。这就是(在虚拟机中)引入对象身份的原因。

在Java语言中,对象身份是由每个对象都持有的equals()方法(以及相关的hashCode()方法)来定义的。无论两个对象是否为同一个实例,equals()方法都应该能够判别出它们是否表示同一个实体。hashCode()方法和equals()方法有关联是因为所有相等的对象都应该返回相同的hashCode.默认情况下,equals()方法仅仅比较对象引用。一个对象和它自身是相等的,而和其他任何实例都不相等。对于持久性对象来说,重写这两个方法,让代表着数据库中同一行的两个对象被视为相等是很重要的。而这对于Java中Collection(Set、Map和List)的正确工作更是尤为重要。

为了阐明实现equal()和hashCode()的不同途径,让我们考虑一个准备持久存储到数据库中的简单对象Person.

public class Person {private Long id;private Integer version;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public Integer getVersion() {return version;}public void setVersion(Integer version) {this.version = version;}// person-specific properties and behavior}

在这个例子中,我们遵循了同时持有id字段和version字段的最佳实践。Id字段保存了在数据库中作为主键使用的值,而version字段则是一个从0开始增长的增量,随着对象的每次更新而变化(这帮助我们避免并发更新的问题)。为了更清楚一些,让我们看看允许Hibernate把这个对象持久存储到数据库的Hibernate映射文件:

PERSON_SEQ

Hibernate映射文件指明了Person的id字段代表数据库中的ID列(也就是说,它是PERSON表的主键)。包含在id标签中的unsaved-value=”null”属性告诉Hibernate使用id字段来判断一个Person对象之前是否被保存过。ORM框架必须依靠这个来判断保存一个对象的时候应该使用SQL的INSERT子句还是UPDATE子句。在这个例子中,Hibernate假定一个新对象的id字段一开始为null值,当它第一次被保存时id才被赋予一个值。generator标签告诉Hibernate当对象第一次保存时,应该从哪里获得指派的id.在这个例子中,Hibernate使用数据库序列作为唯一ID的来源。最后,version标签告诉Hibernate使用Person对象的version字段进行并发控制。Hibernate将会执行乐观锁定方案,根据这个方案,Hibernate在保存对象之前会根据数据库版本号检查对象的版本号。 我们的Person对象还缺少的是equals()方法和hashCode()方法的实现。既然这是一个持久性对象,我们并不想依赖于这两个方法的默认实现,因为默认实现并不能分辨代表数据库中同一行的两个不同实例。一种简单而又显然的实现方法是利用id字段来进行equal()方法的比较以及生成hashCode()方法的结果。

public boolean equals(Object o) {if (this == o) return true;if (o == null || !(o instanceof Person))return false;Person other = (Person)o;if (id == other.getId()) return true;if (id == null) return false;// equivalence by idreturn id.equals(other.getId());}public int hashCode() {if (id != null) {return id.hashCode();} else {return super.hashCode();}}

不幸的是,这个实现存在着问题。当我们首次创建Person对象时id的值为null,这意味着任何两个Person对象只要尚未保存,就将被认为是相等的。如果我们想创建一个Person对象并把它放到一个Set中,再创建一个完全不同的Person对象也把它放到同一个Set里面,事实上第二个Person对象并不能被加入。这是因为Set会断定所有未保存的对象都是相同的。

您可能会试图去实现一个使用id(只在已设置id的情况下)的equals()方法。毕竟,如果两个对象都没有被保存过,我们可以假定它们是不同的对象。这是因为在它们被保存到数据库的时候,它们会被赋予不同的主键。

public boolean equals(Object o) {if (this == o) return true;if (o == null || !(o instanceof Person))return false;Person other = (Person)o;// unsaved objects are never equalif (id == null || other.getId() == null)return false;return id.equals(other.getId());}

这里有个隐含的问题。Java Collection框架在Collection的生命周期中需要基于不变字段的equals()和hashCode()方法。换句话来说,当一个对象处在Collection中的时候,不可以改变equals()和hashCode()的值。举个例子,下面这段程序:

Person p = new Person();Set set = new HashSet();set.add(p);System.out.println(set.contains(p));p.setId(new Long(5));System.out.println(set.contains(p));输出结果:true false

对set.contains(p)的第2次调用返回false,这是因为Set再也找不到p了。用专业术语来讲,就是Set丢失了这个对象!这是因为当对象在集合中时,我们改变了hashCode()的值。

当您想要创建一个将其他域对象保存在Set、Map或是List中的域对象时,这是一个问题。为了解决这个问题,您必须为所有对象提供一种equals()和hashCode()的实现,这种实现能够保证在它们在对象保存前后正确工作并且当对象在内存中时(返回值)不可变。Hibernate Reference Documentation (v. 3)提供了以下的建议:

“不要使用数据库标识符来实现相等性判断,而应该使用业务键(business key),这是一个唯一的、通常不改变的属性的组合体。当一个瞬态对象(transient object)被持久化的时候,数据库标识符会发生改变。当一个瞬态实例(常常与detached实例一起使用)保存在一个Set中时,哈希码的改变会破坏Set的约定。业务键的属性并不要求和数据库主键一样稳定,只要保证当对象在同一个Set中时它们的稳定性。”(Hibernate Reference Documentation v. 3.1.1)。

“我们推荐通过判断业务键相等性来实现equals()和hashCode()。业务键相等性意味着equals()方法只比较能够区分现实世界中实例的业务键(普通候选键)的属性。”(Hibernate Reference Documentation v. 3.1.1)。

换句话说,普通键用于equals()和hashCode(),而Hibernate生成的代理项键用于对象的id.这要求对于每个对象有一个相关的不可变的业务键。可是,并不是每个对象类型都有这样的一种键,这时候您可能会尝试使用会改变但不经常改变的字段。这和业务键不必与数据库主键一样稳定的思想相吻合。如果这种键在对象所在集合的生存期中不改变,那这就“足够好”了。这是一种危险的观点,因为这意味着您的应用程序可能不会崩溃,但是前提是没有人在特定的情况下更新了特定的字段。所以,应当有一种更好的解决方案,这种解决方案确实也存在。不要让Hibernate管理您的id.

试图创建和维护对象及数据库行的各自身份定义是目前为止所有讨论问题的根源。如果我们统一所有身份形式,这些问题都将不复存在。也就是说,作为以数据库为中心和以对象为中心的ID的替代品,我们应该创建一种通用的、特定于实体的ID来代表数据实体,这种ID应该在数据第一次输入的时候创建。无论这个唯一数据实体是保存在数据库中,是作为对象驻留在内存中,还是存储在其他格式的介质中,这个通用ID都应该可以识别它。通过使用数据实体第一次创建时指派的实体ID,我们可以安全地回到equals()和hashCode()的原始定义,它们只需使用这个id:

public class Person {// assign an id as soon as possibleprivate String id = IdGenerator.createId();private Integer version;public String getId() {return id;}public void setId(String id) {this.id = id;}public Integer getVersion() {return version;}public void setVersion(Integer version) {this.version = version;}// Person-specific fields and behavior herepublic boolean equals(Object o) {if (this == o) return true;if (o == null || !(o instanceof Person))return false;Person other = (Person)o;if (id == null) return false;return id.equals(other.getId());}public int hashCode() {if (id != null) {return id.hashCode();} else {return super.hashCode();}}}

这个例子使用对象id作为equals()方法判断相等的标准,以及hashCode()返回哈希码的来源。这就简单了许多。但是,要让它正常工作,我们需要两样东西。首先,我们需要保证每个对象在被保存之前都有一个id值。在这个例子里,当id变量被声明的时候,它就被指派了一个值。其次,我们需要一种判断这个对象是新生成的还是之前保存过的的手段。在我们最早的例子中,Hibernate通过检查id字段是否为null来判断对象是否为新的。既然对象id永不为null,很显然这种方法不再有效。通过配置Hibernate,让它检查version字段,而不是id字段是否为null, 我们可以很容易地解决这个问题。version字段是一个更恰当的用来判断对象是否被保存过的指示符。

 

Comments (115)

Java EE开发四大常用框架之Hibernate

1顶一下Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了轻量级的对象封装,使得Java程序员可以使用对象编程思维来操纵 数据库。Hibernate可以在应用EJB的Java EE架构中取代CMP,完成数据持久化。它还可以应用在任何使用JDBC的场合,既可以在Java的客户端程序实用,也可以在Servlet/JSP的 Web应用中使用 Hibernate的工作方式 Hibernate不会对您造成妨碍,也不会强迫您修改对象的行...[阅读全文]

1
顶一下

Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了轻量级的对象封装,使得Java程序员可以使用对象编程思维来操纵 数据库。Hibernate可以在应用EJB的Java EE架构中取代CMP,完成数据持久化。它还可以应用在任何使用JDBC的场合,既可以在Java的客户端程序实用,也可以在Servlet/JSP的 Web应用中使用

Hibernate的工作方式

Hibernate不会对您造成妨碍,也不会强迫您修改对象的行为方式。它们不需要实现任何不可思议的接口以便能够持续存在。惟一需要做的就是 创建一份 XML“映射文档”,告诉Hibernate您希望能够保存在数据库中的类,以及它们如何关联到该数据库中的表和列,然后就可以要求它以对象的形式获取数 据,或者把对象保存为数据。与其他解决方案相比,它几乎已经很完美了。

由于本文只是一篇介绍性的文章,所以不会引入构建和使用Hibernate映射文档的具体例子(我在《Hibernate: A Developer‘s Notebook》一书的头几章中已经介绍了一个例子)。此外,在网上和Hibernate的在线文档中,还可以找到一些不错的例子,请参见下面的“其他 信息”部分。它实际上相当直观。应用程序对象中的属性以一种简单而自然的方式与正确的数据库结构相关联。

运行时,Hibernate读取映射文档,然后动态构建Java类,以便管理数据库与Java之间的转换。在 Hibernate中有一个简单而直观的API,用于对数据库所表示的对象执行查询。要修改这些对象,(一般情况下)只需在程序中与它们进行交互,然后告 诉Hibernate保存修改即可。类似地,创建新对象也很简单;只需以常规方式创建它们,然后告诉Hibernate有关它们的信息,这样就能在数据库 中保存它们。

Hibernate API学习起来很简单,而且它与程序流的交互相当自然。在适当的位置调用它,就可以达成目的。它带来了很多自动化和代码节省方面的好处,所以花一点时间学 习它是值得的。而且还可以获得另一个好处,即代码不用关心要使用的数据库种类(否则的话甚至必须知道)。我所在的公司就曾有过在开发过程后期被迫更换数据 库厂商的经历。这会造成巨大的灾难,但是借助于Hibernate,只需要简单地修改Hibernate配置文件即可。

这里的讨论假定您已经通过创建Hibernate映射文档,建立了一个关系数据库,并且拥有要映射的Java 类。有一个Hibernate“工具集”可在编译时使用,以支持不同的工作流。例如,如果您已经拥有Java类和映射文档,Hibernate可以为您创 建(或更新)必需的数据库表。或者,仅仅从映射文档开始,Hibernate也能够生成数据类。或者,它可以反向设计您的数据库和类,从而拟定映射文档。 还有一些用于Eclipse的alpha 插件,它们可以在IDE中提供智能的编辑支持以及对这些工具的图形访问。

使用Hibernate的场合

既然Hibernate看起来如此灵活好用,为什么还要使用其他的工具呢?下面有一些场景,可以帮助您做出判断(或许通过提供一些比较和上下文,可以有助于鉴别非常适用Hibernate的场合)。

如果应用对于数据存储的需要十分简单——例如,您只想管理一组用户优先选择——您根本不需要数据库,更不用说一个优秀的对象-关系映射系统了 (即使它也如Hibernate这般易于使用)!从Java 1.4开始,有一个标准的Java Preferences API可以很好地发挥这个作用。

对于熟悉使用关系数据库和了解如何执行完美的SQL查询与企业数据库交互的人来说,Hibernate似乎有些碍手碍脚,这就像带有动力和自动 排挡的快艇车会使注重性能的赛车驾驶员不耐烦一样。如果您属于这种人,如果您所在的项目团队拥有一个强大的DBA,或者有一些存储过程要处理,您可能想研 究一下iBATIS。Hibernate的创建者本身就把iBATIS当作是另一种有趣的选择。我对它很有兴趣,因为我们曾为一个电子商务站点开发了一个 类似的系统(其功能更为强大),而且从那时到现在,我们已经在其他环境中使用过它,尽管在发现Hibernate之后,在新项目中我们通常更喜欢使用 Hibernate。您可以认为,以SQL为中心的解决方案(比如iBATIS)是“反向的”对象/关系映射工具,而 Hibernate是一个更为传统的ORM。

当然,还有其他的外部原因会导致采用另外的方法。比如,在一个企业环境中,必须使用成熟的EJB架构(或者其他的一些非普通对象映射系统)。可 以为提供自己的数据存储工具的平台量身定做代码,比如Mac OS X’s Core Data。使用的可能是像XML DTD这样的存储规范,而它根本不涉及关系数据库。

但是,如果您使用的是富对象模型,而且想要灵活、轻松且高效地保存它(无论您是否正要开始或已经决定使用关系数据库,只要这是一个选择——而且 存在可用的优秀免费数据库,比如MySQL,或可嵌入Java的HSQLDB,它就应该始终是一个选择),那么 Hibernate很可能就是您理想的选择。您可能会惊讶于节省的时间之多,以及您将会多么地喜欢使用它。

Comments (9)

Hibernate缓存使用

1顶一下 缓存分类: 一级缓存Session级 二级缓存SessionFactory级别JVM级别 查询缓存不固定(更具生命周期来说不固定) 生命周期: 一级缓存是和session会话一直产生一直消失 二级缓存是和sessionFacotry一致 查询缓存生命周期不固定,当数据库表发生改变的使用查询缓存马上消失 使用方法: 一级缓存:这个就不用说了 二级缓存:首先拷贝使用查询缓存类别.xml到classpath目录下面,然后到hibern...[阅读全文]

1
顶一下

缓存分类:

一级缓存Session级

二级缓存SessionFactory级别JVM级别

查询缓存不固定(更具生命周期来说不固定)

生命周期:

一级缓存是和session会话一直产生一直消失

二级缓存是和sessionFacotry一致

查询缓存生命周期不固定,当数据库表发生改变的使用查询缓存马上消失

使用方法:

一级缓存:这个就不用说了

二级缓存:首先拷贝使用查询缓存类别.xml到classpath目录下面,然后到hibernate.cfg.xml里面配置

开启二级缓存(默认开启),定义要使用二级缓存的实体类,然后就是在程序中要显示的指定session

使用二级缓存的类别有三种,Normal,GET,PUT默认使用的是Normal即可以写也可以读取二级缓存

(这里读写是指的会话Session)

查询缓存:首先也是到hibernate配置文件中去开启查询缓存,然后程序中也要显示的调用方法来开启查询缓存

eg:query.setCachemodel(true);

缓存的保存对象:

一级缓存:缓存的是实体

二级缓存缓存的也是实体

查询缓存缓存的是查询出来的实体的部分属性结果集和实体的ID(注意这里不是实体)

缓存的使用对象:

一级缓存:Load(Lazy加载)使用一级缓存当load的使用首先查找把序列号去和一级缓存匹配是否有,就直接取出来如果没有就发出SQL语句

Get也使用一级缓存

List接口query.list()不使用一级缓存每次都要发出SQLeg:(select*fromtudent)

Iterator接口query.iterate();使用一级缓存首先是要发出一条SQL来取得ID,eg:select

idfromstudent;然后把ID拿到缓存中去匹配如果有就直接取如果没有

就要再发出SQL如果都没有将发出N+1条SQL这就是N+1问题

二级缓存:都使用了二级缓存

查询缓存:看到名字顾名思义就知道是查询那么就是对List和Iterator接口起作用,但是查询缓存对Iterator不起作用,只对List起作用

下面我们这种介绍把二级缓存和查询缓存结合使用

当只是用查询缓存而关闭二级缓存的时候:

第一:如果查询的是部分属性结果集:那么当第二次查询的时候就不会发出SQL直接从查询缓存中取数据

第二:如果查询的是实体结果集eg(fromStudent)这个HQL那么查询出来的实体首先,查询缓存存放实体的ID,第二次查询,的时候就到查询缓存中取出ID一条一条的到数据库查询这样将发出N条SQL造成了SQL泛滥,当都开启查询缓存和二级缓存的时候

第一:如果查询的是部分属性结果集:这个和上面只是用查询缓存而关闭二级缓存的时候一致因为不涉及实体不会用到二级缓存

第二:如果查询的是实体结果集eg(fromStudent)这个HQL那么查询出来的实体首,查询缓存存放实体的ID,第二次查询,的时候就到查询缓存中取出ID,拿到二级缓存区找数据,如果有数据就不会发出SQL如果都有一条SQL都不会发出直接从二级缓存中取数据。

Comments (257)