使用樂觀鎖定分成xml和annotation兩種設定
※XML設定:
記得要將hibernate.cfg.xml設成mapping resource
通過版本實現樂觀鎖定:
準備資料:
DROP TABLE emp; CREATE TABLE emp ( empno NUMBER(4) CONSTRAINT PK_EMP PRIMARY KEY, ename VARCHAR2(10), JOB VARCHAR2(9), mgr NUMBER(4) default 1 not null, hiredate DATE, sal NUMBER(7,2), comm NUMBER(7,2), deptno NUMBER(2) CONSTRAINT FK_DEPTNO REFERENCES DEPT ); INSERT INTO emp VALUES (7369,'SMITH','CLERK',7902,to_date('17-12-1980','dd-mm-yyyy'),800,NULL,20); INSERT INTO emp VALUES (7499,'ALLEN','SALESMAN',7698,to_date('20-2-1981','dd-mm-yyyy'),1600,300,30); INSERT INTO emp VALUES (7521,'WARD','SALESMAN',7698,to_date('22-2-1981','dd-mm-yyyy'),1250,500,30); INSERT INTO emp VALUES (7566,'JONES','MANAGER',7839,to_date('2-4-1981','dd-mm-yyyy'),2975,NULL,20); INSERT INTO emp VALUES (7654,'MARTIN','SALESMAN',7698,to_date('28-9-1981','dd-mm-yyyy'),1250,1400,30); INSERT INTO emp VALUES (7698,'BLAKE','MANAGER',7839,to_date('1-5-1981','dd-mm-yyyy'),2850,NULL,30); INSERT INTO emp VALUES (7782,'CLARK','MANAGER',7839,to_date('9-6-1981','dd-mm-yyyy'),2450,NULL,10); INSERT INTO emp VALUES (7788,'SCOTT','ANALYST',7566,to_date('19-04-1987','dd-mm-yyyy')-85,3000,NULL,20); INSERT INTO emp VALUES (7839,'KING','PRESIDENT',NULL,to_date('17-11-1981','dd-mm-yyyy'),5000,NULL,10); INSERT INTO emp VALUES (7844,'TURNER','SALESMAN',7698,to_date('8-9-1981','dd-mm-yyyy'),1500,0,30); INSERT INTO emp VALUES (7876,'ADAMS','CLERK',7788,to_date('23-05-1987','dd-mm-yyyy')-51,1100,NULL,20); INSERT INTO emp VALUES (7900,'JAMES','CLERK',7698,to_date('3-12-1981','dd-mm-yyyy'),950,NULL,30); INSERT INTO emp VALUES (7902,'FORD','ANALYST',7566,to_date('3-12-1981','dd-mm-yyyy'),3000,NULL,20); INSERT INTO emp VALUES (7934,'MILLER','CLERK',7782,to_date('23-1-1982','dd-mm-yyyy'),1300,NULL,10); commit;
※這些測試資料和之前一樣,只是將mgr修改一下,一定不能給null,不然待會測試就算資料庫有值,第二次的樂觀鎖定會拋出NullPointerException
emp.hbm.xml
<class name="vo.Emp" table="Emp" optimistic-lock="version"> <id name="empno" type="int"> <column name="EMPNO" /> <generator class="assigned" /> </id> <version name="mgr" type="java.lang.Integer"> <column name="MGR" not-null="true"/> </version> <property name="ename" type="java.lang.String"> <column name="ENAME" /> </property> <property name="job" type="java.lang.String"> <column name="JOB" /> </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>
※只是將要實現的版本控制欄位改成version而已
※version一定要放在id下,不然會出「org.hibernate.InvalidMappingException: Unable to read XML」的錯
※class的optimistic-lock="version",我沒設也可以,不過最好是設定一下
※optimistic-lock有四種值:
.all:檢查所有屬性實現樂觀鎖定
.dirty:檢查發生變動過得屬性實現樂觀鎖定
.none:無鎖定
.version:使用版本機制實現樂觀鎖定
TestHibernate.java
SessionFactory sf = HibernateUtil2.getSessionFactory(); Session s1 = sf.openSession(); Session s2 = sf.openSession(); try { Criteria c1 = s1.createCriteria(Emp.class).add(Restrictions.eq("empno", 7369)); Criteria c2 = s2.createCriteria(Emp.class).add(Restrictions.eq("empno", 7369)); Emp e1 = (Emp) c1.uniqueResult(); Emp e2 = (Emp) c2.uniqueResult(); e1.setEname("SMITH"); s1.beginTransaction().commit(); e2.setEname("OOO"); s2.beginTransaction().commit(); } catch (Exception e) { System.err.println("例外錯誤!"); e.printStackTrace(); } finally { if (s1.isOpen()) { s1.close(); } if (s2.isOpen()) { s2.close(); } }
※在執行最後一行commit時,因為發現版本不符,所以會出「org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): 」的錯
※注意控制台的SQL語法where最後有「and MGR=?」,就知道版本控制設定成功了
※第一個commit會先看需不需要修改,如果和資料庫一樣,就不修改;
而第二個commit我設不一樣,所以這時如果第一個commit有修改,就會報版本第一個※的錯; 如果沒修改就修改
※通過時間戳實現樂觀鎖定:
Emp.hbm.xml
<class name="vo.Emp" table="Emp" optimistic-lock="version"> <id name="empno" type="int"> <column name="EMPNO" /> <generator class="assigned" /> </id> <timestamp name="hiredate" column="HIREDATE" /> <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="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>
※控制台一樣在where後會出現and HIREDATE=?,就知道時間戳設定成功了
※timestamp一樣寫在id後,optimistic-lock還是一樣,不寫我試的結果也是可以
※注意timestamp的子層沒有column屬性,用content assist就知道了,不然執行時,還是會出Unable to read XML的錯
※測試代碼還是一樣,執行到第二次commit也是一樣的錯
※和版本控制一樣,不要有null,所以在create table時,要設定not null,只是timestamp在資料庫有值時,程式很順利進行; 但版本控制不管有沒有值都會出錯
※Annotation設定
記得要將hibernate.cfg.xml設成mapping classEmp.java
@Version @Column(name = "MGR") public Integer getMgr() { return mgr; } 或 @Version @Column(name = "HIREDATE") public Date getHiredate() { return hiredate; }
※使用annotation很簡單,就在就版本控制的getter方法上加@Version即可
※測試類還是和上面一樣
※如果要設optimistic-lock,可以看最下面的「樂觀鎖定的其他相關設定」
※其他相關設定
※Hibernate有dynamic-update和dynamic-insert可以設定,如下:<class name="vo.Emp" table="Emp" dynamic-insert="true" dynamic-update="true"> ... </class>
或者是用annotation設定
import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; @org.hibernate.annotations.Entity( dynamicUpdate = true, dynamicInsert = true ) @Entity @Table(name = "EMP", schema = "c##scott") public class Emp { //... }
※注意Entity就有兩種
※這個設定表示效能有所提升,如果沒設定時,新增和修改如下:
Emp emp = new Emp(); emp.setEmpno(9997); emp.setDeptno(30); emp.setSal(1000.5); emp.setMgr(7369); s1.saveOrUpdate(emp); s1.beginTransaction().commit();
※此時控制台會出insert或者update的SQL語法
insert into Emp (ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO, EMPNO) values (?, ?, ?, ?, ?, ?, ?, ?)
update Emp set ENAME=?, JOB=?, MGR=?, HIREDATE=?, SAL=?, COMM=?, DEPTNO=? where EMPNO=?
但是如果有加dynamic,控制台的SQL語法只有針對用到的才會新增或刪除
insert into Emp (MGR, SAL, DEPTNO, EMPNO) values (?, ?, ?, ?)
update Emp set ENAME=? where EMPNO=?
※樂觀鎖定的其他設定
在XML設定的hbm.xml的optimistic-lock=還有兩個設定all和dirty,這兩個設定都要加上dynamic-update="true",不然會出「org.hibernate.MappingException: optimistic-lock=all|dirty requires dynamic-update="true":」的錯設定範例如下:
<class name="vo.Emp" table="Emp" optimistic-lock="all" dynamic-update="true"> ... </class>
或者
@org.hibernate.annotations.Entity( dynamicUpdate = true, dynamicInsert = true, optimisticLock = OptimisticLockType.DIRTY ) @Entity @Table(name = "EMP", schema = "c##scott") public class Emp { //... }
※測試類還是一樣
沒有留言:
張貼留言