Hibernate读书笔记-----Hibernate的关联映射之1-1关联映射
二、1—1
无论是单向1-1映射关联,还是双休1-1映射关联,都有三种映射策略:基于主键、基于外键、采用连接表。
1、单向1-1映射关联
1.1、基于主键的单向1-1映射关联
对于基于主键的单向1-1关联,基于主键关联的持久化类不能拥有自己的主键生成器策略,它的主键由关联实体来负责生成。
是根据他自己的person属性来获得的。即他通过自身的一个getPerson()方法来获得Person对象。然后通过Person对象中的getID()方法获得id,然后赋值给自身id。这样就可以不需要自己来生成id。
采用基于主键的1-1关联时,应使用<one-to-one.../>元素来映射关联实体,配置<one-to-one.../>元素时需要指定一个name属性。
实例(Person<--IDCard)
Person
public class Person { private Integer id; private String name; private IDCard idCard; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public IDCard getIdCard() { return idCard; } public void setIdCard(IDCard idCard) { this.idCard = idCard; } }
IDCard:
public class IDCard { private Integer id; private String useLife; public String getUseLife() { return useLife; } public void setUseLife(String useLife) { this.useLife = useLife; } private Person person; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } }
映射文件:
Person.hbm.xml
<hibernate-mapping package="com.hibernate.domain"> <class name="Person" table="person"> <id name="id" column="personID"> <generator class="native" /> </id> <property name="name" column="personName" /> </class> </hibernate-mapping>
IDCard.hbm.xml
<hibernate-mapping package="com.hibernate.domain"> <class name="IDCard" table="idCard"> <id name="id" column="idCardID"> <!-- 基于主键关联时,主键生成策略是foreign,表明根据关联类的主键来生成该实体的主键 --> <generator class="foreign"> <!-- 指定引用关联实体的属性名 --> <param name="property">person</param> </generator> </id> <property name="useLife" column="useLife" /> <one-to-one name="person" constrained="true" /> </class> </hibernate-mapping>
在上面的映射文件中,采用这种关联映射策略是,idCard表作为从表,此时idCard表的主键将没有自己的主键生成策略。他是根据person表中的主键来生成的。同时从表(idCard)里记录的主键将会与主表(person)里的记录保存一致。
IDCard中的id既是主键也是外键。那么idCard表如何通过person表来生成主键的呢?
foreign生成器中有一个元素:property。这个元素代表了该表的外键是从哪一个属性中获得的。通过上面的配置可以发现idCard表的外键是从person属性中获得的。它的外键就是通过person属性中的getId获得id,然后将该id直接赋给id。
使用constrained="true"表明该类对应表和被关联的对象所对应的数据库表之间通过一个外键引用对主键进行约束。
通过上面的配置后,就可以对两个实体进行操作了:
static void add(){ Session session = null; Transaction tx = null; try{ session = HibernateUtil.getSession(); tx = session.beginTransaction(); IDCard idCard = new IDCard(); idCard.setUseLife("10年"); Person person = new Person(); person.setName("chentmt"); idCard.setPerson(person); session.save(person); session.save(idCard); tx.commit(); }finally{ if(session!=null) session.close(); } }
1.2、基于外键的单向1-1映射关联
基于外键的关联映射与一般的N-1关联映射没有多大区别,只需要将<many-to-one.../>元素中增加unique="true"属性即可。如下:
<hibernate-mapping package="com.hibernate.domain">
<class name="IDCard" table="idCard">
<id name="id" column="idCardID">
<generator class="native" />
</id>
<property name="useLife" column="useLife" />
<many-to-one name="person" column="personID" unique="true" />
</class>
</hibernate-mapping>
unique="true"代表了idCard表的personid外键列上增加了唯一约束--这样就完成基于外键的单向1-1映射了。
其他的配置文件和持久化类都不需要做修改。
1.3、有连接表的单向1-1映射关联
和上面差不多,只需要在有连接表的N-1关联映射中的<many-to-one.../>元素增加一个unique="true"即可。如下:
<hibernate-mapping package="com.hibernate.domain"> <class name="IDCard" table="idCard"> <id name="id" column="idCardID"> <generator class="native" /> </id> <property name="useLife" column="useLife" /> <!-- 使用join元素强制使用连接表 --> <join table="person_idCard"> <key column="idCardID"/> <many-to-one name="person" column="personID" unique="true"/> </join> </class> </hibernate-mapping>
2、双向1-1关联映射
对于双向的1-1关联需要让两个持久化列都增加引用关联实体的属性,并为该属性提供setter和getter方法。持久化类:如上。
2.1基于主键的双向1-1关联映射
基于主键的双向1-1关联映射同样需要在一端的主键生成器策略使用foreign策略,表明将根据对方的主键来生成自己的主键,本实体不能拥有自己的主键生成策略。另一端需要使用<one-to-one.../>元素用来映射关联实体,否则就变成了单向的。映射文件如下:
Person.hbm.xml
<hibernate-mapping package="com.hibernate.domain"> <class name="Person" table="person"> <id name="id" column="personID"> <generator class="native" /> </id> <property name="name" column="personName" /> <!-- 映射关联实体 --> <one-to-one name="idCard" /> </class> </hibernate-mapping>
IDCard.hbm.xml
<hibernate-mapping package="com.hibernate.domain"> <class name="IDCard" table="idCard"> <id name="id" column="idCardID"> <!-- 基于主键关联时,主键生成策略是foreign,表明根据关联类的主键来生成该实体的主键 --> <generator class="foreign"> <!-- 指定引用关联实体的属性名 --> <param name="property">person</param> </generator> </id> <property name="useLife" column="useLife" /> <one-to-one name="person" constrained="true" /> </class> </hibernate-mapping>
对于操作这个两个实体增加同时。因为可以通过两边来访问,所以这里就演示查询
这里查询分为两种:基于主表查询和基于从表查询
基于主表查询(通过主表查询从表记录)
static void query(int personid){ Session session = null; try{ session = HibernateUtil.getSession(); Person person = (Person) session.get(Person.class, personid); System.out.println("useLife="+person.getIdCard().getUseLife()); }finally{ if(session!=null) session.close(); } }
我们知道对于N-1关联查询的时候,系统会产生两条sql查询语句来检索从表对象:一条先查询主表,然后根据外键从从表中获取相对应的记录。但是对于1-1关联时,它并不是产生两条sql语句来查询。而是一条sql语句,通过外连接来连接两张表的。如下
Hibernate: select person0_.personID as personID1_1_, person0_.personName as personName1_1_, idcard1_.idCardID as idCardID2_0_, idcard1_.useLife as useLife2_0_ from person person0_ left outer join idCard idcard1_ on person0_.personID=idcard1_.idCardID where person0_.personID=?
基于从表查询(通过从表查询主表)
static void query(int personid){ Session session = null; try{ session = HibernateUtil.getSession(); IDCard idCard = (IDCard) session.get(IDCard.class, 1); System.out.println(idCard.getPerson().getName()); }finally{ if(session!=null) session.close(); } }
通过从表查询主表与通过主表查询从表又有点不同了。在这里它不再是产生一条sql语句,而是两条。如下:
Hibernate: select idcard0_.idCardID as idCardID2_0_, idcard0_.useLife as useLife2_0_ from idCard idcard0_ where idcard0_.idCardID=? Hibernate: select person0_.personID as personID1_1_, person0_.personName as personName1_1_, idcard1_.idCardID as idCardID2_0_, idcard1_.useLife as useLife2_0_ from person person0_ left outer join idCard idcard1_ on person0_.personID=idcard1_.idCardID where person0_.personID=?
它会先查询从表,获取记录,然后再通过外连接方式连接两张表根据personID获取记录。
2.2基于外键的双向1-1关联映射
对于基于外键的双向1-1关联映射。外键可以存放任意一边。需要存放外键的一端,需要增加<many-to-one../>元素。同时也需要添加unique="true"属性。
对于双向单位1-1关联映射,两个实体原本是处于平等状态的。但是当我们选择一个表来增加外键后,该表就变成了从表,另一个表变成主表。
另一端需要使用<one-to-one../>元素,该元素需要使用name属性指定关联属性名,同时也需要使用property-ref属性来指定引用关联类的属性。property-ref的值是从表中的引用属性。
映射文件如下:
Person.hbm.xml
<hibernate-mapping package="com.hibernate.domain"> <class name="Person" table="person"> <id name="id" column="personID"> <generator class="native" /> </id> <property name="name" column="personName" /> <!-- 映射关联实体 --> <one-to-one name="idCard" property-ref="person"/> </class> </hibernate-mapping>
IDCard.hbm.xml
<hibernate-mapping package="com.hibernate.domain"> <class name="IDCard" table="idCard"> <id name="id" column="idCardID"> <generator class="native" /> </id> <property name="useLife" column="useLife" /> <many-to-one name="person" column="personID" unique="true" /> </class> </hibernate-mapping>
2.3有连接表的双向1-1关联映射
采用这个方式是非常少的,因为这中情况映射相当复杂,数据模型繁琐,一般不推荐采用这种策略。
双向1-1关联两端都需要使用<join.../>元素指定连接表,<join../>元素的table属性用于指定连接表的表名,所有两端的table属性值应该是一致的。同时两个也需要增加key元素映射连接表的外键列,还需要增加<many-to-one../.>元素映射关联属性,两个<many-to-one.../>元素都需要增加unique="true"属性。注意这里两端的key元素和<many-to-one../>中column的属性值应该是相反的。
同时为了让hibernate在连接表的两个数据列上增加唯一约束,映射文件应该为两个<key.../>元素指定unique="true"。
当使用连接表来建立1-1关联关系时,两个实体应该是绝对的平等,不存在任何的主从约束关系,hibernate映射他们的连接表时,将会选择某一个外键作为连接表的主键--因此两个持久化类的映射文件依然不是完全相同的。映射文件必须在一端的<join.../>元素中指定inverse="true",而另一端则指定option="true"。下面是两个映射文件
Person.hbm.xml
<hibernate-mapping package="com.hibernate.domain"> <class name="Person" table="person"> <id name="id" column="personID"> <generator class="native" /> </id> <property name="name" column="personName" /> <join table="person_idCard" inverse="true"> <key column="personID" unique="true" /> <many-to-one name="idCard" column="idCardID" unique="true" /> </join> </class> </hibernate-mapping>
IDCard.hbm.xml
<hibernate-mapping package="com.hibernate.domain"> <class name="IDCard" table="idCard"> <id name="id" column="idCardID"> <generator class="native" /> </id> <property name="useLife" column="useLife" /> <join table="person_idCard" optional="true"> <key column="idCardID" unique="true" /> <many-to-one name="person" column="personID" unique="true"></many-to-one> </join> </class> </hibernate-mapping>