1

I have a list of child objects mapped to a parent object in my application. Now, when the user deletes any child entries from the UI, I have to remove the child and parent object association and remove the child records from the database. Everything in my code seems to be fine, but the code neither removes the parent-child association nor deletes the child records from the database. Below is my code:

Service code:

List<ServerConfig> existingConfig = serverMasterService.getServerConfigForServer(serverId);
            Set<ServerConfig> temp = new HashSet<ServerConfig>();

            for (ServerConfig sConfig : serverMstr.getServerConfigs()) {
                    for (ServerConfig eConfig : existingConfig){
                        if(sConfig.getAttributeId() == eConfig.getAttributeId()){
                            sConfig.setConfigId(eConfig.getConfigId());
                        }else{
                            temp.add(sConfig);
                        }
                    }
                    sConfig.setServer(serverMstr);
            }
            serverMstr.getServerConfigs().removeAll(temp);
        }
    this.serverMasterService.saveServerMasters(serverMstr);

DAO code:

public void saveServerMasters(ServerMstr serverMstr) {
        hibernateTemplate.saveOrUpdate(serverMstr);
    }

Parent HBM file:

<set name="serverConfigs" inverse="true" cascade="all" lazy="false">
         <key>
            <column name="serverId" not-null="true"/>
         </key>
         <one-to-many class="com.serverApp.business.model.ServerConfig"/>
   </set> 

Child HBM file:

<many-to-one name="server" class="com.serverApp.business.model.ServerMstr" 
    cascade="all" lazy="false" foreign-key="FK_SERVERMSTR" >
        <column name="ServerId" not-null="true"/>
    </many-to-one>

Even if I delete all the child records by the below code, child objects are not getting deleted.

serverMstr.getServerConfigs().removeAll(serverMstr.getServerConfigs());
this.serverMasterService.saveServerMasters(serverMstr);

Edit

Parent Object class:

package com.serverApp.business.model;

import java.util.Set;

import javax.xml.bind.annotation.XmlRootElement;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@XmlRootElement(name = "ServerMaster")
@JsonIgnoreProperties({"environmentMstr"})  
public class ServerMstr      {

@JsonProperty("ID")
private Long id;

@JsonProperty("ServerConfigs")
private Set<ServerConfig> serverConfigs;

public Set<ServerConfig> getServerConfigs() {
    return serverConfigs;
}


public void setServerConfigs(Set<ServerConfig>  serverConfigs) {
    this.serverConfigs = serverConfigs;
}

public Long getId() {
    return id;
}


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

@JsonProperty("Name")
private String name;

@JsonProperty("Notes")
private String notes;

@JsonProperty("Location")
private String location; 

@JsonProperty("SerialNo")
private String serialNo;

@JsonProperty("ServerFunction")
private String serverFunction; 

@JsonProperty("ServerType")
private String serverType; 

@JsonProperty("PrimAppl")
private String primAppl; 

@JsonProperty("Status")
private String status; 

@JsonProperty("IPAddress")
private String ipAddr; 

public String getLocation() {
    return location;
}


public void setLocation(String location) {
    this.location = location;
}


public String getSerialNo() {
    return serialNo;
}


public void setSerialNo(String serialNo) {
    this.serialNo = serialNo;
}


public String getServerFunction() {
    return serverFunction;
}


public void setServerFunction(String serverFunction) {
    this.serverFunction = serverFunction;
}


public String getServerType() {
    return serverType;
}


public void setServerType(String serverType) {
    this.serverType = serverType;
}


public String getPrimAppl() {
    return primAppl;
}


public void setPrimAppl(String primAppl) {
    this.primAppl = primAppl;
}


public String getStatus() {
    return status;
}


public void setStatus(String status) {
    this.status = status;
}


public String getIpAddr() {
    return ipAddr;
}


public void setIpAddr(String ipAddr) {
    this.ipAddr = ipAddr;
}


public String getNotes() {
    return notes;
}

public void setNotes(String notes) {
    this.notes = notes;
}

public ServerMstr(){
}

public ServerMstr(   String name, String notes ){
    this.name = name;
    this.notes=notes;
} 

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}


