※準備工作
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)刪除從表只會刪從表
沒有留言:
張貼留言