/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package openbook.domain;
import java.io.Serializable;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
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.OneToOne;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
/**
* A persistent entity to demonstrate Master in a Master-Details or Composite pattern for
* persistent domain model.
* <br>
* The Purchase Order - Line Items relationship typically demonstrates a Master-Details pattern.
* In JPA 2.0, following new features are added to support this common pattern used in domain modeling,
* and this example demonstrates them.<br>
* <LI> Compound, Derived identity: This feature allows the Details type to compose its identity from the
* the Master's identity.
* <LI> Orphan Delete or Dependent relation: This feature allows to impose composite relation semantics to
* normally associative relation semantics implied in Java. Composite relation in persistence terms also
* translates to deletion of Details record from database when the details lose their relation to master.
* <br>
* Besides the above two key features, this persistent type also shows usage of
* <LI>Auto-generated identity.
* <LI>Enum type persistent attribute.
* <LI>Date type persistent attribute.
* <LI>One-to-One uni-directional, immutable mapping to Customer.
* <LI>One-to-Many bi-directional, immutable mapping to LineItem.
*
* @author Pinaki Poddar
*
*/
@SuppressWarnings("serial")
@Entity
public class PurchaseOrder implements Serializable {
/**
* Enumerates the status of a Purchase Order.
*
*/
public enum Status {PENDING, DELIVERED};
/**
* <A name="id">
* Persistent Identity is generated by the provider.
*/
@Id
@GeneratedValue
private long id;
@OneToOne(optional=false)
private Customer customer;
/**
* <A name="items">
* A composite relation.
* All persistence operation on this order are cascaded to its line items.
* Moreover, a line item ceases to exist even in the database if it is no
* more referred to by a order. This is known as <em>orphan delete</em>
* and can be termed as <em>persistent garbage collection</em>.
*
*/
@OneToMany(mappedBy="order", fetch=FetchType.EAGER, cascade=CascadeType.ALL, orphanRemoval=true)
private List<LineItem> items;
private Status status;
private int total;
@Temporal(TemporalType.TIMESTAMP)
private Timestamp placedOn;
@Temporal(TemporalType.TIMESTAMP)
private Timestamp deliveredOn;
/**
* A protected constructor satisfies two purposes:<br>
* <LI>i) Status and creation time is set consistently.
* <LI>ii) OpenJPA Bytecode Enhancer requires an empty constructor for the domain classes.
* The public constructor of Purchase Order takes a Shopping Cart as argument.
*
*/
protected PurchaseOrder() {
status = Status.PENDING;
placedOn = new Timestamp(System.currentTimeMillis());
}
/**
* <A name="init"/>
* Construct a new order by transferring the content of the given {@linkplain ShoppingCart}.
*
* @param cart a non-null, non-empty Shopping cart
* @exception NullPointerException if the cart is null
* @exception IllegalStateException if the cart is empty
*/
public PurchaseOrder(ShoppingCart cart) {
this();
if (cart == null)
throw new NullPointerException("Can not create new Purchase Order from null Shopping Cart");
if (cart.isEmpty())
throw new IllegalStateException("Can not create new Purchase Order from empty Shopping Cart");
customer = cart.getCustomer();
Map<Book, Integer> items = cart.getItems();
for (Map.Entry<Book, Integer> entry : items.entrySet()) {
addItem(entry.getKey(), entry.getValue());
}
}
/**
* Gets the immutable, auto-generated persistent identity of this Purchase Order.
*/
public long getId() {
return id;
}
/**
* Gets the customer who placed this Purchase Order.
*
* @return immutable Customer.
*/
public Customer getCustomer() {
return customer;
}
/**
* Gets the status of this Purchase Order.
*
*/
public Status getStatus() {
return status;
}
public boolean isDelivered() {
return Status.DELIVERED.equals(status);
}
/**
* <A name="setDelivered"/>
* Sets the status of this Purchase Order as delivered.
* Setting an order status as delivered nullifies the association to Line Items.
* Nullifying this association has the important side-effect of Line Item records
* be deleted from the database because the relation is annotated as orphan delete.
*
* @exception IllegalStateException if this order has already been delivered.
*/
public void setDelivered() {
if (this.status == Status.DELIVERED)
throw new IllegalStateException(this + " has been delivered");
this.status = Status.DELIVERED;
this.deliveredOn = new Timestamp(System.currentTimeMillis());
this.items = null;
}
/**
* Gets the items for this Purchase Order.
*
* @return the line items of this order. The line items for a delivered order is always null.
* @see #setDelivered()
*/
public List<LineItem> getItems() {
return items;
}
/**
* Adds an item to this Purchase Order.
* The total is adjusted as a side-effect.
*/
void addItem(Book book, int quantity) {
if (book == null)
throw new NullPointerException("Can not add Line Item to Purchase Order for null Book");
if (quantity < 1)
throw new IllegalArgumentException("Can not add Line Item to Purchase Order for negative (=" +
quantity + ") number of Book " + book);
if (items == null)
items = new ArrayList<LineItem>();
items.add(new LineItem(this, items.size()+1, book, quantity));
total += (book.getPrice() * quantity);
}
/**
* Gets the total cost of all the items in this order.
*/
public double getTotal() {
return total;
}
/**
* Gets the time when this order was placed.
*/
public Timestamp getPlacedOn() {
return placedOn;
}
/**
* Gets the time when this order was delivered.
*
* @return null if the order has not been delivered yet.
*/
public Date getDeliveredOn() {
return deliveredOn;
}
}