次讲@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"的,他的返回值即:被维护方。(外键方)