Hibernate使用原生SQL查询
Hibernate提供了原生的SQL语句的查询,通过createSQLQuery(String)和createNativeQuery(String)
,createSQLQuery(String)
是Hibernate5.x之前的版本的,在Hibernate5.x和之后,都采用的是createNativeQuery(String)
,使用原生SQL虽然麻烦,但是一些复杂的查询明显可以使用原生SQL更加方便实现各种复杂的关联查询,这里主要记录一下原生SQL查询在开发中经常使用到的一些用法。主要参考了官方的文档:https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#sql
准备
使用的Hibenate版本为5.4的;这里使用的数据库是MySQL,测试的数据库时sakila数据库,数据库初始化脚本: https://download.csdn.net/download/strive_or_die/11928949 ,如果需要但是没有积分,也可以在评论区留下邮箱号,看到了会直接发送到邮箱。。
普通的查询
Hibernate会通过ResultSetMetadata来判断每个字段值的存放位置以及类型,返回一个Object[]类型的数组,其实这种结果集应用场景并不广,所以实际开发中使用起来也不方便。
//查询所有的列
List<Object[]> objList = session.createNativeQuery("select * from actor").list()
//查询指定的列
List<Object[]> list = session.createNativeQuery("select actor_id as actorId,first_name as firstName from actor")
.setFirstResult(0)
.setMaxResults(5)
.list();
查询结果转为Map
使用setResultTransformer转为Map,一般转为了Map比上面的方式要好很多了,可以通过一些反射进行转为对象。如下所示:
List<Map> mapList = session.createNativeQuery("select actor_id as actorId,first_name as firstName from actor")
.unwrap(NativeQueryImpl.class)
.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP)
.setFirstResult(0)
.setMaxResults(5)
.list();
查询结果转为Bean对象
使用setResultTransformer
转为指定对象,如果会报Hibernate的类型和实体类的类型不匹配的错误,则需要使用addScalar
指定结果列的Hibernate类型,使得两者类型匹配;例如下例中,Hibernate查出来的actorId是Short
,而Entity中定义了为Integer
,执行则会报错,所以添加了addScalar("actorId",IntegerType.INSTANCE)
将Hibernate的类型修改为Integer
。
List<Actor> actors = session.createNativeQuery("select actor_id as actorId,first_name as firstName from actor")
.addScalar("actorId",IntegerType.INSTANCE)
.unwrap(NativeQueryImpl.class)
//将查询结果列转为指定的对象,可以是实体类,也可以是非实体类例如VO和DTO
.setResultTransformer(Transformers.aliasToBean(Actor.class))
.setFirstResult(0)
.setMaxResults(5)
.list();
标量查询
标量查询主要用于指定查询语句返回哪些字段的值,例如下列的select * from actor
通过标量查询,进行指定结果列,这样就不会将所有结果列都查询出来,只查询我们需要的结果列。
List<Object[]> listB = session.createNativeQuery("select * from actor")
.addScalar("actor_id")
.addScalar("first_name")
.setFirstResult(0)
.setMaxResults(5)
.list();
转为实体对象
a.返回一个实体对象
这种通过addEntity
的查询方式,可以将结果查询转为指定的实体类,但是有个点需要特别注意,**如果使用了实体对象,那么在查询时要将实体对象中有的属性全部在SQL语句中查询出来,否则就会报错。**报的错误如:SQLException: Column 'actor_id' not found
List<Actor> actorList = session.createNativeQuery("select actor_id,first_name ,last_name,last_update from actor")
.addEntity("com.hibernate.learn.po.Actor")
.setFirstResult(0)
.setMaxResults(5)
.list();
b.返回多个实体对象
通过addEntity
指定别名和实体类,添加多个Entity,查询多个实体类结果。例如下列所示:
String multiEntitySQL = "select ac.*,fi.* from actor ac " +
"inner join film_actor fa on ac.actor_id = fa.actor_id " +
"inner join film fi on fi.film_id = fa.actor_id";
List<Object[]> multiList = session.createSQLQuery(multiEntitySQL)
.addEntity("ac",Actor.class)
.addEntity("fi", Film.class)
.setFirstResult(0)
.setMaxResults(5)
.list();
//多个实体,按照addEntity的顺序排列
for (Object[] multiEntity : multiList){
System.out.println("actor :"+ ((Actor)multiEntity[0]));
System.out.println("film :"+ ((Film)multiEntity[1]));
}
当使用连接查询查询多个对象时,可以通过addEntity("xxx", XXX.class)
方法来根据数据库表的别名来引入多个实体类,这时如果需要将查询出来的所有的对象分别存入实体类中,只需要在查询出来的对象上添加{}
号即可,此时就会自动分类,{}
这种方式主要用于返回多个实体对象,而且实体对象的列名可能存在相同的,那么就需要使用{}
,这样Hibernate就可以区分了。例如上面语句可以改为如下所示:
String multiEntitySQL = "select {ac.*},{fi.*} from actor ac " +
"inner join film_actor fa on ac.actor_id = fa.actor_id " +
"inner join film fi on fi.film_id = fa.actor_id";
List<Object[]> multiList = session.createSQLQuery(multiEntitySQL)
.addEntity("ac",Actor.class)
.addEntity("fi", Film.class)
.setFirstResult(0)
.setMaxResults(5)
.list();
//多个实体,按照addEntity的顺序排列
for (Object[] multiEntity : multiList){
System.out.println("actor :"+ ((Actor)multiEntity[0]));
System.out.println("film :"+ ((Film)multiEntity[1]));
}
总结
一般来说,Hibernate提供的原生SQL查询的方式,常用的就是这些了,很多使用我们使用的都是将查询结果转为实体对象,这个可以通过addEntity
方式实现,还有将连接查询的结果列转为VO或者DTO,这个可以通过setResultTransformer(Transformers.aliasToBean(XXX.class))
实现,对于查询结果为Object[]数组的是在是不方便处理。更多的实现可以参考Hibernate的官方文档:https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#sql