现实应用中存在很多一对多的情况,如一项订单中存在一个或多个订购项。下面就以订单为例介绍存在一对多及多对一双向关系的实体bean 开发。
需要映射的数据库表
Orders
字段名称 | 字段类型 | 属性描述 |
orderid | Int | 订单号 |
amount | float | 订单金额 |
createdate | datetime | 订单创建日期 |
Orderitems
字段名称 | 字段类型 | 属性描述 |
id | Int | 订单项ID |
productname | Varchar(255) | 订购产品名称 |
price | float | 产品价格 |
order_id | Int | 订单号 |
双向一对多关系,一是关系维护端(owner side),多是关系被维护端(inverse side)。在关系被维护端建立外键列指向关系维护端的主键列。
Order.java
package com.zhaosoft.bean;
import java.io.Serializable;
import java.util.Date;
import java.util.HashSet;
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;
@SuppressWarnings("serial")
@Entity
@Table(name = "Orders")
public class Order implements Serializable {
private Integer orderid;
private Float amount;
private Set<OrderItem> orderItems = new HashSet<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)指明Order 与OrderItem 关联关系为一对多关系,下面是@OneToMany 注释的属性介绍:
* 1>targetEntity Class 类型的属性。
*
**************************************************************************/
@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) {
orderitem.setOrder(null);
this.orderItems.remove(orderitem);
}
}
上面声明一个Set 变量orderItems 用来存放多个OrderItem对象, 注释@OneToMany(mappedBy="order",cascade =
CascadeType.ALL, fetch = FetchType.LAZY)指明Order 与OrderItem 关联关系为一对多关系,下面是@OneToMany
注释的属性介绍:
1>targetEntity
Class 类型的属性。
定义关系类的类型,默认是该成员属性对应的类类型,所以通常不需要提供定义。
2>mappedBy
String 类型的属性。
定义类之间的双向关系。如果类之间是单向关系,不需要提供定义,如果类和类之间形成双向关系,我们就需要
使用这个属性进行定义,否则可能引起数据一致性的问题。
3>cascade
CascadeType[]类型。
该属性定义类和类之间的级联关系。定义的级联关系将被容器视为对当前类对象及其关联类对象采取相同的操
作,而且这种关系是递归调用的。举个例子:Order 和OrderItem 有级联关系,那么删除Order 时将同时删除它所
对应的OrderItem 对象。而如果OrderItem 还和其他的对象之间有级联关系,那么这样的操作会一直递归执行下去。
cascade 的值只能从CascadeType.PERSIST(级联新建)、CascadeType.REMOVE(级联删除)、CascadeType.REFRESH
(级联刷新)、CascadeType.MERGE(级联更新)中选择一个或多个。还有一个选择是使用CascadeType.ALL,表
示选择全部四项。
4>fatch
FetchType 类型的属性。
可选择项包括:FetchType.EAGER 和FetchType.LAZY。前者表示关系类(本例是OrderItem 类)在主类(本例是Order
类)加载的时候同时加载,后者表示关系类在被访问时才加载。默认值是FetchType. LAZY。
@OrderBy(value = "id ASC")注释指明加载OrderItem 时按id 的升序排序
addOrderItem 和removeOrderItem 方法用来添加/删除订单项。
OrderItem.java
package com.zhaosoft.bean;
import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@SuppressWarnings("serial")
@Entity
@Table(name = "OrderItems")
public class OrderItem implements Serializable {
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;
}
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 指明OrderItem 和Order 之间为多对一关系,多个OrderItem 实例关联的都是同一个Order 对象。
@ManyToOne(cascade = CascadeType.REFRESH, optional = false)
@JoinColumn(name = "order_id")
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
}
注释@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表有记录,查询结果仍有记录。
@JoinColumn(name = "order_id")注释指定OrderItem 映射表的order_id 列作为外键与Order 映射表的主键列
关联。
为了使用上面的实体Bean,我们定义一个Session Bean 作为他的使用者。下面是Session Bean 的业务接口,他定
义了三个业务方法insertOrder,getOrderByID 和getAllOrder,三个方法的业务功能是:
insertOrder 添加一个订单(带两个订单项)进数据库
getOrderByID 获取指定订单号的订单
getAllOrder 获取所有订单
下面是Session Bean 的业务接口及实现类
OrderDAO.java
package com.zhaosoft.session;
import java.util.List;
import com.zhaosoft.bean.Order;
public interface OrderDAO {
public void insertOrder();
public Order getOrderByID(Integer orderid);
public List getAllOrder();
public void deleteOrder(Integer orderid);
}
OrderDAOBean.java
package com.zhaosoft.session;
import java.util.Date;
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.zhaosoft.bean.Order;
import com.zhaosoft.bean.OrderItem;
@Stateless
@Remote( { OrderDAO.class })
public class OrderDAOBean implements OrderDAO {
@PersistenceContext
protected EntityManager em;
public void insertOrder() {
Order order = new Order();
order.setCreatedate(new Date());
order.addOrderItem(new OrderItem("笔记本电脑", new Float(13200.5)));
order.addOrderItem(new OrderItem("U盘", new Float(620)));
order.setAmount(new Float(13200.5 + 620));
em.persist(order);
}
// 删除数据得方法
public void deleteOrder(Integer orderid) {
try {
Order order = em.find(Order.class, orderid);
em.remove(order);
} catch (Exception e) {
e.printStackTrace();
}
}
public Order getOrderByID(Integer orderid) {
Order order = em.find(Order.class, orderid);
order.getOrderItems().size();
// 因为是延迟加载,通过执行size()这种方式获取订单下的所有订单项
return order;
}
public List getAllOrder() {
Query query = em
.createQuery("select DISTINCT o from Order o inner join fetch o.orderItems order by o.orderid");
List result = query.getResultList();
return result;
}
}
上面有一点需要强调:当业务方法需要把一个实体Bean 作为参数返回给客户端时,除了实体Bean 本身需要实现Serializable 接口之外,如果关联类(OrderItem)是延迟加载,还需在返回实体Bean 之前通过访问关联类的方式加载
关联类。否则在客户端访问关联类时将会抛出加载例外。另外不管是否延迟加载,通过join fetch 关联语句都可显式加载关联类,如业务方法getAllOrder 。
下面是Session Bean 的JSP 客户端代码:
OneToManyTest.jsp
<%@ page contentType="text/html; charset=GBK"%>
<%@ page import="com.zhaosoft.session.OrderDAO,com.zhaosoft.bean.*,javax.naming.*,java.util.*"%>
<%
Properties props = new Properties();
props.setProperty("java.naming.factory.initial","org.jnp.interfaces.NamingContextFactory");
props.setProperty("java.naming.provider.url", "localhost:1099");
props.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming");
InitialContext ctx = new InitialContext(props);
try {
OrderDAO orderdao = (OrderDAO) ctx.lookup("OrderDAOBean/remote");
//插入数据
orderdao.insertOrder();
//查询数据
List list = orderdao.getAllOrder();
if (list!=null){
for(int i=0; i<list.size();i++){
Order od = (Order) list.get(i);
if (od!=null){
out.println("==============订单号:"+ od.getOrderid()+"=================<br>");
//以上得操作都是关于order
Iterator iterator = od.getOrderItems().iterator();
while (iterator.hasNext()){
OrderItem SubOrder = (OrderItem) iterator.next();
out.println("订购产品:"+ SubOrder.getProductname() +"<br>");
}
}
}
}else{
out.println("获取不到订单列表");
}
} catch (Exception e) {
out.println(e.getMessage());
}
%>
我喜欢的
浏览器 |
我喜欢的
文化礼品 |
我喜欢的
ISP网站 |
我喜欢的
网站 |
FireFox 2.0
|
100部奥斯卡影片
|
时代互联
|
博告网
|
|
|
时代互联10元换空间
|
加入博告网日进斗金不是梦!
|
聚合到我的好诶网博告网 提供的广告 |