实体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表中,都不会有重复的元素存在了.并且关系也明朗了许多.
尽管千里冰封
依然拥有晴空
你我共同品味JAVA的浓香.
	posted on 2007-09-25 09:00 
千里冰封 阅读(1111) 
评论(3)  编辑  收藏  所属分类: 
JAVAEE