2015年10月25日 星期日

HQL 的CRUD 及分頁 (Hibernate3.x 六)

※刪除

Session session = HibernateUtil2.getSession();
try {
    Query hqlDelete = session.createQuery(" delete from Dept where deptno = :deptno");
    hqlDelete.setParameter("deptno", 60);
    int rtn = hqlDelete.executeUpdate();
    if(rtn > 1){
        System.out.println("刪除成功!");
    } else {
        System.out.println("無資料可刪除!");
    }
} catch (Exception e) {
    System.err.println("例外錯誤!");
    e.printStackTrace();
} finally {
    session.close();
}

※因為hibernate是資料庫和物件的轉換,而HQL是物件的操作,所以必需注意Dept大小寫要和hbm.xml的java類名稱一致,否則會出「xxx is not mapped」的錯誤

※刪除時,from可以不寫

※hibernate-mapping有個屬性auto-import,預設是true,所以Dept才可以不用寫包.類,但如果有兩個以上都是同名,還是會報錯,還有另一種方法,就是使用別名

Dept.hbm.xml

<hibernate-mapping>
    <import class="vo.Dept" rename="xxx"/>
    <class>
        <!-- ... -->
    </class>
</hibernate-mapping>

※這時就可以使用xxx代替Dept了(class一定要包.類),但別名如果和其他的class同名(cfg.xml有設定mapping resource或mapping class),而且auto-import是true或沒設,假設我打Emp,這時就會出「duplicate import: Emp refers to both vo.Dept and vo.Emp (try using auto-import="false")」的錯



※修改

Session session = HibernateUtil2.getSession();
try {
    Query hqlUpdate = session.createQuery(" update Dept set dname = ?, loc = :loc where deptno = 60 ");
    hqlUpdate.setString(0, "IT");
    hqlUpdate.setString("loc", "zh_CN");
    int rtn = hqlUpdate.executeUpdate();
    if(rtn > 1){
        System.out.println("修改成功!");
    } else {
        System.out.println("無資料可修改!");
    }
} catch (Exception e) {
    System.err.println("例外錯誤!");
    e.printStackTrace();
} finally {
    session.close();
}

※Query有很多的setXxx可以用,還可以像刪除那樣,用setParameter,會自動偵測

※Query的第一個參數可用數字或字串,數字從0開始,字串必須對應「:字串」,也可以混用

※混用時必須注意數字(也就是?)必須在字串前面,不然會出「cannot define positional parameter after any named parameters have been defined」的錯

※新增

Session session = HibernateUtil2.getSession();
try {
    Query hqlInsert = session.createQuery(" insert into Dept(deptno, dname, loc) select 60, 'IT', 'zh_TW' from Emp where empno = 7369 ");
    int rtn = hqlInsert.executeUpdate();
    if(rtn > 1){
        System.out.println("新增成功!");
    } else {
        System.out.println("無資料可新增!");
    }
} catch (Exception e) {
    System.err.println("例外錯誤!");
    e.printStackTrace();
} finally {
    session.close();
}

※新增不支援values的寫法,這裡的最後一張圖,往上一點點,only開頭的有寫
可能是hibernate一定要先查詢才有辦法做到吧!

※新增時不能用setXxx,像我是寫死的,這個例子舉的不好

※所以大部分新增都是用save()或者是Session的createSQLQuery()達成

※createSQLQuery(),裡面的SQL就是怕hibernate的功能不能達到時,可以根據各家的SQL進行撰寫,而且還加入一些較特殊的用法

※查詢

Session session = HibernateUtil2.getSession();
try {
    Query hqlQuery = session.createQuery(" from Dept ");
    List<?> list = hqlQuery.list();
    for(Object o:list){
        Dept d = (Dept) o;
        System.out.println(d.getDeptno());
    }
} catch (Exception e) {
    System.err.println("例外錯誤!");
    e.printStackTrace();
} finally {
    session.close();
}

※不能寫select *,會說「*」是不認識的字元

※from Dept 等同 select d from Dept d

※List<Object>會出黃黃的警告,所以我用List<?>

※取特定的欄位

Query hqlQuery1 = session.createQuery(" select dname from Dept ");
List<?> list = hqlQuery1.list();
for(Object o:list){
    System.out.println(o);
}



Query hqlQuery2 = session.createQuery(" select deptno, dname, loc from Dept ");
    
@SuppressWarnings("unchecked")
List<Object[]> list = hqlQuery2.list();
for (Object[] oArray : list) {
    for (Object o : oArray) {
        System.out.println(o);
        System.out.println("-----");
    }
}

※所以只有一個欄位回傳的是List<Object>;
多個欄位就是List<Object[]>,官方說一條結果返回多個物件叫tuples

