• Hibernate是DAO层技术,对数据的使用,查询是最为重要的。Hibernate的查询技术非常强大,支持原始SQL语句查询,支持QBC查询以及Hibernate特有的HQL查询。
  • HQL,Hibernate Query Language,Hibernate查询语言,它和SQL非常相似,但是,HQL是面向对象的查询语言,而SQL是面向二维表的。HQL查询语句中使用的是类名和属性名,而SQL语句使用的是表名和字段名。
  • 需要注意的是,在不使用类的别名的情况下,在HQL中直接使用字段名也是可以通过的。但是若使用“类别名.属性名"的形式,则不能够使用字段名。
  • QBC,Query By Criteria,标准查询,一种比HQL更为面向对象的查询方法。

1 API

1.1 Query接口

  • Hibernate进行HQL查询的接口,支持动态绑定参数的功能。使用Session对象的createQuery方法可获取Query对象。
  • Query query = session.createQuery(hql);。

1.2 SQLQuery接口

  • Hibernate进行SQL原生查询的接口,支持动态绑定参数的功能,是Query接口的子接口。使用Session对象的createSQLQuery()方法可获取SQLQuery对象。
  • SQLQuery sqlQuery = session.createSQLQuery(sql);。
  • 其查询出的结果对象默认为Object,当然,若结果为List,则其元素为Object。使用SQLQuery的addEntity(Xxx.class)方法,可以将其结果泛型设定为指定类型。

1.3 Criteria接口

  • Criteria标准、准则,Hibernate进行Criteria查询的接口,与Query接口无关。使用Session对象的createCriteria()方法可获取Criteria对象。
  • Criteria criteria = session.createCriteria(Xxx.class);。

2 分类查询

2.1 查询测试前的准备工作

2.1.1 定义查询的实体

  • 下面查询举例,均是对实体类Student进行操作,其映射关系如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.eason.hibernate.po">
	<class name="Student" table="t_student">
		<id name="id" column="sid">
			<generator class="native"></generator>
		</id>
		<property name="name" column="sname"></property>
		<property name="age" column="sage"></property>
		<property name="score" column="sscore"></property>
	</class>
</hibernate-mapping>

2.1.2 定义主配置文件

<session-factory>
		<!-- 连接四要素 -->
		<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
		<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/test</property>
		<property name="hibernate.connection.username">root</property>
		<property name="hibernate.connection.password">02000059</property>
		
		<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
		<property name="hibernate.current_session_context_class">thread</property>
		<property name="hibernate.hbm2ddl.auto">update</property>
		<property name="hibernate.show_sql">true</property>
		<property name="hibernate.format_sql">true</property>	
		<mapping resource="com/eason/hibernate/po/student.hbm.xml"/>
	</session-factory>

2.1.3 定义Hibernate工具类

	public class HbnUtils {
		private static SessionFactory sessionFactory;
		public static Session getSession() {
			return getSessionFactory().getCurrentSession();
		}

		private static SessionFactory getSessionFactory() {
			if(sessionFactory == null || sessionFactory.isClosed()) {
				sessionFactory = new Configuration().configure().buildSessionFactory();
			}
			return sessionFactory;
		}
	}

2.1.4 定义测试类的setUp()

  • 创建好测试类MyTest后,完成初始化方法setUp():
public class MyTest {
	private Session session;
	@Before
	public void setUp() {
		session = HbnUtils.getSession();
	}
}

