千里冰封
JAVA 浓香四溢
posts - 151,comments - 2801,trackbacks - 0
实体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 千里冰封 阅读(1094) 评论(3)  编辑  收藏 所属分类: JAVAEE

FeedBack:
# re: EJB学习日记(13)
2007-09-25 14:03 | Java.net
打算用EJB做项目吗?比较看到Spring+EJB+JSF的应用模式.
使用Spring管理bean,ejb的无状态会话bean,在Spring管理下的ejb比较优雅,但不知道效率会怎么样.  回复  更多评论
  
# re: EJB学习日记(13)
2007-09-25 15:04 | 千里冰封
呵呵,只是学学而已,我目前的工作还用不到EJB:)  回复  更多评论
  
# re: EJB学习日记(13)
2007-09-28 10:42 | 同声传译
学写日记也一件很让人有回忆感  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航: