package edu.ualberta.med.biobank.common.wrappers; import edu.ualberta.med.biobank.common.wrappers.actions.WrapperAction; import edu.ualberta.med.biobank.common.wrappers.listener.WrapperEvent; import edu.ualberta.med.biobank.common.wrappers.listener.WrapperEvent.WrapperEventType; import edu.ualberta.med.biobank.common.wrappers.tasks.RebindableWrapperQueryTask; import edu.ualberta.med.biobank.server.applicationservice.exceptions.BiobankSessionException; import gov.nih.nci.system.query.SDKQuery; import gov.nih.nci.system.query.SDKQueryResult; import gov.nih.nci.system.query.example.DeleteExampleQuery; import java.io.Serializable; import java.text.MessageFormat; import org.hibernate.PropertyValueException; import org.hibernate.Query; import org.hibernate.Session; /** * Delete the wrapped object of the given {@link ModelWrapper} on the server. * Also sets the given {@link ModelWrapper}'s wrapped model object to be the * object result of the {@link SDKQueryResult}, when informed and notifies * listeners. * * @author jferland * */ public class DeleteModelWrapperQueryTask<E> implements RebindableWrapperQueryTask { private final ModelWrapper<E> modelWrapper; public DeleteModelWrapperQueryTask(ModelWrapper<E> modelWrapper) { this.modelWrapper = modelWrapper; } @Override public SDKQuery getSDKQuery() { return new DeleteAction<E>(modelWrapper); } @Override public void afterExecute(SDKQueryResult result) { // TODO: not sure this is necessary. modelWrapper.setId(null); WrapperEventType eventType = WrapperEventType.DELETE; WrapperEvent event = new WrapperEvent(eventType, modelWrapper); modelWrapper.notifyListeners(event); } @Override public ModelWrapper<?> getWrapperToRebind() { return modelWrapper; } /** * Delete the wrapped object of the given {@link ModelWrapper}. Necessary * because {@link DeleteExampleQuery} does not return the model object. * * @author jferland * * @param <E> */ private static class DeleteAction<E> extends WrapperAction<E> { private static final long serialVersionUID = 1L; private static final String HQL = "DELETE FROM {0} WHERE {1} = ?"; //$NON-NLS-1$ public DeleteAction(ModelWrapper<E> wrapper) { super(wrapper); } @Override public Object doAction(Session session) throws BiobankSessionException { E model = getModel(); // Calling session.delete() does not sometimes work because it // null-checks values, and can fail. For example, trying to delete a // contact whose clinic value is null will not work. The values were // nulled by a bi-directional setter/ getter. Further, deleting with // HQL does not consider cascades, so, reload the object before // deleting it (see // https://forum.hibernate.org/viewtopic.php?f=1&t=944722) try { Serializable id = getIdProperty().get(model); @SuppressWarnings("unchecked") E loadedModel = (E) session.load(getModelClass(), id); session.delete(loadedModel); // Remove the loaded model from the Session so that it is not // automagically saved (when the session is closed or flush() is // called). // TODO: version check and throw a StaleStateException? } catch (PropertyValueException e) { // TODO: determine why the HQL delete is still necessary, test // fail if this is removed. Is there a better option? doHqlDelete(session); } // set the id to null so that the object is not loaded and so that // Hibernate won't do any cascades with it because it has no // identifier getIdProperty().set(model, null); return model; } private void doHqlDelete(Session session) { Integer id = getIdProperty().get(getModel()); if (id != null) { String className = getModelClass().getName(); String idPropName = getIdProperty().getName(); String hql = MessageFormat.format(HQL, className, idPropName); Query query = session.createQuery(hql); query.setParameter(0, id); query.executeUpdate(); } } } }