这几天在改一个项目源码,遇到一个问题坑了很久。场景如下(注:此处是借鉴网络上的例子,并不是自己的实验环境):
一夫一妻制——比如夫妻关系的两张数据表,一个是wif表,一个是husban表,其数据表信息如下:
CREATE TABLE `wife` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8
CREATE TABLE `husband` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`wifeid` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fk_wife` (`wifeid`),
CONSTRAINT `fk_wife` FOREIGN KEY (`wifeid`) REFERENCES `wife` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8
即一个wife表和一个husband表,这里wife表为基表,husband表中的wifeid依赖于wife的主键id。
下面是相应的POJO:
Wife:
1. package com.linys.model;
2.
3. /**
4. * Wife entity. @author MyEclipse Persistence Tools
5. */
6.
7. public class Wife implements java.io.Serializable {
8.
9. // Fields
10.
11. /**
12. *
13. */
14. private static final long serialVersionUID = 1L;
15. private Integer id;
16. private String name;
17. private Husband husband;
18. // Constructors
19.
20. /** default constructor */
21. public Wife() {
22. }
23.
24. /** minimal constructor */
25. public Wife(String name) {
26. this.name = name;
27. }
28.
29.
30. // Property accessors
31.
32. public Integer getId() {
33. return this.id;
34. }
35.
36. public void setId(Integer id) {
37. this.id = id;
38. }
39.
40. public String getName() {
41. return this.name;
42. }
43.
44. public void setName(String name) {
45. this.name = name;
46. }
47.
48. public Husband getHusband() {
49. return husband;
50. }
51.
52. public void setHusband(Husband husband) {
53. this.husband = husband;
54. }
55.
56.
57. }
Husband:
1. package com.linys.model;
2.
3. /**
4. * Husband entity. @author MyEclipse Persistence Tools
5. */
6.
7. public class Husband implements java.io.Serializable {
8.
9. // Fields
10.
11. /**
12. *
13. */
14. private static final long serialVersionUID = 1L;
15. private Integer id;
16. private Wife wife;
17. private String name;
18.
19. // Constructors
20.
21. /** default constructor */
22. public Husband() {
23. }
24.
25. /** minimal constructor */
26. public Husband(String name) {
27. this.name = name;
28. }
29.
30. /** full constructor */
31. public Husband(Wife wife, String name) {
32. this.wife = wife;
33. this.name = name;
34. }
35.
36. // Property accessors
37.
38. public Integer getId() {
39. return this.id;
40. }
41.
42. public void setId(Integer id) {
43. this.id = id;
44. }
45.
46. public Wife getWife() {
47. return this.wife;
48. }
49.
50. public void setWife(Wife wife) {
51. this.wife = wife;
52. }
53.
54. public String getName() {
55. return this.name;
56. }
57.
58. public void setName(String name) {
59. this.name = name;
60. }
61.
62. }
以上是基本的数据表映射。这时候假设我们需要一对一双向关联,即在保存或者更新时,可以根据一个husband实例可以获得其wife实例,也可根据wife实例去获得一个husband实例。
一对一双向关联比较特殊,不像单向关联,在hbm配置文件中仅仅使用one-to-one配置即可,这种情况下需要使用may-to-one和one-to-one来模拟一一对应,既然是一一对应,所以在many-to-one的一段,要加上unique=true属性,从而保证其唯一性。不要问我为什么,这是Hibernate的机制吧,其实说到这里,我对于Hibernate还不是很了解,如果有大神读到希望可以指点一二。从项目经验来看,如果不这么做,在同时修改了这两个实体,然后保存到库中时,总是报错,错误信息为session无法同步,或者外键id为NULL。使用了这样的配置后,就没有问题了。下面看二者的配置文件:
Wife.hbm.xml
Java代码
1. <?xml version="1.0" encoding="utf-8"?>
2. <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
3. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
4. <!--
5. Mapping file autogenerated by MyEclipse Persistence Tools
6. -->
7. <hibernate-mapping>
8. <class name="com.linys.model.Wife" table="wife">
9. <id name="id" type="java.lang.Integer">
10. <column name="id" />
11. <generator class="native" />
12. </id>
13. <property name="name" type="java.lang.String">
14. <column name="name" length="50" not-null="true" />
15. </property>
16. <one-to-one name="husband" class="com.linys.model.Husband" property-ref="wife" cascade="all"/>
17. </class>
18. </hibernate-mapping>
Husband.hbm.xml:
1. <?xml version="1.0" encoding="utf-8"?>
2. <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
3. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
4. <!--
5. Mapping file autogenerated by MyEclipse Persistence Tools
6. -->
7. <hibernate-mapping>
8. <class name="com.linys.model.Husband" table="husband">
9. <id name="id" type="java.lang.Integer">
10. <column name="id" />
11. <generator class="native" />
12. </id>
13. <property name="name" type="java.lang.String">
14. <column name="name" length="50" not-null="true" />
15. </property>
16. <may-to-one name="wife" class="com.linys.model.Wife" fetch="select"/>
17. <column name="wifeid" unique="true" not-null="true" />
18. </many-to-one>
19. </class>
20. </hibernate-mapping>
外键所依赖的那个表的配置文件(wife)使用one-to-one,外键所在的表(husband)用many-to-one,但是要指定unique为true从而保证其唯一性。注意many这一端中的colum,配置的应该是外键所在表的外键列名,对应这里也就是husband表中的“wifeid”,需要与数据库中的数据表表中一致,切勿弄错。
one-to-one:指定在Wife这个类中用于双向关联的属性husband
property-ref: 在关联对象中用于与本对象关联的属性。
注意:property-ref="wife"不能少,否则会造成查询时关联查询失败!
以上是实际经验的总结,如有错误,欢迎指正。