JAVA框架——Hibernate(四)——查询语句的学习,查询优化,类级别加载,关联级别加载

一、 Hibernate的检索方式

实际开发中,对数据进行最多的就是查询操作。数据查询在所有的ORM框架中占有重要的作用。
Hibernate的检索方式有5中,分别为对象图检索方式,OID检索方式,HQL检索方式、QBC检索方式和SQL检索方式

二、 对象图检索方式

根据已经加载的对象,导航到他的关联对象。其利用类与类之间的关系来检索对象。
如:查找一个联系人对应的客户。可以由联系人对象自动导航找到联系人所属的客户对象。

		Session session = HibernateUtils.getCurrentSession();
		LinkMan linkMan =session.get(LinkMan.class, 1l);
		Customer customer =linkMan.getCustomer();

三、 OID检索方式

使用session的get()和load()方法加载某条记录对应的对象。如根据客户的主键获取客户对象

	Session session = HibernateUtils.getCurrentSession();
	Customer customer = session.get(Customer.class, 1l);
	Customer customer = session.load(Customer.class, 1l);

四、 HQL单表查询

Hibernate自带的查询语言。与SQL类似

1. 基本检索

	String HQL1="from com.aiit.domain.Customer";
//完整写法
		String HQL2="from Customer";				//简单写法
		String HQL3="from java.lang.Object";//查询所有属于
		Query query = session.createQuery(HQL2);
		List<Customer> list = query.list();
		
		for (Customer customer : list) {
			System.out.println(customer);
		}

2. 排序检索

	String HQL1="from com.aiit.domain.Customer  order by cust_id desc";//根据Id倒序排列
	String HQL2="from Customer  order by cust_id asc";				//简单写法根据id升序
	Query query = session.createQuery(HQL2);
	List<Customer> list = query.list();
	
	for (Customer customer : list) {
		System.out.println(customer);
	}

3. 条件检索

		//根据位置绑定参数	
		String HQL1="from Customer  where cust_name =?";			
		Query query1 = session.createQuery(HQL1);
		//query.setString(0, "虞俊文"); 该方法过期也有setInt()等
		//根据下标索引进行设置
		query1.setParameter(0, "虞俊文");
		List<Customer> list1 = query1.list();

		
		//根据名称绑定参数
		String HQL2="from Customer  where cust_name=:name";
		Query query2 = session.createQuery(HQL2);
		query2.setParameter("name", "宋进锋");
		Customer customer = (Customer) query2.uniqueResult();

4. 分页检索

		//根据位置绑定参数	
		String HQL="from Customer";			
		Query query = session.createQuery(HQL);
				//分页页码:(当前页面-1)* 每页条数
			   query.setFirstResult(1);//从第一条开始查
			   query.setMaxResults(2);//每页最大2条数据
				//根据下标索引进行设置
				List<Customer> list1 = query.list();

5. 统计检索

//根据位置绑定参数	
		String HQL="select count(*) from Customer";	
		String HQL1="select sum(cust_id) from Customer";			
		String HQL2="select avg(cust_id) from Customer";			
		String HQL3="select max(cust_id) from Customer";			
		Query query = session.createQuery(HQL);		
				Number num = (Long)query.uniqueResult();
				System.out.println(num);

6. 投影检索

			//查询一列	
			String HQL="select cust_name from Customer";	
			//查询多列
			String HQL2="select cust_id,cust_name, from Customer";
			String HQL3="select new Customer(cust_id,cust_name) from Customer";
			//此时我们需要在构造函数写书写一个构造
					Query query = session.createQuery(HQL2);
					List<Object[]> list = query.list();

五、 SQL的多表查询

1.交叉连接(避免)

返回的结果是被连接的两个表中所有数据行的笛卡尔积。如:

SELECT * FROM1 CROSS JOIN2
SELECT * FROM1,表2

其结果就是两个表所有数据的组合。如表1中有4个属性,表2中有3个属性,那么一共查出4*3=12个属性。在实际开发中少见。

2.内连接(分为隐式和显示)

使用比较运算符对两个表中的数据进行比较,列出与连接条件匹配的数据行,组成新记录。

SELECT 查询字段 FROM1 [INNER] JOIN2 ON1.关系字段=2.关系字段
  • 隐式内连接

看不到inner join的关键字。用where来代替。在交叉连接基础上的升级(多使用了where)

SELECT * FROM1.2 WHERE1.关系字段 =2.关系字段
  • 显示内连接

显示内连接:明显调用了inner join的关键字

SELECT * FROM1 INNER JOIN2 ON1.关系字段 =2.关系字段
SELECT * FROM1 JOIN2 ON1.关系字段 =2.关系字段

