Skip to content

Commit

Permalink
Merge pull request #225 from croz-ltd/bugfix_fixUpdatingAssociations
Browse files Browse the repository at this point in the history
Fix updating association values in registry module
  • Loading branch information
jzrilic authored Dec 17, 2024
2 parents e2dd23a + f48fa66 commit 8ca82bb
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,11 @@
import net.croz.nrich.search.api.model.sort.SortProperty;
import net.croz.nrich.search.api.util.PageableUtil;
import net.croz.nrich.search.support.JpaQueryBuilder;
import net.croz.nrich.search.util.PathResolvingUtil;
import org.modelmapper.ModelMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.support.PageableExecutionUtils;
Expand All @@ -47,6 +50,7 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

// TODO better error handling and maybe versioning
Expand Down Expand Up @@ -128,6 +132,8 @@ public <T> T update(String classFullName, Object id, Object entityData) {
setIdFieldToOriginalValue(wrapper, entityData, id);
}

clearAssociationValues(wrapper, instance, entityData);

modelMapper.map(entityData, instance);

return mergeAndInitializeEntity(instance);
Expand Down Expand Up @@ -219,11 +225,29 @@ private void setIdFieldToOriginalValue(ManagedTypeWrapper managedTypeWrapper, Ob
modelMapper.map(idValueMap, entityData);
}

private void clearAssociationValues(ManagedTypeWrapper managedTypeWrapper, Object instance, Object entityData) {
Set<String> associationList = managedTypeWrapper.getSingularAssociationList().stream()
.map(association -> PathResolvingUtil.convertToPathList(association.path())[0])
.collect(Collectors.toSet());

associationList.forEach(association -> clearValue(instance, entityData, association));
}

private <T> T mergeAndInitializeEntity(T instance) {
T mergedInstance = entityManager.merge(instance);

HibernateUtil.initialize(mergedInstance);

return mergedInstance;
}

// when updating associations ModelMapper will use reflection and directly change the id value which causes
// org.springframework.orm.jpa.JpaSystemException: identifier of an instance of ... was changed ... from ... to ... exception
private void clearValue(Object instance, Object entityData, String path) {
BeanWrapper entityDataWrapper = PropertyAccessorFactory.forBeanPropertyAccess(entityData);

if (entityDataWrapper.isReadableProperty(path) && entityDataWrapper.getPropertyValue(path) != null) {
PropertyAccessorFactory.forBeanPropertyAccess(instance).setPropertyValue(path, null);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ void shouldReturnErrorWhenUpdatingWithInvalidData() throws Exception {
RegistryTestEntity registryTestEntity = executeInTransaction(platformTransactionManager, () -> createRegistryTestEntity(entityManager));

String requestUrl = fullUrl("update");
UpdateRegistryRequest request = updateRegistryRequest(objectMapper, REGISTRY_TYPE_NAME, registryTestEntity.getId(), null);
UpdateRegistryRequest request = updateRegistryRequest(objectMapper, REGISTRY_TYPE_NAME, registryTestEntity.getId(), (String) null);

// when
ResultActions result = performPostRequest(requestUrl, request);
Expand All @@ -213,6 +213,23 @@ void shouldNotFailUpdatingRegistryEntityWithAssociation() throws Exception {
.andExpect(jsonPath("$.name").value(entityName));
}

@Test
void shouldNotFailUpdatingRegistryEntityWithChangedAssociation() throws Exception {
// given
RegistryTestEntity registryTestEntity = executeInTransaction(platformTransactionManager, () -> createRegistryTestEntityWithParent(entityManager));
RegistryTestEntity registryParentEntity = executeInTransaction(platformTransactionManager, () -> createRegistryTestEntity(entityManager));

String requestUrl = fullUrl("update");
UpdateRegistryRequest request = updateRegistryRequest(objectMapper, REGISTRY_TYPE_NAME, registryTestEntity.getId(), registryParentEntity.getId());

// when
ResultActions result = performPostRequest(requestUrl, request);

// then
result.andExpect(status().isOk())
.andExpect(jsonPath("$.parent.id").value(registryParentEntity.getId()));
}

@Test
void shouldDeleteRegistryEntity() throws Exception {
// given
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import lombok.Setter;

import jakarta.validation.constraints.NotNull;
import java.util.Map;

@Setter
@Getter
Expand All @@ -34,4 +35,6 @@ public class RegistryTestEntityUpdateRequest {
@NotNull
private Integer age;

Map<String, Long> parent;

}
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,13 @@ public static UpdateRegistryRequest updateRegistryRequest(ObjectMapper objectMap
return new UpdateRegistryRequest(classFullName, id, jsonData);
}

@SneakyThrows
public static UpdateRegistryRequest updateRegistryRequest(ObjectMapper objectMapper, String classFullName, Long id, Long parentId) {
String jsonData = objectMapper.writeValueAsString(createRegistryTestEntityRequest("name", parentId));

return new UpdateRegistryRequest(classFullName, id, jsonData);
}

@SneakyThrows
public static UpdateRegistryRequest updateRegistryRequestWithId(ObjectMapper objectMapper, String classFullName, Long id) {
String jsonData = objectMapper.writeValueAsString(createUpdateRegistryTestEntityRequest(id));
Expand Down

0 comments on commit 8ca82bb

Please sign in to comment.