Skip to content

Tutorial 4: Patient CRUD Operations

Pages: 2


📖 Page 1: Service Layer + CREATE/READ

Create PatientService

Create src/main/java/com/healthcare/pms/service/PatientService.java:

package com.healthcare.pms.service;

import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import com.healthcare.pms.dto.PatientDTO;
import com.healthcare.pms.mapper.PatientMapper;
import lombok.RequiredArgsConstructor;
import org.hl7.fhir.r4.model.*;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class PatientService {

    private final IGenericClient fhirClient;
    private final PatientMapper patientMapper;

    // CREATE
    public PatientDTO createPatient(PatientDTO dto) {
        Patient fhirPatient = patientMapper.toFhirResource(dto);

        MethodOutcome outcome = fhirClient.create()
            .resource(fhirPatient)
            .execute();

        Patient created = (Patient) outcome.getResource();
        return patientMapper.toDTO(created);
    }

    // READ
    public PatientDTO getPatientById(String id) {
        try {
            Patient patient = fhirClient.read()
                .resource(Patient.class)
                .withId(id)
                .execute();

            return patientMapper.toDTO(patient);
        } catch (ResourceNotFoundException e) {
            throw new RuntimeException("Patient not found: " + id);
        }
    }

    // GET ALL (for list page)
    public List<PatientDTO> getAllPatients() {
        Bundle bundle = fhirClient.search()
            .forResource(Patient.class)
            .returnBundle(Bundle.class)
            .execute();

        List<PatientDTO> patients = new ArrayList<>();
        for (Bundle.BundleEntryComponent entry : bundle.getEntry()) {
            if (entry.getResource() instanceof Patient) {
                patients.add(patientMapper.toDTO((Patient) entry.getResource()));
            }
        }
        return patients;
    }
}

📖 Page 2: UPDATE/DELETE/SEARCH

Add Remaining Operations

Add to PatientService.java:

    // UPDATE
    public PatientDTO updatePatient(String id, PatientDTO dto) {
        dto.setId(id);
        Patient fhirPatient = patientMapper.toFhirResource(dto);

        fhirClient.update()
            .resource(fhirPatient)
            .withId(id)
            .execute();

        return getPatientById(id);
    }

    // DELETE
    public void deletePatient(String id) {
        fhirClient.delete()
            .resourceById("Patient", id)
            .execute();
    }

    // SEARCH by name
    public List<PatientDTO> searchPatients(String name) {
        if (name == null || name.trim().isEmpty()) {
            return getAllPatients();
        }

        Bundle bundle = fhirClient.search()
            .forResource(Patient.class)
            .where(Patient.NAME.matches().value(name))
            .returnBundle(Bundle.class)
            .execute();

        return bundle.getEntry().stream()
            .map(entry -> (Patient) entry.getResource())
            .map(patientMapper::toDTO)
            .collect(Collectors.toList());
    }

    // FILTER (we'll add in Tutorial 6)
    public List<PatientDTO> filterPatients(String gender, String bloodGroup, 
                                          Boolean active) {
        List<PatientDTO> patients = getAllPatients();

        return patients.stream()
            .filter(p -> gender == null || gender.isEmpty() || 
                   p.getGender().equalsIgnoreCase(gender))
            .filter(p -> bloodGroup == null || bloodGroup.isEmpty() || 
                   bloodGroup.equals(p.getBloodGroup()))
            .filter(p -> active == null || active.equals(p.getActive()))
            .collect(Collectors.toList());
    }

Test Service

Create test method in Application.java:

@SpringBootApplication
public class Application implements CommandLineRunner {

    @Autowired
    private PatientService patientService;

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Override
    public void run(String... args) {
        // Create test patient
        PatientDTO dto = new PatientDTO();
        dto.setFirstName("John");
        dto.setLastName("Doe");
        dto.setBirthDate(LocalDate.of(1990, 5, 15));
        dto.setGender("male");
        dto.setEmail("john@example.com");
        dto.setActive(true);

        PatientDTO created = patientService.createPatient(dto);
        System.out.println("Created patient: " + created.getId());

        // Read it back
        PatientDTO retrieved = patientService.getPatientById(created.getId());
        System.out.println("Retrieved: " + retrieved.getFullName());
    }
}

Run and check console!


✅ CRUD Complete

  • CREATE - createPatient()
  • READ - getPatientById(), getAllPatients()
  • UPDATE - updatePatient()
  • DELETE - deletePatient()
  • SEARCH - searchPatients()
  • FILTER - filterPatients()

🚀 Next

Service layer done! Now let's add web interface.

→ Tutorial 5: Controller & UI


💡 Quick Tips

Error Handling

Wrap FHIR calls in try-catch for production

Pagination

getAllPatients() returns all - we'll paginate in Tutorial 7

Bundle

FHIR search returns Bundle (collection of resources)