简体   繁体   中英

Why unidirectional @OneToMany update is not working

I am completely new to JPA and ORM concept, so I am hoping someone can lucidly explain what might be the problem with my code.

@Entity
@Table(name = "PERSISTENCE_customer")

public class Customer implements Serializable {
    
     private static final long serialVersionUID = 1005220876458L;
     @Id
     @GeneratedValue(strategy = GenerationType.AUTO)
     private Long id;
     private String firstName;
     private String lastName;
     @OneToMany (cascade = CascadeType.ALL, orphanRemoval = true)
     private List<CustomerOrder> orders;
}
@Entity
@Table(name = "PERSISTENCE_ORDER")
public class CustomerOrder implements Serializable{
    private static final long serialVersionUID = 199102142021L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @NotNull
    String status;
    @NotNull
    @OneToMany (cascade = CascadeType.ALL, orphanRemoval = true)
    private List<LineItem> lineItems = new ArrayList();
    @NotNull
    private String orderNumber;
    ................
    ................
}

@Entity
@Table(name = "PERSISTENCE_LINEITEM")
public class LineItem implements Serializable {
    private static final long serialVersionUID = 1991217202100959L;
     @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @NotNull
    private Integer quantity;
    @NotNull
    private Part part;
}

Initially, the Customer entity is created through the user interface and persisted successfully. Later, the customer has an order and I update the Customer with CustomerOrder as follow:

private void UpdateCustomer(Customer customer) {
        FacesContext fc = FacesContext.getCurrentInstance();
        List<ShoppingCartItem> shoppingCart = getShoppingCart();
        CustomerOrder order = new CustomerOrder();
        List<CustomerOrder> orders = customer.getOrders();
        order.setLastUpdated(new Date());
        order.setOrderNumber(getInvoiceNumber());
        List<LineItem> lineItems = shoppingCart
                .stream()
                .map(e -> (new LineItem(e.getPart(), e.getQuantity())))
                .collect(Collectors.toList());
        order.setLineItems(lineItems);
        order.setStatus("Pending Shipment");
        order.setTotal(getTotal());
        orders.add(order);
        customer.setOrders(orders);
        try {
            updateOrders(customer, orders);
            fc.addMessage(null,
                    new FacesMessage("Customer order added successfuly"));
        } catch (ListServiceException e) {
            FacesMessage errMsg = new FacesMessage(FacesMessage.SEVERITY_FATAL,
                    "Error while adding customer order: ", e.getMessage());
            fc.addMessage(null, errMsg);
        }
    }
private void updateOrders(Customer cust, List<CustomerOrder> orders) throws ListServiceException {
            
        try { //em is the EntityManager injected as the field member
            if (em != null) {
                if (em.isOpen()) {
                    Customer c = getCustomer(cust.getId());
                    c.setOrders(orders);
                    em.merge(c);
                 } else {
                    logger.severe("Entity manager is closed");
            }
            else {
                logger.severe("Entity manager is NULL");
            }
        } catch (Exception e) {
            throw ThrowListServiceException.wrapException(e);
        }
    }

Once the EntityManage merges I get the following exception. I was under the impression that I don't need to explicitly persist the LineItem and CustomerOrder entities myself. I thought that the JPA will persist all the entities in the object graph. Why am I getting this exception? (I am using GlassFish 5.1 server with EclipseLink JPA)

Thanks in advance.

Internal Exception: java.sql.SQLIntegrityConstraintViolationException: Column 'ORDERS_ID'  cannot accept a NULL value.
Error Code: 30000
Call: INSERT INTO PERSISTENCE_CUSTOMER_PERSISTENCE_ORDER (orders_ID, Customer_ID) VALUES (?, ?)
    bind => [2 parameters bound]
Query: DataModifyQuery(name="orders" sql="INSERT INTO PERSISTENCE_USER_PERSISTENCE_ORDER (orders_ID, User_ID) VALUES (?, ?)")
    at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:331)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:905)
 ...............................
.................................
Caused by: java.sql.SQLIntegrityConstraintViolationException: Column 'ORDERS_ID'  cannot accept a NULL value.

Update

Using the IDE (Netbeans) debugger, I stepped through the code, and as I predicted, during the entity merge the JPA does not add new entities that are part of the object graph to the persistence context. For instance, in the updateOrders() method when I try to update the existing Customer object with a list of new CustomerOrder object, the JPA doesn't figure out that the elements in the List are not part of the persistence context yet and they need to be added. As a result, I had to modify my code to first add the List to the persistence context and then merge the Customer object with the newly persisted List. By doing so I no longer get the exception.

By the way, at moment, all the mapping relationships are unidirectional because I didn't see any reasons to use bidirectional mapping. However, would I gain anything by making these mappings bidirectionals?

Your OneToMany mapping is missing join specification or mappedBy value

I noticed that. Firstly You should commit new order to database.Then you should link it with user.I'm not sure if this solves your problem but this is a problem.Can you check it?

In my opinion, if you keep customer information in your Order entity, it may solve this problem.

@ManyToOne ()
    private Customer customer;

And in your Customer entity you should put mappedBy=customer for orders field.

After that, instead of giving orders for customer, you can give customer for a specific order. In my opinion it will achieve a better relationship mapping;

order.setCustomer(customer);

I hope i understood it right and this will solve your problem. When you give customer detail for order, you dont need to give orderlist detail for the same customer. Only one of them should be enough.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM