Hibernate HQL引數化查詢,動態
阿新 • • 發佈:2019-02-15
Hibernate中對動態查詢引數繫結提供了豐富的支援,那麼什麼是查詢引數動態繫結呢?其實如果我們熟悉傳統JDBC程式設計的話,我們就不難理解查詢引數動態繫結,如下程式碼傳統JDBC的引數繫結:
PrepareStatement pre=connection.prepare(“select * from User where user.name=?”);
pre.setString(1,”zhaoxin”);
ResultSet rs=pre.executeQuery();
在Hibernate中也提供了類似這種的查詢引數繫結功能,而且在Hibernate中對這個功能還提供了比傳統JDBC操作豐富的多的特性,在Hibernate中共存在4種引數繫結的方式,下面我們將分別介紹:
A、 按引數名稱繫結:
在HQL語句中定義命名引數要用”:”開頭,形式如下:
Query query=session.createQuery(“from User user where user.name=:customername and user:customerage=:age ”);
query.setString(“customername”,name);
query.setInteger(“customerage”,age);
上面程式碼中用:customername和:customerage分別定義了命名引數customername和customerage,然後用Query介面的setXXX()方法設定名引數值,setXXX()方法包含兩個引數,分別是命名引數名稱和命名引數實際值。
B、 按引數位置邦定:
在HQL查詢語句中用”?”來定義引數位置,形式如下:
Query query=session.createQuery(“from User user where user.name=? and user.age =? ”);
query.setString(0,name);
query.setInteger(1,age);
同樣使用setXXX()方法設定繫結引數,只不過這時setXXX()方法的第一個引數代表邦定引數在HQL語句中出現的位置編號(由0開始編號),第二個引數仍然代表引數實際值。
注:在實際開發中,提倡使用按名稱邦定命名引數,因為這不但可以提供非常好的程式可讀性,而且也提高了程式的易維護性,因為當查詢引數的位置發生改變時,按名稱邦定名引數的方式中是不需要調整程式程式碼的。
C、 setParameter()方法:
在Hibernate的HQL查詢中可以通過setParameter()方法邦定任意型別的引數,如下程式碼:
String hql=”from User user where user.name=:customername ”;
Query query=session.createQuery(hql);
query.setParameter(“customername”,name,Hibernate.STRING);
如上面程式碼所示,setParameter()方法包含三個引數,分別是命名引數名稱,命名引數實際值,以及命名引數對映型別。對於某些引數型別setParameter()方法可以根據引數值的Java型別,猜測出對應的對映型別,因此這時不需要顯示寫出對映型別,像上面的例子,可以直接這樣寫:
query.setParameter(“customername”,name);但是對於一些型別就必須寫明對映型別,比如java.util.Date型別,因為它會對應Hibernate的多種對映型別,比如Hibernate.DATA或者Hibernate.TIMESTAMP。
D、 setProperties()方法:(setEntity())
在Hibernate中可以使用setProperties()方法,將命名引數與一個物件的屬性值繫結在一起,如下程式程式碼:
Customer customer=new Customer();
customer.setName(“pansl”);
customer.setAge(80);
Query query=session.createQuery(“from Customer c where c.name=:name and c.age=:age ”);
query.setProperties(customer);
setProperties()方法會自動將customer物件例項的屬性值匹配到命名引數上,但是要求命名引數名稱必須要與實體物件相應的屬性同名。
這裡還有一個特殊的setEntity()方法,它會把命名引數與一個持久化物件相關聯,如下面程式碼所示:
Customer customer=(Customer)session.load(Customer.class,”1”);
Query query=session.createQuery(“from Order order where order.customer=:customer ”);
query. setEntity(“customer”,customer);
List list=query.list();
上面的程式碼會生成類似如下的SQL語句:
Select * from order where customer_ID=’1’;
E、 使用繫結引數的優勢:
我們為什麼要使用繫結命名引數?任何一個事物的存在都是有其價值的,具體到繫結引數對於HQL查詢來說,主要有以下兩個主要優勢:
①、 可以利用資料庫實施效能優化,因為對Hibernate來說在底層使用的是PrepareStatement來完成查詢,因此對於語法相同引數不同的SQL語句,可以充分利用預編譯SQL語句快取,從而提升查詢效率。
②、 可以防止SQL Injection安全漏洞的產生:
SQL Injection是一種專門針對SQL語句拼裝的攻擊方式,比如對於我們常見的使用者登入,在登入介面上,使用者輸入使用者名稱和口令,這時登入驗證程式可能會生成如下的HQL語句:
“from User user where user.name=’”+name+”’ and user.password=’”+password+”’ ”
這個HQL語句從邏輯上來說是沒有任何問題的,這個登入驗證功能在一般情況下也是會正確完成的,但是如果在登入時在使用者名稱中輸入”zhaoxin or ‘x’=’x”,這時如果使用簡單的HQL語句的字串拼裝,就會生成如下的HQL語句:
“from User user where user.name=’zhaoxin’ or ‘x’=’x’ and user.password=’admin’ ”;
顯然這條HQL語句的where字句將會永遠為真,而使使用者口令的作用失去意義,這就是SQL Injection攻擊的基本原理。
而使用繫結引數方式,就可以妥善處理這問題,當使用繫結引數時,會得到下面的HQL語句:
PrepareStatement pre=connection.prepare(“select * from User where user.name=?”);
pre.setString(1,”zhaoxin”);
ResultSet rs=pre.executeQuery();
在Hibernate中也提供了類似這種的查詢引數繫結功能,而且在Hibernate中對這個功能還提供了比傳統JDBC操作豐富的多的特性,在Hibernate中共存在4種引數繫結的方式,下面我們將分別介紹:
A、 按引數名稱繫結:
在HQL語句中定義命名引數要用”:”開頭,形式如下:
Query query=session.createQuery(“from User user where user.name=:customername and user:customerage=:age ”);
query.setString(“customername”,name);
query.setInteger(“customerage”,age);
上面程式碼中用:customername和:customerage分別定義了命名引數customername和customerage,然後用Query介面的setXXX()方法設定名引數值,setXXX()方法包含兩個引數,分別是命名引數名稱和命名引數實際值。
B、 按引數位置邦定:
在HQL查詢語句中用”?”來定義引數位置,形式如下:
Query query=session.createQuery(“from User user where user.name=? and user.age =? ”);
query.setString(0,name);
query.setInteger(1,age);
同樣使用setXXX()方法設定繫結引數,只不過這時setXXX()方法的第一個引數代表邦定引數在HQL語句中出現的位置編號(由0開始編號),第二個引數仍然代表引數實際值。
注:在實際開發中,提倡使用按名稱邦定命名引數,因為這不但可以提供非常好的程式可讀性,而且也提高了程式的易維護性,因為當查詢引數的位置發生改變時,按名稱邦定名引數的方式中是不需要調整程式程式碼的。
C、 setParameter()方法:
在Hibernate的HQL查詢中可以通過setParameter()方法邦定任意型別的引數,如下程式碼:
String hql=”from User user where user.name=:customername ”;
Query query=session.createQuery(hql);
query.setParameter(“customername”,name,Hibernate.STRING);
如上面程式碼所示,setParameter()方法包含三個引數,分別是命名引數名稱,命名引數實際值,以及命名引數對映型別。對於某些引數型別setParameter()方法可以根據引數值的Java型別,猜測出對應的對映型別,因此這時不需要顯示寫出對映型別,像上面的例子,可以直接這樣寫:
query.setParameter(“customername”,name);但是對於一些型別就必須寫明對映型別,比如java.util.Date型別,因為它會對應Hibernate的多種對映型別,比如Hibernate.DATA或者Hibernate.TIMESTAMP。
D、 setProperties()方法:(setEntity())
在Hibernate中可以使用setProperties()方法,將命名引數與一個物件的屬性值繫結在一起,如下程式程式碼:
Customer customer=new Customer();
customer.setName(“pansl”);
customer.setAge(80);
Query query=session.createQuery(“from Customer c where c.name=:name and c.age=:age ”);
query.setProperties(customer);
setProperties()方法會自動將customer物件例項的屬性值匹配到命名引數上,但是要求命名引數名稱必須要與實體物件相應的屬性同名。
這裡還有一個特殊的setEntity()方法,它會把命名引數與一個持久化物件相關聯,如下面程式碼所示:
Customer customer=(Customer)session.load(Customer.class,”1”);
Query query=session.createQuery(“from Order order where order.customer=:customer ”);
query. setEntity(“customer”,customer);
List list=query.list();
上面的程式碼會生成類似如下的SQL語句:
Select * from order where customer_ID=’1’;
E、 使用繫結引數的優勢:
我們為什麼要使用繫結命名引數?任何一個事物的存在都是有其價值的,具體到繫結引數對於HQL查詢來說,主要有以下兩個主要優勢:
①、 可以利用資料庫實施效能優化,因為對Hibernate來說在底層使用的是PrepareStatement來完成查詢,因此對於語法相同引數不同的SQL語句,可以充分利用預編譯SQL語句快取,從而提升查詢效率。
②、 可以防止SQL Injection安全漏洞的產生:
SQL Injection是一種專門針對SQL語句拼裝的攻擊方式,比如對於我們常見的使用者登入,在登入介面上,使用者輸入使用者名稱和口令,這時登入驗證程式可能會生成如下的HQL語句:
“from User user where user.name=’”+name+”’ and user.password=’”+password+”’ ”
這個HQL語句從邏輯上來說是沒有任何問題的,這個登入驗證功能在一般情況下也是會正確完成的,但是如果在登入時在使用者名稱中輸入”zhaoxin or ‘x’=’x”,這時如果使用簡單的HQL語句的字串拼裝,就會生成如下的HQL語句:
“from User user where user.name=’zhaoxin’ or ‘x’=’x’ and user.password=’admin’ ”;
顯然這條HQL語句的where字句將會永遠為真,而使使用者口令的作用失去意義,這就是SQL Injection攻擊的基本原理。
而使用繫結引數方式,就可以妥善處理這問題,當使用繫結引數時,會得到下面的HQL語句:
from User user where user.name=’’zhaoxin’’ or ‘’x=’’x’’ ‘ and user.password=’admin’;由此可見使用繫結引數會將使用者名稱中輸入的單引號解析成字串(如果想在字串中包含單引號,應使用重複單引號形式),所以引數繫結能夠有效防止SQL Injection安全漏洞。
具體示例:
配置檔案
<?xml version='1.0' encoding='UTF-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <!-- <session-factory> <property name="myeclipse.connection.profile">JDBC for MySQL</property> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="connection.url">jdbc:mysql://localhost:3306/book</property> <property name="connection.username">root</property> <property name="connection.password"></property> <property name="hibernate.show_sql">true</property> <property name="dialect">org.hibernate.dialect.MySQLDialect</property> <property name="hibernate.format_sql">true</property> <!–生成特定資料庫sql–> <property name="hibernate.hbm2ddl.auto">update</property> <!–thread指定當前執行緒來跟蹤管理–> <property name="current_session_context_class">thread</property> <mapping resource="Dog.xml"></mapping> </session-factory> --> <session-factory> <property name="connection.driver_class">oracle.jdbc.OracleDriver</property> <property name="connection.url">jdbc:oracle:thin:@localhost:1521:orcl</property> <property name="connection.username">sll</property> <property name="connection.password">sll</property> <!-- SQL dialect --> <property name="dialect">org.hibernate.dialect.Oracle10gDialect</property> <property name="current_session_context_class">thread</property> <property name="show_sql">true</property> <!--<property name="hbm2ddl.auto">update</property>--> <property name="connection.autocommit">true</property> <!-- <mapping resource="cn/happy/hibernate01/Cat.hbm.xml"/> <mapping resource="cn/happy/hibernate02/Dept.hbm.xml"/>--> <mapping resource="cn/happy/hibernate03hql/Emp.hbm.xml"/> </session-factory> </hibernate-configuration>
小配置
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="cn.happy.hibernate02"> <class name="Dept" table="Dept" schema="sll"> <id name="deptno" column="deptno"> <generator class="native"></generator> </id> <property name="deptname"></property> </class> </hibernate-mapping>
另外一個 我是2個包裡面的所以用2個
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.happy.hibernate03hql">
<class name="cn.happy.hibernate03hql.Emp" table="EmpSLL">
<id name="empno" column="empno" >
<generator class="native" />
</id>
<property name="enmae" column="enmae"/>
<property name="job" column="job"/>
<property name="mgr" column="mgr"/>
<property name="hiredate" column="hiredate"/>
<property name="sal" column="sal"/>
<property name="comm" column="comm"/>
<property name="deptno" column="deptno"/>
</class>
</hibernate-mapping>
實體類
package cn.happy.hibernate02;
/**
* Created by linlin on 2017/9/25.
*/
public class Dept {
private Integer deptno;
private String deptname;
public Dept() {
}
public Dept(Integer deptno, String deptname) {
this.deptno = deptno;
this.deptname = deptname;
}
public Integer getDeptno() {
return deptno;
}
public void setDeptno(Integer deptno) {
this.deptno = deptno;
}
public String getDeptname() {
return deptname;
}
public void setDeptname(String deptname) {
this.deptname = deptname;
}
}
動態查詢 需要的實體類
package cn.happy.hibernate03hql;
import cn.happy.hibernate02.Dept;
import java.util.Date;
/**
* Created by linlin on 2017/9/25.
*/
public class Emp {
private Long empno;
private String enmae;
private String job;
private Long mgr;
private Date hiredate;
private Long sal;
private Long comm;
private Byte deptno;
private conEmp ce;
public conEmp getCe() {
return ce;
}
public void setCe(conEmp ce) {
this.ce = ce;
}
public Long getEmpno() {
return empno;
}
public void setEmpno(Long empno) {
this.empno = empno;
}
public String getEnmae() {
return enmae;
}
public void setEnmae(String enmae) {
this.enmae = enmae;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public Long getMgr() {
return mgr;
}
public void setMgr(Long mgr) {
this.mgr = mgr;
}
public Date getHiredate() {
return hiredate;
}
public void setHiredate(Date hiredate) {
this.hiredate = hiredate;
}
public Long getSal() {
return sal;
}
public void setSal(Long sal) {
this.sal = sal;
}
public Long getComm() {
return comm;
}
public void setComm(Long comm) {
this.comm = comm;
}
public Byte getDeptno() {
return deptno;
}
public void setDeptno(Byte deptno) {
this.deptno = deptno;
}
}
package cn.happy.hibernate03hql;
import java.util.Date;
/**
* Created by linlin on 2017/9/25.
*/
public class conEmp {
private Date hiredateEnd;
private Date hiredateStart;
private String job;
private Long sal;
public conEmp(Date hiredateEnd, Date hiredateStart, String job, Long sal) {
this.hiredateEnd = hiredateEnd;
this.hiredateStart = hiredateStart;
this.job = job;
this.sal = sal;
}
public conEmp() {
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public Long getSal() {
return sal;
}
public void setSal(Long sal) {
this.sal = sal;
}
public Date getHiredateStart() {
return hiredateStart;
}
public void setHiredateStart(Date hiredateStart) {
this.hiredateStart = hiredateStart;
}
public Date getHiredateEnd() {
return hiredateEnd;
}
public void setHiredateEnd(Date hiredateEnd) {
this.hiredateEnd = hiredateEnd;
}
}
具體的測試類
package cn.happy.test;
import cn.happy.hibernate01.Cat;
import cn.happy.hibernate02.Dept;
import cn.happy.hibernate03hql.Emp;
import cn.happy.hibernate03hql.conEmp;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.Before;
import org.junit.Test;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
/**
* Created by linlin on 2017/9/22.
*/
public class test0925 {
Configuration cfg;
Transaction tx;
Session session;
SessionFactory factory;
@Before
public void myBefore(){
//建立配置物件
cfg = new Configuration().configure("Hibernate.cfg.xml");
//根據配置物件建立SessionFactory
factory = cfg.buildSessionFactory();
//根據SessionFactory建立Session
session= factory.openSession();
//在Session建立後開啟事務
tx = session.beginTransaction();
}
@Test
public void test01() {
Cat dog = new Cat();
dog.setName("哈哈2");
dog.setAge(2);
session.save(dog);
// tx.commit();
session.close();
System.out.println("新增成功!!!");
}
@Test
public void test03() {
Cat dog = new Cat();
dog.setId(8);
dog.setName("蘇us");
dog.setAge(11);
session.saveOrUpdate(dog);
tx.commit();
session.close();
System.out.println("ok!!!");
}
@Test
public void test0333() {
Dept dog = new Dept();
dog.setDeptno(2);
dog.setDeptname("保潔部");
session.save(dog);
tx.commit();
session.close();
System.out.println("ok!!!");
}
//查詢所有
@Test
public void test0select() {
String hql="from Dept";
Query query = session.createQuery(hql);
List<Dept> list=query.list();
for (Dept item:list){
System.out.println(item.getDeptname());
}
System.out.println("ok");
}
//檢索部分記錄
@Test
public void test0sel8ct() {
String hql="from Dept where deptname='開發部'";
Query query = session.createQuery(hql);
List<Dept> list=query.list();
for (Dept item:list){
System.out.println(item.getDeptname());
}
System.out.println("ok");
}
@Test
public void test8ct() {
String hql="select deptname from Dept";
Query query = session.createQuery(hql);
List<Dept> list=query.list();
System.out.println(list);
System.out.println("ok");
}
//部分多列
@Test
public void test0sel8() {
String hql="select deptno,deptname from Dept";
Query query = session.createQuery(hql);
List<Object[]> list=query.list();
for (Object[] item:list){
for (Object child:item)
System.out.println(child);
}
System.out.println("ok");
}
//檢索部分集合 強型別
@Test
public void testl8() {
String hql="select new Dept(deptno,deptname) from Dept";
Query query = session.createQuery(hql);
List<Dept> list=query.list();
for (Dept item:list){
System.out.println(item.getDeptname());
}
System.out.println("ok");
}
@Test
public void testl834() {
String hql="from Dept where deptname=?";
Query query = session.createQuery(hql);
query.setParameter(0,"開發部");
List<Dept> list=query.list();
for (Dept item:list){
System.out.println(item.getDeptname());
}
System.out.println("ok");
}
@Test
public void testl() {
String hql="from Dept where deptname=:deptname";
Query query = session.createQuery(hql);
query.setParameter("deptname","開發部");
List<Dept> list=query.list();
for (Dept item:list){
System.out.println(item.getDeptname());
}
System.out.println("ok");
}
@Test
public void test4() {
String hql="from Dept dept where dept.deptname=deptname";
Query query = session.createQuery(hql);
Dept dd=new Dept();
dd.setDeptname("開發部");
query.setProperties(dd);
List<Dept> list=query.list();
for (Dept item:list){
System.out.println(item.getDeptname());
}
System.out.println("ok");
}
//動態查詢
@Test
public void testselect() {
conEmp c=new conEmp();
c.setSal(1000L);
c.setJob("CLERK");
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
Date fromdate=null;
Date enddate=null;
try {
fromdate=sdf.parse("1981-04-01");
enddate=sdf.parse("1985-09-09");
} catch (ParseException e) {
e.printStackTrace();
}
c.setHiredateEnd(enddate);
c.setHiredateStart(fromdate);
String hql="from Emp where 1=1 ";
StringBuffer sb=new StringBuffer(hql);
if(c.getJob()!=null){
sb.append(" and job=:job ");
}
if(c.getSal()!=null){
sb.append(" and sal>:sal ");
}
if(c.getHiredateStart()!=null){
sb.append(" and hiredate>=:hiredateStart ");
}
if(c.getHiredateEnd()!=null){
sb.append(" and hiredate<=:hiredateEnd ");
}
Query query = session.createQuery(sb.toString());
query.setProperties(c);
List<Emp> list=query.list();
for (Emp item:list){
System.out.println(item.getEmpno());
}
}
}