一对多及多对一映射

需要映射的数据库表

orders和 orderitems   一项订单中存在一个或多个订购项

双向一对多关系,一是关系维护端(owner side),多是关系被维护端(inverse side)。在关系被维护端建立外键列指向关系维护端的主键列  


Order.java

package com.foshanshop.ejb3.bean;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Date;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
@Table(name = "Orders")
public class Order implements Serializable {

private static final long serialVersionUID = 4970325922198249712L;
private Integer orderid;
private Float amount;
private Set<OrderItem> orderItems = new HashSet<OrderItem>();  // 声明一个Set 变量orderItems 用来存放多个OrderItem对象

private Date createdate;

@Id
@GeneratedValue
public Integer getOrderid() {
return orderid;
}
public void setOrderid(Integer orderid) {
this.orderid = orderid;
}

public Float getAmount() {
return amount;
}
public void setAmount(Float amount) {
this.amount = amount;
}

@OneToMany(mappedBy="order",cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@OrderBy(value = "id ASC")
public Set<OrderItem> getOrderItems() {
return orderItems;
}
public void setOrderItems(Set<OrderItem> orderItems) {
this.orderItems = orderItems;
}

@Temporal(value=TemporalType.TIMESTAMP)
public Date getCreatedate() {
return createdate;
}
public void setCreatedate(Date createdate) {
this.createdate = createdate;
}

public void addOrderItem(OrderItem orderitem) {
if (!this.orderItems.contains(orderitem)) {
this.orderItems.add(orderitem);
orderitem.setOrder(this);
}
}

public void removeOrderItem(OrderItem orderitem) {
if (this.orderItems.contains(orderitem)) {
orderitem.setOrder(null);
this.orderItems.remove(orderitem);
}
}

/**
* 返回对象的散列代码值。该实现根据此对象
* 中 orderid 字段计算散列代码值。
* @return 此对象的散列代码值。
*/
@Override
public int hashCode() {
int hash = 0;
hash += (this.orderid != null ? this.orderid.hashCode() : 0);
return hash;
}

/**
* 确定其他对象是否等于此 Order。当且仅当
* 参数不为 null 且该参数是具有与此对象相同 orderid 字段值的 Order 对象时,
* 结果才为 <code>true</code>。
* @param 对象,要比较的引用对象
* 如果此对象与参数相同,则 @return <code>true</code>;
* 否则为 <code>false</code>。
*/
@Override
public boolean equals(Object object) {
if (!(object instanceof Order)) {
return false;
}
Order other = (Order)object;
if (this.orderid != other.orderid && (this.orderid == null || !this.orderid.equals(other.orderid))) return false;
return true;
}

/**
* 返回对象的字符串表示法。该实现根据 orderid 字段
* 构造此表示法。
* @return 对象的字符串表示法。
*/
@Override
public String toString() {
return this.getClass().getName()+ "[orderid=" + orderid + "]";
}
}

OrderItem.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.ManyToOne;

@Entity
public class OrderItem implements Serializable {

private static final long serialVersionUID = -1166337687856636179L;
private Integer id;
private String productname;
private Float price;
private Order order;

public OrderItem() {}

public OrderItem(String productname, Float price) {
this.productname = productname;
this.price = price;
}

@Id
@GeneratedValue
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}

@Column(length=100, nullable=false)
public String getProductname() {
return productname;
}
public void setProductname(String productname) {
this.productname = productname;
}

public Float getPrice() {
return price;
}
public void setPrice(Float price) {
this.price = price;
}

@ManyToOne(cascade=CascadeType.REFRESH, optional=false)
@JoinColumn(name = "order_id")

//@JoinColumn(name = "order_id")注释指定OrderItem 映射表的order_id 列作为外键与Order 映射表的主键列关联。
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}

/**
* 返回对象的散列代码值。该实现根据此对象
* 中 id 字段计算散列代码值。
* @return 此对象的散列代码值。
*/
@Override
public int hashCode() {
int hash = 0;
hash += (this.id != null ? this.id.hashCode() : super.hashCode());
return hash;
}

/**
* 确定其他对象是否等于此 OrderItem。当且仅当
* 参数不为 null 且该参数是具有与此对象相同 id 字段值的 OrderItem 对象时,
* 结果才为 <code>true</code>。
* @param 对象,要比较的引用对象
* 如果此对象与参数相同,则 @return <code>true</code>;
* 否则为 <code>false</code>。
*/
@Override
public boolean equals(Object object) {
if (!(object instanceof OrderItem)) {
return false;
}
OrderItem other = (OrderItem)object;
if (this.id != other.id && (this.id == null || !this.id.equals(other.id))) return false;
return true;
}

/**
* 返回对象的字符串表示法。该实现根据 id 字段
* 构造此表示法。
* @return 对象的字符串表示法。
*/
@Override
public String toString() {
return this.getClass().getName()+ "[id=" + id + "]";
}

