实体BEAN的七种关系之---------多对多双向
Many-to-Many Bidirectional Relationship
一般来说,多对多的双向发生在双方都持有对方的很多引用,A可能持有很多个B,B也可能持有很多个A,并且A和B之间还要求能够互相查询.在现实中,我们可以用如下的例子来说明这种关系:
人和航班,一个人可以订很多次航班,可以是订了今天的,也可以订明天的,因为他工作繁忙,同样的,一个航班不可能只为一个人而开,也可以接受很多个人的预订.并且这种查询是双向的,一个人他可以查询他订了多少个航班,一个航班也可以查询它被多少人订了,这样才好根据订的情况进行安排.
先看看代码吧.
还是老样子,Person类的代码
/*
* Person.java
*
* Created on 2007-9-15, 0:11:58
*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package lbf.entitybean.test1;
import java.io.Serializable;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
/**
*
* @author Admin
*/
@Entity
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String name;
private String sex;
private int age;
private Address address;
private List<Phone> phones;
private IDCard idCard;
private Country country;
private List<Car> cars;
private List<Flight> flights;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "PersonANDFlight", joinColumns = {@JoinColumn(name = "personID")}, inverseJoinColumns = {@JoinColumn(name = "flightID")})
public List<Flight> getFlights() {
return flights;
}
public void setFlights(List<Flight> flights) {
this.flights = flights;
}
@OneToMany(cascade = CascadeType.ALL, mappedBy = "person")
public List<Car> getCars() {
return cars;
}
public void setCars(List<Car> cars) {
this.cars = cars;
}
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "countryID")
public Country getCountry() {
return country;
}
public void setCountry(Country country) {
this.country = country;
}
@OneToOne(cascade = CascadeType.ALL)
public IDCard getIdCard() {
return idCard;
}
public void setIdCard(IDCard idCard) {
this.idCard = idCard;
}
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "personID")
public List<Phone> getPhones() {
return phones;
}
public void setPhones(List<Phone> phones) {
this.phones = phones;
}
@OneToOne(cascade = {CascadeType.ALL})
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public void setId(Long id) {
this.id = id;
}
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
}
代表航班的Flight类的代码:
/*
* Flight.java
*
* Created on 2007-9-24, 14:35:45
*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package lbf.entitybean.test1;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Temporal;
/**
*
* @author hadeslee
*/
@Entity
public class Flight implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
@Temporal(javax.persistence.TemporalType.TIME)
public Date getArriveTime() {
return arriveTime;
}
public void setArriveTime(Date arriveTime) {
this.arriveTime = arriveTime;
}
public String getFlightNumber() {
return flightNumber;
}
public void setFlightNumber(String flightNumber) {
this.flightNumber = flightNumber;
}
public String getFromCity() {
return fromCity;
}
public void setFromCity(String fromCity) {
this.fromCity = fromCity;
}
@Temporal(javax.persistence.TemporalType.TIME)
public Date getLeaveTime() {
return leaveTime;
}
public void setLeaveTime(Date leaveTime) {
this.leaveTime = leaveTime;
}
@ManyToMany(mappedBy="flights")
public List<Person> getPersons() {
return persons;
}
public void setPersons(List<Person> persons) {
this.persons = persons;
}
public String getToCity() {
return toCity;
}
public void setToCity(String toCity) {
this.toCity = toCity;
}
private String flightNumber;
private String fromCity,toCity;
private Date leaveTime,arriveTime;
private List<Person> persons;
public void setId(Long id) {
this.id = id;
}
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
}
我们再来看一看ManyToMany的声明
public @interface ManyToMany
{
Class targetEntity( ) default void.class;
CascadeType[] cascade( ) default {};
FetchType fetch( ) default LAZY;
String mappedBy( ) default "";
}
从代码可以看出,注释都差不多,只不过多对多的时候,仅仅从两张用外键相连是不够的,必须生成一张用于连接的中间表.也就如下代码所声明的地方:
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "PersonANDFlight", joinColumns = {@JoinColumn(name = "personID")}, inverseJoinColumns = {@JoinColumn(name = "flightID")})
public List<Flight> getFlights() {
return flights;
}
我们声明了一张用来连接的表,并且声明了主控端的列名和反转端的列名,其实这个声明不是必要的,当我们不用@JoinTable来声明的时候,JBOSS也会为我们自动生成一个连接用的表,表名默认是主控端的表名加上下划线"_"再加上反转端的表名.
从上面的注释我们可以看出,此关系的主控端在Person这一方,因为我们可以在Flight那一方看到如下注释:
@ManyToMany(mappedBy="flights")
public List<Person> getPersons() {
return persons;
}
正是因为双向关系的存在,也由于Person是主控端, 所以Person要取消某次预定只要remove相应的Flight就可以了,而Flight由于是反转端,所以虽然它也可以得到它的所有预定的人,但是它却无法改变这种关系,即使它remove掉了某个Person,但是这种关系并不会在数据库里面表现出来,因为毕竟航班是不能随便取消一个人的登机资格的.
其实按我的理解来说,多对多的双向有点类似于一对多的单向,只不过双方都是一对多,我们这个例子完全可以用一对多来实现,但是一对多实现的话,就会有很多重复的数据存在,因为每个关系都可能会有重复的元素,比如我们这个例子,如果一对多的话,每个航班都会对应几百人,哪怕这几百人下次还坐你的航班,你还要重新定义一下.因为上次的几百人的外键已经指向你了.还要再指向另一个你,必须要重新生成几百个元素,所以在这种情况下,多对多就可以很好的重用数据库里面的表了,在Person和Flight表中,都不会有重复的元素存在了.并且关系也明朗了许多.