3.外连接(分为左连接和右连接)

  • 左连接:返回包括左表中的所有记录和右表中符合连接条件的记录
SELECT * FROM1 LEFT OUTER JOIN2 ON1.关系字段 =2.关系字段
  • 右连接:返回包括右表中的所有记录和左表中符合连接条件的记录
SELECT * FROM1 RIGHT OUTER JOIN2 ON1.关系字段 =2.关系字段

4.图例

在这里插入图片描述

  • 内连接:红色部分
  • 左外连接:灰色+红色部分
  • 右外连接:红色+蓝色部分

由此引出HQL的多表查询

六、 HQL的多表查询

HQL会比原来SQL分类的基础上多出一些。其分类有以下几种:

1. 内连接

  • 普通内连接
String hql="from Customer  c  inner join c.linkMen";
//此举类似于SELECT * FROM cst_customer c INNER JOIN cst_linkman l ON c.cust_id =l.link_cust_id
		Query query = session.createQuery(hql);
		List<Object[]> list = query.list();
		for (Object[] objects : list) {
			System.out.println(Arrays.toString(objects));
		}

此时查询出来的语句应该是客户人和联系人之间有联系的所有数据,客户但没有联系人或者有联系人但没有客户的数据,均不会被查询出来

  • 迫切内连接
String hql="from Customer  c  inner join fetch c.linkMen";
		Query query = session.createQuery(hql);
		List<Customer> list = query.list();
		System.out.println(list);

其与普通连接效果类似。但是迫切内连接是讲我们的外键数据,封装到一个对象中进行输出。而普通外连接是将连接的两端对象分别返回,放到一个数组中

2. 外连接

  • 左外连接
	
			String hql="from Customer  c  left join c.linkMen";
			//c表示别名
			Query query = session.createQuery(hql);
			List<Object[]> list = query.list();
			for (Object[] objects : list) {
				System.out.println(Arrays.toString(objects));
			}		

该查询语句表示,客户中有联系人的客户也通通打印出联系人。客户没有联系人,都打出联系人为null

  • 右外连接
			String hql="from Customer  c  right join c.linkMen";	
				Query query = session.createQuery(hql);
				List<Object[]> list = query.list();
				for (Object[] objects : list) {
					System.out.println(Arrays.toString(objects));
				}

该语句表示我们打印出所有的联系人,并且有客户的联系人,打印出客户,但是没有客户的联系人,客户设置为null

  • 左,右外迫切连接
    同样的与内连接的效果类似,把查出来的语句自动封装成一个对象。并且 在语句后面加上fetch即可

七、 Criteria的单表查询

Criteria也是HibernateAPI中的一个查询接口,它需要由session进行创建。

1. 基本检索

Criteria criteria = session.createCriteria(Customer.class);
List<Customer> list = criteria.list();
System.out.println(list);

2. 条件查询

使用add添加条件,其中括号里面的参数根据参数表选择。具体可以查询API或者函数

Criteria criteria =session.createCriteria(Customer.class);
//设置条件
			//查询id为1的查询
			criteria.add(Restrictions.idEq(2l));
			
			criteria.add(Restrictions.like("cust_name", "%小%"));
			List<Customer> list = criteria.list();
			System.out.println(list);
		}

3. 分页检索

Criteria criteria = session.createCriteria(Customer.class);
	criteria.setFirstResult(2);
	criteria.setMaxResults(7);
	List<Customer> list = criteria.list();
	for (Customer customer : list) {
		System.out.println(customer);
	}

4. 排序检索

Criteria criteria = session.createCriteria(Customer.class);
		criteria.addOrder(Order.desc("cust_id"));
		criteria.addOrder(Order.asc("cust_id"));
		List<Customer> list = criteria.list();
		for (Customer customer : list) {
			System.out.println(customer);
			}

5. 统计检索

Criteria criteria = session.createCriteria(Customer.class);
		criteria.setProjection(Projections.rowCount());
		Long count = (Long) criteria.uniqueResult();
	List<Customer> list = criteria.list();
			for (Customer customer : list) {
			System.out.println(customer);
	}

6. 离线条件检索

传统Criteria对象,他的创建依赖于session。在做一些特别复杂的条件查询的时候,往往会在WEB层向Service层传输很多参数,Service再将数据数据出纳递给Dao层。最后再Dao层中拼接SQL完成查询。有了离线条件查询对象,可以直接再WEB层进行封装完毕,传递到Service层。再由Service层传递给Dao层完成查询

