如果你不能确定你要寻找的对象的标示符,请使用Session的find()方法。Hibernate使用一种简单而强大的面向对象查询语言。
List cats = sess.find( "from Cat as cat where cat.birthdate = ?", date, Hibernate.DATE ); List mates = sess.find( "select mate from Cat as cat join cat.mate as mate " + "where cat.name = ?", name, Hibernate.STRING ); List cats = sess.find( "from Cat as cat where cat.mate.bithdate is null" ); List moreCats = sess.find( "from Cat as cat where " + "cat.name = 'Fritz' or cat.id = ? or cat.id = ?", new Object[] { id1, id2 }, new Type[] { Hibernate.LONG, Hibernate.LONG } ); List mates = sess.find( "from Cat as cat where cat.mate = ?", izi, Hibernate.entity(Cat.class) ); List problems = sess.find( "from GoldFish as fish " + "where fish.birthday > fish.deceased or fish.birthday is null" );
find()的第二个参数接受一个对象或者对象数组。第三个参数接受一个Hibernate类型或者类型的数组。这些指定的类型用来把给定的对象绑定到查询中的?占位符(实际上对应的是JDBC PreparedStatement的传入参数)。就像在JDBC中一眼,你应该优先使用这种参数绑定的方式,而非组装字符串。
Hibernate类定义了一些静态方法和常量,提供了访问大部分内置类型的手段。这些内置类型是net.sf.hibernate.type.Type的实例。
如果你知道你的查询会返回非常大量的对象,但是你不希望全部使用它们,你可以用iterate()方法获得更好的性能,它会返回一个java.util.Iterator。这个迭代器会在需要的时候装载对象,所使用的标识符来自一个前导的SQL查询。(一共是N+1次查询)
// fetch ids Iterator iter = sess.iterate("from eg.Qux q order by q.likeliness"); while ( iter.hasNext() ) { Qux qux = (Qux) iter.next(); // fetch the object // something we couldnt express in the query if ( qux.calculateComplicatedAlgorithm() ) { // delete the current instance iter.remove(); // dont need to process the rest break; } }
很不幸,java.util.Iterator没有声明任何exception。所以,发生的任何SQL或者Hibernate的exception都会被包装在一个LazyInitializationException中(它是RuntimeException的子类)。
如果你预期大部分的对象已经装载过,存在于session的缓存中了,或者查询结果包含同样的对象很多次,那么iterator()方法也会获得更好的性能。(如果没有任何数据被缓存或者重复出现,则find()总是会更快。)下面是一个应该使用iterator()调用的查询例子:
Iterator iter = sess.iterate( "select customer, product " + "from Customer customer, " + "Product product " + "join customer.purchases purchase " + "where product = purchase.product" );
如果对上面的查询使用find(),会返回一个非常大的JDBCResultSet,包含很多重复的相同数据。
有时候Hibernate查询会每行返回多种对象,这种情况下,每行会返回一个数组,包含多个对象元素:
Iterator foosAndBars = sess.iterate( "select foo, bar from Foo foo, Bar bar " + "where bar.date = foo.date" ); while ( foosAndBars.hasNext() ) { Object[] tuple = (Object[]) foosAndBars.next(); Foo foo = tuple[0]; Bar bar = tuple[1]; .... }
查询可以在select子句中指定类的属性。甚至可以调用SQL的统计函数。属性或者统计值被称为“标量(scalar)”结果。
Iterator results = sess.iterate( "select cat.color, min(cat.birthdate), count(cat) from Cat cat " + "group by cat.color" ); while ( results.hasNext() ) { Object[] row = results.next(); Color type = (Color) row[0]; Date oldest = (Date) row[1]; Integer count = (Integer) row[2]; ..... }
Iterator iter = sess.iterate( "select cat.type, cat.birthdate, cat.name from DomesticCat cat" );
List list = sess.find( "select cat, cat.mate.name from DomesticCat cat" );
如果你需要为你的结果集设置边界(你需要获取的最大行数与/或你希望获取的第一行),你应该得到一个net.sf.hibernate.Query的实例:
Query q = sess.createQuery("from DomesticCat cat"); q.setFirstResult(20); q.setMaxResults(10); List cats = q.list();
你甚至可以在映射文档中定义命名查询。(记得用一个CDATA块把你的查询包含起来,否则在分析的时候可能引起误解。)
<query name="eg.DomesticCat.by.name.and.minimum.weight"><![CDATA[ from eg.DomesticCat as cat where cat.name = ? and cat.weight > ? ] ]></query>
Query q = sess.getNamedQuery("eg.DomesticCat.by.name.and.minimum.weight"); q.setString(0, name); q.setInt(1, minWeight); List cats = q.list();
查询界面支持使用命名参数。命名参数用:name的形式在查询字符串中表示。在Query中有方法把实际参数绑定到命名参数或者JDBC风格的?参数。 和JDBC不同,Hibernate的参数从0开始计数。 使用命名参数有一些好处:
命名参数不依赖于它们在查询字符串中出现的顺序
在同一个查询中可以使用多次
他们可读性好
//named parameter (preferred) Query q = sess.createQuery("from DomesticCat cat where cat.name = :name"); q.setString("name", "Fritz"); Iterator cats = q.iterate();
//positional parameter Query q = sess.createQuery("from DomesticCat cat where cat.name = ?"); q.setString(0, "Izi"); Iterator cats = q.iterate();
//named parameter list List names = new ArrayList(); names.add("Izi"); names.add("Fritz"); Query q = sess.createQuery("from DomesticCat cat where cat.name in (:namesList)"); q.setParameterList("namesList", names); List cats = q.list();
如果你的JDBC驱动支持可滚动的ResuleSet,Query接口可以获取一个ScrollableResults,允许你在查询结果中灵活游走。
Query q = sess.createQuery("select cat.name, cat from DomesticCat cat " + "order by cat.name"); ScrollableResults cats = q.scroll(); if ( cats.first() ) { // find the first name on each page of an alphabetical list of cats by name firstNamesOfPages = new ArrayList(); do { String name = cats.getString(0); firstNamesOfPages.add(name); } while ( cats.scroll(PAGE_SIZE) ); // Now get the first page of cats pageOfCats = new ArrayList(); cats.beforeFirst(); int i=0; while( ( PAGE_SIZE > i++ ) && cats.next() ) pageOfCats.add( cats.get(1) ); }
scroll()的行为方式与iterate()很类似,除了对象可以有选择的用get(int)初始化,而非整个行都一次性被初始化。
集合filter是一种特殊的查询,用于一个持久化集合或者数组。查询字符串可以引用this,意为当前的数组元素。
Collection blackKittens = session.filter( pk.getKittens(), "where this.color = ?", Color.BLACK, Hibernate.enum(Color.class) );
返回的集合被认为是一个包(bag)。
请注意filter并不需要from 子句(当然需要的话它们也可以加上)。Filter不限定返回它们自己的集合元素。
Collection blackKittenMates = session.filter( pk.getKittens(), "select this.mate where this.color = eg.Color.BLACK" );
HQL极为强大,但是有些人希望能够动态的使用一种面向对象API创建查询,而非在他们的Java代码中嵌入字符串。对于那部分人来说,Hibernate提供了一种直观的Criteria查询API。
Criteria crit = session.createCriteria(Cat.class); crit.add( Expression.eq("color", eg.Color.BLACK) ); crit.setMaxResults(10); List cats = crit.list();
如果你对类似于SQL的语法不是感觉很舒服的话,用这种方法开始使用Hibernate可能更容易。这种API也比HQL更可扩展。程序可以提供它们自己的Criterion接口的实现。
你可以使用createSQLQuery()方法,用SQL来表达查询。你必须把SQL别名用大括号包围起来。
List cats = session.createSQLQuery( "SELECT {cat.*} FROM CAT AS {cat} WHERE ROWNUM<10", "cat", Cat.class ).list();
List cats = session.createSQLQuery( "SELECT {cat}.ID AS {cat.id}, {cat}.SEX AS {cat.sex}, {cat}.MATE AS {cat.mate}, {cat}.SUBCLASS AS {cat.class}, ... " + "FROM CAT AS {cat} WHERE ROWNUM<10", "cat", Cat.class ).list()
和Hibernate查询一样,SQL查询也可以包含命名参数或者顺序参数。