Hibernate:HQL查询、条件查询、SQL查询

1,HQL查询

Hibernate 支持两种主要的查询方式:

  • Criteria查询,又称为“对象查询”,它用面向对象的方式将构造查询的过程做了封装映射文件。
  • HQL(Hibernate Query Languge,Hibernate 查询语言)查询,是一种面向对象的查询语言,其中没有表和字段的概念,只有类、对象和属性的概念。

1.1,Query接口

HQL是Hibernate Query Language的缩写,HQL的语法与SQL相似,但HQL是一种面向对象的查询语言SQL的操作对象是数据表、列等数据库对象,而HQL操作对象是类、对象、属性等。

HQL是完全面向对象的查询语言,支持继承和多态等特性。

HQL语句本身是不区分大小写的,也就是说HQL语句的关键字、函数都是不区分大小写的。但HQL语句中所有使用的包名、类名、实例名、属性名都区分大小写。

HQL查询依赖于Query类,每个Query对象对应一个查询对象。查询步骤如下:

  • 获取Hibernate Session对象。
  • 编写HQL查询语句。
  • 以HQL语句为参数,调用session的createQuery方法,创建查询对象Query。
  • 如果HQL语句中带有参数,则调用Query的setXxx方法,对参数赋值。
  • 调用Query的list, unique Result等方法,查询获得结果。

Query 接口用来执行 HQL,Query 接口实例可以从 Session 对象 session 中生成:session.createQuery(String hql)。

Query 的主要方法:

  • setXXX()方法:用于设置HQL中的问号或变量的值
  • list()方法:返回查询结果,并把查询结果转变为 List对象
  • excuteUpdate()方法:执行更新或删除语句
  • iterator()方法:也用于执行查询语句,返回的结果是一个Iterator对象,在读取时只能按照顺序方式读取。
  • uniqueResult()方法:用于返回唯一的结果,在确保最多只有一个记录满足查询条件的情况下可以使用该方法。
  • setFirstResult()方法:可以设置所获取的第一个记录的位置,从0开始计算,用于筛选选取记录的范围。
  • setMaxResults()方法:设置结果集的最大记录数,可以与setFirstResult()方法结合使用,限制结果集的范围,在实现分页功能时非常有用。

1.2,from子句

from是最简单的HQL语句,也是最基本的HQL语句。from关键字后紧跟持久化类的类名。例如:

from Person
– 表明从Person 持久化类中选出全部的实例。
from Person as p | from Person p
–	为实体类指定别名
Configuration conf = new Configuration().configure();
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().configure().build();
SessionFactory sf = conf.buildSessionFactory(serviceRegistry);
Session sess = sf.openSession();
Transaction tx = sess.beginTransaction();
Query q = sess.createQuery("from person");
List<person> personList = (List<person>)q.list();
for(person p : personList) {
    System.out.println(p.getName());		
}
tx.commit();
sess.close();
sf.close();

 from后还可以同时出现多个持久化类,此时将产生一个笛卡尔积或跨表的连接。但实际上这种用法很少使用,因为通常需要使用跨表连接时,可以考虑使用隐式连接或者显式连接,而不是直接在from后紧跟多个表名。

1.3,关联和连接

当程序需要从多个数据表中取得数据时,SQL语句将会考虑多表连接查询。Hibernate使用关联映射来处理底层数据表之间的连接,一旦提供了正确的关联映射后,当程序通过Hibernate进行持久化访问时,将可利用Hibernate关联来进行连接。

HQL支持两种关联连接形式:显式和隐式。

隐式连接不需要使用join关键字,使用英文点号(.)来隐式连接关联实体,而Hibernate底层将自动进行关联查询。

from MyEvent e where e.person.name='张三'

使用显示连接可以为相关联的实体,甚至是关联集合中的全部元素指定一个别名。

  • inner join内连接,可简写为join
  • left outer join左外连接
  • right outer join右外连接
  • full join全连接
String hql="select p from person p inner join p.myevent e where e.title like'%上课%'";

隐式连接和显式连接的区别:

  • 隐式连接底层将转换成SQL99的交叉连接,显式连接将转换成SQL99的inner join、left join、right join等连接。
  • 隐式连接和显式连接查询后返回的结果不同。当HQL省略关键字select时,隐式连接返回的结果是多个被查询实体组成的集合;显示连接查询返回的是被查询的持久化对象和被关联的持久化对象组成的数组。
