[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:
parent
c2637e27e2
commit
0c98cad7aa
4 changed files with 49 additions and 15 deletions
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue