State pattern in JPA

285 Views Asked by At

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

0

There are 0 best solutions below