下面是@OneToMany注释的属性介绍:

1>mappedBy

String 类型的属性。

定义类之间的双向关系。如果类之间是单向关系,不需要提供定义,如果类和类之间形成双向关系,我们就需要

使用这个属性进行定义,否则可能引起数据一致性的问题。

2>cascade

CascadeType[]类型。

该属性定义类和类之间的级联关系。定义的级联关系将被容器视为对当前类对象及其关联类对象采取相同的操作,而且这种关系是递归调用的。举个例子:Order 和OrderItem 有级联关系,那么删除Order 时将同时删除它所对应的OrderItem 对象。而如果OrderItem 还和其他的对象之间有级联关系,那么这样的操作会一直递归执行下去。cascade 的值只能从CascadeType.PERSIST(级联新建)、CascadeType.REMOVE(级联删除)CascadeType.REFRESH

(级联刷新)、CascadeType.MERGE(级联更新)中选择一个或多个。还有一个选择是使用CascadeType.ALL,表示选择全部四项。

3>fatch

FetchType 类型的属性。

可选择项包括:FetchType.EAGER 和FetchType.LAZY。前者表示关系类(本例是OrderItem 类)在主类(本例是Order

类)加载的时候同时加载,后者表示关系类在被访问时才加载。默认值是FetchType. LAZY。

@OrderBy(value = "id ASC")注释指明加载OrderItem 时按id 的升序排序


注释@ManyToOne

 指明OrderItem 和Order 之间为多对一关系,多个OrderItem 实例关联的都是同一个Order 对象。

@ManyToOne 注释有四个属性:targetEntity、cascade、fetch 和optional,前三个属性的具体含义和@OneToMany

注释的同名属性相同,但@ManyToOne 注释的fetch 属性默认值是FetchType.EAGER。

optional 属性是定义该关联类是否必须存在,值为false 时,关联类双方都必须存在,如果关系被维护端不存在,查询的结果为null。值为true 时, 关系被维护端可以不存在,查询的结果仍然会返回关系维护端,在关系维护端中指向关系被维护端的属性为null。optional 属性的默认值是true。

optional 属性实际上指定关联类与被关联类的join 查询关系,如optional=false 时join 查询关系为inner join, optional=true 时join 查询关系为left join。

代码片断解释如下:

public class OrderItem implements Serializable {
@ManyToOne(cascade=CascadeType.REFRESH,optional=false)
@JoinColumn(name = "order_id")
public Order getOrder() {
return order;
}
//获取OrderItem时的SQL为:select * from OrderItem item inner join Orders o on o.order_id=item.id,
OrderItem表与orders表都必须有关联记录时,查询结果才有记录。


@ManyToOne(cascade=CascadeType.REFRESH,optional=true)
@JoinColumn(name = "order_id")
public Order getOrder() {
return order;
}
//获取OrderItem时的SQL为:select * from OrderItem item left outer join Orders o on o.order_id=item.id
如果orders表没有记录,OrderItem表有记录,查询结果仍有记录。

下面是Session Bean 的业务接口及实现类

package com.foshanshop.ejb3.impl;

import java.util.List;

import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import com.foshanshop.ejb3.OrderDAO;
import com.foshanshop.ejb3.bean.Order;

@Stateless
@Remote (OrderDAO.class)
public class OrderDAOBean implements OrderDAO {
@PersistenceContext protected EntityManager em;

public void insertOrder(Order order){
em.persist(order);
}

public Order getOrderByID(Integer orderid) {
Order order = em.find(Order.class, orderid);
order.getOrderItems().size();
//因为是延迟加载,通过执行size()这种方式获取订单下的所有订单项
return order;
}

@SuppressWarnings("unchecked")
public List<Order> getAllOrder() {
Query query = em.createQuery("select DISTINCT o from Order o inner join fetch o.orderItems order by o.orderid");
return (List<Order>) query.getResultList();
}
}

当业务方法需要把一个实体Bean 作为参数返回给客户端时,除了实体Bean 本身需要实现Serializable 接口之外,如果关联类(OrderItem)是延迟加载,还需在返回实体Bean 之前通过访问关联类的方式加载关联类。否则在客户端访问关联类时将会抛出加载例外。

public Order getOrderByID(Integer orderid) {
Order order = em.find(Order.class, orderid);
order.getOrderItems().size();
//因为是延迟加载,通过执行size()这种方式获取订单下的所有订单项
return order;
}

另外不管是否延迟加载,通过join fetch 关联语句都可显式加载关联类,如业务方法getAllOrder 。

@SuppressWarnings("unchecked")
public List<Order> getAllOrder() {
Query query = em.createQuery("select DISTINCT o from Order o inner join fetch o.orderItems order by o.orderid");
return (List<Order>) query.getResultList();
}