※尤於沒有List<?[]>,因為?是什麼都不知道了,怎麼可能還有陣列,所以只好寫List<Object[]>

※將欄位轉換成VO物件

Query hqlQuery = session.createQuery(" select deptno as deptno, dname as dname, loc as loc from Dept ");
    
hqlQuery.setResultTransformer(new AliasToBeanResultTransformer(Dept.class));
List<?> list = hqlQuery.list();
Iterator<?> it = list.iterator();
while(it.hasNext()){
    Dept d = (Dept) it.next();
    System.out.println(d.getDname());
}

※雖然select的欄位名稱和vo物件一樣,但還是要「as欄位名」,連as都要打,as能省略的只有物件名,如from Dept d,如果不打會出「PropertyNotFoundException: Could not find setter for 0 on class vo.Dept」的錯

※將SQL或HQL分離

※XML設定


hbm.xml
<class name="vo.Dept" table="DEPT">
    <!-- ... -->
</class>
<query name="xxx">from Dept</query>
<sql-query name="ooo">SELECT * FROM DEPT</sql-query>

※query name為hql,類似createQuery(); sql-query為sql,類似createSQLQuery()

TestHibernate.java
Query hqlQuery = session.getNamedQuery("xxx");
List<?> listXxx = hqlQuery.list();
for(Object o:listXxx){
    Dept d = (Dept)o;
    System.out.println(d.getDname());
}
    
Query sqlQuery = session.getNamedQuery("ooo");
List<Object[]> listOoo = sqlQuery.list();
    
for(Object[] oArray:listOoo){
    for(Object o:oArray){
        System.out.println(o);
    }
}

※Annotation設定

Dept.java
@NamedQueries({ @NamedQuery(name = "xxx", query = "from Dept") })
@NamedNativeQueries({ @NamedNativeQuery(name = "ooo", query = "SELECT * FROM DEPT", resultClass=Dept.class) })
@Entity
@Table(name = "DEPT", schema = "c##scott")
public class Dept {
    //...
}

※sql一定要加resultClass,不然執行時會報「org.hibernate.cfg.NotYetImplementedException: Pure native scalar queries are not yet supported」的錯; 而hql不用,也沒這個選項可設定

TestHibernate.java
Query query = session.getNamedQuery("ooo");
List<?> list = query.list();
    
for(Object o:list){
    Dept d = (Dept)o;
    System.out.println(d.getDname());
}

※用Annotation時輸出的結果,統一都用hql的寫法才不會報強制轉換的錯誤

※回傳只有一筆

Query hql = session.createQuery(" from Dept where deptno = 20 ");
Dept dept1 = (Dept) hql.list().get(0);
Dept dept2 = (Dept) hql.uniqueResult();
    
System.out.println(dept1.getDname());
System.out.println(dept2.getDname());

※使用uniqueResult()看起來較方便

※分頁

Query hql = session.createQuery(" from Emp ");
hql.setFirstResult(2);//從第幾筆開始
hql.setMaxResults(3);//一頁顯示幾筆
    
System.out.println("從0開始,機器的角度");
Iterator<?> it1 = hql.list().iterator();
while (it1.hasNext()) {
    Emp e = (Emp) it1.next();
    System.out.println(e.getEmpno());
}
    
System.out.println("從1開始,人的角度");
Iterator<?> it2 = hql.iterate();
while (it2.hasNext()) {
    Emp e = (Emp) it2.next();
    System.out.println(e.getEmpno());
}

※資料庫的內容
第一個迴圈的結果為: 7521、7566、7654

第二個迴圈的結果為: 7499、7521、7566

※工作時常這樣用

※機器角度的算法:
int currentPage = 3;//模擬前端輸入第幾頁
int pageSize = 3;
    
Query hql = session.createQuery(" from Emp ");
hql.setFirstResult((currentPage - 1) * pageSize);//公式
hql.setMaxResults(pageSize);
    
Iterator<?> it1 = hql.list().iterator();
while (it1.hasNext()) {
    Emp e = (Emp) it1.next();
    System.out.println(e.getEmpno());
}

※注意setFirstResult的算法

結果為: 7782、7788、7839




人角度的算法:
int currentPage = 3;//模擬前端輸入第幾頁
int pageSize = 3;
    
Query hql = session.createQuery(" from Emp ");
hql.setFirstResult(currentPage * pageSize - pageSize + 1);//公式
hql.setMaxResults(pageSize);
    
Iterator<?> it2 = hql.iterate();
while (it2.hasNext()) {
    Emp e = (Emp) it2.next();
    System.out.println(e.getEmpno());
}

結果當然還是和上面一樣

如果在一級或二級快取已有資料,那iterate()比list()快,否則較慢

沒有留言:

張貼留言