※準備工作
DROP TABLE CAR PURGE; DROP TABLE CAR_ATTR PURGE; CREATE TABLE CAR ( CID NUMBER(5,0), NAME VARCHAR2(20), CONSTRAINT CAR_PK PRIMARY KEY(CID) ); CREATE TABLE CAR_ATTR ( CAID NUMBER(5,0), COLOR VARCHAR2(20), CC NUMBER(3,0), CAR_CID NUMBER(5,0), CONSTRAINT CAR_ATTR_PK PRIMARY KEY(CAID), CONSTRAINT CAR_ATTR_FK FOREIGN KEY(CAR_CID) REFERENCES CAR(CID) ON DELETE CASCADE );
※和一對一雙向PK關聯多了一個CAR_CID的FK
※XML設定
Car.java
public class Car { private Integer cid; private String name; private CarAttr carAttr; // setter/getter... }
CarAttr.java
public class CarAttr { private Integer caid; private String color; private Integer cc; private Car car; // setter/getter... }
※和一對一雙向PK關聯一模一樣,也不用多CAR_CID的欄位
Car.hbm.xml
<class name="vo.Car" table="CAR"> <id name="cid" type="java.lang.Integer"> <column name="CID" /> <!-- <generator class="assigned" /> --> <generator class="sequence"> <param name="sequence">CAR_SEQ</param> </generator> </id> <property name="name" type="java.lang.String"> <column name="NAME" /> </property> <one-to-one name="carAttr" class="vo.CarAttr" cascade="all" fetch="select" lazy="false" property-ref="car" /> </class>
※one-to-one可說是主表,另一方就是從表
※property-ref裡面的字要對應CarAttr.hbm.xml裡的many-to-one的name
CarAttr.hbm.xml
<class name="vo.CarAttr" table="CAR_ATTR"> <id name="caid" type="java.lang.Integer"> <column name="CAID" /> <!-- <generator class="assigned" /> --> <generator class="sequence"> <param name="sequence">CAR_ATTR_SEQ</param> </generator> </id> <property name="color" type="java.lang.String"> <column name="COLOR" /> </property> <property name="cc" type="java.lang.Integer"> <column name="CC" /> </property> <many-to-one name="car" class="vo.Car" column="CAR_CID" unique="true" not-null="true" lazy="false" /> </class>
※many-to-one可說是從表,另一方就是主表,但要加上unique=true才是一對一
新增測試
Session s = HibernateUtil2.getSession(); Transaction tx = s.beginTransaction(); try { Car c = new Car(); // c.setCid(1); c.setName("寶石傑1"); CarAttr ca = new CarAttr(); // ca.setCaid(1); ca.setColor("藍1"); ca.setCc(530); ca.setCar(c); c.setCarAttr(ca); s.save(c); // s.saveOrUpdate(ca);//會報錯 tx.commit(); } catch (Exception e) { tx.rollback(); System.err.println("例外錯誤!"); e.printStackTrace(); } finally { if (s.isOpen()) { s.close(); } }
※如果新增ca,會報「PropertyValueException: not-null property references a null or transient value: vo.CarAttr.car」的錯
修改測試
Session s = HibernateUtil2.getSession(); Transaction tx = s.beginTransaction(); try { Car c = new Car(); c.setCid(28); c.setName("寶石傑2"); CarAttr ca = new CarAttr(); // ca.setCaid(40); ca.setColor("藍2"); ca.setCc(530); ca.setCar(c); c.setCarAttr(ca); s.update(c); // s.update(ca);//會報錯 tx.commit(); } catch (Exception e) { tx.rollback(); System.err.println("例外錯誤!"); e.printStackTrace(); } finally { if (s.isOpen()) { s.close(); } }
※如果修改ca,會報「TransientObjectException: The given object has a null identifier: vo.CarAttr」的錯
※修改時,一定要塞Car的PK值,這很正常,不然資料庫怎麼會知道你要改哪一筆?
我本來想說塞Car的PK就會幫我關聯到FK了,但看起來是沒有(我有試過兩邊都加cascade="all",但還是沒用),所以CarAttr不塞會變成新增(因為PK沒有當然是新增)
查詢主表測試
Car car = (Car) s.get(Car.class, 31); s.close(); System.out.println(car.getName()); System.out.println(car.getCarAttr().getColor());
※如果使用load,怎麼設都是不行的,因為session提早關了
※因為是查主表,所以設定要看Car.hbm.xml
fetch預設是select,上一篇的一對一雙向PK關聯,控制台顯示的是join,但這次真的是select了,
修改成join也真的變join了
※lazy我不打和false、proxy(預設)、no-proxy,我查詢都沒有問題
查詢從表測試
CarAttr carAttr = (CarAttr) s.get(CarAttr.class, 46); s.close(); System.out.println(carAttr.getColor()); System.out.println(carAttr.getCar().getName());
※使用load當然還是不行
※因為是查從表,所以設定要看CarAttr.hbm.xml,fetch不管怎麼設都是select,控制台都不會join了
※session提早關閉只能查到自己的表(CarAttr),查Car的會報session已經關閉的訊息,此時加上lazy="false",就可以查到了,lazy的其他兩個選項也都會報session已經關閉的訊息
刪除測試
Car c = new Car(); c.setCid(31); s.delete(c); tx.commit(); CarAttr ca = new CarAttr(); ca.setCaid(47); s.delete(ca); tx.commit();
※刪除主表OK,但資料庫要加ON DELETE CASCADE,我把cascade="all"拿掉還是可以
※刪除從表會出「PropertyValueException: not-null property references a null or transient value: vo.CarAttr.car」的錯
另一種刪除
Car c = new Car(); c.setCid(60); CarAttr ca = new CarAttr(); ca.setCaid(63);// 實務上須自行用Car.cid去資料庫撈CarAttr.caid出來 ca.setCar(c);// 一定要有 c.setCarAttr(ca); s.delete(c); tx.commit();
※這種寫法感覺多此一舉了,但如果上面的刪除,在資料庫沒有ON DELETE CASCADE,那也就加減用了
※Annotation設定
Car.java
@Entity @Table public class Car { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "OOO") @SequenceGenerator(name = "OOO", sequenceName = "CAR_SEQ") private Integer cid; private String name; @OneToOne(mappedBy = "car", cascade = CascadeType.ALL, fetch = FetchType.EAGER) private CarAttr carAttr; //setter/getter... }
※mappedBy裡面的字要對應CarAttr.java裡的屬性名稱
CarAttr.java
@Entity @Table(name = "CAR_ATTR") public class CarAttr { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "XXX") @SequenceGenerator(name = "XXX", sequenceName = "CAR_ATTR_SEQ") private Integer caid; private String color; private Integer cc; @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "CAR_CID") @LazyToOne(value = LazyToOneOption.FALSE) private Car car; //setter/getter... }
※和XML設定不一樣,是one-to-one,不是many-to-one加上unique="true"哦
測試只列出和XML設定不一樣的地方
新增測試
s.save(ca);會報「org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: vo.Car」的錯修改測試
s.update(ca),只會修改Car_Attr查主表
預設為join查從表
會查兩次預設為join,所以控制台會出現兩個(+),如果改為select,會出現一個(+),和兩次的select car_attr,很奇怪
刪除測試
刪主表在控制台只會出現刪主表,但實際上連從表也刪了(ON DELETE CASCADE)刪除從表只會刪從表