I need to implement a state pattern in JPA. I'm working with a delivery System for products. I can create orders, each order contains an order status (for example: Sent, Delivered, Cancelled, etc), so the order will be in different states.
Here's my code (I ommited details for the sake of simplicity)
Order.java
@Entity
@Table (name="Orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column (name = "id", nullable = false)
private long id;
@Embedded
private OrderStatus status;
....
}
OrderStatus.java
@MappedSuperclass
@Embeddable
public abstract class OrderStatus {
private String name;
private Date startDate;
@OneToOne( fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn( name="id_order" )
private Order order;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public boolean canAddItem() { return false; }
public boolean canAssign() { return false; }
public boolean canRefuse() { return false; }
public boolean canDeliver() { return false; }
public boolean canFinish() { return false; }
public boolean canCancel() { return false; }
public boolean addItem() throws Exception{
throw new Exception("No se puede realizarse esta accion");
}
public boolean assign(DeliveryMan deliveryMan) throws Exception{
throw new Exception("No se puede realizarse esta accion");
}
public boolean refuse() throws Exception{
throw new Exception("No se puede realizarse esta accion");
}
public boolean deliver() throws Exception{
throw new Exception("No se puede realizarse esta accion");
}
public boolean cancel() throws Exception{
throw new Exception("No se puede realizarse esta accion");
}
public boolean finish() throws Exception{
throw new Exception("No se puede realizarse esta accion");
}
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
}
Pending.java
@Embeddable
public class Pending extends OrderStatus{
public Pending() {
}
public Pending(Order order, Date startDate) {
this.setStartDate(startDate);
this.setOrder(order);
this.setName("Pending");
}
@Override
public boolean assign(DeliveryMan deliveryMan) throws Exception{
Order order = this.getOrder();
order.setDeliveryMan(deliveryMan);
Assigned assigned = new Assigned(order, order.getDateOfOrder());
order.setStatus(assigned);
return true;
}
}
If I do something like the following
CreateOrder.java
Order order_1 = new Order();
order_1.setStatus(new Pending(order_1, order_1.getDateOfOrder()));
orderRepository.save(order_1);
Then in the next code snippet I'm trying to move from Pending state to Assigned state.
TestState.java
private void TestState() throws Exception {
Optional<Order> order = orderRepository.findById(1L);
if(order.isPresent()) {
// Get the order status which was previously setted
OrderStatus orderStatus = order.get().getStatus();
// invoke assign method
orderStatus.assign(new DeliveryMan());
}
}
If OrderStatus is abstract I get this exception
Caused by: org.hibernate.InstantiationException: Cannot instantiate abstract class or interface: : com.bd.tpfinal.model.OrderStatus
If OrderStatus is not an abstract class, I get this output
Caused by: java.lang.Exception: No se puede realizarse esta accion
This is triggering the method hosted in the superclass.
My Order table seems fine. It contains extra fields (name = Pending, startdate = Date.now()). But I cannot trigger the methods for each state.
I've read that inheritance is not supported using embedded types in JPA / hibernate. Any help? thanks