※XML設定
※Set
假設新的學期開始,每個人都有很多不同的課本,所以在資料庫增加兩個table,如下:
※前置作業
DROP TABLE PERSON PURGE; DROP TABLE BOOK PURGE; CREATE TABLE PERSON( PID VARCHAR(10) , PERSON_NAME VARCHAR(50) , CONSTRAINT PERSON_PK PRIMARY KEY(PID) ); CREATE TABLE BOOK( BID VARCHAR(50) , BOOK_NAME VARCHAR(200) , CONSTRAINT BOOK_FK FOREIGN KEY(BID) REFERENCES PERSON(PID) ON DELETE CASCADE );
Person.java
public class Person { private Integer pid; private String personName; private Set<Object> xxx = new HashSet<Object>(0); //setter/getter... }
※不需要Book.java,集合映射會去找
Person.hbm.xml
<class name="vo.Person" table="PERSON"> <id name="pid" type="java.lang.Integer"> <column name="PID" /> <generator class="assigned" /> </id> <property name="personName" type="java.lang.String"> <column name="PERSON_NAME" /> </property> <set name="xxx" table="BOOK"> <!--lazy="false"> --> <key not-null="true"> <column name="BID" /> </key> <element type="java.lang.String" column="BOOK_NAME"/> </set> </class>
測試類:
Session s = HibernateUtil2.getSession(); Transaction tx = s.beginTransaction(); try { Person p = new Person(); p.setPid(1); p.setPersonName("john"); p.getXxx().add("國文"); p.getXxx().add("英文"); p.getXxx().add("英文"); p.getXxx().add(null); s.saveOrUpdate(p); tx.commit(); Person person = (Person) s.get(Person.class, 1); //s.close(); System.out.println(person.getPersonName()); System.out.println(person.getXxx()); } catch (Exception e) { System.err.println("例外錯誤!"); e.printStackTrace(); tx.rollback(); } finally { if (s.isOpen()) { s.close(); } }
在控制台會顯示:
※第一次會執行select person,然後insert person,再連續執行三次的insert book
第二次發現資料庫有值時,inser person會變成delete book的動作,其他都一樣
※設定null是可以的,就算hbm.xml設定not-null="true"也沒用,其實我一直都在用3.6.10在測,屬性常常都沒什麼用,譬如前面的<property>或者它底下的<column>,我設定length,長度故意設的比資料庫還短,或者兩個都設,但還是新增修改成功
※上面的s.close()註解打開,一樣可以很順利執行完,應該是一級快取的關係
但如果只查詢且提找關閉,也就是把commit()上面的都註解掉且close打開,而且只查xxx,就會出「org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: vo.Person.xxx, no session or session was closed」的錯誤,這時就要把hbm.xml的lazy="false"註解打開即可解決,此時控制台會先執行select person後,再執行select book
再看一個例子:
Session s = HibernateUtil2.getSession(); Transaction tx = s.beginTransaction(); try { Person person = (Person) s.get(Person.class, 1); s.close(); // Detached Set<Object> p = new HashSet<Object>(); p.addAll(person.getXxx()); p.add("數學"); person.setXxx(p); HibernateUtil2.getSession().update(person); // Persistent HibernateUtil2.getSession().beginTransaction().commit(); } catch (Exception e) { System.err.println("例外錯誤!"); e.printStackTrace(); HibernateUtil2.getSession().beginTransaction().rollback(); } finally { if (s.isOpen()) { s.close(); } }
※尤於一查到就close,所以狀態已變成Detached,包括tx都依賴s,都不能用了,所以後面都是從HibernateUtil2開頭
※控制台會先select person-->select book-->update person-->delete book-->insert book三次
可以看出會整個bid為1的全部刪除再新增
※delete時,因為準備資料那裡有加on delete cascade,所以會幫book刪除
如果沒加就出錯,反正以資料庫為準,hibernate不幫忙,就算在set加cascade="all",還是沒用,這點很奇怪
※List
※前置作業
DROP TABLE PERSON PURGE; DROP TABLE BOOK PURGE; CREATE TABLE PERSON( PID VARCHAR(10) , PERSON_NAME VARCHAR(50) , CONSTRAINT PERSON_PK PRIMARY KEY(PID) ); CREATE TABLE BOOK( BID VARCHAR(50) , BOOK_NAME VARCHAR(200) , IND NUMBER(3), CONSTRAINT BOOK_FK FOREIGN KEY(BID) REFERENCES PERSON(PID) ON DELETE CASCADE );
※尤於index為關鍵字,只好取ind
Person.hbm.xml
<class name="vo.Person" table="PERSON"> <id name="pid" type="java.lang.Integer"> <column name="PID" /> <generator class="assigned" /> </id> <property name="personName" type="java.lang.String"> <column name="PERSON_NAME" /> </property> <list name="xxx" table="BOOK" lazy="false"> <key> <column name="BID" /> </key> <index column="IND" /> <!-- <list-index column="IND" /> --> <element type="java.lang.String" column="BOOK_NAME"/> </list> </class>
※list底下一定要有index,不然會報錯
※list-index和index我試的結果都可以
※測試類都和Set一樣,此時可以重覆,ind欄位預設會從0遞增,可以用base屬性改變
※Array
和List很像,只是hbm.xml的list改成array,而vo將List改成String[],其他都和上面的List一樣,也是index和list-index都可以
屬性有些不太一樣,像lazy,在Array就沒有這個屬性
屬性有些不太一樣,像lazy,在Array就沒有這個屬性
※Bag
和List很像,但沒有index,所以前置作業和Set一樣,而hbm.xml將List改成bag,index或list-index不要,其他都和上面的List一樣,還是有lazy屬性※Map
※前置作業
DROP TABLE PERSON PURGE; DROP TABLE BOOK PURGE; CREATE TABLE PERSON( PID VARCHAR(10) , PERSON_NAME VARCHAR(50) , CONSTRAINT PERSON_PK PRIMARY KEY(PID) ); CREATE TABLE BOOK( BID VARCHAR(10) , BOOK_NAME VARCHAR(50) , BOOK_PRICE NUMBER(4), CONSTRAINT BOOK_FK FOREIGN KEY(BID) REFERENCES PERSON(PID) ON DELETE CASCADE );
※增加BOOK_PRICE,將書名和書的價錢挷在一起變成Map
Person.hbm.xml
<class name="vo.Person" table="PERSON"> <id name="pid" type="java.lang.Integer"> <column name="PID" /> <generator class="assigned" /> </id> <property name="personName" type="java.lang.String"> <column name="PERSON_NAME" /> </property> <map name="xxx" table="BOOK" lazy="false"> <key> <column name="BID" /> </key> <map-key type="java.lang.String" column="BOOK_NAME" /> <!-- <index column="BOOK_NAME" type="java.lang.String" /> --> <element type="java.lang.Integer" column="BOOK_PRICE"/> </map> </class>
※map-key也可以用List的index,就是註解那一行,而list-index就沒這種屬性了
Person.java
public class Person { private Integer pid; private String personName; private Map<String, Integer> xxx = new HashMap<String, Integer>(0); //setter/getter... }
測試類:
Session s = HibernateUtil2.getSession(); Transaction tx = s.beginTransaction(); try { Person p = new Person(); p.setPid(1); p.setPersonName("john"); p.getXxx().put("國文", 120); p.getXxx().put("英文", 80); p.getXxx().put("數學", 60); s.saveOrUpdate(p); tx.commit(); Person person = (Person) s.get(Person.class, 1); s.close(); System.out.println(person.getXxx()); } catch (Exception e) { System.err.println("例外錯誤!"); e.printStackTrace(); HibernateUtil2.getSession().beginTransaction().rollback(); } finally { if (s.isOpen()) { s.close(); } }
※Annotation設定
Set和Bag
@Entity @Table(name = "PERSON") public class Person { @Id private Integer pid; @Column(name = "PERSON_NAME") private String personName; // @CollectionOfElements(targetElement=String.class, fetch = FetchType.EAGER) 已廢棄,用@ElementCollection @ElementCollection(targetClass = String.class, fetch = FetchType.EAGER) // @CollectionTable(name = "BOOK", joinColumns = { @JoinColumn(name = "BID")}) 和@JoinTable取其一,都可用 @JoinTable(name = "BOOK", joinColumns = { @JoinColumn(name = "BID") }) @Column(name = "BOOK_NAME") private Set<Object> xxx = new HashSet<Object>(); //setter/getter... }
※FetchType有LAZY和EAGER,EAGER就是急切,不懶的意思
※FetchType.LAZY 就是XML設定的fetch="select";
FetchType.EAGER就是XML設定的 fetch ="join"
List
//和Set一樣, 但多兩行,ooo要對應好, //不能和下面Array的Annotation混用,但不會報錯,只是IND永遠是Null,控制台的insert語句並不會包括IND @CollectionId(columns = @Column(name="IND", nullable=false), type=@Type(type="long"), generator = "ooo") @GenericGenerator(name="ooo",strategy="increment") private List<Object> xxx = new ArrayList<Object>();
※從1開始累加,我找不到像Array的base選項可以設定開始值,可能是以資料庫設定為主吧
※strategy共有13種,官網在這的5.1.2.2.1. Various additional generators
Array
//和Set一樣,不能和List的Annotation混用,會報錯 @IndexColumn(name = "IND", base = 1) private String[] xxx;
※base=1表示從1開始累加,XML也有
Map
@Entity @Table(name = "PERSON") public class Person { @Id private Integer pid; @Column(name = "PERSON_NAME") private String personName; @ElementCollection(targetClass=Integer.class, fetch = FetchType.EAGER) // @org.hibernate.annotations.MapKey(columns = { @Column(name = "BOOK_NAME", nullable = false) }) 已廢棄,用@MapKeyColumn @MapKeyColumn(name = "BOOK_NAME") @CollectionTable(name = "BOOK", joinColumns = { @JoinColumn(name = "BID") }) @Column(name = "BOOK_PRICE") private Map<String, Integer> xxx = new HashMap<String, Integer>(0); //setter/getter... }
※用MapKey時,注意import哪一個package,所以我將全名打出來,不過反正已廢棄了
沒有留言:
張貼留言