[Bug #1238] Fix concurrent modification exception

The fix consists of changing the persistence model of the Dependency entity.
So far, it was deleted in the database when orphan dependencies appear in the Hibernate session
being flushed. Now, the orphan dependencies are removed explicitely on saving the Planning state.
An orphan dependency in the database is the one which has origin or destination NULL.

FEA: ItEr75S04BugFixing
This commit is contained in:
Javier Moran Rua 2011-11-10 11:40:37 +01:00
parent c2637e27e2
commit 0c98cad7aa
4 changed files with 49 additions and 15 deletions

View file

@ -20,21 +20,40 @@
*/
package org.libreplan.business.planner.daos;
import java.util.List;
import org.hibernate.Criteria;
import org.hibernate.criterion.Restrictions;
import org.libreplan.business.common.daos.GenericDAOHibernate;
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
import org.libreplan.business.planner.entities.Dependency;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
/**
* DAO for entity @{link Dedenpency}
*
* @author Javier Moran Rua <jmoran@igalia.com>
*
*/
@Repository
@Scope(BeanDefinition.SCOPE_SINGLETON)
public class DependencyDAO extends GenericDAOHibernate<Dependency,Long>
implements IDependencyDAO {
public class DependencyDAO extends GenericDAOHibernate<Dependency, Long>
implements IDependencyDAO {
@Override
@Transactional
public void deleteUnattachedDependencies() throws InstanceNotFoundException {
Criteria c = getSession().createCriteria(Dependency.class);
c.add(Restrictions.or(Restrictions.isNull("origin"),
Restrictions.isNull("destination")));
List<Dependency> results = c.list();
for (Dependency each : results) {
remove(each.getId());
}
}
}

View file

@ -21,6 +21,7 @@
package org.libreplan.business.planner.daos;
import org.libreplan.business.common.daos.IGenericDAO;
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
import org.libreplan.business.planner.entities.Dependency;
/**
@ -28,8 +29,9 @@ import org.libreplan.business.planner.entities.Dependency;
* entity
*
* @author Javier Moran Rua <jmoran@igalia.com>
*
*/
public interface IDependencyDAO extends IGenericDAO<Dependency,Long> {
public interface IDependencyDAO extends IGenericDAO<Dependency, Long> {
void deleteUnattachedDependencies() throws InstanceNotFoundException;
}

View file

@ -36,13 +36,13 @@
<one-to-one name="taskSource" cascade="delete" />
<!-- Indexed on the other side -->
<set name="dependenciesWithThisOrigin" cascade="all-delete-orphan">
<set name="dependenciesWithThisOrigin" cascade="all">
<key column="origin"></key>
<one-to-many class="Dependency" />
</set>
<!-- Not indexed -->
<set name="dependenciesWithThisDestination" cascade="all-delete-orphan">
<set name="dependenciesWithThisDestination" cascade="all">
<key column="destination"></key>
<one-to-many class="Dependency" />
</set>

View file

@ -57,6 +57,7 @@ import org.libreplan.business.orders.entities.Order;
import org.libreplan.business.orders.entities.OrderElement;
import org.libreplan.business.orders.entities.OrderLineGroup;
import org.libreplan.business.planner.daos.IConsolidationDAO;
import org.libreplan.business.planner.daos.IDependencyDAO;
import org.libreplan.business.planner.daos.ISubcontractedTaskDataDAO;
import org.libreplan.business.planner.daos.ITaskElementDAO;
import org.libreplan.business.planner.daos.ITaskSourceDAO;
@ -125,7 +126,7 @@ public class SaveCommandBuilder {
"/planner/index.zul;company_scheduling", ISaveCommand.class,
result);
}
public static void dontPoseAsTransientAndChildrenObjects(
Collection<? extends ResourceAllocation<?>> resourceAllocations) {
for (ResourceAllocation<?> each : resourceAllocations) {
@ -134,8 +135,7 @@ public class SaveCommandBuilder {
for (DayAssignment eachAssignment : each.getAssignments()) {
eachAssignment.dontPoseAsTransientObjectAnymore();
}
for (DerivedAllocation eachDerived : each
.getDerivedAllocations()) {
for (DerivedAllocation eachDerived : each.getDerivedAllocations()) {
eachDerived.dontPoseAsTransientObjectAnymore();
Collection<DerivedDayAssignmentsContainer> containers = eachDerived
.getContainers();
@ -151,8 +151,7 @@ public class SaveCommandBuilder {
}
}
private static void dontPoseAsTransient(
LimitingResourceQueueElement element) {
private static void dontPoseAsTransient(LimitingResourceQueueElement element) {
if (element != null) {
for (LimitingResourceQueueDependency d : element
.getDependenciesAsOrigin()) {
@ -199,6 +198,9 @@ public class SaveCommandBuilder {
@Autowired
private IOrderAuthorizationDAO orderAuthorizationDAO;
@Autowired
private IDependencyDAO dependencyDAO;
private class SaveCommand implements ISaveCommand {
private PlanningState state;
@ -209,7 +211,7 @@ public class SaveCommandBuilder {
private IAdapterToTaskFundamentalProperties<TaskElement> adapter;
private List<IAfterSaveListener> listeners = new ArrayList<IAfterSaveListener>();
private final List<IAfterSaveListener> listeners = new ArrayList<IAfterSaveListener>();
public SaveCommand(PlanningState planningState,
PlannerConfiguration<TaskElement> configuration) {
@ -270,7 +272,7 @@ public class SaveCommandBuilder {
}
doTheSaving();
return null;
}
}
});
dontPoseAsTransientObjectAnymore(state.getOrder());
state.getScenarioInfo().afterCommit();
@ -286,7 +288,8 @@ public class SaveCommandBuilder {
try {
String message = validationException.getMessage();
for (InvalidValue invalidValue : validationException.getInvalidValues()) {
for (InvalidValue invalidValue : validationException
.getInvalidValues()) {
message += "\n" + invalidValue.getPropertyName() + ": "
+ invalidValue.getMessage();
}
@ -329,6 +332,7 @@ public class SaveCommandBuilder {
checkConstraintOrderUniqueCode(order);
checkConstraintHoursGroupUniqueCode(order);
state.synchronizeTrees();
TaskGroup rootTask = state.getRootTask();
if (rootTask != null) {
// This reattachment is needed to ensure that the root task in
@ -339,8 +343,10 @@ public class SaveCommandBuilder {
taskElementDAO.reattach(rootTask);
}
orderDAO.save(order);
saveDerivedScenarios(order);
deleteOrderElementWithoutParent(order);
deleteUnboundedDependencies();
updateTasksRelatedData();
removeTasksToRemove();
@ -492,6 +498,14 @@ public class SaveCommandBuilder {
}
}
private void deleteUnboundedDependencies() {
try {
dependencyDAO.deleteUnattachedDependencies();
} catch (InstanceNotFoundException e) {
throw new RuntimeException(e);
}
}
private void tryToRemove(OrderElement orderElement) {
// checking no work reports for that orderElement
if (orderElementDAO
@ -843,7 +857,6 @@ public class SaveCommandBuilder {
}
}
private void dontPoseAsTransientObjectAnymore(OrderElement orderElement) {
orderElement.dontPoseAsTransientObjectAnymore();
dontPoseAsTransientObjectAnymore(orderElement.getOrderVersions());