2.1.5 准备测试数据

	//完成数据插入
	@Test
	public void test00() {
		try {
			session.beginTransaction();
			for(int i = 0; i < 10; i ++) {
				Student student = new Student("n_" + i, 15 +i , 75 + i);
				session.save(student);
			}
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.2 查询所有

  • 查询所有的特点是,其查询结果为List集合。

2.2.1 SQL查询

//使用SQLQuery查询所有
	@Test
	public void test02_SQL() {
		try {
			session.beginTransaction();
			String sql = "select * from t_student";
			List<Student> students = session.createSQLQuery(sql).addEntity(Student.class).list();
			for(Student student : students) {
				System.out.println(student);
			}
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.2.2 HQL查询

//使用HQL查询所有
	@Test
	public void test02_HQL() {
		try {
			session.beginTransaction();
			String hql = "from Student";
			List<Student> students = session.createQuery(hql).list();
			for(Student student : students) {
				System.out.println(student);
			}
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.2.3 QBC查询

//使用QBC查询所有
	@Test
	public void test02_QBC() {
		try {
			session.beginTransaction();
			List<Student> students = session.createCriteria(Student.class).list();
			for(Student student : students) {
				System.out.println(student);
			}
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.3 查询结果排序

  • 排序,即order by,asc表示升序,desc表示降序。

2.3.1 SQL查询

	//使用SQLQuery查询所有,并排序
	@Test
	public void test03_SQL() {
		try {
			session.beginTransaction();
			String sql = "select * from t_student order by sscore desc";
			
			List<Student> students = session.createSQLQuery(sql).addEntity(Student.class).list();
			for(Student student : students) {
				System.out.println(student);
			}
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.3.2 HQL查询

//使用HQL查询所有,并排序
	@Test
	public void test03_HQL() {
		try {
			session.beginTransaction();
			String hql = "from Student order by score desc";
			
			List<Student> students = session.createQuery(hql).list();
			for(Student student : students) {
				System.out.println(student);
			}
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.3.3 QBC查询

  • 通过addOrder()方法可以向Criteria对象添加排序功能。Order类有两个静态方法desc()和asc(),分别用于降序和升序功能,它们的参数为排序属性名,注意非字段名。
	//使用QBC查询所有,并排序
	@Test
	public void test03_QBC() {
		try {
			session.beginTransaction();
			List<Student> students = session.createCriteria(Student.class).addOrder(Order.desc("score")).list();
			for(Student student : students) {
				System.out.println(student);
			}
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.4 动态参数绑定查询

  • 动态参数绑定查询,即参数的值是由代码传递来的,而非固定在查询中。

2.4.1 SQL查询

  • 方式一:使用”?"占位符占位,setXxx()方法绑定参数:setXxx(int position, Object value)。方法名中的Xxx表示该位置参数的类型。position表示第几个占位符,从0开始计数。
		//使用SQLQuery查询年龄大于20岁,成绩小于98分的学生
		@Test
		public void test04_SQL_setXxx() {
			try {
				session.beginTransaction();
				String sql = "select * from t_student where sage > ? and sscore < ?";
				List<Student> students = session.createSQLQuery(sql)
						.addEntity(Student.class).setInteger(0, 20).setDouble(1, 98).list();
				for(Student student : students) {
					System.out.println(student);
				}
				session.getTransaction().commit();
			} catch (Exception e) {
				e.printStackTrace();
				session.getTransaction().rollback();
			}
		}
  • 方式二:使用别名占位,setXxx()绑定参数:setXxx (String name, Object value)。别名的命名规则为:冒号后跟别名,别名可以是任意名称。
//使用SQLQuery查询年龄大于20岁,成绩小于98分的学生
	@Test
	public void test04_SQL_setXxx2() {
		try {
			session.beginTransaction();
			String sql = "select * from t_student where sage > :tage and sscore < :tscore";
			List<Student> students = session.createSQLQuery(sql)
					.addEntity(Student.class).setInteger("tage", 20).setDouble("tscore", 98).list();
			for(Student student : students) {
				System.out.println(student);
			}
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}
  • 方式三:使用“?”占位符占位,setParameter()绑定参数:SetParameter(int position, Object value)。position表示第几个占位符,从0开始计数。
//使用SQLQuery查询年龄大于20岁,成绩小于98分的学生
	@Test
	public void test04_SQL_setParameter() {
		try {
			session.beginTransaction();
			String sql = "select * from t_student where sage > ? and sscore < ?";
			List<Student> students = session.createSQLQuery(sql)
					.addEntity(Student.class).setParameter(0, 20).setParameter(1, 98).list();
			for(Student student : students) {
				System.out.println(student);
			}
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}
  • 方式四:使用别名占位,setParameter()绑定参数:SetParameter(String name, Object value)。别名的命名规则为:冒号后跟别名,别名可以是任意名称。
//使用SQLQuery查询年龄大于20岁,成绩小于98分的学生
	@Test
	public void test04_SQL_setParameter2() {
		try {
			session.beginTransaction();
			String sql = "select * from t_student where sage > :tage and sscore < :tscore";
			List<Student> students = session.createSQLQuery(sql)
					.addEntity(Student.class).setParameter("tage", 20).setParameter("tscore", 98).list();
			for(Student student : students) {
				System.out.println(student);
			}
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.4.2 HQL查询

  • 使用HQL查询时,以上四种为动态参数赋值的方式均可成立,其用法相同。
//使用HQL查询年龄大于20岁,成绩小于98分的学生
	@Test
	public void test04_HQL() {
		try {
			session.beginTransaction();
			String hql = "from Student where sage > :tage and sscore < :tscore";
			List<Student> students = session.createQuery(hql)
					.setParameter("tage", 20).setParameter("tscore", 98).list();
			for(Student student : students) {
				System.out.println(student);
			}
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.4.3 QBC查询

  • 需要向Criteria对象添加限制条件Restrictions,即添加查询条件。(Restriction,中文意思:限制,约束。)Restrictions具有很多静态方法可用于表示关系比较。
  • 注意double类型数据的写法。
//使用QBC查询年龄大于20岁,成绩小于98分的学生
	@Test
	public void test04_QBC() {
		try {
			session.beginTransaction();
			List<Student> students = session.createCriteria(Student.class)
					.add(Restrictions.gt("age", 20))
					 //需要注意,这里不能够写成98
					.add(Restrictions.lt("score", 98.0)).list();
					
			for(Student student : students) {
				System.out.println(student);
			}
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.5 分页查询

  • 分页查询的SQL语句:select * from … where …limit startIndex, pageSize。
  • 其中,startIndex为开始索引,设置从总查询结果集的第几条记录作为本查询结果子集的开始,即本页的开始。总查询结果集从0开始计数。pageSize为该页所包含的记录数。

2.5.1 SQL查询

//使用SQLQuery查询出所有学生中从第4条开始的5名学生信息
	@Test
	public void test05_SQL() {
		try {
			session.beginTransaction();
			String sql = "select * from t_student limit ?, ?";
			List<Student> students = session.createSQLQuery(sql)
					.addEntity(Student.class).setParameter(0, 3).setParameter(1, 5).list();
			for(Student student : students) {
				System.out.println(student);
			}
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.5.2 HQL查询

  • Query接口中具有setFirstResult()方法,用于设置从总查询结果集的第几条记录作为本查询结果子集的开始,即本页的开始。总查询结果集从0开始计数。而setMaxResults()方法用于设置该页所包含的记录数。
//使用HQL查询出所有学生中从第4条开始的5名学生信息
	@Test
	public void test5_HQL() {
		try {
			session.beginTransaction();
			String hql = "from Student";
			List<Student> students = session.createQuery(hql)
					.setFirstResult(3).setMaxResults(5).list();
			for(Student student : students) {
				System.out.println(student);
			}
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.5.3 QBC查询

  • Criteria接口中也有setFirstResult()与setMaxResults()方法,其用法与意义和Query接口中的完全相同。由于Criteria与Query接口无关,所以这两组完全相同的方法也是没有关系的,仅仅是用法和写法上得相同而已。
//使用QBC查询出所有学生中从第4条开始的5名学生信息
	@Test
	public void test05_QBC() {
		try {
			session.beginTransaction();
			List<Student> students = session.createCriteria(Student.class)
					.setFirstResult(3).setMaxResults(5).list();
			for(Student student : students) {
				System.out.println(student);
			}
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.6 模糊查询

2.6.1 SQL查询

//使用SQLQuery查询出所有姓名中包含字符n的学生
	@Test
	public void test06_SQL() {
		try {
			session.beginTransaction();
			String sql = "select * from t_student where sname like :tname";
			List<Student> students = session.createSQLQuery(sql)
					.addEntity(Student.class).setParameter("tname", "%n%")
					.list();
			for(Student student : students) {
				System.out.println(student);
			}
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.6.2 HQL查询

//使用HQL查询出所有姓名中包含字符n的学生
	@Test
	public void test06_HQL() {
		try {
			session.beginTransaction();
			String hql = "from Student where name like :tname";
			List<Student> students = session.createQuery(hql)
					.setParameter("tname", "%n%")
					.list();
			for(Student student : students) {
				System.out.println(student);
			}
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.6.3 QBC查询

//使用QBC查询出所有姓名中包含字符n的学生
	@Test
	public void test06_QBC() {
		try {
			session.beginTransaction();
			List<Student> students = session.createCriteria(Student.class)
					.add(Restrictions.like("name", "%n%")).list();
			for(Student student : students) {
				System.out.println(student);
			}
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.7 唯一性查询

2.7.1 SQL查询

  • SQLQuery接口的uniqueResult()方法表示其查询结果为一条数据,其返回值为Object。
//使用SQLQuery查询id为5的学生
	@Test
	public void test07_SQL() {
		try {
			session.beginTransaction();
			String sql = "select * from t_student where sid = ?";
			Student student = (Student) session.createSQLQuery(sql)
					.addEntity(Student.class).setInteger(0, 5).uniqueResult();
			System.out.println(student);
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.7.2 HQL查询

//使用HQL查询id为5的学生
	@Test
	public void test07_HQL() {
		try {
			session.beginTransaction();
			String hql = "from Student where id = ?";
			Student student = (Student) session.createQuery(hql)
					.setInteger(0, 5).uniqueResult();
			System.out.println(student);
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.7.3 QBC查询

//使用QBC查询id为5的学生
	@Test
	public void test07_QBC() {
		try {
			session.beginTransaction();
			Student student = (Student) session.createCriteria(Student.class)
					.add(Restrictions.eq("id", 5)).uniqueResult();
			System.out.println(student);
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.8 聚合函数查询

2.8.1 SQL查询

  • 注意区分count()与count(name)的不同。count()与count(id)等价,均表示未空记录数,而count(name)则表示字段name的非空记录数。
//查询记录总数以及姓名非空的记录数
	@Test
	public void test08_SQL() {
		try {
			session.beginTransaction();
			String sql1 = "select count(*) from t_student";
			Object total1 = session.createSQLQuery(sql1).uniqueResult();
			System.out.println(total1);
			String sql2 = "select count(sname) from t_student";
			Object total2 = session.createSQLQuery(sql2).uniqueResult();
			System.out.println(total2);
			
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.8.2 HQL查询

//查询记录总数以及姓名非空的记录数
	@Test
	public void test08_HQL() {
		try {
			session.beginTransaction();
			String hql1 = "select count(*) from Student";
			Object total1 = session.createQuery(hql1).uniqueResult();
			System.out.println(total1);
			String hql2 = "select count(sname) from Student";
			Object total2 = session.createQuery(hql2).uniqueResult();
			System.out.println(total2);
			
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.8.3 QBC查询

  • Projection,中文意思:投影。Projection中包含很多聚合函数的静态方法。
//查询记录总数以及姓名非空的记录数
	@Test
	public void test08_QBC() {
		try {
			session.beginTransaction();
			Object total1 = session.createCriteria(Student.class)
					.setProjection(Projections.count("id")).uniqueResult();
			System.out.println(total1);
		
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.9 投影查询

2.9.1 SQL查询

  • 要求查询的字段的别名要与实体类的属性名相同,这样才能将结果转换为相应类的对象。
//使用SQLQuery查询所有学生的姓名与年龄
	@Test
	public void test09_SQL() {
		try {
			session.beginTransaction();
			String sql = "select sname name, sage age from t_student";
			List<Student> students = session.createSQLQuery(sql)
					.setResultTransformer(Transformers.aliasToBean(Student.class)).list();
			for(Student student: students) {
				System.out.println(student);
			}
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}
  • aliasToBean()方法首先会创建一个空的Student对象,然后会将别名和Student属性名对比,再用查询出的值初始化创建的Student对象。

2.9.2 HQL查询

  • 该查询要求实体类中具有相同投影作为参数的带参构造器。所以,首先要在Student类中添加相应的带参构造器。
	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
//使用HQL查询所有学生的姓名与年龄
	@Test
	public void test09_HQL() {
		try {
			session.beginTransaction();
			String hql = "select new Student(name, age) from Student";
			List<Student> students = session.createQuery(hql).list();
			for(Student student: students) {
				System.out.println(student);
			}
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.10 分组查询

2.10.1 SQL查询

  • having,对分组结果进行筛选。下面代码实现的功能是,查询出所有年龄段,以及人数多于1人的年龄段。
	//查询出所有的年龄段,以及人数大于1人的年龄段
	@Test
	public void test10_SQL() {
		try {
			session.beginTransaction();
			String sql = "select sage from t_student group by sage";
			List<Object> result1 = session.createSQLQuery(sql).list();
			for(Object object: result1) {
				System.out.println(object);
			}		
			System.out.println("------------------");
			String sql2 = "select sage from t_student group by sage having count(sage) > ?";
			List<Object> result2 = session.createSQLQuery(sql2).setInteger(0, 1).list();
			for(Object object: result2) {
				System.out.println(object);
			}		
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.9.2 HQL查询

//查询出所有的年龄段,以及人数大于1人的年龄段
	@Test
	public void test10_HQL() {
		try {
			session.beginTransaction();
			String hql1 = "select age from Student group by age";
			List<Object> result1 = session.createQuery(hql1).list();
			for(Object object: result1) {
				System.out.println(object);
			}		
			System.out.println("------------------");
			String hql2 = "select age from Student group by age having count(age) > ?";
			List<Object> result2 = session.createQuery(hql2).setInteger(0, 1).list();
			for(Object object: result2) {
				System.out.println(object);
			}		
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.9.3 QBC查询

  • Hibernate不直接支持having操作。
//查询出所有的年龄段
	@Test
	public void test10_QBC() {
		try {
			session.beginTransaction();
			List<Object> result1 = session.createCriteria(Student.class).
					setProjection(Projections.groupProperty("age")).list();
			for(Object object: result1) {
				System.out.println(object);
			}		
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.11 Query的list()与iterator()

2.11.1 Query的list()查询

//使用Query的list()查询所有
	@Test
	public void test11_HQL_list() {
		try {
			session.beginTransaction();
			String hql = "from Student";
			
			//第一次查询
			List<Student> students1 = session.createQuery(hql).list();
			for(Student student : students1) {
				System.out.println(student);
			}
			//第二次查询
			List<Student> students2 = session.createQuery(hql).list();
			for(Student student : students2) {
				System.out.println(student);
			}
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.10.2 Query的iterate()查询

//使用Query的iterate()查询所有
	@Test
	public void test11_HQL_list_iterate() {
		try {
			session.beginTransaction();
			String hql = "from Student";
			
			//第一次查询
			Iterator<Student> it1 = session.createQuery(hql).iterate();
			while(it1.hasNext()) {
				Student student = it1.next();
				System.out.println(student);
			}
			//第二次查询
			Iterator<Student> it2 = session.createQuery(hql).iterate();
			while(it2.hasNext()) {
				Student student = it2.next();
				System.out.println(student);
			}
			
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.10.3 Query的list()与iterator()的区别

  • 使用Query接口的list()和iterate()进行查询,查看其控制台的SQL语句的输出情况,可以看到它们区别主要有两点: 1、使用list(),会一次性将所有符合条件的记录查询出来,而使用iterate(),则首先会查询所有符合条件的记录的id,然后在根据这些id逐个查询出记录的具体内容。 2、使用list(),不会使用缓存机制,即每次执行一次查询代码,控制台会执行一次SQL查询语句;而使用iterate(),则会使用缓存机制,只有第一次会执行SQL查询,再往后的查询会直接从缓存中读取。

2.10.4 N + 1问题

  • 使用Query的iterate()方法虽然使用了Hibernate的缓存机制,但是同时也出现了N+1问题。
  • 所谓N+1问题是指,从查询出有效数据角度来说,若要查询符合某条件的对象时,使用list(),则一次性即可查询出所有。而若使用iterate(),则会先查询出所有满足条件的对象的id,然后再逐个id进行select查询,即需要经过N+1次才能得出有效结果。这就是N+1问题。

2.10.5 N + 1问题的避免

  • 若想要使用Hibernate缓存,即使用iterate()方法,还想要避免N + 1问题,就要保证在执行iterate()时,缓存中已经有数据,这样既可利用缓存,有可以避免N+1问题。所以,可以在第一次查询时使用list(),而以后的查询则使用iterate()。
//N + 1问题的避免
	@Test
	public void test11_HQL_list_iterate() {
		try {
			session.beginTransaction();
			String hql = "from Student";
			
			//第一次查询
			List<Student> students = session.createQuery(hql).list();
			for(Student student : students) {
				System.out.println(student);
			}
			//第二次查询
			Iterator<Student> it2 = session.createQuery(hql).iterate();
			while(it2.hasNext()) {
				Student student = it2.next();
				System.out.println(student);
			}
			
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}

2.12 命名查询namedQuery

  • 所谓namedQuery,是指HQL语句不直接写在Java代码中,而写入到映射文件。Java代码从映射文件中将被命名的HQL,通过名称读出执行。
  • 将HQL写入到配置文件中的好处是,在项目真正上线后,若只需要修改HQL就可进行检索优化,则需修改配置文件中的HQL后,重启服务器即可,无需再次编译项目。
  • 在映射文件中通过<query/>通过命名定义HQL,该标签写到哪个映射文件均可。
<query name="queryById">from Student where id = ?</query>
  • java代码中通过Sesion的getNamedQuery()方法可以将映射文件中指定的HQL读取出来。
//命名查询
	@Test
	public void test12() {
		try {
			session.beginTransaction();
			Student student = (Student) session.getNamedQuery("queryById").setInteger(0, 5).uniqueResult();
			System.out.println(student);
			
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}
	}