当我们需要使用多个属性变量(表中的多列)联合起来作为主键,我们需要使用复合主键。复合主键要求我们编写一个复合主键类( Composite Primary Key Class )。复合主键类需要符合以下一些要求:
·复合主键类必须是public 和具备一个没有参数的构造函数
·复合主键类的每个属性变量必须有getter/setter,如果没有,每个属性变量则必须是public 或者protected
·复合主键类必须实现java.io.serializable
·复合主键类必须实现equals()和hashcode()方法
·复合主键类中的主键属性变量的名字必须和对应的Entity 中主键属性变量的名字相同
·一旦主键值设定后,不要修改主键属性变量的值
·复合主键类必须是public 和具备一个没有参数的构造函数
·复合主键类的每个属性变量必须有getter/setter,如果没有,每个属性变量则必须是public 或者protected
·复合主键类必须实现java.io.serializable
·复合主键类必须实现equals()和hashcode()方法
·复合主键类中的主键属性变量的名字必须和对应的Entity 中主键属性变量的名字相同
·一旦主键值设定后,不要修改主键属性变量的值
本节以航线为例,介绍复合主键的开发过程,航线以出发地及到达地作为联合主键,航线与航班存在一对多的关联关系,下面是他们的数据库表

flight
按照复合主键类的要求,我们编写一个复合主键类AirtLinePK.java,
package com.foshanshop.ejb3.bean;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Embeddable;
@Embeddable 复合主键使用一个可嵌入的类作为主键表示,@Embeddable 注释指明这是一个可嵌入的类
public class AirtLinePK implements Serializable {
private static final long serialVersionUID = -8430464367315480936L;
private String leavecity;
private String arrivecity;
public AirtLinePK(){}
public AirtLinePK(String leavecity, String arrivecity) {
this.leavecity = leavecity;
this.arrivecity = arrivecity;
}
@Column(nullable=false,length=3,name="LEAVECITY")
public String getLeavecity() {
return leavecity;
}
public void setLeavecity(String leavecity) {
this.leavecity = leavecity;
}
@Column(nullable=false,length=3,name="ARRIVECITY")
public String getArrivecity() {
return arrivecity;
}
public void setArrivecity(String arrivecity) {
this.arrivecity = arrivecity;
}
@Override
public int hashCode() {
int hash = 0;
hash += (this.leavecity!=null && this.arrivecity!=null ? (this.leavecity+ "-"+ this.arrivecity).hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
if (!(object instanceof AirtLinePK)) {
return false;
}
AirtLinePK other = (AirtLinePK)object;
if (this.leavecity != other.leavecity && (this.leavecity == null || !this.leavecity.equalsIgnoreCase(other.leavecity))) return false;
if (this.arrivecity != other.arrivecity && (this.arrivecity == null || !this.arrivecity.equalsIgnoreCase(other.arrivecity))) return false;
return true;
}
@Override
public String toString() {
return this.getClass().getName()+"[leavecity="+ leavecity + ",arrivecity="+ arrivecity+ "]";
}
}
下面AirLine.java 用复合主键类AirtLinePK 作为其主键。@EmbeddedId 注释指明复合主键类作为主键。AirLine.java
package com.foshanshop.ejb3.bean;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
@Entity
public class AirLine implements Serializable {
private static final long serialVersionUID = -515855962558721968L;
private AirtLinePK id;
private Boolean onoff;
private Set<Flight> flights = new HashSet<Flight>();
public AirLine(){}
public AirLine(AirtLinePK id, Boolean onoff){
this.id = id;
this.onoff = onoff;
}
@EmbeddedId
public AirtLinePK getId() {
return id;
}
public void setId(AirtLinePK id) {
this.id = id;
}
public Boolean getOnoff() {
return onoff;
}
public void setOnoff(Boolean onoff) {
this.onoff = onoff;
}
@OneToMany(mappedBy="airline",cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@OrderBy(value = "id ASC")
public Set<Flight> getFlights() {
return flights;
}
public void setFlights(Set<Flight> flights) {
this.flights = flights;
}
public void addFlight(Flight flight) {
if (!this.flights.contains(flight)) {
this.flights.add(flight);
flight.setAirline(this);
}
}
public void removeFlight(Flight flight) {
if (this.flights.contains(flight)) {
this.flights.remove(flight);
}
}
@Override
public int hashCode() {
int hash = 0;
hash += (this.id != null ? this.id.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
if (!(object instanceof AirLine)) {
return false;
}
AirLine other = (AirLine)object;
if (this.id != other.id && (this.id == null || !this.id.equals(other.id))) return false;
return true;
}
@Override
public String toString() {
return this.getClass().getName()+ "[id=" + id + "]";
}
}
Flight.javapackage com.foshanshop.ejb3.bean;
import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.ManyToOne;
@Entity
public class Flight implements Serializable {
private static final long serialVersionUID = 5224268726211078831L;
private Integer id;
private String flightno;//航班号
private String leavetime;//起飞时间
private String arrivetime;//到达时间
private AirLine airline;
public Flight(){}
public Flight(String flightno, String leavetime, String arrivetime) {
this.flightno = flightno;
this.leavetime = leavetime;
this.arrivetime = arrivetime;
}
@Id
@GeneratedValue
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(nullable=false,length=10)
public String getFlightno() {
return flightno;
}
public void setFlightno(String flightno) {
this.flightno = flightno;
}
@Column(length=10)
public String getArrivetime() {
return arrivetime;
}
public void setArrivetime(String arrivetime) {
this.arrivetime = arrivetime;
}
@Column(length=10)
public String getLeavetime() {
return leavetime;
}
public void setLeavetime(String leavetime) {
this.leavetime = leavetime;
}
@ManyToOne(cascade=CascadeType.REFRESH,optional=false)
@JoinColumns ({
@JoinColumn(name="Leave_City", referencedColumnName = "LEAVECITY", nullable=false),
@JoinColumn(name="Arrive_City", referencedColumnName = "ARRIVECITY", nullable=false)
})
public AirLine getAirline() {
return airline;
}
public void setAirline(AirLine airline) {
this.airline = airline;
}
@Override
public int hashCode() {
int hash = 0;
hash += (this.id != null ? this.id.hashCode() : super.hashCode());
return hash;
}
@Override
public boolean equals(Object object) {
if (!(object instanceof Flight)) {
return false;
}
Flight other = (Flight)object;
if (this.id != other.id && (this.id == null || !this.id.equals(other.id))) return false;
return true;
}
@Override
public String toString() {
return this.getClass().getName()+ "[id=" + id + "]";
}
}
AirLineDAOBean.javapackage com.foshanshop.ejb3.impl;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import com.foshanshop.ejb3.AirLineDAO;
import com.foshanshop.ejb3.bean.AirLine;
import com.foshanshop.ejb3.bean.AirtLinePK;
import com.foshanshop.ejb3.bean.Flight;
@Stateless
@Remote (AirLineDAO.class)
public class AirLineDAOBean implements AirLineDAO {
@PersistenceContext protected EntityManager em;
public void insertAirLine() {
//如果你试图使用select count(a) from AirLine a 统计记录数,将会报错,这是Jboss4.2.2GA处理复合主键的Bug
Query query = em.createQuery("select count(a.id.leavecity) from AirLine a where a.id.leavecity =?1 and a.id.arrivecity=?2");
query.setParameter(1, "PEK");
query.setParameter(2, "CAN");
int result = Integer.parseInt(query.getSingleResult().toString());
if (result==0){
AirLine airLine = new AirLine(new AirtLinePK("PEK","CAN"), true);
//PEK为首都机场三字码,CAN为广州白云机场三字码
airLine.addFlight(new Flight("CA1321","08:45","11:50"));
airLine.addFlight(new Flight("CZ3102","12:05","15:05"));
airLine.addFlight(new Flight("HU7801","15:05","17:45"));
em.persist(airLine);
}
}
public AirLine getAirLineByID(String leavecity, String arrivecity) {
AirLine airLine = em.find(AirLine.class, new AirtLinePK(leavecity,arrivecity));
airLine.getFlights().size();
//因为是延迟加载,通过执行size()这种方式获取航线下的所有航班
return airLine;
}
}