Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ plugins {
}

group = 'com.flexcodelabs'
version = '0.0.30'
version = '0.0.31'
description = 'Flextuma App'

java {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ public ResponseEntity<List<EntityFieldDTO>> getFields() {
}

@GetMapping("/{id}")
public ResponseEntity<T> getById(@PathVariable UUID id) {
return service.findById(id)
public ResponseEntity<T> getById(
@PathVariable UUID id,
@RequestParam(required = false) String fields) {
return service.findById(id, fields)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ public class Owner extends BaseEntity {
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "creator", referencedColumnName = "id", nullable = false, updatable = false)
@CreatedBy
@JsonIgnoreProperties({ "roles", "organisation", "createdBy", "updatedBy", "password", "salt" })
private User createdBy;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "updator", referencedColumnName = "id")
@LastModifiedBy
@JsonIgnoreProperties({ "roles", "organisation", "createdBy", "updatedBy", "password", "salt" })
private User updatedBy;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@
import jakarta.persistence.criteria.*;
import jakarta.persistence.metamodel.Attribute;
import jakarta.persistence.metamodel.EntityType;
import jakarta.persistence.metamodel.ManagedType;
import org.hibernate.Hibernate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
Expand Down Expand Up @@ -178,6 +182,7 @@ private Specification<T> buildTenantSpec() {
}

private Pagination<T> buildPaginatedResponse(Page<T> resultPage, Pageable pageable) {
resultPage.getContent().forEach(entity -> initializeAssociationsForResponse(entity, 1));
return Pagination.<T>builder()
.page(pageable.getPageNumber() + 1)
.total(resultPage.getTotalElements())
Expand All @@ -190,7 +195,9 @@ private Pagination<T> buildPaginatedResponse(Page<T> resultPage, Pageable pageab
public List<T> findAll() {
checkPermission(getReadPermission());
Specification<T> spec = buildTenantSpec();
return getRepositoryAsExecutor().findAll(spec);
List<T> results = getRepositoryAsExecutor().findAll(spec);
results.forEach(entity -> initializeAssociationsForResponse(entity, 1));
return results;
}

@Transactional(readOnly = true)
Expand All @@ -215,13 +222,17 @@ private List<T> doFindAll(String fields, List<String> filter, String rootJoin) {
if (fields != null && !fields.isBlank()) {
spec = spec.and(buildFetchSpec(fields));
}
return getRepositoryAsExecutor().findAll(spec);
List<T> results = getRepositoryAsExecutor().findAll(spec);
results.forEach(entity -> initializeAssociationsForResponse(entity, 1));
return results;
}

@Transactional(readOnly = true)
public Optional<T> findById(UUID id) {
checkPermission(getReadPermission());
return getRepository().findById(id);
Optional<T> result = getRepository().findById(id);
result.ifPresent(entity -> initializeAssociationsForResponse(entity, 1));
return result;
}

@Transactional(readOnly = true)
Expand All @@ -232,14 +243,17 @@ public Optional<T> findById(UUID id, String fields) {
if (fields != null && !fields.isBlank()) {
spec = spec.and(buildFetchSpec(fields));
}
return getRepositoryAsExecutor().findOne(spec);
Optional<T> result = getRepositoryAsExecutor().findOne(spec);
result.ifPresent(entity -> initializeAssociationsForResponse(entity, 1));
return result;
}

@Transactional
public T save(T entity) {
checkPermission(getAddPermission());
onPreSave(entity);
T saved = getRepository().save(entity);
initializeAssociationsForResponse(saved, 1);
onPostSave(saved);
eventPublisher.publishEvent(new EntityEvent<>(this, saved, EntityEvent.EntityEventType.CREATED));
return saved;
Expand All @@ -255,6 +269,7 @@ public T update(UUID id, T entity) {
String[] excludedFields = getNullPropertyNames(entity);
org.springframework.beans.BeanUtils.copyProperties(entity, existing, excludedFields);
T saved = getRepository().save(existing);
initializeAssociationsForResponse(saved, 1);
eventPublisher.publishEvent(new EntityEvent<>(this, saved, EntityEvent.EntityEventType.UPDATED));
return saved;
}
Expand Down Expand Up @@ -430,6 +445,84 @@ protected void onPreSave(T entity) {
protected void onPostSave(T entity) {
}

protected void initializeAssociationsForResponse(Object entity, int depth) {
if (entity == null || depth < 0) {
return;
}

Hibernate.initialize(entity);
ManagedType<?> managedType = resolveManagedType(Hibernate.getClass(entity));
if (managedType == null) {
return;
}

BeanWrapper wrapper = new BeanWrapperImpl(entity);
for (Attribute<?, ?> attribute : managedType.getAttributes()) {
if (!attribute.isAssociation() || !wrapper.isReadableProperty(attribute.getName())) {
continue;
}

Object value = wrapper.getPropertyValue(attribute.getName());
if (value == null) {
continue;
}

Hibernate.initialize(value);
if (depth == 0) {
continue;
}

if (value instanceof Collection<?> collection) {
for (Object item : collection) {
initializeSingularAssociations(item, depth - 1);
}
continue;
}

initializeSingularAssociations(value, depth - 1);
}
}

private void initializeSingularAssociations(Object entity, int depth) {
if (entity == null || depth < 0) {
return;
}

Hibernate.initialize(entity);
ManagedType<?> managedType = resolveManagedType(Hibernate.getClass(entity));
if (managedType == null) {
return;
}

BeanWrapper wrapper = new BeanWrapperImpl(entity);
for (Attribute<?, ?> attribute : managedType.getAttributes()) {
if (!attribute.isAssociation() || attribute.isCollection() || !wrapper.isReadableProperty(attribute.getName())) {
continue;
}

Object value = wrapper.getPropertyValue(attribute.getName());
if (value == null) {
continue;
}

Hibernate.initialize(value);
if (depth > 0) {
initializeSingularAssociations(value, depth - 1);
}
}
}

private ManagedType<?> resolveManagedType(Class<?> javaType) {
try {
if (entityManager == null || entityManager.getMetamodel() == null) {
return null;
}
return entityManager.getMetamodel().managedType(javaType);
} catch (IllegalArgumentException ex) {
return null;
}
}

private final ObjectMapper objectMapper = new ObjectMapper()
.setSerializationInclusion(JsonInclude.Include.NON_NULL);

Expand All @@ -440,4 +533,4 @@ protected T onPreUpdate(T newEntity, T oldEntity) {
return newEntity;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public void getById_shouldReturnEntity_whenFound() throws Exception {
T entity = createEntity();
entity.setId(id);

when(getService().findById(id)).thenReturn(Optional.of(entity));
when(getService().findById(eq(id), any())).thenReturn(Optional.of(entity));

mockMvc.perform(get(getBaseUrl() + "/" + id))
.andExpect(status().isOk())
Expand All @@ -94,7 +94,7 @@ public void getById_shouldReturnEntity_whenFound() throws Exception {
@Test
public void getById_shouldReturnNotFound_whenNotFound() throws Exception {
UUID id = UUID.randomUUID();
when(getService().findById(id)).thenReturn(Optional.empty());
when(getService().findById(eq(id), any())).thenReturn(Optional.empty());

mockMvc.perform(get(getBaseUrl() + "/" + id))
.andExpect(status().isNotFound());
Expand Down