@Test
	public void fun1() {
		//WEB/Service层
		//---------------------------------------------
		DetachedCriteria dc =DetachedCriteria.forClass(Customer.class);
		dc.add(Restrictions.idEq(4l));//拼接条件(全部与普通Criteria一致)
		//------------------------------------------
		
		Session session = HibernateUtils.openSession();
		Transaction tx = session.beginTransaction();
		Criteria criteria = dc.getExecutableCriteria(session);
		List list = criteria.list();
		System.out.println(list);
		
		
		tx.commit();
		session.close();
	}

八、 查询优化——类级别加载策略

  • 所谓的延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作
  • 通常延迟加载分为两类,一类叫做类级别延迟,另一类叫做关联级别的延迟
  • 类级别延迟指的是当查询某个对象的时候,是否采用延迟。其通常在或上配置lay属性
  • 我们再上述内容中,我们调用session.get()方法时,同时可以使用session.load()方法。其中load()方法可以进行慢加载。
//快加载
		@Test
		public void fun1() {
			Session session = HibernateUtils.openSession();
			Transaction tx = session.beginTransaction();
			Customer customer = session.get(Customer.class, 1l);
			System.out.println(customer);					
			tx.commit();
			session.close();
		}
		//慢加载
				@Test
				public void fun2() {
					Session session = HibernateUtils.openSession();
					Transaction tx = session.beginTransaction();
					Customer customer = session.load(Customer.class, 1l);
					System.out.println(customer);					
					tx.commit();
					session.close();
				}

同样,我们也可以对load进行修改。可以再orm元数据中去修改该配置

<class name="Customer" table="cst_customer" lazy="true">
		<id name="cust_id" >
			<generator class="native"></generator>
		</id>

总结:

  • get方法没有任何策略,调用即立即查询数据库加载数据
  • load方法:应用类级别的加载策略。我们可以在类的orm元数据中设置lazy属性,即会返回代理对象,会在使用属性时,根据关联的session查询数据库,加载数据。
  • 为了提交效率。建议使用延迟加载
  • 使用懒加载时要确保调用属性加载数据时,session还是打开的,否则抛出异常

九、查询优化——关联级别的加载

关联级别的延迟加载指的是查询到某个对象以后,检索它的关联对象的时候是否采取延迟加载

通过客户查询到其他象关的联系人对象,在查询联系人的时候是否采用延迟加载称为关联级别的延迟。关联级别的延迟通常是<set><many-to-one>上来进行配置。

  • <set>标签上的lazy通常有三个取值
    • true:默认值,采用延迟加载
    • false:检索关联对象的时候,不采用延迟加载
    • extra:及其懒惰的
  • <many-to-one>标签上的lazy通常有三个取值
    • proxy:默认值。是否采用延迟取决于一的一方类上的lazy属性的值。
    • false:检索关联对象的时候,不采用延迟加载

抓取策略是指查询到某个对象的时候,如何抓取其关联对象-。这个也可以通过配置完成。在关联对象的标签上设置fetch属性。关联上就分为是在和上,也都有不同的取值。

  • <set>标签上的fetch通常有三个取值
    • select(默认值):单表查询加载
    • join:多表查询加载集合。发送一条迫切左外连接查询
    • subselect:发送一条子查询语句查询其关联对象
  • <many-to-one>标签上的fetch有两个取值
    • select :默认值,发送一条普通的select语句查询关联对象
    • join:发送一条迫切左外连接语句查询其关联对象
 <!-- 
	 		lazy属性:决定是否延迟加载
	 				true(默认值):延迟加载
	 				false:立即加载
	 				extra:及其懒惰
	 		fetch属性:决定加载抓取策略,使用什么类型SQL语句加载集合数据
	 				select(默认值):单表查询加载
	 				join:多表查询加载集合
	 				subselect:shi用子查询加载集合
	  -->
	<set name="linkMen" inverse="true" lazy="true" fetch="select">
		<key column="lkm_cust_id"></key>

		<one-to-many class="LinkMan"/>
	</set>

通常set集合属性设置lazy fetch属性。搭配配置。如:fetch属性设置为join。而lazy属性不论设置任何属性(true,false,extra)均失效。均为立即加载

 <!-- 
	 	fetch:决定加载的sql语句
	 		select:使用单表查询
	 		join:多表查询
	 	lazy决定加载实际
	 		false:立即加载
	 		proxy:由customer的类级别加载策略决定
	  -->
	<many-to-one name="customer" column="lkm_cust_id" class="Customer" fetch="select" lazy="proxy"></many-to-one>

结论:

  • 为了提高效率,fetch的选择应该选择select,lazy的选择应该选择true

  • no-session问题解决:扩大session的作用范围。默认session默认是service层调用。我们可以扩展到扩大到三层架构。我们使用Filter,进行扩大session调用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值