Hibernate查询数据方式
Hibernate是通过检索对象来查询数据的,下面我们了解一下,Hibernate提供的几种检索对象的方式:
l 对象导航检索方式:根据已经加载的对象导航到其他对象,主要针对关联集合对象的查询。(针对多表)
l OID检索方式:根据对象的OID来检索对象。(单表ById)
l HQL检索方式:使用面向对象的HQL(Hibernate Query Language)查询语言来检索对象,Hibernate底层会自动将HQL转换为SQL。
l Native SQL检索方式:本地(原生)SQL检索,使用本地数据库的SQL查询语句来检索对象。
l QBC检索方式:使用完全面向对象的QBC(Query By Criteria)的API来检索对象,该API底层封装了查询语句。
其中,前两种属于快捷检索方式,比较简单且常用,当这两种检索方式不能满足需要的时候,就需要使用后面几种检索方式,来自定义检索,如复杂检索条件等等。
1.1. 对象导航检索方式
什么是对象导航检索?
两个PO对象之间有关联,当应用程序已经拿到其中一个对象时,可以通过访问其关联到另外一个对象的属性,来隐式的获取到另外的关联对象数据。
如:Customer和Order对象的对象导航检索:
【示例】
1).查询某客户信息,并且打印其下订单的数量;
2).查询某订单的信息,并打印其所属客户的信息。
//1).查询某客户信息,并且打印其下订单的数量;
//2).查询某订单的信息,并打印其所属客户的信息。
@Test
public void testNavigate(){
Session session = HibernateUtils.openSession();
session.beginTransaction();
//1).查询某客户信息,并且打印其下订单的数量;
// Customer customer=(Customer)session.get(Customer.class, 1);
// //导航查询下属订单:(hibernate自动发出查询语句,不需要手动发出查询)
// Set<Order> orders = customer.getOrders();
// //打印订单数量
// System.out.println(orders.size());
//2).查询某订单的信息,并打印其所属客户的信息。
Order order =(Order)session.get(Order.class, 1);
//通过导航方式获取所属的客户.不需要手动发出查询动作
Customer customer1=(Customer)order.getCustomer();
System.out.println(customer1);
session.getTransaction().commit();//flush
session.close();
}
【注意】
导航检索必须是持久态对象,否则不能导航!
【导航检索的概念扩展】
导航检索就是在查询出某个的PO对象(持久态)后,再访问其关联的集合对象属性的时候,会自动发出SQL,来填充关联属性的所引用的对象。
如:查询客户后,再访问其订单属性的时候,Hibernate会自动发出查询订单的语句,并自动填充订单的值。
其他检索方式基本操作
OID检索方式:session.get(entity.class,id),session.load(entity.class,id)
HQL检索方式:session.createQuery(hql).list(),uniqu…
SQL检索方式(Native Query):session.createSQLQuery(sql).list(),
QBC检索方式:(Query by Criteria):session.createCriteria(Entity.class).list()
【三种方式的选择】
其中HQL和QBC是Hibernate推荐的检索方式,但性能上会有折扣,而SQL的检索方式虽然效率很高,但不是面向对象的,开发上麻烦一些。
基础查询
【示例】
查询出所有客户信息。
@Test
//基础查询:查询出所有客户的信息
public void testBaseQuery(){
Session session = HibernateUtils.openSession();
session.beginTransaction();
//hql:hql语句可以控制的,但具体的sql是自动生成。
List<Customer> list1 = session.createQuery("from Customer").list();
System.out.println(list1);
//sql:自定义的sql语句,你写的啥语句,就发什么语句。
// List<Object[]> list2=session.createSQLQuery("select * from t_customer").list();
List<Customer> list2=session.createSQLQuery("select * from t_customer").addEntity(Customer.class).list();
System.out.println(list2);
//qbc:hibernate底层自动拼接语句,不会sql的人也能用,语句不可自定义控制。
Criteria criteria = session.createCriteria(Customer.class);
List<Customer> list3 = criteria.list();
System.out.println(list3);
session.getTransaction().commit();//flush
session.close();
}
【提示】
1.sql查询的默认结果是List<Object[]>(用数组包装了很多小customer),需要进行实体的绑定SQLQuery提供了addEntity方法。
2.SQL的语句生成方式:
Hql和sql的方式:语句是可控的,可以自定义的
Qbc:语句是完全由hibernate自己生成。
条件查询
HQL和SQL的查询时的条件值可以直接写死,也可以使用匿名参数(占位符?),还可以使用命名参数。
l 匿名参数(?):query.setParameter(索引,参数值)
l 命名参数(:paramname):query.setParameter(命名参数,参数值)
【示例】
查询姓名是Rose的客户,只返回rose的一条记录。
@Test
//条件查询
//查询姓名是Rose的客户,只返回rose的一条记录。
public void queryByCondition(){
Session session = HibernateUtils.openSession();
session.beginTransaction();
//hql
//写死值
Customer customer11=(Customer)session.createQuery("from Customer where name='Rose'").uniqueResult();
System.out.println(customer11);
//匿名占位符?
Customer customer12=(Customer)session.createQuery("from Customer where name=?")
.setParameter(0, "Rose").uniqueResult();
System.out.println(customer12);
//命名占位符 “:任意的名字"
Customer customer13=(Customer)session.createQuery("from Customer where name=:cname1")
.setParameter("cname1", "Rose").uniqueResult();
System.out.println(customer13);
//扩展补充:
//上面的占位符的数据类型,其实是hibernate自动反射过去的
//为了提升性能,可以自己指定类型,不使用自动反射
Customer customer14=(Customer)session.createQuery("from Customer where name=?")
.setString(0, "Rose").uniqueResult();
System.out.println(customer14);
//sql
Customer customer21=(Customer)session.createSQLQuery("select * from t_customer where name='Rose'").addEntity(Customer.class).uniqueResult();
System.out.println(customer21);
//匿名参数
Customer customer22=(Customer)session.createSQLQuery("select * from t_customer where name=?")
.addEntity(Customer.class).setParameter(0, "Rose").uniqueResult();
System.out.println(customer22);
//命名参数
Customer customer23=(Customer)session.createSQLQuery("select * from t_customer where name=:cname")
.addEntity(Customer.class).setString("cname", "Rose").uniqueResult();
System.out.println(customer23);
//qbc:
//没有占位符的说法,语句是不可控,自动生成,语句生成后,就是匿名占位符的
Customer customer31 = (Customer)session.createCriteria(Customer.class)
.add(Restrictions.eq("name", "Rose"))
// .add(criterion)//qbc的优势就是条件任意加,语句不需要考虑
.uniqueResult();
System.out.println(customer31);
session.getTransaction().commit();
session.close();
}
【HQL和QBC支持的各种运算和对应关系】:
排序查询
【示例】
按照名字对客户信息进行排序。
//排序查询
@Test
//按照名字对客户信息进行排序。(倒序)
public void testQueryByOrder(){
Session session = HibernateUtils.openSession();
session.beginTransaction();
//hql:
List<Customer> list1= session.createQuery("from Customer order by name desc").list();
System.out.println(list1);
//sql:
List<Customer> list2= session.createSQLQuery("select * from t_customer order by name desc")
.addEntity(Customer.class).list();
System.out.println(list2);
//qbc:
Criteria criteria = session.createCriteria(Customer.class);
//排序:倒序
criteria.addOrder(Order.desc("name"));
List<Customer> list3 = criteria.list();
System.out.println(list3);
session.getTransaction().commit();//flush
session.close();
}
分页查询
【示例】
将订单进行分页查询,每页10条记录,现在需要显示第二页的数据。
@Test
//分页查询
//将订单进行分页查询,每页10条记录,现在需要显示第二页的数据。
public void testQueryByPage(){
Session session = HibernateUtils.openSession();
session.beginTransaction();
//分页的逻辑分析和计算方式
//相当于前台传递过来的分页数据
int pageSize=10;//每页10条
int page=2;//第二页
//计算:
//起始索引
int firstResult=(page-1)*pageSize;
int maxResults=pageSize;
//hql:
//setFirstResult(firstResult)设置起始索引
//.setMaxResults(maxResults):设置最大查询的数量
List<Order> list1 = session.createQuery("from Order")
.setFirstResult(firstResult)
.setMaxResults(maxResults)//最大条数
.list();
System.out.println(list1);
//sql:limit 起始的索引,显示的最大条数
List<Order> list21= session.createSQLQuery("select * from t_order limit "+firstResult+","+maxResults)
.addEntity(Order.class).list();
System.out.println(list21);
List<Order> list22= session.createSQLQuery("select * from t_order limit ?,?")
.addEntity(Order.class)
.setParameter(0, firstResult).setParameter(1, maxResults)
.list();
System.out.println(list22);
//qbc:
Criteria criteria = session.createCriteria(Order.class);
List<Order> list3 = criteria.setFirstResult(firstResult)
.setMaxResults(maxResults)
.list();
System.out.println(list3);
session.getTransaction().commit();//flush
session.close();
}
【扩展oracle的sql语句的写法】
//oracle:写的技巧:先在sql编辑器中写好,再复制进来改一改就行了。
List<Order> list2 = session.createSQLQuery("SELECT * FROM (SELECT t.*,ROWNUM r FROM t_order t WHERE ROWNUM<="+(firstResult+maxResults)+") t2 WHERE t2.r>="+(firstResult+1)).addEntity(Order.class).list();
System.out.println(list2);
投影查询
什么是投影查询?
投影查询就是查询结果仅包含实体的部分属性,即只查询表中的部分指定字段的值,不全部查询。如:
select t.a,t.b,t.c from t;或者select count(*) from table; (是一种特殊的投影查询)
投影的实现:
l HQL和SQL中可以通过SELECT关键字实现。
l QBC中,需要通过criteria.setProjection(投影列表)方法实现,投影列表通过add方法添加:Projections.projectionList().add(Property.forName("id"))。
【示例】
查询用户的id和姓名。
@Test
//投影查询
//查询用户的id和姓名。
public void testQueryByProjection(){
Session session = HibernateUtils.openSession();
session.beginTransaction();
//hql:
//结果集根据结果,自动封装为Object[],不会自动封装回实体类
// List<Object[]> list1 = session.createQuery("select id,name from Customer").list();
//对象别名的
// List<Object[]> list1 = session.createQuery("select c.id,c.name from Customer as c").list();
// System.out.println(list1);
// for (Object[] objects : list1) {
// System.out.println("用户的编号:"+objects[0]+",用户的姓名:"+objects[1]);
// }
// //使用别名查询所有数据
// List<Customer> listall = session.createQuery("select c from Customer c").list();
// System.out.println(listall);
//sql:
// session.createSQLQuery("select id,name from t_customer ")
// session.createSQLQuery("select c.id,c.name from t_customer as c")
//hibernate会根据结果集的类型,自动封装(能往实体封就封,不能就封成object[]),无法addentity
List<Object[]> list2 = session.createSQLQuery("select c.id,c.name from t_customer c")
// .addEntity(Customer.class)
.list();
System.out.println(list2);
//qbc:
Criteria criteria = session.createCriteria(Customer.class);
//加上投影:结果集不会自动封装为实体,会根据结果的值自动封装为object数组
//Projections.projectionList()投影列表,目标:在投影列表中加入投影(属性对象)
//Property.forName("id"),引包Criterion包,使用了反射机制,将id字符串转换成id的属性,类似与Class.forName("驱动字符串")
List<Object[]> list3 = criteria.setProjection(Projections.projectionList().add(Property.forName("id"))
.add(Property.forName("name"))
).list();
System.out.println(list3);
session.getTransaction().commit();//flush
session.close();
}
【注意】
经过投影查询的结果,默认都不会封装到实体类型中,而是根据实际查询的结果自动封装(object[]),如查询id和name,返回的object[]的list集合。
最大的坏处:一级缓存不放该对象。无法使用hibernate的一些特性,比如快照等等。
【应用提示】
实际hibernate开发中,一般较少使用投影查询(除了统计).一般我们都查询出所有字段,让其自动封装到实体类中就行了.
投影查询也可以封装到实体类中。
//查询用户的id和姓名。
@Test
//投影查询:只查询部分属性的值
public void queryByProjection(){
Session session = HibernateUtils.openSession();
session.beginTransaction();
//hql
//结果集是根据返回的数据,自动封装为Object[],没有封装为实体对象
// List<Object[]> list = session.createQuery("select id,name from Customer").list();
//如果要封装为实体对象,需要提供一个投影属性的构造方法,不会再调用默认的构造器
//尽管被封装为实体对象,但该对象,是个非受管对象。不是被session管理
// List list = session.createQuery("select new Customer(id,name) from Customer").list();
// System.out.println(list);
// for (Object[] obj : list) {
// System.out.println(obj[1]);
// }
//sql
//结果集也是根据返回的数据的结果自动封装为Object[]
List list2 = session.createSQLQuery("select id,name from t_customer")
//设置结果集封装策略
//类似于dbutil中的beanhandler,自动通过反射机制,自动将结果集封装到指定的类型中
// .setResultTransformer(new AliasToBeanResultTransformer(Customer.class))
//官方提供了一个工具类,简化代码编写
.setResultTransformer(Transformers.aliasToBean(Customer.class))
.list();
// ResultTransformer
System.out.println(list2);
//qbc
List list3 = session.createCriteria(Customer.class)
//设置投影列表
.setProjection(Projections.projectionList()
//给属性起别名
.add(Property.forName("id").as("id"))
.add(Property.forName("name").as("name")))
//添加结果集的封装策略
//发现了,该结果集封装策略,是根据字段的别名来自动封装
//解决方案:增加别名
.setResultTransformer(Transformers.aliasToBean(Customer.class))
.list();
// Projection
// Property
System.out.println(list3);
session.getTransaction().commit();
session.close();
}
小结:hibernate开发的情况下,一般,不使用投影,查询所有字段,因为查询出来的对象被hibernate管理,它是是一个受管对象。但如果使用投影,则将不是一个受管对象,无法使用到hibernate的一些特性,比如快照更新等。
如果字段确实非常多,为了提升性能,可以使用投影查询部分字段,(仅仅查询用)。
还有一种情况,必须使用投影!统计的时候!