今天看资料,看到了用户角色和权限的关系分析,一个角色可以有多个权限,一个权限可以对应多个用户。因此总体上是多对多的关系。但资料中在hibernate中配置时使用的是联合主键配置,当时看不太懂,网上查了hibernate关联映射学了半天还算有收获,但回头一看,还是没能解决资料上看不懂的这个问题,这才又把资料上的一个关键字composite加入关键字进行搜索,这才知道这是联合主键配置映射关系。在搜索是也找到了有人说不提倡使用联合主键:http://zhidao.baidu.com/link?url=b592rkm5zNtWeq64ccfhP5KmQ2wYhsTF0TCFZWC-FQMS5FQMgvO3uKDdMtZ0cfpoqWawVrfBbjbfcBFuPE6ycK
原来看了半天的hibernate实体关联关系映射,本以为掌握所有情况了(确实解决了所有情况,但没有用联合主键的方式),结果联合主键楞是看不懂了,可能看的太多迷糊了,这半天看的其实很够了,能应付任何情况了。
首先特别推荐这片博文:http://lavasoft.blog.51cto.com/62575/39398/ 罗列了所有的情况,给出每种情况的配置方案,看懂这些就足够了。
下面是自己总结:
按自己的习惯来对hibernate关联关系映射进行分类:
--> 一对一
--> 一对一单向关联
--> 一对一外键单向关联
--> 一对一主键单向关联
--> 一对一 连接表 单向关联
--> 一对一双向关联
--> 一对一外键单向关联
--> 一对一主键双向关联
--> 一对一 连接表 双向关联
--> 一对多与多对一
--> 一对多单向关联
--> 一对多外键单向关联
--> 一对多 连接表 单向关联
--> 多对一单向关联
--> 多对一外键单向关联
-->多对一 连接表 单向关联
--> 一对多(多对一)双向关联 (因为是双向关联,一对多也就是多对一)
--> 一对多(多对一)外键双向关联
-->一对多(多对一) 连接表 双向关联
--> 多对多
--> 多对多单向关联
--> 多对多双向关联
下面按照自己的理解,尽量将自己带入hibernate的角色(尽管有点可笑,hibernate只是机械的程序而已-_-||)。看看如果自己编程一行行的代码,怎么处理这么容易绕晕人的关联配置。
说明:以下例子都是以person类和address类为例,person中有id,name,age三个基本属性,address中有id,addressdetail两个基本属性。当关联查询时,会在类中写上另外一个类的成员属性。如在一对一单向关联时,person中有address的属性。(建议先看上文中特别推荐的博文,再顺着这篇文章理下思路)。
--> 一对一(一个person对应一个address的情况)
--> 一对一单向
下面会贴出三种 一对一单向关联 的配置,建议复制到三个txt中,按照最后的思路对比来看。这三种配置中,都在person类中有address类型的成员变量。
一对一 主键 单向关联的配置(如下)
<hibernate-mapping>
<class name="com.lavasoft.dx._1_1_pk.Person11pk" table="PERSON_11pk">
<id name="personid" column="presonid">
<!--基于主键关联时,主键生成策略是foreign,表明根据关联类生成主键-->
<generator class="foreign">
<!--关联持久化类的属性名-->
<param name="property">address11pk</param>
</generator>
</id>
<property name="name"/>
<property name="age"/>
<!--用于映射1-1关联-->
<one-to-one name="address11pk" constrained="true"/>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="com.lavasoft.dx._1_1_pk.Address11pk" table="ADDRESS_11pk">
<id name="addressid">
<generator class="identity"/>
</id>
<property name="addressdetail"/>
</class>
</hibernate-mapping>
一对一 外键 单向关联配置(如下)
<hibernate-mapping>
<class name="com.lavasoft.dx._1_1_fk.Address11fk" table="ADDRESS_11fk">
<id name="addressid">
<generator class="identity"/>
</id>
<property name="addressdetail"/>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="com.lavasoft.dx._1_1_fk.Person11fk" table="PERSON_11fk">
<id name="personid">
<generator class="identity"/>
</id>
<property name="name"/>
<property name="age"/>
<!--用来映射关联PO column是Address在该表中的外键列名,增加unique变成“1-1”-->
<many-to-one name="address11fk" column="addressId" unique="true"/>
</class>
</hibernate-mapping>
一对一 连接表 单向关联配置(如下)
<hibernate-mapping>
<class name="com.lavasoft.dx._1_1_tab.Person11tab" table="PERSON_11tab">
<id name="personid">
<generator class="identity"/>
</id>
<property name="name"/>
<property name="age"/>
<!--使用join元素显式确定链接表-->
<join table="join_11tab">
<key column="personid"/>
<!--映射1-1关联属性,其中unique=“true”属性确定为“1-1”-->
<many-to-one name="address11tab" unique="true"/>
</join>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="com.lavasoft.dx._1_1_tab.Address11tab" table="ADDRESS_11tab">
<id name="addressid">
<generator class="identity"/>
</id>
<property name="addressdetail"/>
</class>
</hibernate-mapping>
*******一对一 主键 单向关联***********
一对一 主键 单向关联的配置算是比较好理解的,很直观的配置中写着one-to-one。这里要说下,一般在写配置前,自己要判断下是使用哪种关联。如果是想要 一对一 主键 单向关联。看着配置这么理解。<id>标签的name和column简单的东西就不说了,反正就是告诉hibernate在我person类里有个name=“”的属性名,你给我匹配的列名得叫column=""。
根据要求,主键单向关联,那主键就不能自己生成了,得靠别人,跟别人的一样。这就得指定id标签内的generator的class值为foreign。
hibernate读到这里就明白:奥,原来我不用单独给你创主键了,根据别的表把主键拿过来给你用就好了,而且你们肯定是 一对一 的(因为主键在另一个表中是唯一的)。hibernate会继续想:你要我把谁的表的主键拿来给你用呢?所以我们就在generator标签内再写一个param标签,用来指定 关联的持久化类的属性名 标签体中写address类型的变量名(上文已提到,一对一中person类中定义了address类型的变量)。
这时hibernate就根据这个变量名在person类中一看,奥,变量名所代表的变量是address类型的啊,那我看看有没有根据address类创建相应的表,当然在下面会查到有,因为<class>标签的name属性就是类的全路径,当然包括类名。
后面写两个<property>标签,就不详说了。再之后是写<one-to-one>标签,我们是怎么记住要写one-to-one标签而不是其他的呢?当然,如果我们alt+/ 可以弹出提示,但我们可以这么理解,我们在前面指定了person表的主键列作为外键来用,使用的是address的主键,这就确定了肯定是一对一的关系,而不可能是一对多等其他关系,所以标签写<one-to-one>。
有人可能会想,是不是我不用写one-to-one标签了呢?因为前面我指定了引用的外键了,到时候肯定能按照外键查询出来。但是不要忘了,我们的目标是在查询person时候,可以通过person直接拿出address来,所以,我们还得继续要求hibernate给我们干活。
hibernate读到one-to-one就会知道,我根据外键能够把address表也给查出来,然后某个变量的类型封装成那个类的实体,返回给person类中的某个变量。这个变量就是o ne-to-one标签中name=“”引号中变量名代表的变量。其中,constrained="true"表示通过外键引用对主键进行约束,在单向关联的one-to-one中,constrained=true,hibernate即会延迟加载sql,只把主表的查出来,等有用到关联表的时候再发送sql取数据。
*******一对一 外键 单向关联***********
如果我们根据需求,需要两个表都有自己独特的主键,那就得额外设置外键了,我们可以想一下我们想要的结果,把person表最后一列,设置为外键列,将对应的address表的主键的值放到person表的最后一列。我们就又可以让hibernate去干活了,通过外键查到address后封装,返回person实体类中直接使用。
前面就不多说了,其实hibernate一看到id标签里,没有使用foreign,就知道不一定是一对一也可能是多对一,所以才在后面写上 more-to-one 标签。从另一个角度来说,我们要为实体类的属性关联对应的列字段,使用的是<property>标签,name=“”对应column=“”,同样,如果我们没有使用 more-to-one 标签,而是<property>标签,hibernate大概会这么来想:奥,下一步我要把name=“address11fk”的属性与表的column="addressId"列字段对应上。然后去person类一看name=“address11fk”的属性的类型,竟然是
我们自定义的类型,hibernate表示很方,这个我搞不了,难道我能把对象存进数据库啊
。所以我们还得用 more-to-one 标签,这时候hibernate就觉得可以接受了,奥这是让我根据外键查询后,封装成address对象,通过person的get/set方法,给person类里的address类型的变量赋值。
按理说,走到这一步对hibernate来说就可以了,可以正常增删改查操作了,但我们知道,两表的实际关系是一对一的关系,而不是多对一的关系。一对一作为多对一的特例,能否将多对一设置成一对一呢?(同时也是为了严谨,尽管对于hibernate来说,可以工作了,但这样实际是多对一的映射,存在危险)
这时,我们经过思考,觉得one-to-one作为more-to-one的特例,应该有个开关式的东西来转换它们俩,于是有了unique属性,设置unique=“true”,就表示其将more-to-one中的more设置成唯一,也就成了one,所以同样的字,含义就变了,实质上也就是one-to-one。
(也许有人会说,一对一也可以是一对多的特例啊,怎么不用one-to-more标签呢?很简单啊,因为unique属性只能设置xxx-to-yyy中的xxx的唯一性)
*******一对一 连接表 单向关联***********
有了上面的经验,这个理解起来就简单多啦!
首先还是根据需要,总之,尽管是一对一,还是奇葩的要通过第三方表,一个连接表来关联查询。前面的配置重复就不详说了。我们也能想到最后的表的结构就是person表就是id列、name列和age列,address表有id列和detail列,而连接表存着personid和addressid。
所以我们要告诉hibernate,你要给我创建第三张表:连接表。用join标签告诉它,并且告诉他表的名字叫什么(table的值)。
还有得告诉它表里有那些列字段。所以就有了key这个标签,告诉hibernate,连接表中要设置personid列,在我们写join标签的时候,hibernate就知道要通过连接表关联查询了,所以肯定在连接表里有一列存放person表的主键值,我们要做的就是指定这一列的名字是什么,所以才会只写上column=“”,而没有写成 name=“” column=“”。
接下来就类似的,要指定是一对一的关联,所以用的是more-to-one,设置unique为true,使more-to-one的内涵变为one-to-one(好像只有指定id的foreign后,才用one-to-one),然后告诉hibernate,在连接表里的第二列给我装address表的id,到时候你给我查询结果,然后封装成address对象赋给name=“”的变量(我这里是简化的描述,我的理解是:写完name=“” column=“” 后,当然column可以省略,hibernate一看name=“”引号中的变量名所代表的变量的类型,然后查找这个类对应的表,把该表的主键放进连接表的第二列)
***********小小的总结下 一对一 ************
如果你真的从头读到这里,那么应该会理解hibernate是怎么来读关联配置的,以及我们怎么根据自己的需求写配置,也能理解一些属性出现的原因,如上文说的unique的作用,hibernate可以看作一种规则,每种规则都是通过逻辑分析搭建出来的,所谓的漏洞就是当时在搭建规则时,思维还不严禁,逻辑有某些地方是模糊的,是没考虑到的。所以每种可以看作是规则的东西,其本身肯定都是能大致上自圆其说的,我们要做的就是,找到这些规律,就像在破解,在玩逻辑游戏,猜测它的规则。总之不管猜测的对与不对,从表现结果上来看,只要结果正确就够用,如果根据自己推测的规则,有某处有问题了,那就要再修改自己推测的规则,修补自己的漏洞,直到有一天,你所根据自己的规
则预测的所有行为均与结果相符,那我们就掌握这种规则啦!!!(所以这也就说明,上面的流程都是我推测出来的,嘿嘿,现在才告诉你们,当然例子是正确的)
上面的一对一的单向关联的三个例子都很简单,是因为对addres的配置,根本就是普通的配置而已。不想person的配置要什么one-to-one、more-to-one之类的,这是因为在person配置关联时,我们写的一些属性或标签就已经告诉hibernate要进行关联查询,例如foreign、 join、 more-to-one、one-to-one。而之所以不用在address配置时,
创造个特定的标签,向hibernate挥手喊:来呀来呀,就是因为,在person类中有address类型的变量,在配置person对应的表时,hibernate就可以通过address类型的变量的变量名,找到person中该变量名对应的变量类型,进而找该类是否有创建对应的表。
********如果你理解了上面的,花个10秒种,学习多对一单向关联**********
多对一 外键 单向关联配置(如下)
<hibernate-mapping>
<class name="com.lavasoft.dx._n_1_fk.Personn1fk" table="PERSON_n1fk">
<id name="personid">
<generator class="identity"/>
</id>
<property name="name"/>
<property name="age"/>
<!--用来映射关联PO column是Address在该表中的外键列名-->
<many-to-one name="addressn1fk" column="addressId"/>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="com.lavasoft.dx._n_1_fk.Addressn1fk" table="ADDRESS_n1fk">
<id name="addressid">
<generator class="identity"/>
</id>
<property name="addressdetail"/>
</class>
</hibernate-mapping>
多对一 连接表 单向关联配置(如下)<hibernate-mapping>
<class name="com.lavasoft.dx._n_1_tab.Personn1tab" table="PERSON_n1tab">
<id name="personid">
<generator class="identity"/>
</id>
<property name="name"/>
<property name="age"/>
<!--使用join元素显式确定链接表-->
<join table="join_n1tab">
<!--映射关联所用的外键-->
<key column="personid"/>
<many-to-one name="addressn1tab"/>
</join>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="com.lavasoft.dx._n_1_tab.Addressn1tab" table="ADDRESS_n1tab">
<id name="addressid">
<generator class="identity"/>
</id>
<property name="addressdetail"/>
</class>
</hibernate-mapping>
很眼熟是不是,正好按照上面的理解,将自己当成是hibernate,每行每行的解读下,遇到关键的标签或属性,想想这表示最为hibernate的你看到这特别的标签或属性应该有什么或将要有什么行动。
其实上面的配置就是一对一的 外键关联与 连接表关联,只是去掉了unique=“true”,是真正的more-to-one。
******************************************************
看来这篇博文要写个系列了,写了这么久才写完一对一的,不过也很开心,下午看了几个小时的hibernate映射配置,应该是一共有14种关系或配置(xml方式非联合主键查询),看的多了就混乱了,而且参考的博文没写出规律不说,还有些属性不知道啥意思,都是自己带入hibernate的角色,猜测设计者设计这么个属性的原因以及遇到这么个属性hibernate会执行的动作,写出来分享给大家,同时给自己查阅方便。
下一篇会写一对一的双向关联查询的配置,有了这个基础,应该会很好写
最后,附上我查阅的资料:
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