EJB3 一对多及多对一映射

 

现实应用中存在很多一对多的情况,如一项订单中存在一个或多个订购项。下面就以订单为例介绍存在一对多及多对一双向关系的实体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 注释有四个属性:targetEntitycascadefetch optional,前三个属性的具体含义和@OneToMany

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

optional 属性是定义该关联类是否必须存在,值为false 时,关联类双方都必须存在,如果关系被维护端不存在,

查询的结果为null。值为true , 关系被维护端可以不存在,查询的结果仍然会返回关系维护端,在关系维护

端中指向关系被维护端的属性为nulloptional 属性的默认值是trueoptional 属性实际上指定关联类与被关

联类的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 的业务接口,他定

义了三个业务方法insertOrdergetOrderByID 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元换空间
  加入博告网日进斗金不是梦!
聚合到我的好诶网博告网 提供的广告

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值