而單雙向又分PK關聯和FK關聯,一樣也是分成XML和Annotation設定
官網連結
※XML設定
※一對一雙向PK關聯
準備工作
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), CONSTRAINT CAR_ATTR_PK PRIMARY KEY (CAID) );
※車子表對應車的屬性表
hibernate.cfg.xml
要增加xml連到hbm.xml的設定<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <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">username</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> <!-- <property name="hibernate.connection.autocommit">true</property> --> <!-- <property name="hibernate.hbm2ddl.auto">update</property> --> <property name="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property> <property name="hibernate.current_session_context_class">thread</property> <!-- <property name="hibernate.cache.use_second_level_cache">true</property> --> <!-- <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property> --> <!-- <property name="hibernate.QueryCacheEnabled">true</property> --> <!-- <property name="hibernate.cache.use_query_cache">true</property> --> <!-- <mapping resource="vo/Car.hbm.xml" /> --> <!-- <mapping resource="vo/CarAttr.hbm.xml" /> --> <mapping class="vo.Car" /> <mapping class="vo.CarAttr" /> <!-- <class-cache usage="read-only" class="vo.Emp"/> --> </session-factory> </hibernate-configuration>
Car.hbm.xml
<class name="vo.Car" table="CAR"> <id name="cid" type="java.lang.Integer"> <column name="CID" /> <generator class="assigned" /> </id> <property name="name" type="java.lang.String"> <column name="NAME" /> </property> <one-to-one name="carAttr" class="vo.CarAttr" cascade="all" /> </class>
※generator設定成assigned表示用寫程式的方式給值,如測試類的c.setCid(1);
我是Oracle的,還可以設成sequence,如下:
<generator class="sequence"> <param name="sequence">XXX</param> </generator>
※而資料庫就要有類似「CREATE SEQUENCE XXX;的語法」,Hibernate就會自動給值
當然還有其他方式,什麼hilo等等的
※cascade表示兩張表的互動關係,共有十種方式(其中delete-orphan用在一對多),看官網
.none(預設):無互動
.save-update:新增和修改才互動
.delete:刪除才互動
.all:等同save-update+delete
CarAttr.hbm.xml
<class name="vo.CarAttr" table="CAR_ATTR"> <id name="caid" type="java.lang.Integer"> <column name="CAID" /> <generator class="foreign"> <param name="property">car</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> <one-to-one name="car" class="vo.Car" constrained="true" /> </class>
※generator為foreign表示與Car用一樣的主鍵
※one-to-one 的constrained為true表示這張表的FK要對應另外一張表的PK,會影響save()和delete()的順序,constrained的說明看5.1.7.2. Sharing the primary key with the associated entity的4和8
※generator的設定可和Car的互換
但本來是為Car塞PK,CarAttr當然就不用,但如果塞了會報錯
互換後,就要為CarAttr的PK塞值,Car可不用,但如果塞了會報錯
Car.java
public class Car { private Integer cid; private String name; private CarAttr carAttr; // setter/getter... }
※Car必需有CarAttr; 而CarAttr也必需有Car,所以這樣才叫雙向
CarAttr.java
public class CarAttr { private Integer caid; private String color; private Integer cc; private Car car; // setter/getter... }
新增測試
Session s = HibernateUtil2.getSession(); Transaction tx = s.beginTransaction(); try { Car c = new Car(); // c.setCid(1); c.setName("寶石傑"); CarAttr ca = new CarAttr(); ca.setColor("藍"); ca.setCc(520); // 互塞 ca.setCar(c); c.setCarAttr(ca); s.save(c); // s.save(ca);// 只會新增car_attr tx.commit(); } catch (Exception e) { tx.rollback(); System.err.println("例外錯誤!"); e.printStackTrace(); } finally { if (s.isOpen()) { s.close(); } }
※ca.setCar(c)一定要有,不然會出「id.IdentifierGenerationException: attempted to assign id from null one-to-one property [vo.CarAttr.car]」的錯,看來是這行在抓Car的id
※id策略是assigned,save(ca)時,只會新增car_attr; id策略是sequence時,就沒問題,但如果再自行塞id,這時又只會新增car_attr,這點很奇怪
修改測試
Car c = new Car(); c.setCid(45); c.setName("寶石傑45"); CarAttr ca = new CarAttr(); ca.setColor("藍45"); ca.setCc(520); ca.setCaid(c.getCid()); // 互塞 ca.setCar(c); c.setCarAttr(ca); s.update(c); // s.update(ca);// 只會修改car_attr tx.commit();
※ca還是要塞id,不然會出兩種錯,分別是「exception.ConstraintViolationException: Could not execute JDBC batch update」和「java.sql.BatchUpdateException: ORA-00001: 違反必須為唯一的限制條件 (xxx.CAR_ATTR_PK)」,這個問題我搞了很久,感覺很奇怪,Hibernate居然沒幫忙
※如果用s.update(ca),只會修改car_attr
查詢Car測試
Car car = (Car) s.get(Car.class, 45); s.close(); System.out.println(car.getName()); System.out.println(car.getCarAttr());
※使用load當然是不行
※控制台會顯示:
Hibernate:
select
car0_.CID as CID0_1_,
car0_.NAME as NAME0_1_,
carattr1_.CAID as CAID1_0_,
carattr1_.COLOR as COLOR1_0_,
carattr1_.CC as CC1_0_
from
CAR car0_,
CAR_ATTR carattr1_
where
car0_.CID=carattr1_.CAID(+)
and car0_.CID=?<br />
是join的寫法,官網說預設是select,但我看控制台就是上面的結果,是join,可在Car.hbm.xml的one-to-one增加fetch="select",控制台會變成
Hibernate:
select
car0_.CID as CID0_0_,
car0_.NAME as NAME0_0_
from
CAR car0_
where
car0_.CID=?
Hibernate:
select
carattr0_.CAID as CAID1_0_,
carattr0_.COLOR as COLOR1_0_,
carattr0_.CC as CC1_0_
from
CAR_ATTR carattr0_
where
carattr0_.CAID=?
查詢Car_Attr測試
CarAttr carAttr = (CarAttr) s.get(CarAttr.class, 45); s.close(); System.out.println(carAttr.getColor()); System.out.println(carAttr.getCar().getName());
※只能查自己的,一執行到Car就會報「SessionException: Session is closed」,但只要在CarAttr.hbm.xml的one-to-one加上lazy="false"就可以了,查詢的結果一樣是join,可是將fetch="select"沒有用,永遠都是join
※lazy有false、proxy(預設)、no-proxy,並沒有true
刪除測試
Car c = new Car(); c.setCid(49); CarAttr ca = new CarAttr(); ca.setCaid(c.getCid()); c.setCarAttr(ca); s.delete(c); tx.commit();
※ca也必須塞値,不然只會刪Car而已
※如果用s.delete(ca),只會刪car_attr,除非加上ca.setCar(c);
-----------------------------------------------------------------------------------------
以上是修改官方的寫法,還有一種寫法是將car和carAttr全部都用<generator class="assigned" />
Car.hbm.xml
<class name="vo.Car" table="CAR"> <id name="cid" type="java.lang.Integer"> <column name="CID" /> <generator class="assigned" /> </id> <property name="name" type="java.lang.String"> <column name="NAME" /> </property> <one-to-one name="carAttr" class="vo.CarAttr" cascade="all" fetch="select"/> </class>
CarAttr.hbm.xml
<class name="vo.CarAttr" table="CAR_ATTR"> <id name="caid" type="java.lang.Integer"> <column name="CAID" /> <generator class="assigned" /> </id> <property name="color" type="java.lang.String"> <column name="COLOR" /> </property> <property name="cc" type="java.lang.Integer"> <column name="CC" /> </property> <one-to-one name="car" class="vo.Car" constrained="true" cascade="all" lazy="false" fetch="join"/> </class>
新增測試
Car c = new Car(); c.setCid(8); c.setName("寶石傑"); CarAttr ca = new CarAttr(); ca.setColor("藍"); ca.setCc(520); ca.setCaid(c.getCid()); // 互塞 ca.setCar(c);// 還是要有 c.setCarAttr(ca); s.save(c); // s.save(ca);// 這行也行 tx.commit();
修改測試
Car c = new Car(); c.setCid(8); c.setName("寶石傑88"); CarAttr ca = new CarAttr(); ca.setColor("藍88"); ca.setCc(520); ca.setCaid(c.getCid()); // 互塞 ca.setCar(c);// 還是要有 c.setCarAttr(ca); s.update(c); // s.update(ca);// 這行也行 tx.commit();
查詢和刪除的測試和上面都一樣
個人感覺做起來比官方的還要順暢※Annotation設定
hibernate.cfg.xml變成這兩行
<mapping class="vo.Car" /> <mapping class="vo.CarAttr" />
Car.java
@Entity @Table public class Car { @Id private Integer cid; private String name; @OneToOne(mappedBy = "car", cascade = CascadeType.ALL, fetch = FetchType.LAZY) private CarAttr carAttr; //setter/getter... }
※FetchType有兩種值,EAGER(預設)和LAZY,EAGER就是XML設定的fetch="join"; LAZY就是XML設定的fetch="select"
※mappedBy裡面的字要對應CarAttr.java裡的屬性名稱
CarAttr.java
import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToOne; import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.Table; import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.Parameter; @Entity @Table(name = "CAR_ATTR") public class CarAttr { @Id @GenericGenerator(name = "xxx", strategy = "foreign", parameters = { @Parameter(name = "property", value = "car") }) @GeneratedValue(generator = "xxx") private Integer caid; private String color; private Integer cc; @OneToOne(fetch = FetchType.LAZY) @PrimaryKeyJoinColumn private Car car; //setter/getter... }
增刪改查的測試只有一個地方不同,那就是查詢CarAttr時,FetchType.LAZY和FetchType.EAGER(也就是select和join)都有用了,這點居然不同步
※sequence設定是這樣
Car.java
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "XXX") @SequenceGenerator(name = "XXX", sequenceName = "CAR_SEQ")
※XXX要對應好,sequenceName對應資料庫的sequence名稱
沒有留言:
張貼留言