隐式连接查询中,在HQL中,省略select,直接使用from开头
例如:from Message m where m.topic.id=3
对Message和Topic进行了连接查询,但是结果只包含Message的数据,结果为:List<Message>
-----------------------------------------------------------------------------
显示连接查询中,在HQL中,省略select,直接使用from开头
例如:from Message m join m.topic t where t.id=3;
对Message和Topic进行了连接查询,每条记录包含Message和Topic两个实体的数据,两个实体构成一个数组,多条记录构成集合,结果为:List<Object[ ]>

1.4,select子句

select子句用于选择指定的属性或直接选择某个实体,当然select选择的属性必须是from后持久化类包含的属性。

select选择的属性是实体类的属性;结果是一个集合,集合中的每个元素是一个Object数组:Object[]

Configuration cfg = new Configuration().configure();
SessionFactory sf = cfg.buildSessionFactory();
Session session = sf.openSession();
String s = "select p.id,p from Animal p";
List<Object[]> q = session.createQuery(s).list();
for (int i=0;i<q.size();i++){
    System.out.println(q.get(i)[0]+" "+q.get(i)[1]);
}
session.close();

查询结果自动封装为对象

String s = "select new Po.Animal(p.id,p.name) from Animal p";
List<Animal> q = session.createQuery(s).list();
for (int i=0;i<q.size();i++){
    System.out.println(q.get(i).getId()+" "+q.get(i).getName());
}

查询结果封装为List

String s = "select new list(p.id,p.name) from Animal p";
List q = session.createQuery(s).list();
for (int i=0;i<q.size();i++){
    System.out.println(q.get(i));
}

1.5,聚集函数

HQL中支持的聚合函数有:concat(str,str)、substring()、trim()、lower()、upper()、length()、abs()、sqrt()、mod()、count()、avg()、min()、max()、sum()、current_date()、current_time()、current_timestamp()、day()、 month()、year()等。

  • 也可以支持数学运算和连接符号等
  • 同时也支持经验证SQL函数,如length()、lower()、upper()、trim()等
  • 可以使用distinct去除重复数据
	public static void main(String[] args) {
		Configuration conf = new Configuration().configure();
		ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().configure().build();
		SessionFactory sf = conf.buildSessionFactory(serviceRegistry);
		Session sess = sf.openSession();
		Transaction tx = sess.beginTransaction();
		//查询person的数量
		String hql = "select count(*) from person p";
		Query query = sess.createQuery(hql);
		Long count = (Long) query.uniqueResult();
		System.out.println("count:" + count);
		//查询最大,最小,平均,总数年龄
		String s = "select max(p.age), min(p.age), avg(p.age), sum(p.age) from person p";
		Query q = sess.createQuery(s);
		Object[] o = (Object[]) q.uniqueResult();
		System.out.println(o[0] + "-" + o[1] + "-" + o[2] + "-" + o[3]);
		//查询。。。。。。
		Query q2 = sess.createQuery("select lower(p.name)," + "upper(p.name)," + "trim(p.name),"
				+ "concat(p.name, '***')," + "length(p.name)" + " from person p ");
		for (Object p : q2.list()) {
			Object[] arr = (Object[]) p;
			System.out.println(arr[0] + "-" + arr[1] + "-" + arr[2] + "-" + arr[3] + "-" + arr[4] + "-");
		}
 
		String s2 = "select abs(p.age), sqrt(p.age), mod(p.age,2) from person p";
		Query q3 = sess.createQuery(s2);
		for (Object o3 : q3.list()) {
			Object[] arr = (Object[]) o3;
			System.out.println(arr[0] + "," + arr[1] + "," + arr[2]);
		}
		tx.commit();
		sess.close();
		sf.close();
	}

1.6,where条件查询

where子句用来筛选选中的结果,缩小选择范围。如果没有持久化实例命名别名,则可以直接使用属性名来引用属性。

where子句中支持大部分SQL的表达式:

  • 数学运算符:+ 、-、*、/
  • 比较运算:=、>=、<=、<、 !=、like
  • 逻辑运算:and、or、not in、between、and、not between、is null、is not null、is empty、is not empty

设置参数:

  • 冒号+参数名
