Hibernate的 native sql 查询
在我们的hibernate中,除了我们常用的HQL查询以外,还非常好的支持了原生的SQL查询,那么我们既然使用了hibernate,为什么不都采用hibernate推荐的HQL查询语句呢?这是因为HQL查询语句虽然方便我们查询,但是基于HQL的查询会将查询出来的对象保存到hibernate的缓存当中,如果在我们的一个大型项目中(数据量超过了百万级),这个时候如果使用hibernate的HQL查询的话,会一次将我们查询的对象查询出来后放到缓存中,这个时候会影响我们的效率,所以当在大型项目中使用hibernate时我们的最佳实践就是–使用原生的SQL查询语句,而不使用HQL语句,因为通过SQL查询的话,是不会经过hibernate的缓存的。接下来我们就来看看hibernate的原生SQL查询
- 标量查询
在hibernate中,我们如果使用原生SQL查询的话,是通过SQLQuery接口进行的,我们首先来看看我们最基本的查询:
String sql = "select * from t_type";
session.createSQLQuery(sql).addEntity(Type.class).list();
这就创建了最简单的SQL查询语句,此时返回的数据库表的字段值是保存在一个Object[]数组中的,数组中的每个元素就是查询出来的t_type表中的每个字段值,Hibernate会通过ResultSetMetadata来判断每个字段值的存放位置以及类型,接下来我们看下标量查询:
session = HibernateUtil.getSession();
List<Object[]> stus = (List<Object[]>)
session.createSQLQuery("select * from T_TYPE s")
.addScalar("ID", StandardBasicTypes.INTEGER).addScalar("NAME").setFirstResult(0).setMaxResults(5).list();
// System.out.println(stus.get(1)[0]);
for (int i = 0; i < stus.size(); i++) {
for(int j=0;j<stus.get(i).length;j++){
System.out.print(stus.get(i)[j]+" ");
}
System.out.println();
}
这个查询语句指定了查询的字符串以及返回的字段和类型
这条查询语句仍然会返回一个Object[]类型的数组,但是此时就不会通过ResultSetMetadata,而是我们明确指定的ID,NAME此时虽然查询语句中有 * 将所有的字段查询出来,但是这个时候仅仅只会返回我们指定的三个字段值,其类型还是由ResultSetMetada来指定的。
- 实体查询
上述的标量查询返回的裸数据,保存到了Object[]数组当中,我们如果要一个实体对象,将其保存到实体对象时就可以使用 addEntity()方法来实现:
@Test
public void testSelectNative() {
System.out.println("hello");
Session session = null;
try {
session = HibernateUtil.getSession();
String sql = "select * from t_type";
List<Type> types = session.createSQLQuery(sql).addEntity(Type.class).list();
for (Type type : types) {
System.out.println(type.getId() + " " + type.getName());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (session != null) {
session.close();
}
}
}
这时我们就指定了将查询出来的对象存进Student这个实体类中,注意:如果使用了实体对象,那么在查询时要将实体对象中有的属性全部在SQL语句中查询出来,否则就会报错。
- 返回不受hibernate管理的实体对象
我们有时候的需求是这样的,将每个表其中的一些字段查询出来,然后存放到一个javabean对象中,但是我们的这个bean对象又不需要存放到数据库,不需要设置成实体对象,这个时候我们往往会创建一个 DTO 的数据传输对象来存放我们要储存的属性,
例如数据库中有t_student 和t_teacher 两张表 属于多对多关系
中间表是teacher_student,现在要进行多表查询,但是要把查询的数据放到一个javabean对象,这时我们可以定义了一个 StudentDTO 对象:
public class StudentDTO implements Serializable {
private static final long serialVersionUID = 1L;
private int sid;
private String sname;
private int tid;
private String tname;
public StudentDTO() {
}
...
}
这时我们的查询语句是
@Test
public void testSelectDTOEntity() {
Session session = null;
try {
session = HibernateUtil.getSession();
/**
* 对于非Entity实体对象的类,我们如果要保持数据,可以通过定义一个DTO对象
* 然后调用setResultTransformer(Transformers.aliasToBean(StudentDTO.
* class))方法 来返回一个不受管的Bean对象
*/
String sql = "SELECT s.id sid , s.name sname, t.id tid,t.name tname "
+ "FROM t_teacher t LEFT JOIN teacher_student ts ON t.id = ts.tid LEFT JOIN t_student s ON ts.sid = s.id";
List<StudentDTO> lists = session.createSQLQuery(sql)
.setResultTransformer(Transformers
.aliasToBean(StudentDTO.class)).list();
for (StudentDTO stu : lists) {
System.out.println(stu.getSid() + " "
+ stu.getSname() + " " + stu.getTname());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (session != null) {
session.close();
}
}
}
我们要将查询出来的这些不同的表的字段存放到一个不是实体对象当中,就可以调用 .setResultTransformer(Transformers.aliasToBean(StudentDTO.class)) 方法来创建一个非受管的 bean 对象,这个时候就hibernate就会将属性值存放到这个 bean 对象当中,注意:查询出来的表的字段值存放到bean对象中,是通过调用 bean对象的 setter方法,如果该属性在bean对象中没有setter方法,则会报错。
本篇随笔主要分析了一下如何来通过原生的SQL语句查询我们需要的信息,我们一定要记住,当数据量非常大的时候,强烈建议使用原生的SQL去查询数据,而不要使用HQL来查询,这样其实使用hibernate来说效率其实也不差,但是我们的增、删、改的操作则完全可以交给hibernate来完成。