tags:

views:

171

answers:

2

I'm deleting entities from a table with a one to many relationship to the same entity (representing node hierarchy). If I make the xincoCoreNodeId relationship a cascade all it works but I don't want that in the real application. I don't want removing a leaf removing its parent. Is there a way to modify this relationship at run time or disable the constraints so I can delete the whole table contents without getting constraint complains?

    package com.bluecubs.xinco.core.server.persistence;

import com.bluecubs.xinco.core.server.AuditedEntityListener;
import com.bluecubs.xinco.core.server.XincoAuditedObject;
import java.io.Serializable;
import java.util.List;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.eclipse.persistence.annotations.PrivateOwned;

/**
 *
 * @author Javier A. Ortiz Bultrón <[email protected]>
 */
@Entity
@Table(name = "xinco_core_node")
@EntityListeners(AuditedEntityListener.class)
@NamedQueries({
    @NamedQuery(name = "XincoCoreNode.findAll", query = "SELECT x FROM XincoCoreNode x"),
    @NamedQuery(name = "XincoCoreNode.findById", query = "SELECT x FROM XincoCoreNode x WHERE x.id = :id"),
    @NamedQuery(name = "XincoCoreNode.findByDesignation", query = "SELECT x FROM XincoCoreNode x WHERE x.designation = :designation"),
    @NamedQuery(name = "XincoCoreNode.findByStatusNumber", query = "SELECT x FROM XincoCoreNode x WHERE x.statusNumber = :statusNumber")})
public class XincoCoreNode extends XincoAuditedObject implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @Column(name = "id", nullable = false)
    private Integer id;
    @Basic(optional = false)
    @Column(name = "designation", nullable = false, length = 255)
    private String designation;
    @Basic(optional = false)
    @Column(name = "status_number", nullable = false)
    private int statusNumber;
    @JoinColumn(name = "xinco_core_language_id", referencedColumnName = "id", nullable = false)
    @ManyToOne(optional = false, fetch = FetchType.LAZY)
    private XincoCoreLanguage xincoCoreLanguageId;
    @OneToMany(cascade = CascadeType.PERSIST, mappedBy = "xincoCoreNodeId", fetch = FetchType.LAZY)
    private List<XincoCoreNode> xincoCoreNodeList;
    @JoinColumn(name = "xinco_core_node_id", referencedColumnName = "id")
    @PrivateOwned
    @ManyToOne(fetch = FetchType.LAZY)
    private XincoCoreNode xincoCoreNodeId;
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "xincoCoreNodeId", fetch = FetchType.LAZY)
    private List<XincoCoreAce> xincoCoreAceList;
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "xincoCoreNodeId", fetch = FetchType.LAZY)
    private List<XincoCoreData> xincoCoreDataList;

    public XincoCoreNode() {
    }

    public XincoCoreNode(Integer id) {
        this.id = id;
    }

    public XincoCoreNode(Integer id, String designation, int statusNumber) {
        this.id = id;
        this.designation = designation;
        this.statusNumber = statusNumber;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getDesignation() {
        return designation;
    }

    public void setDesignation(String designation) {
        this.designation = designation;
    }

    public int getStatusNumber() {
        return statusNumber;
    }

    public void setStatusNumber(int statusNumber) {
        this.statusNumber = statusNumber;
    }

    public XincoCoreLanguage getXincoCoreLanguageId() {
        return xincoCoreLanguageId;
    }

    public void setXincoCoreLanguageId(XincoCoreLanguage xincoCoreLanguageId) {
        this.xincoCoreLanguageId = xincoCoreLanguageId;
    }

    public List<XincoCoreNode> getXincoCoreNodeList() {
        return xincoCoreNodeList;
    }

    public void setXincoCoreNodeList(List<XincoCoreNode> xincoCoreNodeList) {
        this.xincoCoreNodeList = xincoCoreNodeList;
    }

    public XincoCoreNode getXincoCoreNodeId() {
        return xincoCoreNodeId;
    }

    public void setXincoCoreNodeId(XincoCoreNode xincoCoreNodeId) {
        this.xincoCoreNodeId = xincoCoreNodeId;
    }

    public List<XincoCoreAce> getXincoCoreAceList() {
        return xincoCoreAceList;
    }

    public void setXincoCoreAceList(List<XincoCoreAce> xincoCoreAceList) {
        this.xincoCoreAceList = xincoCoreAceList;
    }

    public List<XincoCoreData> getXincoCoreDataList() {
        return xincoCoreDataList;
    }

    public void setXincoCoreDataList(List<XincoCoreData> xincoCoreDataList) {
        this.xincoCoreDataList = xincoCoreDataList;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (id != null ? id.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof XincoCoreNode)) {
            return false;
        }
        XincoCoreNode other = (XincoCoreNode) object;
        if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "com.bluecubs.xinco.core.server.persistence.XincoCoreNode[id=" + id + "]";
    }
}
A: 

If you are using Hibernate, you can use its support for batch processing to delete many rows in a table efficiently. Basically, the query gets translated from HQL to SQL and runs against the database directly. There are limitations with this approach, though. For example, in-memory state won't be updated if you are holding references to any of the affected objects.

I don't know if JPA itself supports batch processing. Probably not. But if you expect to be affecting a lot of rows (hundreds or thousands), then this is one of those cases where ORM is not really the right tool for the job, anyway. It might make more sense to do it in straight SQL or perhaps a stored procedure. Either way, you can isolate the code inside a Data Access Object so that high-level business logic need not care how the operation is implemented.

Rob H
I'm using Eclipselink due to some issues I found with Hibernate.
javydreamercsw
OK, so assuming Eclipselink doesn't support batch processing natively, I'd recommend going with the SQL option.
Rob H
I'm trying to avoid SQL as the target database can be anything supported by the JPA implementation. I treated this as an exception and handled it in a special manner. See working code below.
javydreamercsw
A: 

I ended doing an special case in Java to handle this.

@Override
public void clearTable() {
    try {
        /**
         * All nodes are linked except the root. So we need to start deleting the leaves first.
         */
        while (new XincoCoreNodeJpaController().findXincoCoreNodeEntities().size() > 0) {
            for (com.bluecubs.xinco.core.server.persistence.XincoCoreNode xcn : getLeaves()) {
                new XincoCoreNodeJpaController().destroy(xcn.getId());
            }
        }
    } catch (XincoException ex) {
        Logger.getLogger(XincoCoreNodeServer.class.getName()).log(Level.SEVERE, null, ex);
    }
}

private Vector<com.bluecubs.xinco.core.server.persistence.XincoCoreNode> getLeaves() throws XincoException {
    Vector<com.bluecubs.xinco.core.server.persistence.XincoCoreNode> leaves =
            new Vector<com.bluecubs.xinco.core.server.persistence.XincoCoreNode>();
    result = XincoDBManager.protectedCreatedQuery("select x from XincoCoreNode x " +
            "where x.id not in (select y.xincoCoreNodeId.id from XincoCoreNode y " +
            "where y.xincoCoreNodeId is not null)",null,true);
    if (result.size() == 0) {
        //Check if the root is there
        for (Object o : new XincoCoreNodeJpaController().findXincoCoreNodeEntities()) {
            leaves.add((com.bluecubs.xinco.core.server.persistence.XincoCoreNode) o);
        }
    }
    for (Object o : result) {
        leaves.add((com.bluecubs.xinco.core.server.persistence.XincoCoreNode) o);
    }
    return leaves;
}
javydreamercsw
The JpaController classes are generated by Netbeans tools from the Entity class in main post.
javydreamercsw