@Override
public String toString() {
    return "ServerMstr [id=" + id + ", serverConfigs=" + serverConfigs
            + ", name=" + name + ", notes=" + notes + ", location="
            + location + ", serialNo=" + serialNo + ", serverFunction="
            + serverFunction + ", serverType=" + serverType + ", primAppl="
            + primAppl + ", status=" + status + ", ipAddr=" + ipAddr + "]";
}

}

Child Object:

package com.serverApp.business.model;


import javax.xml.bind.annotation.XmlRootElement;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;


@XmlRootElement(name = "ServerConfig")
@JsonIgnoreProperties({"configId", "server"})
public class ServerConfig    {

@JsonIgnore
private ServerMstr server; 

private Long configId; 



@Override
public String toString() {
    return "ServerConfig [server=" + server.getName() + ", configId=" + configId
            + ", attributeId=" + attributeId + ", value=" + value
            + ", serverId=" + serverId + "]";
}

@JsonProperty("AttributeId")
private Long attributeId;

@JsonProperty("Value")
private String value;

@JsonProperty("ServerId")
private Long serverId; 


public Long getConfigId() {
    return configId;
}

public void setConfigId(Long configId) {
    this.configId = configId;
}

public Long getServerId() {
    return serverId;
}

public void setServerId(Long serverId) {
    this.serverId = serverId;
}

public Long getAttributeId() {
    return attributeId;
}

public void setAttributeId(Long attributeId) {
    this.attributeId = attributeId;
}


public String getValue() {
    return value;
}

public void setValue(String value) {
    this.value = value;
}

public ServerConfig() {
}

public ServerConfig( Long serverId, Long attributeId, String value )
{
    this.serverId = serverId;
    this.attributeId = attributeId;
    this.value=value;
 }

public void setServer(ServerMstr server) {
    this.server = server;
}

public ServerMstr getServer() {
    return server;
}

}

DAO code:

package com.serverApp.business.dao;

import java.util.List;

import org.apache.log4j.Logger;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.serverApp.business.model.ServerConfig;
import com.serverApp.business.model.ServerMstr;

@Repository ("serverMstrDao")
public class ServerMstrDaoImpl extends AbstractDAOImpl implements  ServerMstrDao{

private static final Logger logger = Logger.getLogger(ServerMstrDaoImpl.class);

/*
 * This Method will be used to retrieve the list of ServerMstr Class Details from the DB
 * (non-Javadoc)
 * @see com.serverApp.business.dao.ServerMstrDao#listServerMstrs()
 */
@Override
@SuppressWarnings({"unchecked" })
public List<ServerMstr> listServerMstrs() {
    if (logger.isDebugEnabled()) {
        logger.debug("Inside listServerMstrs() ");
    }
    return (List<ServerMstr>)hibernateTemplate.find("from ServerMstr");
}

/*
 * This Method will be used to save new server to database
 *  (non-Javadoc)
 * @see com.serverApp.business.dao.ServerMstrDao#saveServerMasters(com.serverApp.business.model.ServerMstr)
 */
@Override
 @Transactional  
    public void saveServerMasters(ServerMstr serverMstr) {
    if (logger.isDebugEnabled()) {
        logger.debug("Inside saveServerMasters() ");
    }

        System.out.println("DAO Print: "+serverMstr.toString());
        hibernateTemplate.saveOrUpdate(serverMstr);
    }

/*
 * This Method will be used to retrieve the ServerMstr Class Details from the DB for the given name
 * (non-Javadoc)
 * @see com.serverApp.business.dao.ServerMstrDao#getServerByName()
 */
@SuppressWarnings("unchecked")
@Override
public ServerMstr getServerByName(String name) {
    if (logger.isDebugEnabled()) {
        logger.debug("fetching server from database with name : " + name );
    }
    DetachedCriteria criteria = DetachedCriteria.forClass(ServerMstr.class);
    criteria.add(Restrictions.eq("name", name));
    List<ServerMstr> serverMstr = hibernateTemplate.findByCriteria(criteria); 
    if(serverMstr.size() != 1)
    {
        logger.error("Multiple or 0 row returned for selection Server name: " + name );
        return null;
    }
    return (ServerMstr)serverMstr.get(0);
}

/*
 * This Method will be used to retrieve the ServerConfig Class List from the DB for the given server id
 * (non-Javadoc)
 * @see com.serverApp.business.dao.ServerMstrDao#getServerConfigForServer()
 */
@SuppressWarnings("unchecked")
@Override
public List<ServerConfig> getServerConfigForServer(Long serverId) {
    if (logger.isDebugEnabled()) {
        logger.debug("fetching ServerConfig list from database for server with id : " + serverId );
    }

    return hibernateTemplate.find("from ServerConfig where serverId = ?",serverId);
}
}

