相关文章推荐
微笑的大象  ·  MockWebserver ...·  1 月前    · 
微笑的大象  ·  SQL - Oracle 11g - ...·  1 月前    · 
微笑的大象  ·  Zabbix Probleme - ...·  9 月前    · 
微笑的大象  ·  A slow running cypher ...·  9 月前    · 
微笑的大象  ·  java.lang.NoSuchMethod ...·  9 月前    · 
微笑的大象  ·  精通 VIM ,此文就够了 - ·  10 月前    · 
微笑的大象  ·  Sorting Collections ...·  10 月前    · 
严肃的青蛙  ·  Java维护由Collectors.grou ...·  38 分钟前    · 
欢乐的柳树  ·  基于SuperMap iObjects ...·  38 分钟前    · 
豪情万千的眼镜  ·  智能指针reset()·  2 小时前    · 
任性的筷子  ·  std::packaged_task::re ...·  2 小时前    · 
爱喝酒的火车  ·  QueryMetricByPage - ...·  2 小时前    · 
@OneToMany(mappedBy = "timeline") @javax.persistence.OrderBy("startYear, endYear") Set events @Entity class Event { @Required Integer startYear Integer endYear @Required String description @ManyToOne Timeline timeline

In the above example I've used the standard JPA (Java Persistence API) @OrderBy annotation which allows you to specify the order of a collection of objects via object properties, in this example a @OneToMany association . I'm ordering first by startYear in ascending order and then by endYear , also in ascending order. This is all well and good, but note that I've specified that only the start year is required. (The @Required annotation is a custom Hibernate Validator annotation which does exactly what you would expect.) How are the events ordered when you have several events that start in the same year but some of them have no end year? The answer is that it depends on how your database sorts null values by default. Under Oracle 10g nulls will come last. For example if two events both start in 2001 and one of them has no end year, here is how they are ordered:

2001 2002 Some event 2001 2003 Other event 2001 Event with no end year

What if you want to control how null values are ordered so they come first rather than last? In Hibernate there are several ways you could do this. First, you could use the Hibernate-specific @Sort annotation to perform in-memory (i.e. not in the database) sorting, using natural sorting or sorting using a Comparator you supply. For example, assume I have an EventComparator helper class that implements Comparator . I could change Timeline 's collection of events to look like this:

@OneToMany(mappedBy = "timeline") @org.hibernate.annotations.Sort(type = SortType.COMPARATOR, comparator = EventCompator) Set<Event> events

Using @Sort will perform sorting in-memory once the collection has been retrieved from the database. While you can certainly do this and implement arbitrarily complex sorting logic, it's probably better to sort in the database when you can. So we now need to turn to Hibernate's @OrderBy annotation, which lets you specify a SQL fragment describing how to perform the sort. For example, you can change the events mapping to :

@OneToMany(mappedBy = "timeline") @org.hibernate.annotations.OrderBy("start_year, end_year") Set<Event> events

This sort order is the same as using the JPA @OrderBy with "startYear, endYear" sort order. But since you write actual SQL in Hibernate's @OrderBy you can take advantage of whatever features your database has, at the possible expense of portability across databases. As an example, Oracle 10g supports using a syntax like "order by start_year, end_year nulls first" to order null end years before non-null end years. You could also say "order by start_year, end year nulls last" which sorts null end years last as you would expect. This syntax is probably not portable, so another trick you can use is the NVL function, which is supported in a bunch of databases. You can rewrite Timeline 's collection of events like so:

@OneToMany(mappedBy = "timeline") @org.hibernate.annotations.OrderBy("start_year, nvl(end_year , start_year)") Set<Event> events

The expression "nvl(end_year , start_year)" simply says to use end_year as the sort value if it is not null, and start_year if it is null. So for sorting purposes you end up treating end_year as the same as the start_year if end_year is null. In the contrived example earlier, applying the nvl-based sort using Hibernate's @OrderBy to specify SQL sorting criteria, you now end with the events sorted like this:

2001 Event with no end year 2001 2002 Some event 2001 2003 Other event

Which is what you wanted in the first place. So if you need more complex sorting logic than what you can get out of the standard JPA @javax.persistence.OrderBy , try one of the Hibernate sorting options, either @org.hibernate.annotations.Sort or @org.hibernate.annotations.OrderBy . Adding a SQL fragment into your domain class isn't necessarily the most elegant thing in the world, but it might be the most pragmatic thing.

About Scott Leberknight

Scott is Chief Architect at Near Infinity Corporation, an enterprise software development and consulting services company based in Reston, Virginia. He has been developing enterprise and web applications for 14 years professionally, and has developed applications using Java, Ruby, Groovy, and even an iPhone application with Objective-C. His main areas of interest include alternative persistence technologies, object-oriented design, system architecture, testing, and frameworks like Spring, Hibernate, and Ruby on Rails. In addition, Scott enjoys learning new languages to make himself a better and more well-rounded developer a la The Pragmatic Programmers' advice to “learn one language per year.”

Scott holds a B.S. in Engineering Science and Mechanics from Virginia Tech, and an M. Eng. in Systems Engineering from the University of Maryland. Scott speaks at the No Fluff Just Stuff Symposiums and various other conferences. In his (sparse) spare time, Scott enjoys spending time with his wife, three children, and cat. He also tries to find time to play soccer, go snowboarding, and mountain bike whenever he can.

  • Cloud
  • Automation Tools: Gradle, Git, Jenkins, Sonar
  • HTML5, CSS3, AngularJS, jQuery, Usability
  • Mobile Apps - iPhone and Android
  • More...
  •  
    推荐文章