※依PK的CRUD
SQL腳本
CREATE TABLE DEPT
( DEPTNO NUMBER(10,0) NOT NULL,
DNAME VARCHAR2(255 BYTE),
LOC VARCHAR2(255 BYTE),
PRIMARY KEY ("DEPTNO")
);
Insert into DEPT (DEPTNO,DNAME,LOC) values (70,'DDD','zzz');
Insert into DEPT (DEPTNO,DNAME,LOC) values (100,'eee','jjj');
Insert into DEPT (DEPTNO,DNAME,LOC) values (10,'ACCOUNTING','NEW YORK');
Insert into DEPT (DEPTNO,DNAME,LOC) values (20,'RESEARCH','DALLAS');
Insert into DEPT (DEPTNO,DNAME,LOC) values (30,'SALES','CHICAGO');
Insert into DEPT (DEPTNO,DNAME,LOC) values (40,'OPERATIONS','BOSTON');
Insert into DEPT (DEPTNO,DNAME,LOC) values (90,'bruce','ZH_TW');
Insert into DEPT (DEPTNO,DNAME,LOC) values (80,'bruce','ZH_TW');
pom.xml
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>3.6.10.Final</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.19.0-GA</version>
</dependency>
javassist不加的話,會在執行時,出「java.lang.ClassNotFoundException: javassist.util.proxy.MethodFilter」
hibernate.cfg.xml
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="hibernate.connection.url">jdbc:oracle:thin:@127.0.0.1:1521:orcl</property>
<property name="hibernate.connection.username">account</property>
<property name="hibernate.connection.password">password</property>
<property name="hibernate.dialect">org.hibernate.dialect.OracleDialect</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<mapping resource="org/hibernate/vo/Dept.hbm.xml" />
</session-factory>
</hibernate-configuration>
Dept.hbm.xml
<hibernate-mapping>
<class name="org.hibernate.vo.Dept" table="DEPT">
<id name="deptno" type="int">
<column name="DEPTNO" />
<generator class="assigned" />
</id>
<property name="dname" type="java.lang.String" access="field">
<column name="DNAME" />
</property>
<property name="loc" type="java.lang.String" column name="LOC" />
</class>
</hibernate-mapping>
※hibernate-mapping有個屬性package,打上package名稱,而class name打class名稱也是可以
※property也可只寫一行,最後一行的property就是這樣
Dept.java
private int deptno;
private String dname;
private String loc;
//setter/getter
TestHibernate.java
Configuration cfg = new Configuration().configure();
SessionFactory factory = cfg.buildSessionFactory();
Session session = factory.openSession();
Dept dept = new Dept();
dept.setDeptno(80);
dept.setDname("bruce");
dept.setLoc("ZH_TW");
Transaction tx = session.beginTransaction();
try {
session.save(dept);
tx.commit();
System.out.println("成功!");
} catch (Exception e) {
tx.rollback();
System.err.println("失敗!");
e.printStackTrace();
} finally {
session.close();
factory.close();
}
專案圖:
TestHibernate.java的session.save(dept);是新增的意思,重要的有六個
※delete:刪除
※save:新增
※update:修改
※saveOrUpdate:新增或修改(資料庫沒有-->新增,有-->修改)
較專業的講法是:如果物件是Transient,就呼叫save(); 如果物件是Detached,則呼叫update()
可參考
官方網站和
良葛格的
只是官網只有三種狀態,良葛格的有四種,而且還有圖,不知從哪來的?
※load:查詢
※get:查詢
※get與load的差別
Configuration cfg = new Configuration().configure();
SessionFactory factory = cfg.buildSessionFactory();
Session session = factory.openSession();
System.out.println("0");
//Dept dept = (Dept) session.get(Dept.class, 80);
Dept dept = (Dept) session.load(Dept.class, 80);
System.out.println(dept);
※如果最後一行註解掉,get在控制台還是會有sql語法,而load什麼都沒有,
因為load()為lazy-initialization,只有真正要用到時,才會真的去做事,
所以load()效能比get()還要高
※如果沒查到:
get()是null
load()會拋org.hibernate.ObjectNotFoundException: No row with the given identifier exists:
※注意事項
Dept dept = (Dept) session.get(Dept.class, 80);
dept.setDname("apple");
Transaction tx = session.beginTransaction();
try {
//session.save(dept);此行不加也行
tx.commit();
System.out.println("成功!");
} catch (Exception e) {
tx.rollback();
System.err.println("失敗!");
e.printStackTrace();
} finally {
session.close();
factory.close();
}
先get或load,然後再set也能成功修改,但還是要commit
如上面的程式碼,整個都在Transaction外面,還是可以
※session混合
Configuration cfg = new Configuration().configure();
SessionFactory factory = cfg.buildSessionFactory();
Session session = factory.openSession();
Dept dept = (Dept) session.load(Dept.class, 80);
dept.setDname("xxx3");
session.close();
Session session2 = factory.openSession();
dept.setLoc("ZH_TW");// 此行對DB無效,因為session已關閉,dept是包在裡面的
// session2.update(dept);// 這行才有用
Transaction tx = session2.beginTransaction();
try {
tx.commit();
System.out.println("成功!");
} catch (Exception e) {
tx.rollback();
System.err.println("失敗!");
e.printStackTrace();
} finally {
session2.close();
factory.close();
}
注意下面的Transaction接收的是session2
session關閉對資料庫沒作用,但java也不會出錯
※資料庫的抓取欄位策略
<hibernate-mapping default-access="field">
<class name="org.hibernate.vo.Dept" table="DEPT">
<id name="deptNo" type="int" access="property">
<column name="DEPTNO" />
<generator class="assigned" />
</id>
<property name="dName" type="java.lang.String" access="property">
<column name="DNAME" />
</property>
<property name="loc" type="java.lang.String" access="property">
<column name="LOC" />
</property>
</class>
</hibernate-mapping>
id和property有個屬性access,有三個可以設定
※property:預設值,vo一定要設setter/getter,不然會報「Could not find a setter for property xxx in class xxx」
※field:hibernate會用反射的技術獲取值
※noop:自定的策略,要implements org.hibernate.property.PropertyAccessor介面
可參考
這裡的5.1.4.2
※hibernate-mapping也有default-access可以設定,上面是針對一個field,這邊的設定是針對整個java bean,但如果都設定,以上面的為主
※我設定field後,將setter/getter刪除,欄位還是private
使用get可以抓到值; 使用load,值都是null
※事務自動提交
在hibernate.cfg.xml加上
<property name="hibernate.connection.autocommit">true</property>
TestHibernate.java
Configuration cfg = new Configuration().configure();
SessionFactory factory = cfg.buildSessionFactory();
Session session = factory.openSession();
Dept dept = new Dept();
dept.setDeptno(80);
dept.setDname("bruce");
dept.setLoc("zh_TW");
session.saveOrUpdate(dept);
session.flush();
// Transaction tx = session.beginTransaction();
try {
session.saveOrUpdate(dept);
// tx.commit();
System.out.println("成功!");
} catch (Exception e) {
// tx.rollback();
System.err.println("失敗!");
e.printStackTrace();
} finally {
session.close();
factory.close();
}
主要就是加上session.flush();
※多個執行一起commit
再創一個腳本
CREATE TABLE EMP
( EMPNO NUMBER(4,0),
ENAME VARCHAR2(10 BYTE),
JOB VARCHAR2(9 BYTE),
MGR NUMBER(4,0),
HIREDATE DATE,
SAL NUMBER(7,2),
COMM NUMBER(7,2),
DEPTNO NUMBER(2,0),
CONSTRAINT "PK_EMP" PRIMARY KEY (EMPNO)
);
Insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (9999,'Bruce','MANAGER',null,to_date('18-10月-15','DD-MON-RR'),5000,null,80);
Insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7369,'SMITH','CLERK',7902,to_date('17-12月-80','DD-MON-RR'),800,null,20);
Insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7499,'ALLEN','SALESMAN',7698,to_date('20-2月 -81','DD-MON-RR'),1600,300,30);
Insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7521,'WARD','SALESMAN',7698,to_date('22-2月 -81','DD-MON-RR'),1250,500,30);
Insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7566,'JONES','MANAGER',7839,to_date('02-4月 -81','DD-MON-RR'),2975,null,20);
Insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7654,'MARTIN','SALESMAN',7698,to_date('28-9月 -81','DD-MON-RR'),1250,1400,30);
Insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7698,'BLAKE','MANAGER',7839,to_date('01-5月 -81','DD-MON-RR'),2850,null,30);
Insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7782,'CLARK','MANAGER',7839,to_date('09-6月 -81','DD-MON-RR'),2450,null,10);
Insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7788,'SCOTT','ANALYST',7566,to_date('24-1月 -87','DD-MON-RR'),3000,null,20);
Insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7839,'KING','PRESIDENT',null,to_date('17-11月-81','DD-MON-RR'),5000,null,10);
Insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7844,'TURNER','SALESMAN',7698,to_date('08-9月 -81','DD-MON-RR'),1500,0,30);
Insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7876,'ADAMS','CLERK',7788,to_date('02-4月 -87','DD-MON-RR'),1100,null,20);
Insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7900,'JAMES','CLERK',7698,to_date('03-12月-81','DD-MON-RR'),950,null,30);
Insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7902,'FORD','ANALYST',7566,to_date('03-12月-81','DD-MON-RR'),3000,null,20);
Insert into EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values (7934,'MILLER','CLERK',7782,to_date('23-1月 -82','DD-MON-RR'),1300,null,10);
Emp.java
private int empno;
private String ename;
private String job;
private Integer mgr;
private Date hiredate;
private Double sal;
private Double comm;
private Integer deptno;
//setter/getter
Emp.hbm.xml
<class name="org.hibernate.vo.Emp" table="Emp">
<id name="empno" type="int">
<column name="EMPNO" />
<generator class="assigned" />
</id>
<property name="ename" type="java.lang.String">
<column name="ENAME" />
</property>
<property name="job" type="java.lang.String">
<column name="JOB" />
</property>
<property name="mgr" type="java.lang.Integer">
<column name="MGR" />
</property>
<property name="hiredate" type="java.util.Date">
<column name="HIREDATE" />
</property>
<property name="sal" type="java.lang.Double">
<column name="SAL" />
</property>
<property name="comm" type="java.lang.Double">
<column name="COMM" />
</property>
<property name="deptno" type="java.lang.Integer">
<column name="DEPTNO" />
</property>
</class>
hibernate.cfg.xml要多增加一行
<mapping resource="org/hibernate/vo/Emp.hbm.xml" />
TestHibernate.java
public static void main(String[] args) {
Configuration cfg = new Configuration().configure();
SessionFactory factory = cfg.buildSessionFactory();
Session session = factory.openSession();
try {
session.saveOrUpdate(saveOrUpdateDept());
session.save(saveEmp());
session.flush();
System.out.println("成功!");
} catch (Exception e) {
System.err.println("失敗!");
e.printStackTrace();
} finally {
session.close();
factory.close();
}
}
public static Dept saveOrUpdateDept(){
Dept dept = new Dept();
dept.setDeptno(80);
dept.setDname("bruce");
dept.setLoc("zh_TW");
return dept;
}
public static Emp saveEmp(){
Emp emp = new Emp();
emp.setEmpno(9999);
emp.setEname("Bruce");
emp.setJob("MANAGER");
emp.setHiredate(new Date());
emp.setSal(5000D);
emp.setDeptno(80);
return emp;
}
※如果是PK可以設定int,不是的話就設Integer,因為int不可以是null,如果怕錯就全部設Wrapper類別
※先用get方法測試Emp行不行,如果如上行設成int,查詢時若資料庫對應的欄位有null值,會出「Null value was assigned to a property of primitive type setter of xxx」的錯
※save是新增,所以執行第二次之後,一定會報錯
※讀取到session.flush();因為是同一個session,會一起commit
※如果要一個一個commit,就在下方多增加一行session.flush(),如下:
session.saveOrUpdate(saveOrUpdateDept());
session.flush();
session.save(saveEmp());
session.flush();
※動態模型
就是沒有class和資料庫的表對應,直接取一個entity名稱就搞定了
Dept.hbm.xml
<class entity-name="entityDept" table="DEPT">
<id name="deptno" type="int">
<column name="DEPTNO" />
<generator class="sequence">
<param name="sequence">DEPT_SEQ</param>
</generator>
</id>
<property name="dname" type="java.lang.String" access="field">
<column name="DNAME" />
</property>
<property name="loc" type="java.lang.String" column="LOC" />
</class>
※這裡的PK用自動的,如果資料庫沒有就下CREATE SEQUENCE DEPT_SEQ吧!
※property的name為必填
新增測試
Session s = HibernateUtil2.getSession();
Transaction tx = s.beginTransaction();
try {
Map<String, Object> dept = new HashMap<>();
dept.put("dname", "業務部");
dept.put("loc", "芝加哥");
s.save("entityDept", dept);
tx.commit();
} catch (Exception e) {
tx.rollback();
System.err.println("例外錯誤!");
e.printStackTrace();
} finally {
if (s.isOpen()) {
s.close();
}
}
}
※map的key是xml的property裡面的name屬性
修改測試
// Map<String, Object> dept = new HashMap<>();
// dept.put("deptno", 41);
// dept.put("dname", "業務部");
// dept.put("loc", "舊金山");
// s.update("entityDept", dept);
Map<String, Object> dept = (Map<String, Object>) s.get("entityDept", 41);
dept.put("loc", "舊金山");
s.update("entityDept", dept);
tx.commit();
※註解的部分也可以,但不寫出來就會給null,這張表只有三個欄位,全寫出來也還好,但如果有很多欄位,譬如20個,那實在是太累了,所以可以用get或load取出來,然後再針對想修改的欄位做修改即可
刪除測試
s.delete(s.load("entityDept", 41));
tx.commit();