Any help/suggestion is really appreciated

Arun kumar
  • 11
  • 5

2 Answers2

0

What you need in this case is deleting orphans. A cascade type of all does indeed cascade all operations, but for a delete operation on the child object to happen, you would need a delete operation on the parent, which is not what you want to do in your example. Change your mapping like this:

<set name="serverConfigs" inverse="true" cascade="all-delete-orphan" lazy="false">
    <key>
        <column name="serverId" not-null="true"/>
    </key>
    <one-to-many class="com.serverApp.business.model.ServerConfig"/>
</set> 
Nándor Előd Fekete
  • 6,988
  • 1
  • 22
  • 47
  • 2
    This is the correct answer. For more information, see the chart from this SO link: http://stackoverflow.com/a/19645397/668622. The third row of the chart shows that to delete a child object when a parent's collection is modified, you must use the all-delete-orphan option. If you're using annotations instead of XML based config, you can use orphanRemoval=true in the e.g. @OneToMany tag. – KyleM Jan 06 '16 at 14:53
  • @KyleM the answer in your link provides a nice way to summarize the behavior, thanks – Nándor Előd Fekete Jan 06 '16 at 15:14
  • I tried changing the `cascade` type to `all-delete-orphan` along with @Pritam Banerjee 's solution, but no luck. – Arun kumar Jan 07 '16 at 09:00
  • Please modify your question to include the source code of the mapped classes and of the DAO class too. – Nándor Előd Fekete Jan 07 '16 at 12:27
  • Updated the question with POJO and DAO code. @NándorElődFekete – Arun kumar Jan 07 '16 at 13:08
  • Add the following line to your hibernate.properties file: `hibernate.show_sql=true` Add the following line at the end of your `saveServerMasters(...)` method in your DAO: `hibernateTemplate.flush();` Break on the newly added line in your DAO, step over it and see your console output. It should contain 'delete from ....' lines. Your cascade should be `all-delete-orphan` for the cascade of the relationship. If you get the expected `delete from ...` lines, it's probably transaction demarcation that doesn't work as it should. – Nándor Előd Fekete Jan 07 '16 at 14:20
0

You will need to override equals and haschode method in the POJO object. If you don't do so Hibernate will not be able to compare the child objects and they will continue to live in the database as orphans.

Another option can be to find all the children of the parent and then delete them individually like the following code. :

Iterator<Child> i = form.getDeleteItems().iterator();
while(i.hasNext()){
Child child = i.next();
for (Iterator<Child> it = parent.getChildren().iterator();) {
     if (child.getId().equals(it.next().getId()) {
         it.remove(); 
     }
   }
} 
Pritam Banerjee
  • 17,953
  • 10
  • 93
  • 108
  • I tried in both the ways. But still hibernate is not deleting any child records or removes the association. Also, when I override equals and hashcode method, I am getting stackOverFlowException. When I manually iterate the existing objects list with new objects list using a `Long` field, I am able to remove the child object from parent object, but when saving the parent object, removed child objects are not removed from the parent object. – Arun kumar Jan 07 '16 at 08:53