from Person p where p.age <:max 
query.setInteger(“max”,20);
  • 问号做占位符
from Person p where p.age <? 
query.setInteger(0,20);
  • 问号+数字
from Person p where p.age <?1
query.setInteger(“1“,20);
public class Test {
	public static void main(String[] args) {
		Configuration cfg = new Configuration().configure();
		SessionFactory sf = cfg.buildSessionFactory();
		Session session = sf.openSession();
		Transaction t = session.beginTransaction();
        //查询姓李的所有学生
		Query query=session.createQuery("from Student where name like '李%'");
		List<Student> studentList = (List<Student>) query.list();
		for (Student p : studentList) {
			System.out.println(p.getName());
		}
		//查询学生的个数
		Query query2 = session.createQuery("select count(*) from Student s");
		Long count = (Long) query2.uniqueResult();
		System.out.println("count:" + count);
        //指定查询,查询年龄在18和?之间的学生
		Query query3=session.createQuery("from Student where age>18 and age<?1");
		query.setInteger("1",20);
		List<Student> studentList2 = (List<Student>) query3.list();
		for (Student p : studentList2) {
			System.out.println(p.getName());
		}
		t.commit();
		session.close();
		sf.close();		
	}
}

1.7,HQL子查询

如果底层数据库支持子查询(所谓子查询就是,要查询的字段及信息在A表,条件在B表),则可以在HQL语句中使用子查询。与SQL中子查询相似的是,HQL中的子查询也需要使用()。

如果子查询返回多条记录,则可以使用下面关键字:

  • all:表示子查询语句返回的所有记录。
  • any:表示子查询语句返回的任意一条结果。
  • some:与”any”等价。
  • in:与”=any”等价。
  • exists:表示子查询语句至少返回一条记录。

如果在子查询中操作集合,HQL提供了一组操纵集合的函数和属性:

  • size()函数和size属性:获得集合中元素的数量。
  • minIndex()函数和minIndex属性:对于建立了索引的集合获得最小索引值(关于集合索引参考第一部分映射值类型集合)。
  • minElement()函数和minElement属性:对于包含基本类型的元素集合,获得集合中值最小的元素。
  • maxElement()函数和maxElement属性:对于包含基本类型元素的集合,获得集合中值最大的元素。
  • element()函数:获得集合中所有元素。
例如:查询订单数大于0的客户
from Customer c where size(c.orders)>0;或者From Customer c where c.orders.size>0;

注:在HQL中子查询必须出现在where子句中,而且必须用一对圆括号括起来。

1.8,更新&删除

更新:执行删除操作调用Query接口的executeUpate()方法,然后提交事。

HQL:Delete from Student where id = 10

删除:执行删除操作调用Query接口的executeUpate()方法,然后提交事。

HQL:update Student set age = 20,name=‘李四’ where id = 20

2,条件查询

2.1,Criteria查询

Criteria接口与Query接口非常类似,也是Hibernate的查询接口,它允许创建并执行面向对象方式的查询。

Criteria的主要作用:

  • Criteria查询通过面向对象的设计,将数据查询条件封装为一个对象。简单的说,Criteria查询可以看成是传统SQL语言的对象化表示。
  • Criteria接口完全封装了基于字符串形式的查询语句,它更擅长于执行动态查询。

条件查询查询的步骤:

  • 获得Hibernate的Session对象。
  • 以Session对象创建Criteria对象。
  • 使用Restrictions的方法为Criteria对象设置查询条件,Order工具类的方法设置排序方式,Projections工具类的方法进行统计和分组。
  • 向Criterion查询添加Criterion查询条件。
  • 执行Criteria的list()或uniqueResult()方法返回结果集。
import org.hibernate.*;
import org.hibernate.boot.registry.*;
import org.hibernate.cfg.*;
import org.hibernate.service.*;

public class HibernateUtil {
    public static final SessionFactory sessionFactory;

