了解跨相关实体的JPA传播更新

时间:2014-08-12 12:56:33

标签: jpa persistence glassfish-3

更新:我考虑了克里斯的答案,但没有帮助 - 我仍然面临着这个问题。我已经更新了下面的代码以包含Chris的答案。需要注意的是,在实施Chris的建议时,关系是持久的,但没有反映在view.xhtml页面上。我不得不用调用GenericDao.update()返回的对象替换db对象。

我有以下关系:

许多PurchaseOrders(PO)的一位客户 许多发票的一个PO。

我已经阅读了双向关系,我知道如果我有双向关系,我应该在更新实体时更新关系的两面。

我执行以下步骤:

  1. 创建客户(客户1)
  2. 创建与客户1
  3. 关联的采购订单(订单1)
  4. 创建与订单1相关联的发票(发票1)。
  5. 我观察到所有实体和关系都是持久的,但不会显示客户订单列表。

    查看客户: Customers' orders are incorrectly not listed but have been persisted.

    查看PurchaseOrder:

    Purchase orders' invoices are correctly listed.

    数据库查询:

    > select * from customer;
    > +----+------------+
    > | ID | NAME       |
    > +----+------------+
    > |  1 | Customer 1 |
    > +----+------------+
    > 1 row in set (0.00 sec)
    > 
    > mysql> select * from purchaseorder;
    > +----+---------+-------------+
    > | ID | NAME    | customer_id |
    > +----+---------+-------------+
    > |  1 | Order 1 |           1 |
    > +----+---------+-------------+
    > 1 row in set (0.00 sec)
    > 
    > mysql> select * from invoice;
    > +----+-----------+------------------+
    > | ID | NAME      | purchaseorder_id |
    > +----+-----------+------------------+
    > |  1 | Invoice 1 |                1 |
    > +----+-----------+------------------+
    > 1 row in set (0.00 sec)
    

    数据库反映了已配置实体之间的关系,因此我知道我的更改是持久的,据我所知,我已经实现了客户PO关系,与PO-Invoice关系的实现方式相同。由于PO的发票清单已正确更新,我认为我没有系统性问题,所以在客户PO和PO发票关系的实施之间必然存在不同之处,但我无法发现它。

    为什么我没有看到我的客户的PO列表,即使为客户配置了明确的PO?

    任何帮助将不胜感激。

    类(为简洁而截断): 实体 客户

    private int id; //@Id and @GeneratedValue(IDENTITY) on getter
    private String name;
    @OneToMany(mappedBy="customer")
    private Set<PurchaseOrder> purchaseOrders;
    
    public Customer()
    {
        purchaseOrders = new HashSet<PurchaseOrder> ();
    }
    
    public Set<PurchaseOrder> getPurchaseOrders()
    {
        return this.purchaseOrders;
    }
    
    public void setPurchaseOrders(Set<PurchaseOrder> orders)
    {
        this.purchaseOrders = orders;
    }
    
    public void addPurchaseOrder(PurchaseOrder purchaseOrder)
    {
        this.purchaseOrders.add(purchaseOrder);
        //this IF is important for avoiding an infinite loop
        if (purchaseOrder.getCustomer() != this)
        {
            purchaseOrder.setCustomer(this);
        }
    }
    
    public void removePurchaseOrder(PurchaseOrder purchaseOrder)
    {
        this.purchaseOrders.remove(purchaseOrder);
        //this IF is important to avoid an infinite loop
        if(purchaseOrder.getCustomer() != null)
        {
            purchaseOrder.removeFromCustomer(this);
        }
    }
    

    PO

    private int id; //@Id and identity column
    private String name;
    @ManyToOne
    @JoinColumn(name="customer_id")
    private Customer customer;
    @OneToMany(mappedBy="purchaseOrder")
    private Set<Invoice> invoices;
    
    public PurchaseOrder() {
        invoices = new HashSet<Invoice> ();
    }
    
    public Customer getCustomer()
    {
        return this.customer;
    }
    
    public void setCustomer(Customer customer)
    {
        this.customer = customer;
    }
    
    public void addToCustomer(Customer customer)
    {
        //this IF is important for avoiding an infinite loop
        if(!customer.getPurchaseOrders().contains(this))
        {
            customer.addPurchaseOrder(this);
        }
        this.customer = customer;
    }
    
    public void removeFromCustomer(Customer customer)
    {
        //this IF is important for avoiding an infinite loop
        if(customer.getPurchaseOrders().contains(this))
        {
            customer.removePurchaseOrder(this);
        }
        this.customer = null;
    }
    
    public Set<Invoice> getInvoices()
    {
        return this.invoices;
    }
    
    public void setInvoices(Set<Invoice> invoices)
    {
        this.invoices = invoices;
    }
    
    public void addInvoice(Invoice invoice)
    {
        this.invoices.add(invoice);
        //this IF is important for avoiding an infinite loop
        if (invoice.getPurchaseOrder() != this)
        {
            invoice.addToPurchaseOrder(this);
        }
    }
    
    public void removeInvoice(Invoice invoice)
    {
        this.invoices.remove(invoice);
        //this IF is important to avoid an infinite loop
        if(invoice.getPurchaseOrder() != null)
        {
            invoice.removeFromPurchaseOrder(this);
        }
    }
    

    发票

    private int id; //@Id and identity column
    private String name;
    @ManyToOne
    @JoinColumn(name="purchaseorder_id")
    private PurchaseOrder purchaseOrder;
    
    public Invoice() {
    }
    
    public PurchaseOrder getPurchaseOrder()
    {
        return this.purchaseOrder;
    }
    
    public void setPurchaseOrder(PurchaseOrder purchaseOrder)
    {
        this.purchaseOrder = purchaseOrder;
    }
    
    public void addToPurchaseOrder(PurchaseOrder purchaseOrder)
    {
        //this IF is important for avoiding an infinite loop
        if(!purchaseOrder.getInvoices().contains(this))
        {
            purchaseOrder.addInvoice(this);
        }
        this.purchaseOrder = purchaseOrder;
    }
    
    public void removeFromPurchaseOrder(PurchaseOrder purchaseOrder)
    {
        //this IF is important for avoiding an infinite loop
        if(purchaseOrder.getInvoices().contains(this))
        {
            purchaseOrder.removeInvoice(this);
        }
        this.purchaseOrder = null;
    }
    

    GenericDao(所有其他DAO的父母)

    @Stateful
    public class GenericDao<T extends Serializable, PK> implements IGenericDao<T, PK>
    {
        @PersistenceContext(unitName = "my_PU")
        protected EntityManager em;
    
        private Class<T> type;
    
        public Class<T> getType()
        {
            return type;
        }
    
        public void setType(Class<T> type)
        {
            this.type = type;
        }
    
        public void create(T newObject)
        {
            em.persist(newObject);
        }
    
        public T read(PK id)
        {
            return em.find(type, id);
        }
    
        public T update(T transientObject)
        {
            return em.merge(transientObject);
        }
    
        public void delete(T objectToDelete)
        {
            em.remove(objectToDelete);
        }
    
        public T getResultObject(String namedQuery, Map<String, Object> criteria)
        throws DatabaseException
        {
            List<T> records = getResultSetList(namedQuery, criteria);
            if(records.isEmpty())
            {
                return null;
            }
            else if (records.size() != 1)
            {
                throw new DatabaseException("Too many records found!");
            }
            else
            {
                return records.remove(0);
            }
        }
    }
    

    控制器 CustomerController

    @RequestScoped
    public class CustomerController extends FormRequestController
    {
        @Inject
        private HTMLDataTableActionBean htmlDataTableActionBean;
        @EJB
        private ICustomerDao customerDao;
        @Inject
        private Customer customer;
    
        @PostConstruct
        public void init() throws DatabaseException
        {
            setEntityObjectList(findAll());
            if (null == this.getCustomer())
            {
                setCustomer(new Customer());
            }
        }
    
        public void processRequest(FormActionToPerform action) throws DatabaseException
        {
            switch (action)
            {
                case SHOW_ADD_VIEW:
                    setCustomer(new Customer());
                    break;
                case SHOW_VIEW_FOR_LIST:
                    setEntityObjectList(findAll());
                    break;
                case SHOW_EDIT_VIEW:
                case SHOW_VIEW_TO_VIEW_SELECTED_OBJECT:
                    setCustomer((Customer) getHtmlDataTableActionBean()
                            .getSelectedEntityObject());
                    break;
                case SHOW_DELETE_VIEW:
                    setCustomer((Customer) getHtmlDataTableActionBean()
                            .getSelectedEntityObject());
                    delete();
                    break;
            }
        }
    
        public String doShowUIView(FormActionToPerform action)
        {
            String responseURL = "fail.xhtml";
            if (null == this.customer)
            {
                return responseURL;
            }
            else
            {
                switch (action)
                {
                    case SHOW_ADD_VIEW:
                        responseURL = "customer.xhtml";
                        break;
                    case SHOW_EDIT_VIEW:
                        responseURL = "customer.xhtml";
                        break;
                    case SHOW_VIEW_TO_VIEW_SELECTED_OBJECT:
                        responseURL = "viewCustomer.xhtml";
                        break;
                    case SHOW_DELETE_VIEW:
                        responseURL = "customerList.xhtml";
                        break;
                    case SHOW_VIEW_FOR_LIST:
                        if (this.entityObjectList.size() == 0)
                        {
                            setErrorMessage("No customers to display");
                        }
                        responseURL = "customerList.xhtml";
                        break;
                    default:
                        responseURL = "index.xhtml";
                }
            }
            return responseURL;
        }
    
        public String save()
        {
            String url = "success.xhtml";
            Customer existingCustomer = null;
            try
            {
                existingCustomer =
                        customerDao.getCustomerByName(this.getCustomer().getName());
    
                if(existingCustomer != null)
                {
                    //there's already a customer with this name, don't make a new one
                    setErrorMessage("Customer already exists");
                    url = "fail.xhtml";
                }
    
                customerDao.update(customer);
            }
            catch (DatabaseException e)
            {
                setErrorMessage(e.toString());
                e.printStackTrace();
                url = "fail.xhtml";
            }
            return url;
        }
    }
    

    POController

    @RequestScoped
    public class PurchaseOrderController extends FormRequestController
    {
        @Inject
        private HTMLDataTableActionBean htmlDataTableActionBean;
        @EJB
        private IPurchaseOrderDao purchaseOrderDao;
        @EJB
        private IInvoiceDao invoiceDao;
        @EJB
        private ICustomerDao customerDao;
        @Inject
        private PurchaseOrder purchaseOrder;
    
        private List<SelectItem> customerList;
        private String selectedCustomer;
    
        @PostConstruct
        public void init() throws DatabaseException
        {
            setEntityObjectList(findAll());
            if (null == purchaseOrder)
            {
                purchaseOrder = new PurchaseOrder();
                setEditMode(false);
            }
        }
    
        public void processRequest(FormActionToPerform action)
        throws DatabaseException
        {
            switch (action)
            {
                case SHOW_ADD_VIEW:
                    setPurchaseOrder(new PurchaseOrder());
                    break;
                case SHOW_VIEW_FOR_LIST:
                    setEntityObjectList(findAll());
                    break;
                case SHOW_EDIT_VIEW:
                case SHOW_VIEW_TO_VIEW_SELECTED_OBJECT:
                    {
                        setPurchaseOrder(
                              (PurchaseOrder)getHtmlDataTableActionBean().
                                                         getSelectedEntityObject());
                    }
                    break;
                case SHOW_DELETE_VIEW:
                    {
                        setPurchaseOrder(
                              (PurchaseOrder)getHtmlDataTableActionBean().
                                                         getSelectedEntityObject());
                        delete();
                    }
                    break;
            }
        }
    
        String doShowUIView(FormActionToPerform action)
        {
            String responseURL = "fail.xhtml";
            switch (action)
            {
                case SHOW_ADD_VIEW:
                    responseURL = "purchaseOrder.xhtml";
                    break;
                case SHOW_EDIT_VIEW:
                    setEditMode(true);
                    setComponent(null);
                    responseURL = "purchaseOrder.xhtml";
                    break;
                case SHOW_DELETE_VIEW:
                case SHOW_VIEW_FOR_LIST:
                    if (this.entityObjectList.size() == 0)
                    {
                        setErrorMessage("No orders to display");
                    }
                    responseURL = "purchaseOrderList.xhtml";
                    break;
    
                case SHOW_VIEW_TO_VIEW_SELECTED_OBJECT:
                    responseURL = "viewPurchaseOrder.xhtml";
                    break;
    
                default:
                    responseURL = HOME;
            }
            return responseURL;
        }
    
        public String save()
        {
            String responseURL = "fail.xhtml";
            try
            {
                PurchaseOrder dbPurchaseOrder =
                   purchaseOrderDao.getPurchaseOrderByName(purchaseOrder.getName());
                if(dbPurchaseOrder == null)
                {
                    dbPurchaseOrder = purchaseOrder;
                }
    
                Customer customer = customerDao.getCustomerByName(selectedCustomer);
                dbPurchaseOrder.addToCustomer(customer);
                purchaseOrder = purchaseOrderDao.update(dbPurchaseOrder);
                //replace the not-yet-persisted dbPurchaseOrder object in customer
                //with the persisted purchaseOrderobject returned from the update()
                //call above.
                customer.removePurchaseOrder(dbPurchaseOrder);
                customer.addPurchaseOrder(purchaseOrder);
                customerDao.update(customer);
                System.out.println("# of Purchase orders for customer: "+
                            purchaseOrder.getCustomer().getPurchaseOrders().size());
                //Output: # of Purchase orders for customer: 1
                responseURL = "success.xhtml";
            }
            catch (DatabaseException e)
            {
                e.printStackTrace();
                setErrorMessage(e.toString());
                responseURL = null;
            }
            return responseURL;
        }
    }
    

    InvoicesController

    @RequestScoped
    public class InvoiceController extends FormRequestController
    {
        @Inject
        private HTMLDataTableActionBean htmlDataTableActionBean;
        @EJB
        private IInvoiceDao invoiceDao;
        @Inject
        private Invoice invoice;
        @EJB
        private IPurchaseOrderDao purchaseOrderDao;
    
        private List<SelectItem> purchaseOrderList;
        private String selectedPurchaseOrder;
    
        @PostConstruct
        public void init() throws DatabaseException
        {
            setEntityObjectList(findAll());
            if (null == invoice)
            {
                invoice = new Invoice();
                setEditMode(false);
            }
        }
    
        public void processRequest(FormActionToPerform action) throws DatabaseException
        {
            switch (action)
            {
                case SHOW_ADD_VIEW:
                    break;
                case SHOW_VIEW_FOR_LIST:
                    setEntityObjectList(findAll());
                    break;
                case SHOW_EDIT_VIEW:
                case SHOW_VIEW_TO_VIEW_SELECTED_OBJECT:
                    {
                        setInvoice((Invoice)getHtmlDataTableActionBean().
                                                         getSelectedEntityObject());
                    }
                    break;
                case SHOW_DELETE_VIEW:
                {
                    setInvoice((Invoice)getHtmlDataTableActionBean().
                                                         getSelectedEntityObject());
                    delete();
                }
                break;
            }
        }
    
        String doShowUIView(FormActionToPerform action)
        {
            String responseUrl = "fail.xhtml";
            if (null == invoice)
            {
                System.out.println("invoice == null");
                return responseUrl;
            }
            else
            {
                switch (action)
                {
                    case SHOW_ADD_VIEW:
                        responseUrl = "invoice.xhtml";
                        break;
                    case SHOW_EDIT_VIEW:
                        setEditMode(true);
                        setComponent(null);
                        responseUrl = "invoice.xhtml";
                        break;
                    case SHOW_VIEW_TO_VIEW_SELECTED_OBJECT:
                        responseUrl = "viewInvoice.xhtml";
                        break;
                    case SHOW_DELETE_VIEW:
                    case SHOW_VIEW_FOR_LIST:
                        if (this.entityObjectList.size() == 0)
                        {
                            setErrorMessage("no invoices to display");
                        }
                        responseUrl = "invoiceList.xhtml";
                        break;
                    default:
                        responseUrl = "index.xhtml";
                }
            }
            return responseUrl;
        }
    
        public String save()
        {
            String responseUrl = "fail.xhtml";
            try
            {
                Invoice dbInvoice = invoiceDao.getInvoiceByName(invoice.getName());
                if(dbInvoice == null)
                {
                    //this is a new invoice
                    dbInvoice = invoice;
                }
                PurchaseOrder purchaseOrder =
                     purchaseOrderDao.getPurchaseOrderByName(selectedPurchaseOrder);
                dbInvoice.addToPurchaseOrder(purchaseOrder);
                invoice = invoiceDao.update(dbInvoice);
                //replace the not-yet-persisted dbInvoice object in purchaseOrder
                //with the persisted invoice object returned from the update() call above.
                purchaseOrder.removeInvoice(dbInvoice);
                purchaseOrder.addInvoice(invoice);
                purchaseOrderDao.update(purchaseOrder);
                System.out.println("# of Invoices for purchase order: "+
                                   invoice.getPurchaseOrder().getInvoices().size());
                //Output: # of Invoices for purchase order: 1
                responseUrl = "success.xhtml";
            }
            catch (Exception e)
            {
                System.out.println(e.toString());
                e.printStackTrace();
                setErrorMessage(e.toString());
            }
            return responseUrl;
        }
    }
    

    FormRequestController

    public abstract class FormRequestController implements NavigationConstants
    {
    
        protected enum FormActionToPerform {
            SHOW_ADD_VIEW,
            SHOW_EDIT_VIEW,
            SHOW_DELETE_VIEW,
            SHOW_VIEW_TO_VIEW_SELECTED_OBJECT,
            SHOW_VIEW_FOR_LIST;
        }
    
        protected FacesContext context;
        protected List<?> entityObjectList;
        private UIComponent component;
        protected boolean editMode;
        protected String componentId = null;
    
        public String showViewDataTable() throws DatabaseException {
            processRequest(FormActionToPerform.SHOW_VIEW_FOR_LIST);
            return doShowUIView(FormActionToPerform.SHOW_VIEW_FOR_LIST);
        }
    
        public String showViewToAdd() throws DatabaseException {
            processRequest(FormActionToPerform.SHOW_ADD_VIEW);
            return doShowUIView(FormActionToPerform.SHOW_ADD_VIEW);
        }
    
        public String showViewToEdit() throws DatabaseException {
            processRequest(FormActionToPerform.SHOW_EDIT_VIEW);
            return doShowUIView(FormActionToPerform.SHOW_EDIT_VIEW);
        }
    
        public String showViewToDeleteDetails() throws DatabaseException {
            processRequest(FormActionToPerform.SHOW_DELETE_VIEW);
            return doShowUIView(FormActionToPerform.SHOW_DELETE_VIEW);
        }
    
        public String showViewToViewDetails() throws DatabaseException {
            processRequest(FormActionToPerform.SHOW_VIEW_TO_VIEW_SELECTED_OBJECT);
            return doShowUIView(FormActionToPerform.SHOW_VIEW_TO_VIEW_SELECTED_OBJECT);
        }
    
        abstract String doShowUIView(FormActionToPerform action);
        abstract void processRequest(FormActionToPerform action) throws DatabaseException;
    
        protected void bindData() {
    
        }
    
        abstract String save();
        abstract void delete() throws DatabaseException;
    
        public List<?> getEntityObjectList() {
            return entityObjectList;
        }
    
        public void setEntityObjectList(List<?> entityObjectList) {
            this.entityObjectList = entityObjectList;
        }
    
        public FacesContext getContext() {
            setContext(FacesContext.getCurrentInstance());
            return context;
        }
    
        public void setContext(FacesContext context) {
            this.context = context;
        }
    
        public UIComponent getComponent() {
            return component;
        }
    
        public void setComponent(UIComponent component) {
            this.component = component;
        }
    }
    

    我正在使用Customer#purchaseOrdersPurchaseOrder#invoicesh:dataTable进行迭代。 SO一直误认为我的JSF代码是错误格式化的代码,并告诉我缩进它,所以我无法显示我的网页代码,但希望上面有足够的内容来发现我的错误。

    提前感谢您的时间。

1 个答案:

答案 0 :(得分:1)

如果JPA设置为使用属性访问权限,则set / get方法中不应包含逻辑。 set方法中的逻辑将导致JPA在构建实体时触发延迟集合等,并且可能具有其他不利影响,具体取决于提供者内部。我会切换你的注释,使它们在字段上,或删除

if(!purchaseOrder.getInvoices().contains(this))
{
    purchaseOrder.addInvoice(this);
}

来自set方法的逻辑。应用程序仍然可以使用addInvoice并设置关系的两侧,因为JPA在加载实体时不使用addInvoice方法。