次讲@OneToOne的用法。而且是基于主外键的关联。因为这个是实际开发中最最常见的。
这里先说明一下,我的java类的命名都以Test开头。而对应的对象名却没有用test开头。这里是为了更好的说明注视中的value到底是类名还是对象名。
先看java代码:
TestUser
package net.paoding.forum.domain; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToOne; @Entity public class TestUser { String id; String username; TestCard card; /** * @return the id */ @Id public String getId() { return id; } /** * @param id the id to set */ public void setId(String id) { this.id = id; } /** * @return the username */ public String getUsername() { return username; } /** * @param username the username to set */ public void setUsername(String username) { this.username = username; } /** * @return the card */ @OneToOne public TestCard getCard() { return card; } /** * @param card the card to set */ public void setCard(TestCard card) { this.card = card; } }
TestCard:
package net.paoding.forum.domain; import javax.persistence.Entity; import javax.persistence.Id; @Entity public class TestCard { String id; String cardNumber; /** * @return the id */ @Id public String getId() { return id; } /** * @param id the id to set */ public void setId(String id) { this.id = id; } /** * @return the cardNumber */ public String getCardNumber() { return cardNumber; } /** * @param cardNumber the cardNumber to set */ public void setCardNumber(String cardNumber) { this.cardNumber = cardNumber; } }
这样子就是OneToOne了。这个是单向的关系,即:由TestCard控制TestUser。看sql语句就知道了。
hibernate自动生成的sql为:
drop table test_card cascade constraints; drop table test_user cascade constraints; create table test_card ( id varchar2(255 char) not null, card_number varchar2(255 char), primary key (id) ); create table test_user ( id varchar2(255 char) not null, username varchar2(255 char), card_id varchar2(255 char), primary key (id) ); alter table test_user add constraint FKB9A96B58A237D846 foreign key (card_id) references test_card;
个人认为,应该这样子理解OneToOne。
首先,他是用来申明在方法上的。(这句好似废话)
然后,他其实是用来描述这个方法的返回值的。即:描述TestCard的。也就是说:TestCard 为控制方。TestUser为被控方。那么对应的表中,test_user表有一个外键字段对应着test_card表中的一个主键。
这样,就好理解了。
上面的为单向关联。那么,双向关联呢?修改TestCard如下:
package net.paoding.forum.domain; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToOne; @Entity public class TestCard { String id; String cardNumber; TestUser user; /** * @return the id */ @Id public String getId() { return id; } /** * @param id the id to set */ public void setId(String id) { this.id = id; } /** * @return the user */ @OneToOne public TestUser getUser() { return user; } /** * @param user the user to set */ public void setUser(TestUser user) { this.user = user; } /** * @return the cardNumber */ public String getCardNumber() { return cardNumber; } /** * @param cardNumber the cardNumber to set */ public void setCardNumber(String cardNumber) { this.cardNumber = cardNumber; } }
这样,产生的sql为:
drop table test_card cascade constraints; drop table test_user cascade constraints; create table test_card ( id varchar2(255 char) not null, card_number varchar2(255 char), user_id varchar2(255 char), primary key (id) ); create table test_user ( id varchar2(255 char) not null, username varchar2(255 char), card_id varchar2(255 char), primary key (id) ); alter table test_card add constraint FKB9A0FA9D7876DA66 foreign key (user_id) references test_user; alter table test_user add constraint FKB9A96B58A237D846 foreign key (card_id) references test_card;
可以看到,当我双方都声明了OneToOne时候,也就达到了双向的One2One效果。但是很遗憾。Hibernate不知道你由哪边控制哪边,或者说由哪边来维护关系。所以,他生成的sql语句中可以看到,都有test_card和test_user是相互关联的,即:都有FK关联上堆放的PK。很明显,这个不是我们要的效果。
我们需要的是,po中能双向one2one,但是db中不需要。所以,我们需要修改一下。
修改TestCard.java代码:
package net.paoding.forum.domain; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToOne; @Entity public class TestCard { String id; String cardNumber; TestUser user; /** * @return the id */ @Id public String getId() { return id; } /** * @param id the id to set */ public void setId(String id) { this.id = id; } /** * @return the user */ @OneToOne(mappedBy="card") public TestUser getUser() { return user; } /** * @param user the user to set */ public void setUser(TestUser user) { this.user = user; } /** * @return the cardNumber */ public String getCardNumber() { return cardNumber; } /** * @param cardNumber the cardNumber to set */ public void setCardNumber(String cardNumber) { this.cardNumber = cardNumber; } }
注意annotation的部分。mappedBy就指出了此处返回的TestUser对应的test_user表中有一个指向TestUser的属性"card"的外键。可以看sql。
drop table test_card cascade constraints; drop table test_user cascade constraints; create table test_card ( id varchar2(255 char) not null, card_number varchar2(255 char), primary key (id) ); create table test_user ( id varchar2(255 char) not null, username varchar2(255 char), card_id varchar2(255 char), primary key (id) ); alter table test_user add constraint FKB9A96B58A237D846 foreign key (card_id) references test_card;
而这得"mappedBy=card"中的card是TestUser中的TestCard类型的成员变量名。这也就是为什么我要用Test修饰类名,而对象名却不用的理由。
或者,还可以这样子来理解OneToOne。
单向OneToOne中。声明了OneToOne这个Annotation的方法,他的返回值即:维护方。(主键方)
双向OneToOne中。在OneToOne中声明了mappedBy="xx"的,他的返回值即:被维护方。(外键方)