1、Hibernate 查询 首先介绍 get()和 load()方法的区别: get()方法和 load()方法的区别主要在于对二级缓存的使用上。 load()方法会使用二级缓存,而 get()方法在一级缓存没有找到会直接查询数据库,不会去二级缓存中查找。 get():如果在数据库中没有记录会返回空,get()无论如何都会返回数据. load():如果数据库中没有记录会抛出异常,如果有数据返回的是一个代理对象。 get()方法默认不支持 lazy(延迟加载)功能,而 load 支持延迟加载get()方法在查询不到数据时,返回 null,而 load 因为支持延迟加载,只有在使用对象时才加载,所以
2、如果数据库中不在数据 load 会抛出异常(org.hibernate.ObjectNotFoundException) 。get()和 load()只根据主键查询,不能根据其它字段查询,如果想根据非主键查询,可以使用HQLlist 和 iterator()方法之间的区别:(N+1?) list()方法在执行时,直接运行查询结果所需要的查询语句。 iterator()方法则是先执行得到对象 ID 的查询,然后在根据每个 ID 值去取得所要查询的对象。 因此:对于 list()方式的查询通常只会执行一个 SQL 语句,而对于 iterator()方法的查询则可能需要执行 N+1 条 SQL 语句
3、(N 为结果集中的记录数). 结果集的处理方法不同: list()方法会一次活的所有的结果集对象,而且他会依据查询的结果初始化所有的结果集对象。如果在结果集非常庞大的时候会占据非常多的内存,甚至会造成内存溢出的情况发生。 iterator()方法在执行时不会一次初始化所有的对象,而是根据对结果集的访问情况来初始化对象。一次在访问中可以控制缓存中对象的数量,以避免占用过多的缓存,导致内存溢出情况的发生。 HQL:HQL 是一种面向对象的查询语言, HQL 的操作对象是类、实例和属性等。 SQL:sql 的操作对象是数据表和列等数据对象。 Hql 是完全面向对象的查询语言,因此可以支持继承和多条等
4、特征。 HQL 查询依赖于 Query 类,每个 Query 实例对应一个查询对象。 定参数的功能,Query 接口才是真正的 HQL 查询接口。 /创建一个 Query 对象 Java 代码 1. Query query = session.createQuery (“from Customer as c where c.name=:customerName and c.age=:customerAge“); /动态绑定参数 Java 代码 1. query.setString(“customerName“,“Tom“); 2. query.setInteger(“customerAge“,
5、21); /执行查询语句,返回结果 Java 代码 1. List result = query.list(); HQL 查询步骤: 1:获取 Hibernate Session 对象。 2:编写 HQL 语句。 3:以 HQL 语句作为参数,调用 Session 的 createQuery 方法创建查询对象。 4:如果 HQL 语句包含参数,调用 Query 的 setXXX()方法为参数赋值。 5: 调用 Query 对象的 list 等方法遍历查询结果。 Query 还包含两个方法: Java 代码 1. setFirstResult(int firstResult):/设置返回的结果集
6、从第几条记录开始。 2. setMaxResults(int maxResults):/设置本次查询返回的结果数。 实体的删除和更新。 投影查询:只查询属性的一部分。 查询一个属性返回的是字符串 查询二个字段返回的是数组 动态构造查询:主要用于几十个表查询; 要构建一个新的对象,要加上一个构造函数; 在 new 对象的时候要加上包名 不要使用 count(*) 要 count(持久化对象) 分组与排序: Order by 子句可以通过 asc 或 desc 关键字排序 如: Java 代码 1. form User u Order by u.name asc,u.age desc; Group
7、 by 子句与统计查询: 如: Java 代码 1. String hql = “select count(u),u.age from User u group by u.age havingcount(u)10“; 2. List list = session.createQuery(hql).list(); 标准的 SQL 聚合函数都可以在 HQL 语句中使用,比如:count(),sum(),max(),min(),age()等 连接查询: 内连接:inner join 左外连接:left outer join 右外连接:right outer join 全连接:full join(不常
8、用) 迫切外连接:left outer join fetch,left join fetch:用于一次性获取连接数据,特别是集合数据。减少与数据库交互的次数。 left out join:使用做外连接位于 left join 左侧表的所有记录及对应的 order 类的记录信息都将显示出来。 right out join:与 left out join 正好相反,right out join 返回的是 HQL 右侧表中的所有记录以及对应的 Customer 对象记录信息。 获取集合数据的四种方式: 1:Hibernate.initialize(user.getOrder(); 2:user.ge
9、tOrder().size(); 3:左右迫切连接 4:高级过滤器 条件查询语句(Criteria Queries):利用对象进行对象查询。 主要的接口有:Criteria、Criterion 和 expression_r 和 Restrictions 类组成。能够支持在运行时动态生成 SQL 语句。 条件查询步骤: 1:通过 seesion 的 CreateCriteria()方法,创建一个 Criteria 对象 2:设置查询对象,name 指对象的属性 3:把查询条件添加到 Criteria 对象中 4:执行 list()查询返回结果。 条件查询通过三个类完成: Criteria:代表一
10、次查询 . Criterion:代表一个查询条件. Restrictions:产生查询条件的工具类. 本地 sql 查询:使用手写的 SQL 来完成所有的 createupdatedelete 操作也包括存储过程 本地 sql 步骤: 1:通过 session 创建一个 SqlQuery 对象。 2:编写 sql 语句 3:以 SQL 语句作为参数,调用 Session 的 createSQLQuery 方法创建查询对象 4:如果 SQL 语句包含参数,则调用 Query 的 setXX 方法为参数赋值 5:调用 SQLQuery 对象的 addEntity 或 addScalar 方法,将选
11、出的结果与实体或标量值关联. 如:HQL 的 sql Java 代码 1. String sql = “select c.c_id as c.id,c.c_name as c.name from CUSTOMER c where c.id = 1“; 注意:如果使用*,表的别名和类的别名要一致 Java 代码 1. String sql1 = “select c.* from CUSTOMER c where c.c_id=2 “; 如:条件查询的 sql Java 代码 1. SQLQuery query = session.createSQLQuery(sql); 2. query.add
12、Entiy(“c“,Customer.class); 多态查询: 多态查询是指可以查询到当前类及所有子类的实例,HQL 和条件查询都支持多态查询。 如:hql 语句查询出所有的持久化对象 Java 代码 1. Query query = session.createQuery(“from java.lang.object“); 2. System.out.println(query.list(); HQL 支持多态查询,多态查询是指查询出当前类及所有子类的实例, session.createQuery(“from Employee“); or session.createCriteria(Em
13、ployee.class); 如果 Employee 类有两个子类: HourlyEmployee 和 SalariedEmployee,那么这个查询语句会查出所有的 Employee 实例,以及 HourlyEmployee 类和 SalariedEmployee 类的实例。 Java 代码 1. session.createQuery(“from HourlyEmployee“); 2. session.createCriteria(HourlyEmployee.class);/只检索子类 查询结果集排序: Hql 与条件查询都支持查询结果集排序,只是 HQL 采用 Order by 关键
14、字,而条件查询采用order 类进行查询结果排序。 分页查询: 分页查询是数据库应用中常见的处理方式,Query 和 Criteia 接口都提供了用于分页查询的方法.Java 代码 1. setFistResult(int): /指定从哪一个对象开始查询,参数是索引位置从 0 开始。2. setMaxResults(int): /指定一次最多查询的对象数量。 子查询: 1:查询有一个订单以上的客户。 如: Java 代码 1. from Customer c where 11 如果子查询返回多条记录,可以使用关键字量化: 1:all 2:any 3:some 4:in 5:exists 如:
15、Java 代码 1. /返回所有订单价格小于 100 的客户 2. from Customer c where 100all(select o.price form c.orders o) 3. /返回有一条订单的价格小于 100 的客户 4. from Customer c where 100any(select o.price from c.orders o) 5. /返回有一条订单的价格等于 100 的客户 6. from Customer c where 100=some(select o.price from c.order o) 7. 8. /返回有一条订单的价格等于 100 的客
16、户 9. from Customer c where 100 = any (select o.prive from c.orders o) 10./返回有一条订单的价格等于 100 的客户 11.from Customer c where 100 in (select o.price from c.orders o) 参数绑定: 1:传统的 JDBC 的参数绑定 如: Java 代码 1. PrepareStatement pre = connection.prepare(“select * from User where user.name = ?“); 2. pre.setString(1
17、,“Zhao“); 3. ResultSet rs = pre.executeQuery(); Hibernate 参数绑定:在 hibernate 中有 4 种参数绑定方式 1:按参数名称(命名参数)绑定,在语句中定义命名参数要用“:“ 开头 2:按参数位置绑定 3:Hql 查询中可以通过 setParameter()方法绑定任意类型的参数 4:setProperties() 方法,将命名参数与一个对象的属性值绑定在一起 如:参数“:“开头 Java 代码 1. Query query = session.createQuery(“from User user where user.name
18、 = customername and user.age = :customerage“); 2. query.setString(“customername“,name); 3. query.setInteger(“customerage“,age); 如:参数位置绑定 Java 代码 1. Query query = session.createQuery(“from User user where user.name =? and user.age = ?“); 2. query.setString(0,name); 3. query.setInteger(1,age); 如:setPa
19、rameter 他有三个参数一般写两个参数 Java 代码 1. String hql = “from User user where user.name = :customername“; 2. Query query = session.createQuery(hql); 3. query.setParameter(“customername“,name,Hibernate.STRING); 如:setProperties Java 代码 1. Customer customer = new Customer(); 2. customer.setName(“sdsd“); 3. custo
20、mer.setAge(99); 4. Query query = session.createQuery(“from Customer c where c.name=:nameand c.age = :age“); 5. query.setProperties(customer); 定义命名查询: 命名的 SQL 语句不是放在程序中,而是放在配置文件中,这种方式是以松耦合的方式配置 SQL语句,可以提高程序解耦. 在 Customer.hbm.xml 配置文件中 Java 代码 1. 2. 3. 程序中的代码: Java 代码 1. Query query = session.getNamed
21、Query(“findCustomer“); 2. query.setString(“name“,“tiger“); 3. List list = query.list(); 命名的 SQL 语句 : Java 代码 1. 2. 3. 4. 5. select c.* 6. from Customer Customer c where c.name like “刘德华“ 7. sql_query 是 hibernate-mapping 元素的子元素,因此可以直接通过 session 访问 缓存是位于应用程序与物理数据源之间,用于临时存放复制数据的内存区域, 目的是为了减少应用程序对物理数据源访
22、问的次数,从而提高应用程序的运行性能. Hibernate 在查询数据时,首先到缓存中去查找,如果找到就直接使用,找不到的时候 就会从物理数据源中检索,所以,把频繁使用的数据加载到缓存区后,就可以大大减少应 用程序对物理数据源的访问,使得程序的运行性能明显的提升. 缓存分两级,一级 session 缓存,就是常说的一级缓存;二级应用缓存(二级缓存); 一级缓存,一级缓存依赖于 session,在一个 session 中就是一个缓存,当 session 失效时,缓存消失。 /*两个 session 两次加载*/ Java 代码 1. public void loadBookAgain() 2.
23、Session session = HibernateSessionFactory.getSession(); 3. Book book1 = (Book) session.get(Book.class, 6); 4. Book book2 = (Book) session.get(Book.class, 6); 5. session.close(); 6. 7. / Session session1 = HibernateSessionFactory.getSession(); 8. / Book book2 = (Book) session1.get(Book.class, 6); 9.
24、/ session1.close(); 10. 在一个 session 里面查询两次相同的 book,只会执行一次 sql。 但若放在不同的 session 中,将会执行两次数据库查询。 解决问题的办法就是用二级缓存。 二级缓存是 SessionFactory 级别的全局缓存,它底下可以使用不同的缓存类库,比如ehcache、oscache 等。 参考 http:/ 不是所有的数据都适合放在二级缓存中 下面这几种情况就不适合加载到二级缓存中 : 1.经常被修改的数据 2.绝对不允许出现并发访问的数据 3.与其他应用共享的数据 下面这己种情况合适加载到二级缓存中 : 1.数据更新频率低 2.允许
25、偶尔出现并发问题的非重要数据 3.不会被并发访问的数据 4.常量数据 5.不会被第三方修改的数据 配置二级缓存比较简单,以 ehcache 为例: 添加缓存文件 ehcache-hibernate-local.xml Java 代码 1. 2. 3. 4. 5. 9. 10. 12. 13. maxElementsInMemory 为缓存对象的最大数目 , eternal 设置是否永远不过期, timeToIdleSeconds 对象处于空闲状态的最多秒数, timeToLiveSeconds 对象处于缓存状态的最多秒数 。 在实体 bean 的 hbm.xml 文件中加上缓存配置: Java
26、 代码 1. 3. 现在大部分的 hibernate 应用不再写实体映射配置文件,那么就在实体 bean 中加上 /默认的缓存策略. Java 代码 1. Cache(usage = CacheConcurrencyStrategy.READ_WRITE) 在 hibernate 定义 sessionFactory 中加上查询缓存配置: Java 代码 1. 2. org.hibernate.cache.EhCacheProvider 3. Java 代码 1. true 2. 4. /ehcache-hibernate-local.xml 5. 如果项目试用了 spring,那么相应配置为:
27、 Java 代码 1. 2. 3. $hibernate.dialect 4. $hibernate.show_sql 5. $hibernate.format_sql 6. org.hibernate.cache.EhCacheProvider 7. /ehcache-hibernate-local.xml 8. 9. 两种配置基本一致。 这个时候在按实体的 ID 来查询的时候即使不在一个 session 中,hibernate 也只是执行一次sql。 查询缓存做到现在,有一点效果,但基本还是个摆设。 看下面代码: Java 代码 1. public void listBookTwice()
28、 2. String hql = “from Book“; 3. Session session = HibernateSessionFactory.getSession(); 4. Query q = session.createQuery(hql); 5. List list1 = q.list(); 6. List list2 = q.list(); 7. session.close(); 同一个 query。list 了两次,按照之前的效果,应该是执行一个 sql。事实是,他要去查两次, 在一个 query 尚且如此,两个不用说,肯定也是没有用到缓存了。 难道缓存失效了?呵呵,其实是因
29、为我们虽然配置了缓存,但是在 query 级却没有设置缓存,如果需要 query 缓存, 则需要手工写入: q.setCacheable(true);来激活查询缓存。 修改代码如下: Java 代码 1. public void listBookTwice() 2. String hql = “from Book“; 3. Session session = HibernateSessionFactory.getSession(); 4. Query q = session.createQuery(hql); 5. q.setCacheable(true); 6. List list1 = q
30、.list(); 7. for(Book b : list1) 8. System.out.println(b.getBname()+“-list1“); 9. 10./ List list2 = q.list(); 11. session.close(); 12. 13. Session session2 = HibernateSessionFactory.getSession(); 14. Query q2 = session2.createQuery(hql); 15. q2.setCacheable(true); 16. List list2 = q2.list(); 17. for(
31、Book b : list2) 18. System.out.println(b.getBname()+“-list2“); 19. 20. session2.close(); 21. 在两个 session 立分别 list 查询,ok,只输出一条 sql。说明二级缓存在 list 查询的时候也起作用了。 那 hibernate 是根据什么来缓存 list 呢,参考:http:/ 对于查询缓存来说,缓存的 key 是根据 hql 生成的 sql,再加上参数,分页等信息(可以通过日志输出看到,不过它的输出不是很可读,最好改一下它的代码)。 比如 hql: from Cat c where c.
32、name like ? 生成大致如下的 sql: select * from cat c where c.name like ? 参数是“tiger%“ ,那么查询缓存的 key*大约*是这样的字符串(我是凭记忆写的,并不精确,不过看了也该明白了): select * from cat c where c.name like ? , parameter:tiger% 这样,保证了同样的查询、同样的参数等条件下具有一样的 key。 现在说说缓存的 value,如果是 list 方式的话,value 在这里并不是整个结果集,而是查询出来的这一串 ID。 也就是说,不管是 list 方法还是 ite
33、rate 方法,第一次查询的时候,它们的查询方式很它们平时的方式是一样的, list 执行一条 sql,iterate 执行 1+N 条,多出来的行为是它们填充了缓存。但是到同样条件第二次查询的时候,就都和 iterate 的行为一样了, 根据缓存的 key 去缓存里面查到了 value,value 是一串 id,然后在到 class 的缓存里面去一个一个的 load 出来。这样做是为了节约内存。 可以看出来,查询缓存需要打开相关类的 class 缓存。list 和 iterate 方法第一次执行的时候,都是既填充查询缓存又填充 class 缓存的。 这里还有一个很容易被忽视的重要问题,即打开
34、查询缓存以后,即使是 list 方法也可能遇到1+N 的问题!相同条件第一次 list 的时候, 因为查询缓存中找不到,不管 class 缓存是否存在数据,总是发送一条 sql 语句到数据库获取全部数据,然后填充查询缓存和 class 缓存。 但是第二次执行的时候,问题就来了,如果你的 class 缓存的超时时间比较短,现在 class 缓存都超时了,但是查询缓存还在, 那么 list 方法在获取 id 串以后,将会一个一个去数据库 load!因此,class 缓存的超时时间一定不能短于查询缓存设置的超时时间 !如果还设置了发呆时间的话,保证 class 缓存的发呆时间也大于查询的缓存的生存时
35、间。这里还有其他情况,比如 class 缓存被程序强制 evict 了, 这种情况就请自己注意了。 另外,如果 hql 查询包含 select 字句,那么查询缓存里面的 value 就是整个结果集了。 可以理解为 hibernate 缓存了每次查询的 hql 语句作为缓存 map 的 key,将对应对象的 id 作为value 缓存,每次遇到相同的 hql,就将 id 取出来, 如果在缓存里面有对象,就从缓存取,没有的话就去数据库 load 缓存什么时候更新呢? 看代码: Java 代码 1. public void update() 2. Session session = Hibernat
36、eSessionFactory.getSession(); 3. Transaction tran = session.beginTransaction(); 4. tran.begin(); 5. Book b = (Book) session.get(Book.class, 7); 6. b.setIsbn(“567890“); 7. session.saveOrUpdate(b); 8. mit(); 9. session.close(); 10. 11.public void listBookTwice() 12. String hql = “from Book“; 13. Sessi
37、on session = HibernateSessionFactory.getSession(); 14. Query q = session.createQuery(hql); 15. q.setCacheable(true); 16. List list1 = q.list(); 17. for(Book b : list1) 18. System.out.println(b.getBname()+“-“+b.getIsbn()+“-list1“); 19. 20. session.close(); 21. 22. 23. public void listBookAgain() 24.
38、String hql = “from Book“; 25. Session session2 = HibernateSessionFactory.getSession(); 26. Query q2 = session2.createQuery(hql); 27. q2.setCacheable(true); 28. List list2 = q2.list(); 29. for(Book b : list2) 30. System.out.println(b.getBname()+“-“+b.getIsbn()+“-list3“); 31. 32. session2.close(); 33.
39、 34. public static void main(String args) 35. BookDao dao = new BookDao(); 36. dao.listBookTwice(); 37. dao.update(); 38. dao.listBookAgain(); 39. 先 list 一次,之间更新一个 book,第二次 list,输出: Java 代码 1. Hibernate: select book0_.id as id0_, book0_.bname as bname0_, book0_.isbn as isbn0_, book0_.price as price0
40、_ from hibernate_test.dbo.book book0_ 2. book1250672666171-123456-list1 3. book1250672666203-123456-list1 4. book1250672666203-123456-list1 5. book1250672666203-123456-list1 6. book1250672666203-123456-list1 7. Hibernate: update hibernate_test.dbo.book set bname=?, isbn=?, price=? where id=? 8. Hibe
41、rnate: select book0_.id as id0_, book0_.bname as bname0_, book0_.isbn as isbn0_, book0_.price as price0_ from hibernate_test.dbo.book book0_ 9. book1250672666171-123456-list3 10.book1250672666203-567890-list3 11.book1250672666203-123456-list3 12.book1250672666203-123456-list3 13.book1250672666203-123456-list3