当我们需要使用多个属性变量(表中的多列)联合起来作为主键,我们需要使用复合主键。复合主键要求我们编写一个复合主键类( 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.java
package 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.java
package 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; } }