2016年1月9日 星期六

Native SQL1-使用程式撰寫 (Hibernate3.x 三十六)

準備工作和三十四篇一樣


※Scalar查詢

Session s = HibernateUtil2.getSession();
Transaction tx = s.beginTransaction();
try {
    final String sql = "SELECT * FROM EMP WHERE SAL >= :sal";
    
    System.out.println("方法一");
    SQLQuery q1 = s.createSQLQuery(sql);
    q1.setInteger("sal", 3000);
    
    @SuppressWarnings("unchecked")
    List<Object[]> list1 = q1.list();
    
    for (Object[] oA : list1) {
        for (Object o : oA) {
            System.out.println(o);
        }
        System.out.println();
    }
    System.out.println("=============\n方法二");
    
    SQLQuery q2 = s.createSQLQuery(sql);
    q2.setInteger("sal", 3200);
    q2.addScalar("DEPTNO", StandardBasicTypes.INTEGER);
    q2.addScalar("COMM", StandardBasicTypes.STRING);
    q2.addScalar("HIREDATE", StandardBasicTypes.DATE);
    
    @SuppressWarnings("unchecked")
    List<Object[][]> list2 = q2.list();
    
    for (Object[] oA : list2) {
        for (Object o : oA) {
            System.out.println(o);
        }
        System.out.println();
    }
} catch (Exception e) {
    tx.rollback();
    System.err.println("例外錯誤!");
    e.printStackTrace();
} finally {
    if (s.isOpen()) {
        s.close();
    }
}



※Entity查詢

final String sql = "SELECT * FROM EMP WHERE SAL >= :sal";
    
SQLQuery q = s.createSQLQuery(sql);
q.setInteger("sal", 3000);
q.addEntity(Emp.class);
    
@SuppressWarnings("unchecked")
List<Emp> list = q.list();
    
for (Emp e : list) {
    System.out.println(e.getEname());
    System.out.println(e.getDept().getDname() + "\n");
}

※如果不用「*」,就會出「java.sql.SQLException: 資料欄名稱無效」的錯,所以應該只能全部,我試過每一個欄位都打或多幾個名稱一樣的欄位也是可以的,但少了就是不行



final String sql = "SELECT e.*, d.* FROM EMP e, DEPT d WHERE e.SAL >= :sal";
    
SQLQuery q = s.createSQLQuery(sql);
q.setInteger("sal", 3000);
q.addEntity("e", Emp.class);
q.addEntity("d", Dept.class);
    
@SuppressWarnings("unchecked")
List<Object[]> list = q.list();
    
for (Iterator<Object[]> it = list.iterator(); it.hasNext();) {
    Emp emp = (Emp) it.next()[0];
    Dept dept = (Dept) it.next()[1];
    
    System.out.println(emp.getEname());
    System.out.println(dept.getDname() + "\n");
}
    
// for (Object[] oA : list) {
// for (Object o : oA) {
// if (o instanceof Emp) {
// Emp emp = (Emp) o;
// System.out.println(emp.getEname());
// } else if (o instanceof Dept) {
// Dept dept = (Dept) o;
// System.out.println(dept.getDname());
// }
// }
// System.out.println();
// }

※多個Entity就對應好別名即可

※註解的部分是另一種撈值的方法,比較好理解



※處理關聯和集合

final String sql = "SELECT e.*, d.* FROM EMP e, DEPT d WHERE e.DEPTNO = d.DEPTNO AND e.SAL >= :sal";
    
SQLQuery q = s.createSQLQuery(sql);
q.setInteger("sal", 3000);
q.addEntity("e", Emp.class);
q.addJoin("d", "e.dept");
    
@SuppressWarnings("unchecked")
List<Object[]> list = q.list();
    
for (Iterator<Object[]> it = list.iterator(); it.hasNext();) {
    Object[] obj = it.next();
    Emp emp = (Emp) obj[0];
    Dept dept = (Dept) obj[1];
    
    System.out.println(emp.getEname());
    System.out.println(dept.getDname() + "\n");
}

※addJoin至少要兩個參數,官方這章節我覺得寫錯的地方很多



※回傳多個Entity

final String sql = "SELECT {e1.*}, {e2.*} FROM EMP e1, EMP e2 WHERE e1.MGR = e2.EMPNO";
    
SQLQuery q = s.createSQLQuery(sql);
q.addEntity("e1", Emp.class);
q.addEntity("e2", Emp.class);
    
@SuppressWarnings("unchecked")
List<Object[]> list = q.list();
    
for (Object[] obj : list) {
    Emp emp1 = (Emp) obj[0];
    Emp emp2 = (Emp) obj[1];
    System.out.println(emp1.getEname());
    System.out.println(emp2.getEname() + "\n");
}

※官方的意思是說e1和e2如果有相同的名稱,就必須用「{}」包起來,否則會出錯,但我把它拿掉還是可以,官網也有說是某些資料庫才會,可能是oracle不會吧!或許版本有差也說不定
但不加addEntity就真的會出錯了,訊息是「未支援 SQL92 符號的位置」,還有要加「{}」就全加,不然就全不加,否則會出「java.sql.SQLException: 資料欄名稱無效」的錯

※官方還有個表,連結往下一點會看到alias injection names,就是「ID as {c.id}」這種東西,我試不出來,譬如說我下SELECT SAL as {e1.sal}, {e2.*} FROM EMP e1, EMP e2 WHERE e1.MGR = e2.EMPNO,
會出「ORA-00918: 資料欄的定義不明確」的錯,因為前面的Entity查詢的addEntity方法本來就要select全部啊!所以我就把全部的欄位都打出來,但還是一樣的錯,不知道它是怎麼使用的
最後我還新增一張沒外鍵的表,hibernate也不做一對x,但結果也是不行



※回傳非管理的Entity


也就是不經過xml的mapping就可以取得資料庫的值


EmpDept.java

public class EmpDept {
    private String ooo;
    private String dname;
    // setter/getter...
}



測試類

final String sql = "SELECT e.ename ooo, d.dname FROM EMP e, DEPT d WHERE e.SAL >= :sal";
    
SQLQuery q = s.createSQLQuery(sql);
q.setInteger("sal", 3000);
q.addScalar("ooo", StringType.INSTANCE);
q.addScalar("dname", StringType.INSTANCE);
q.setResultTransformer(Transformers.aliasToBean(EmpDept.class));
    
@SuppressWarnings("unchecked")
List<EmpDept> list = q.list();
    
for (Iterator<EmpDept> it = list.iterator(); it.hasNext();) {
    EmpDept empDept = it.next();
    System.out.println(empDept.getOoo() + "\n");
}

※如果不加addScalar的話,會出「Could not find setter for OOO on class EmpDept」的錯,所以我將vo改成大寫就OK了,看來可能是資料庫幫我轉大寫了

※如果不想加addScalar,但vo又想使用依java命名慣例,使用小寫這時可以用:
e.ename \"ooo\", d.dname \"dname\",就就是用「"」包起來,但前後已經有「"」了,所以只好再加個跳脫字元「\」,最後就變成了「\"」
「'」我試過了,不行



※參數

和HQL一樣都有支援「?」和「:varName」如:
SELECT * FROM EMP WHERE ENAME like ?
SELECT * FROM EMP WHERE ENAME like :xxx
然後用Query.setXXX,?從0開始,和jdbc一樣;而xxx就對應好就可以了

沒有留言:

張貼留言