    static {
        try {
            // 使用默认的hibernate.cfg.xml配置文件创建Configuration实例
            Configuration cfg = new Configuration().configure();
            // 以Configuration实例来创建SessionFactory实例
            ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().configure().build();
            sessionFactory = cfg.buildSessionFactory(serviceRegistry);
        } catch (Throwable ex) {
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    // ThreadLocal可以隔离多个线程的数据共享,因此不再需要对线程同步
    public static final ThreadLocal<Session> session = new ThreadLocal<Session>();

    public static Session currentSession() throws HibernateException {
        Session s = session.get();
        // 如果该线程还没有Session,则创建一个新的Session
        if (s == null) {
            s = sessionFactory.openSession();
            // 将获得的Session变量存储在ThreadLocal变量session里
            session.set(s);
        }
        return s;
    }

    public static void closeSession() throws HibernateException {
        Session s = session.get();
        if (s != null)
            s.close();
        session.set(null);
    }
}
Session session = HibernateUtil.currentSession();
// 使用createCriteria开始条件查询
List list = session.createCriteria(Student.class)
			// 根据Student的属性进行过滤数据
			.add( Restrictions.gt("name" , "a")).list();

在条件查询中,Criteria接口代表一次查询,该查询本身不具备任何数据筛选功能,Session调用createCriteria(Class clazz)方法对某个持久化类创建条件查询实例。

常用的Criteria操作方法:

  • add()方法:它用来设置查询的条件,可以根据查询条件的个数,追加任意个add()方法。
  • addOrder()方法:用来设置查询结果集的排序规则,相当于SQL语句中的order by子句。
  • createCriteria()方法:当需要从多张表中联合查询时可使用createCriteria()方法。
  • setFirstResult(int firstResult):设置查询返回的第一行记录。
  • setMaxResults(int maxResults):设置查询返回的记录数。
//分页查询前10条
Criteria criteria = session.createCriteria(Customer.class);
criteria.addOrder( Order.asc("name") ); //排序方式
criteria.setFirstResult(0);
criteria.setMaxResults(10);
List result = criteria.list()

在条件查询中,Criteria接口代表一次查询,该查询本身不具备任何的数据筛选功能但程序可以通过向Criteria对象中组合多个Criterion来实现数据过滤。

List list = sess.createCriteria(person.class)
				// 根据Student的属性进行过滤数据
				/*.add(Restrictions.gt("age" , "14"))*/
				.add(Restrictions.ilike("name", "张",MatchMode.ANYWHERE))
				.list();

Criterion接口代表一个查询条件,该查询条件由Restrictions负责产生。Restrictions是专门用于产生查询条件的工具类,它的大部分都是静态方法,常用的方法如下:

  • static eq|ne|gt|ge|lt|le(String propertyName,Object value):判断指定属性值是否等于|不等于|大于|大于等于|小于|小于等于指定值。
  • static eq|ne|gt|ge|lt|le(String propertyName,String otherPropertyName):判断第一个属性值是否等于|不等于|大于|大于等于|小于|小于等于第二个属性值。
  • static Cirterion allEq(Map propertyNameValues):判断指定属性(由Map参数的key指定)和指定值(由Map参数的value指定)是否完全相等。
  • static Criterion between(String propertyName,Object lo,Object hi):判断属性值在某个值范围之内。
  • static Criterion ilike(String propertyName,Object value):判断属性值匹配某个字符串(不区分大小写)。
  • static Criterion ilike(String propertyName,Object value,MatchMode matchMode):判断属性值匹配某个字符串(不区分大小写),并确定匹配模式。
  • static Criterion like(String propertyName,Object value):判断属性值匹配某个字符串(区分大小写)。
  • static Criterion like(String propertyName,Object value,MatchMode matchMode):判断属性值匹配某个字符串(区分大小写),并确定匹配模式。
  • static Criterion in(String propertyName,Collection values):判断属性值在某个集合内。
  • static Criterion in(String propertyName,Object[] values):判断属性值是数组元素的其中之一。
  • static Criterion isEmpty(String propertyName):判断属性值是否为空。
  • static Criterion isNotEmpty(String propertyName):判断属性值是否不为空。
  • static Criterion isNotNull(String propertyName):判断属性值是否不为空。
  • static Criterion isNull(String propertyName):判断属性值是否为空。
  • static Criterion not(Criterion expression):对Criterion求否。
  • static Criterion sizeEq(String propertyName,int size):判断某个属性的元素个数是否与size相等。
  • static Criterion sqlRestriction(String sql):直接使用SQL语句作为筛选条件。
  • static Criterion sqlRestriction(String sql,Object[] values,Type[] types):直接使用带参数占位符的SQL语句作为条件,并指定多个参数值。
  • static Criterion sqlRestriction(String sql,Object value,Type type):直接使用带参数占位符的SQL语句作为条件,并指定参数值。

Order实例代表一个排序标准,Order有如下静态方法:

  • static Order asc(String propertyName):根据propertyName属性升序排序。
  • static Order desc(String propertyName):根据propertyName属性降序排序。
 List list = session.createCriteria(Animal.class).addOrder(Order.asc("name"))
.add( Restrictions.gt("name" , "a")).list();

2.2,关联和动态查询

如果需要使用关联实体的属性来增加查询条件,则应对属性再次使用createCriteria()方法。createCriteria()方法有如下重载版:

  • Criteria createCriteria(String associationPath):使用默认的连接方式进行关联。
  • Criteria createCriteria(String associationPath,JoinType joinType):以JoinType指定的连接方式()进行连接。
  • Criteria createCriteria(String associationPath,String alias):该方法的功能与上面第1个方法的功能基本相似,只是该方法允许为关联实体指定别名。
  • Criteria createCriteria(String associationPath,String alias,JoinType joinType):该方法的功能与上面第2个方法的功能基本相似,只是该方法允许为关联实体指定别名。
  • Criteria createCriteria(String associationPath,String alias,JoinType joinType,Criterion withClause):该方法的功能最强大,该方法既可为关联实体指定别名,也可指定连接类型,还可通过withClause指定自定义的连接条件——这可用于非等值连接。
List list = session.createCriteria(Student.class)
    // 此处增加限制条件必须是Student实体存在的属性
	.add( Restrictions.gt("studentNumber" , 20050231))
	// 如果要增加对Student的关联类的属性的限制
	// 则必须重新createCriteria()
	// 如果此关联属性是集合,则只要集合里任意一个对象的属性满足下面条件即可
    //.createCriteria("enrolments")
    //.add( Restrictions.gt("semester" , 2))
			.createAlias("enrolments", "en")
	.add( Restrictions.gt("en.semester" , 2))
	.list();

为了达到这种效果,也可以使用createAlias()方法来代替createCriteria()方法。createAlias()方法同样有三个重载版本:

  • Criteria createAlias(String associationPath,String alias):该方法的功能基本等同于Criteria createCriteria(String associationPath,String alias)。
  • Criteria createAlias(String associationPath,String alias,JoinType joinType):该方法的功能基本等同于Criteria createCriteria(String associationPath,String alias,JoinType joinType)。
  • Criteria createCriteria(String associationPath,String alias,JoinType joinType,Criterion withClause):该方法的功能基本等同于Criteria createCriteria(String associationPath,String alias,JoinType joinType,Criterion withClause)。

在默认情况下,条件查询将根据持久化注解指定的延迟加载策略来加载关联实体,如果希望在条件查询中改变延迟加载策略(就像在HQL查询中使用fetch关键字一样),则可通过Criteria的setFetchMode()方法来实现,该方法也接受一个FetchMode枚举类型的值,FetchMode支持如下枚举值:

  • DEFAULT:使用配置文件指定延迟加载策略。
  • JOIN:使用外连接、预初始化所有的关联对象。
  • SELECT:启用延迟加载,系统将使用单独的select语句来初始化关联实体。只有当真正访问实体的时候,才会执行第二条select语句。
List list =session.createCriteria(Animal.class).setFetchMode("name",FetchMode.DEFAULT).list();

2.3,投影、聚合和分组

投影算是一种基于列的运算,通常用于投影到指定列(也就是过滤其他列,类似于select子句的作用),还可以完成SQL语句中常用的分组、组筛选等功能。

Hibernate的条件过滤中使用Projection代表投影运行,Projection是一个接口,而Projections作为Proection的工厂,负责生成Projection对象。

一旦产生了Projection对象之后,就可通过Criteria提供的setProjection(Projection projection)方法来进行投影运算。从该方法上看,每个Criteria只能接受一个投影运算,似乎无法进行多个投影运算,但实际上Hibernate又提供了一个ProjectionList类,该类是Projection的子类,并可以包含多个投影运算,通过这种方法即可完成多个投影运算:

List list = session.createCriteria(Animal.class).setProjection(Projections.projectionList().add(Projections.rowCount()).add(Projections.max("age"))).list();

Projections工具类提供了如下几个方法:

  • AggregateProjection avg(String propertyName):计算特定属性的平均值。类似于avg函数。
  • CountProjection count(String propertyName):统计查询结果在某列上的记录数。类似于count(column)函数。
  • Countprojection countDistinct(String propertyName):统计查询结果在某列上的不重复的记录数。类似于count(distinct column)函数。
  • PropertyProjection groupProperty(String propertyName):将查询结果按某列上的值进行分组。类似于添加gourp by子句。
  • AggregateProjection max(String propertyName):统计查询结果在某列上的最大值。类似于max函数。
  • AggregateProjection max(String propertyName):统计查询结果在某列上的最小值。类似于min函数。
  • Projection rowCount():统计查询结果的记录条数。类似于在某列上的总和。类似于sum函数。
List list = session.createCriteria(Enrolment.class)
			.createAlias("student", "s")
			.setProjection(Projections.projectionList()
				// 统计记录条数
				.add(Projections.rowCount())
				// 统计选择该课程里最大的学生姓名
				.add(Projections.max("s.name"))
				// 按course进行分组
				.add(Projections.groupProperty("course"))
			).list();
===================================================
List list = session.createCriteria(Enrolment.class)
			.setProjection(Projections.projectionList()
			// 按course进行分组
			.add(Projections.groupProperty("course"))
			// 统计记录条数,并为统计结果指定别名c
			.add(Projections.alias(Projections.rowCount(), "c")))
			.addOrder(Order.asc("c"))
			.list();
===================================================
List list = session.createCriteria(Enrolment.class)
			.setProjection(Projections.projectionList()
			// 按course进行分组,并为分组结果指定别名c
			.add(Projections.groupProperty("course").as("c"))
			// 统计记录条数
			.add(Projections.rowCount()))
			.addOrder(Order.asc("c"))
			.list();
===================================================
List list = session.createCriteria(Enrolment.class)
			.setProjection(Projections.projectionList()
			// 按course进行分组,指定别名为c
			.add(Projections.groupProperty("course") , "c")
			// 统计每组记录的条数,指定别名为rc
			.add(Projections.rowCount(), "rc"))
			.addOrder(Order.asc("rc"))
			.list();
===================================================
List list = session.createCriteria(Enrolment.class)
			.createAlias("student", "s")
			.setProjection(Projections.projectionList()
			.add(Property.forName("course"))
			.add(Property.forName("s.name")))
			.add(Property.forName("s.name").eq("孙悟空"))       // ①
			.list();

3,SQL查询

Hibernate还支持使用原生SQL查询,使用原生SQL查询可以利用某些数据库的特性,或者需要将原有JDBC应用迁移到Hibernate应用上,也可能需要使用原生SQL查询。类似于HQL查询,原生SQL查询也支持将SQL语句放在配置文件中配置,从而提高程序的解耦。命名SQL查询还可以用于调用存储过程。

SQL查询是通过SQLQuery接口来表示的,SQLQuery 接口是Query 接口的子接口,因此完全可以调用Query 接口的方法:

  • list():获取记录集。
  • uniqueResult():返回唯一记录。
  • addEntity():将查询到的数据封装为特定的实体。
  • setFirstResult():设置结果集的起点。
  • setMaxResults():设置获取记录的最大条数。
  • addScalarˈskeɪlə ():将查询的记录关联成标量值。

执行SQL查询的步骤:

  • 获取Session对象。
  • 编写SQL语句。
  • 以SQL语句作为参数,调用Session的createSQLQuery()方法创建查询对象。
  • 调用SQLQuery对象的addScalar()或addEntity()方法将选出的结果与标量值或实体进行关联。
  • 如果SQL语句包含参数,则调用Query的setXxx()方法为参数赋值。
  • 调用Query的list()方法或uniqueResult()方法返回查询的结果集。

3.1,标量查询

最基本的SQL查询就是获得一个标量(数值)列表。

session.createSQLQuery("select * from student").list();

默认情况下,查询语句将返回由Object数组组成的List,数据的每个元素时student表的列值,Hibernate会通过ResultSetMetadata来判定所返回数据列的实际顺序和类型。如果select后面只有一个字段,那么返回的List集合元素就不是数组,而只是单个的变量值。

在JDBC中使用过多ResultMetadata会降低系统性能,因此建议为这些数据列指定更明确的返回值类型。明确指定返回值类型通过addScalar()方法实现。addScalar()主要有两个作用:

  • 指定查询结果包含那些数据列——没有被addScalar()选出的列将不会包含在查询结果里面中。
  • 指定查询结果中数据列的数据类型。
List list = session.createSQLQuery("select * from student")
            .addScalar("name", StandardBasicTypes.STRING)
            .list();

此时Hibernate不再需要使用ResultSetMetadata来获取信息,而是直接从ResultSet中取出name列的值,并把name列的数据类型当成字符串处理。因此,即使使用了“*”作为查询的字段列表,但Hibernate查询的结果也只是name字段所组成的表。

在原生SQL查询里,程序指定的原生SQL语句时标准的SQL语句,因此SQL语句中使用的就是数据表、数据列等对象,而不是持久化类、属性。

3.2,实体查询

前面的标量值查询只是返回一些标量的结果集,这种查询方式与使用JDBC查询的效果基本类似。查询返回多个记录行,每个记录对应一个列表的元素,每个列表元素是一个数组,每个数组元素对应当前行、当前列的值。

如果查询返回了某个数据表的全部数据列,且该数据表有对应的持久化类映射,接下来就可把查询结果转换成实体。将查询结果转换成实体,可以使用SQLQuery提供的多个重载addEntity()方法。

List list = session.createSQLQuery("select * from animal")
            .addEntity(Animal.class)
            .list();

使用原生SQL查询必须注意的是,程序必须选出所有数据列才可被转换成持久化实体。假设实体在映射时有一个@ManyToOne关联指向另外一个实体,则SQL查询中必须返回该@ManyToOne关联实体对应的外键列,否则将抛出“column not found”异常。最简单的做法是,在SQL字符串中使用星号(*)来表示返回所有列。

在原生SQL中一样支持使用参数,这些参数既可以使用问号+索引的占位符参数(?N),也可以使用名字参数。

(1)不仅如此,如果在SQL语句中显式使用了多表连接,则SQL语句可以选出多个数据表的数据。Hibernate还支持将查询结果转换成多个实体。如果要将查询结果转换成多个实体,则SQL字符串中应为不同数据表指定不同的别名,并调用addEntity(String alias,Class entityClass)方法将不同的数据表转换成不同实体。

String sqlString = "select s.*,e.*,c.* "
			+ "from student_inf s,enrolment_inf e,course_inf c "
			+ "where s.student_id = e.student_id "
			+ "and e.course_code = c.course_code";
		List list = session.createSQLQuery(sqlString)
			// 指定将从s表查询得到的记录行转换成Student实体
			.addEntity("s", Student.class)
			// 指定将从e表查询得到的记录行转换成Enrolment实体
			.addEntity("e", Enrolment.class)
			// 指定将从c表查询得到的记录行转换成Course实体
			.addEntity("c", Course.class)
			.list();

(2)不仅如此,Hibernate还可以将查询结果转换成非持久化实体(即普通的JavaBean),只要该JavaBean为这些数据提供了对应的setter和getter方法即可。Query接口提供了一个setResulltTransformer()方法,该方法可接受一个Transformers对象,通过使用该对象即可把查询到的结果集转换成JavaBean集。

String sqlString = "select s.name stuName, c.name courseName "
			+ "from student_inf s,enrolment_inf e,course_inf c "
			+ "where s.student_id = e.student_id "
			+ "and e.course_code = c.course_code ";
List list = session.createSQLQuery(sqlString)
			// 指定将查询的记录行转换成StudentCourse对象
			.setResultTransformer(Transformers
				.aliasToBean(StudentCourse.class))
			.list();

3.3,处理关联和继承

只要原生SQL查询选出了足够的数据列,则程序除了可以将指定数据列转换成持久化实体之外,还可以将实体的关联实体转换成查询结果。将关联实体转换成查询结果的方法是SQLQuery addJoin(String alias,String path),该方法的第一个参数是转换后的实体名,第二个参数是待转换的实体属性。

String sqlString = "select s.* , e.* from student_inf s , "
		+ "enrolment_inf e where s.student_id=e.student_id";
List list = session.createSQLQuery(sqlString)
		.addEntity("s", Student.class)
		.addJoin("e" , "s.enrolments")
		.list();

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

燕双嘤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值