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的一些特性,比如快照更新等。

如果字段确实非常多,为了提升性能,可以使用投影查询部分字段,(仅仅查询用)。

还有一种情况,必须使用投影!统计的时候!