转载 2011年10月16日 13:21:41

Real-World Experiences With Hibernate - Best Practices

'Best Practices'

Real-World Experiences With Hibernate says that we won't be advocating such practices?". Well, the Hibernate team has it's own "Best Practices" - and we've found that you can get into a mess if you apply them prescriptively without thinking about it. This section aims to examine some of those practices.

Components and Queries

Components provide an excellent technique for refactoring. However, one nuisance that we've encountered is that you cannot drop a component into a query. Consider the following component definition:

public class MembershipId {
    private int memberPrefix;

    private int memberNumber;

    int getMemberPrefix() {
        return memberPrefix;

    int getMemberNumber() {
        return memberNumber;

Say that this component is used in another entity:

public class MemberNote {
    private int id;

    private MembershipId membershipId;

    private String text;

and that you wanted to write a DAO method that returned all of the member notes for a particular membership ID. You can't just drop the component into a HQL query:

public List getMemberNotes(MembershipId membershipId) {
    return session.createQuery(
            "from MemberNote where membershipId=:membershipId")
            .setParameter("membershipId", membershipId).list();

Because it'll generate SQL that looks like this:

select as id2_, membernote0_.isocode as isocode2_, 
       membernote0_.memberNumber as memberNu3_2_, 
       membernote0_.text as text2_ 
from MemberNote membernote0_ 
where (membernote0_.isocode, membernote0_.memberNumber)=?

This doesn't work with Oracle or HSQLDB. Instead, you have to fully qualify each part of the key:

public List getMemberNotes(MembershipId membershipId) {
    return session.createQuery(
            "from MemberNote where "
                    + "membershipId.memberNumber=:memberNumber and "
                    + "membershipId.memberPrefix=:memberPrefix")
            .setInteger("memberNumber", membershipId.getMemberNumber())
            .setInteger("memberPrefix", membershipId.getMemberPrefix())

The documentation says "(n)ever try to use a path-expression that ends in a property of component type"(see here) . Frankly we're a little unsure as to why Hibernate behaves this way with simple equality checks. It'd be understandable if we tried to do a more complex comparison like a greater-than or less-than.

Incidentally, if you repeatedly find yourself doing foreign-key lookups like this, it might be worth mapping a relationship instead and have Hibernate do the work.

Generated Keys

The Hibernate documentation recommends the use of synthetic primary keys (see here) and in our experience, Hibernate works best with them.

However, it can be difficult to test business logic code that is dependent on the value of generated keys. This is because you have reduced control over what keys are generated. For example, say that you had a synthetic primary key on a class and decided that you would sort results by this ID. Because this ID is generated, it can be difficult to ensure that objects will have IDs generated in a particular order and to then check that these objects are sorted correctly.

To summarise, if you're using generated synthetic primary keys, it's best to just set them up so that Hibernate can use them, and then forget about them. Consider them a requirement of Hibernate's internal operation. If possible, don't use them yourself - not if you want to test them anyway.

The equals() Method

A prime example of a Hibernate "best practice" that has caused us problems is the equals() method. However, let me emphasis that the question is not if you should implement the equals() and hashCode() methods - the testing benefits alone make it worthwhile - but how you should implement them.

The Hibernate documentation recommends using "Business key equality" (see here to see what this means). Whilst this technique is certainly the most appropriate from a Hibernate point-of-view, we ran into problems when other users of the database (who were not even using Hibernate) inserted rows with duplicated 'business keys'. This caused our code to behave in unexpected ways.

Given that the columns we had dictated to be the "business key" did not actually constitute the real primary key, it rapidly became apparent that it was unreasonable to expect other users to not insert whatever they want into them. Consequently, we had to revert back to use of the primary key in the equals() method. Unfortunately, a large number of tests that had been written to set up "business key" data had to be modified.

Whilst usage of primary keys in equals() has its downsides (for example, unsaved objects that use a key generator cannot be put into sets), the sad fact in this case was that from the perspective of all users of the system, a primary key was a primary key - and a 'business key' wasn't!

For those cases where you have a composite primary key, we highly recommend implementing your equals() and hashCode() methods with the Jakarta Commons libraries - in particular the EqualsBuilder and HashCodeBuilder classes.

Custom Types

The Hibernate team recommends the use of custom types where possible. These can be extremely useful, but have one significant limitation: instances of custom types that map to multiple columns can't be put into HQL queries - at least not if your database doesn't support tuples (which Oracle doesn't).

For example, we had a database where dates were stored in separate day, month and year columns. This occurred in many tables, so we wrote a custom type and used it when mapping to these tables. Here is an example of its usage:

import javax.persistence.Column;

public class Membership {
    private int id;

    @org.hibernate.annotations.Type(type = "DateUserType")
    @org.hibernate.annotations.Columns(columns = {
            @Column(name = "BIRTH_DD"), @Column(name = "BIRTH_MM"),
            @Column(name = "BIRTH_YYYY") })
    private java.util.Date birthDate;

However, attempts to pass this date directly into a query failed:

public List getMemberships(Date birthDate) {
    return session.createQuery(
            "from Membership m where m.birthDate=:birthDate").setParameter(
            "birthDate", birthDate,

Because Hibernate will generate the following SQL:

select as id3_, membership0_.MBR_BIRTH_DD as MBR2_3_,
       membership0_.MBR_BIRTH_MM as MBR3_3_, 
       membership0_.MBR_BIRTH_YYYY as MBR4_3_ 
from Membership membership0_
where (membership0_.MBR_BIRTH_DD, membership0_.MBR_BIRTH_MM, membership0_.MBR_BIRTH_YYYY)=?

(Note that this is similar to the problem described in Components and Queries).

In our case, the only thing that we were able to do was to define a component that wraps the date:

import java.util.Calendar;
import java.util.Date;

public class DateWrapper
    private int dayOfMonth;
    private int month;
    private int year;
    private final Calendar calendar = Calendar.getInstance();
    DateWrapper(Date date) {        
        dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
        month = calendar.get(Calendar.MONTH);
        year = calendar.get(Calendar.YEAR);
    Date getDate()
        calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
        calendar.set(Calendar.MONTH, month);
        calendar.set(Calendar.YEAR, year);            
        return calendar.getTime();

    int getDayOfMonth() {
        return dayOfMonth;

    int getMonth() {
        return month;

    int getYear() {
        return year;

and use it to map properties that were going to be used in HQL queries:

import javax.persistence.AttributeOverride;
import javax.persistence.Column;

public class Membership {
    private int id;

            @AttributeOverride(name = "dayOfMonth", column = @Column(name = "BIRTH_DD")),
            @AttributeOverride(name = "month", column = @Column(name = "BIRTH_MM")),
            @AttributeOverride(name = "year", column = @Column(name = "BIRTH_YYYY")) })
    private DateWrapper birthDate;

Because the type of dateOfBirth was changed from Date to DateWrapper, any code that was using dateOfBirth directly needed to be modified to use DateWrapper.getDate(). We also needed to modify our DAO to explicitly qualify each part of the component:

public List getMemberships(Date birthDate) {
    DateWrapper birthDateWrapper = new DateWrapper(birthDate);
    String dayOfMonthParameterName = "dayOfMonth";
    String monthParameterName = "month";
    String yearParameterName = "year";
    return session.createQuery(
            "from Membership where birthDate.dayOfMonth=:"
                    + dayOfMonthParameterName + " and birthDate.month=:"
                    + monthParameterName + " and birthDate.year=:"
                    + yearParameterName).setInteger(
            dayOfMonthParameterName, birthDateWrapper.getDayOfMonth())
            .setInteger(monthParameterName, birthDateWrapper.getMonth())
            .setInteger(yearParameterName, birthDateWrapper.getYear())

Note that as far as we can tell, this problem only occurs when you have a custom type that maps to multiple columns. In short, custom types are still very useful, but may not be worth using for when you are mapping to multiple columns and need to use the mapped property in a HQL query.


我们还有对付”反盗链”的方式,对付防盗链,服务器会识别headers中的referer是不是它自己,如果不是,有的服务器不会响应,所以我们还可以在headers中加入referer 例如我们可以...
  • xixi880928
  • xixi880928
  • 2017年10月25日 10:30
  • 162

Django 大文件下载

def down(request,operatetype=""): from cyun import settings apkpath = os.path.join(setti...
  • selifecn
  • selifecn
  • 2015年04月01日 16:17
  • 254

  • gzliu_hit
  • gzliu_hit
  • 2013年03月25日 15:43
  • 1260

  • lindanzhu
  • lindanzhu
  • 2015年07月13日 10:15
  • 862


PCA的数学原理 作者 张洋 | 发布于 2013-06-22 机器学习 线性代数 PCA PCA(Principal Component Analysis)是一种常用的数据分析方法。PCA通过...
  • brightming
  • brightming
  • 2015年12月26日 15:31
  • 905

[翻译Joel On Software]Unicode/Unicode

所有软件开发者绝对,一定,必须,至少要知道的Unicode和字符集(没有任何借口) 我很失望的发现原来那么多的软件开发者对于神奇的字符集,编码,Unicode等等东西的了解真的并不是那么到位。几年前...
  • zinking3
  • zinking3
  • 2013年11月03日 21:03
  • 2021

Some Thoughts on the Common Toad

Before the swallow, before the daffodil, and not much later than the snowdrop, the common toad salut...
  • lililingling1985
  • lililingling1985
  • 2006年09月11日 02:55
  • 616

搭建IIS服务器80端口却已被占用的问题和用了Sc config http start= disabled命令导致HTTP服务无法启动的解决方案

80端口这个确实是一个很麻烦的问题,这几天一直在这个问题上打转,下面总结了几点错误和解决方案提供给大家。 之前我有一篇博客就是讲tomcat搭建服务器的80端口问题,用了 net stop htt...
  • u010792238
  • u010792238
  • 2014年03月31日 14:00
  • 4263


通常被选为担任Scrum Master角色的人多来自于那些有管理背景的人。组织期望这些所谓的管理大师能够按时交付Scrum项目,并且可以同时管理其它的项目。设定期望本身就是需要解决的第一重阻碍。记住,...
  • corrine_lei
  • corrine_lei
  • 2016年09月20日 18:06
  • 112


转载自通过金矿模型介绍动态规划 对于动态规划,每个刚接触的人都需要一段时间来理解,特别是第一次...
  • CodeEmperor
  • CodeEmperor
  • 2016年05月23日 20:11
  • 1933