接上篇。
一对一双向关联的配置
说明:仍是以person与address为例,在person类中有一个类型为address的变量,在address类中有一个类型为person的变量。
一对一 主键 双向关联配置(如下)
<hibernate-mapping>
<class name="entity.Person" table="person">
<id name="id" type="java.lang.Long">
<column name="id" />
<generator class="identity" />
</id>
<property name="name" type="java.lang.String">
<column name="name" length="24" not-null="true">
<comment>姓名</comment>
</column>
</property>
<!-- cascade="all":在保存person对象的时候,级联保存person对象关联的address对象 -->
<one-to-one name="address" cascade="all" />
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="entity.Address" table="address" catalog="mydb">
<id name="id" type="java.lang.Long">
<column name="id" />
<!-- class="foreign": 一对一主键映射中,使用另外一个相关联的对象的标识符 -->
<generator class="foreign">
<param name="property">person</param>
</generator>
</id>
<property name="detail" type="java.lang.String">
<column name="detail" length="120" not-null="true">
<comment>详细地址</comment>
</column>
</property>
<!-- 表示在address表存在一个外键约束,外键参考相关联的表person -->
<one-to-one name="person" constrained="true" />
</class>
</hibernate-mapping>
一对一 外键 双向关联配置(如下)
<hibernate-mapping>
<class name="entity.Person" table="person">
<id name="id" type="java.lang.Long">
<column name="id" />
<generator class="identity" />
</id>
<property name="name" type="java.lang.String">
<column name="name" length="24" not-null="true">
<comment>姓名</comment>
</column>
</property>
<one-to-one name="address" cascade="all" />
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="entity.Address" table="address" catalog="testdb">
<id name="id" type="java.lang.Long">
<column name="id" />
<generator class="identity" />
</id>
<property name="detail" type="java.lang.String">
<column name="detail" length="120" not-null="true">
<comment>详细地址</comment>
</column>
</property>
<many-to-one name="person" class="entity.Person"
fetch="select" unique="true">
<column name="personid">
<comment>人的ID</comment>
</column>
</many-to-one>
</class>
</hibernate-mapping>
一对一 连接表 双向关联配置(如下)
<hibernate-mapping>
<class name="com.lavasoft.sx._1_1_tab.Person11tab_sx" table="PERSON_11tab_sx">
<id name="personid">
<generator class="identity"/>
</id>
<property name="name"/>
<property name="age"/>
<join table="join_11tab_sx"
optional="true">
<key column="personid"
unique="true"/>
<many-to-one name="address11tab_sx"
column="addressid"
not-null="true"
unique="true"/>
</join>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="com.lavasoft.sx._1_1_tab.Address11tab_sx" table="ADDRESS_11tab_sx">
<id name="addressid">
<generator class="identity"/>
</id>
<property name="addressdetail"/>
<join table="join_11tab_sx"
optional="true"
inverse="true">
<key column="addressid"
unique="true"/>
<many-to-one name="person11tab_sx" column="personid"
not-null="true" unique="true"/>
</join>
</class>
</hibernate-mapping>
*********一对一 主键 双向关联*************
首先,还是我们根据需求,确定用一对一 主键 双向关联,那么,两个表肯定是同样的主键(要不还能是哪一列充当俩个表之间的桥梁),有人会说,我可以在person表的最后添加上addres表的主键列,也可以架起两个表之间的桥梁,但是这样就不是主键的关联了而是外键方式的关联。
同样,我们一行一行的往下读,前面的太简单就不多说了,在person表中,最后出现一个one-to-one,指定了关联查询address表后,把查到的数据封装成address对象,复制给name=“”引号中的变量,供person调用(name=“”引号内的变量名是在person类内作为成员变量存在的)。
可能有人注意到了,上篇说道,有foreign的时候,采用one-to-one 啊,确实,在person中没有配置这个啊,这是因为,这里设置的person表可以生成主键,但是address表的主键就得通过foreign来设置了。因为是双向关联的嘛,总得有一个是主体,另一个引用主体的内容。
最后,在person中有个cascade="all" 表示对增删改查都得做级联操作,也就是更改两个表的内容。
再看address的配置,对主键的增长方式generator选择的是foreign,引用别的表的主键作为自己的主键。引用的是谁的呢?标签体内写着一个叫“person”的属性。也就是hibernate会在address类中找一个名为person的变量,看它的类型是什么,再看这个类创建有没有对应的表,有的话引用它的主键。
同样,最后有个one-to-one标签。表示关联查询后,查询到的结果要封装成person属性对应的类的对象。然后赋给person属性(通过address类中写好的get/set方法),以供address调用。
one-to- one的双向关联中,必须设置constrained=true,要不然会有重复数据读。(这一句为引用,参考附在最后)
*********一对一 外键 双向关联*************
确定好使用外键关联查询的话,也就想上面分析的,得在其中一张表的最后一列添加另一张表的主键,架起两张表的桥梁,就可以关联查询啦。
这里是在address表后添加person的主键作为外键。
在看了上一篇的同学可能会有疑问,为什么person表用个的是one-to-one而address表用的是more-to-one呢?其实,我刚刚也在为什么,现在大体明白了,简述下:上一篇中对one-to-one的使用场景概括的不够笼统、不够抽象,上篇说道在遇到id的generator是foreign的时候用one-to-one,其实现在看来应该是,当确定两个表之间的桥梁已将架好之后,若表还需要进行关联查询并且符合一对一的条件就用one-to-one。
已现在的配置为例子,address的配置中用的more-to-one在address最后一列加上了列字段为personid的列,引用的是person表的主键。所以一是两个表之间的桥梁已经架好了,而是是表address使用的more-to-one就能被hibernate识别并完成关联person表查询的功能。但再看person表,它也需要进行关联查询,查询address的内容,并且符合one-to-one的条件。
好了,上面是规律的总结。那我们再深入下,从hibernate看到one-to-one后会执行什么动作再来分析。其实hibernate看到one-to-one和more-to-one标签后,应该会想:嗯,这时要我去进行关联查询了,让我看看标签的name=“”引号内的属性名是什么(如在address配置中,就是person),然后找到属性对应的类型(也就是person类),查看hibernate有没有创建该类对应的表(person类对应的表),通过架好的桥梁(相同的主键、外键、连接表)进行关联查询(查询person的表),结果封装成那个类的对象(封装成person类的对象),赋值到属性中(赋值给address类中的person类型的成员变量),供调用。
从上面的功能来看,one-to-one和more-to-one的不同也就是表的关系不同,一对一的表关系或多对一的表关系,而且more-to-one还会转化成one-to-one。
但是one-to-one和more-to-one不同的是,hibernate看到more-to-one,会在该表中创建一列,列名根据column指定,若没有写column,则默认与name=“”一致,也就是说,more-to-one有能力搭建桥梁,而one-to-one只能使用这个桥梁进行关联查询。
所以,我们回想下,在一对一的外键单向关联时,使用的是more-to-one,利用unique=“true”使其变成one-to-one。而一对一的主键单向关联之所以能够使用one-to-one,就是因为foreign属性把桥梁搭建好了。
理解了hibernate遇到one-to-one和more-to-one后的不同执行过程,我们在看一下more-to-one,利用unique=“true”使其变成one-to-one,应该是升级版的one-to-one,它也能搭建桥梁了。我们再从逻辑上理解下more-to-one转为one-to-one的过程。
这里要理解的是:more-to-one的意思,谁是more谁是one。还是现在一对一 外键 双向关联的例子,在address的配置中,写的是more-to-one,也就是可以有more个address对应one个person。在数据表上的体现就是:more条记录或说数据的addressid(根据主键特性,它们肯定是不同的)都有相同的(one个)personid值。而我们在more-to-one中指定unique=“true”,也就是说:不再是more个address对应one个person,它要求address是唯一的,那就成了one个address对应one个person。是more-to-one的一个特例,虽然写起来还是more-to-one,但内涵已变成one-to-one。同时它又具有one-to-one不具有的搭建桥梁的能力,所以说是升级版的one-to-one。
*********一对一 连接表 双向关联*************
当确定使用一对一 连接表 双向关联后,我们分析下:
有了上一篇的基础,就好理解了。假设hibernate一行一行的看,看到join后就知道,奥,原来是要创建一个连接表当作桥梁啊,好我给你们创建一个,名字就叫你后面紧跟着写的table=“”引号中的内容。并且hibernate还挺聪明,知道是要通过连接表(中间表)进行关联查询,那当前正在配置的这张表的主键妥妥的得放入连接表(中间表)才行。(中间表的逻辑就是把要关联查询的两个表的主键都放到这一张表中,A表的主键对应着B表的主键,选择A表的一条数据后,根据该数据的主键一般是id,到中间表查找对应的B表的id,再根据找到的B表的id到B表查询出数据。)
hibernate就是这么聪明,所以接下来的key中,只让你写列名column=“”,也就是为该表的主键放到中间表某一列的列字段起个名字。接下来就是more-to-one加上unique=“true”,变成升级版的one-to-one,可以搭建桥梁还能关联查询。
所以就在连接表(中间表)添加了一个叫column=“addressid”的列字段,与该字段对应的是person类中一个叫做name="address11tab_sx" 名为address11tab_sx的变量,打开person类看到变量address11tab_sx是address类型的,hibernate就很聪明,找了下address类是否创建了对应的表。
根据<class name="com.lavasoft.sx._1_1_tab.Address11tab_sx" table="ADDRESS_11tab_sx">,hibernate找到了这张表,并把主键内容加到连接表(中间表)的addressid列字段中。
但是要小心,这里有个小坑。那就是:
在address和person表的配置中,都会配置连接表(中间表)的列字段名字。我们要使其名字统一。
那为什么都要设置名字呢?如果大家还记得hibernate看到more-to-one后执行的动作或许会理解。
more-to-one是设置在join标签中,也就是在连接表中设置(虽然看起来是在person的配置中),所以在hibernate在连接表开辟一个列,并自动把主键所在列搬过去,然我们制定这个列的名称之外,我们还要搭建person表与连接表的桥梁,所以要用到more-to-one。同理,也需要搭建起address表和连接表的桥梁,这样最终才能实现person表和address表的关联。所以都要用到more-to-one来分别搭建两表与连接表间的桥梁。
在person中我们要指定主键在连接表的列字段,同样,在address中我们也要指定它的主键在连接表的列字段。这样就相当于中间表的两个列字段已经确定了。而在more-to-one中,如果我们不指定column的值,默认与name的值相同。这将很可能导致问题。
以person举例,person已经指定了person的主键在连接表中的列名叫做personid,在more-to-one中如果不设置column的值,那么address的主键所在列会默认同name一样,也就是address11tab_sx。然而在address中我们又指定了address的主键在连接表的列名叫addressid。这就会冲突。
当然,以上是我的理解,按照我推测的规则,如果指定列名不妥,会出错。当然不排除hibernate够聪明,它以<key>标签中的 column值作为最终参考,那是再好不过了,我们就可以不必写more-to-one中的column值,它自己去判断。这个推测还需要实际中去实验一下。
****************一对多 双向关联配置就这样********
下面是参考的资料:
http://lavasoft.blog.51cto.com/62575/39398/ 强烈推荐先看,博文写了14种情况下的配置,我就是看了这个再结合其他资料摸索的规律
http://blog.sina.com.cn/s/blog_62f0eaa80101bpaf.html
http://blog.sina.com.cn/s/blog_62f0eaa80101bpah.html
http://blog.youkuaiyun.com/sanjy523892105/article/details/7061602
http://blog.youkuaiyun.com/linminqin/article/details/6324567
http://blog.youkuaiyun.com/jialinqiang/article/details/8704538