Building a Hospital Management System in Java: A Comprehensive Guide

A Hospital Management System (HMS) is a comprehensive software solution that manages the day-to-day operations of a healthcare facility. This article provides a complete guide to designing and implementing a robust HMS using Java, covering architecture, key modules, and best practices.


System Architecture Overview

A well-designed HMS follows a multi-layer architecture:

Presentation Layer (Java Swing/JavaFX/Web)
↓
Business Layer (Spring Boot/Java EE)
↓
Data Access Layer (JPA/Hibernate)
↓
Database (MySQL/PostgreSQL)

Core Modules and Features

1. Patient Management

  • Patient registration and profile management
  • Medical history tracking
  • Appointment scheduling
  • Billing and payments

2. Doctor Management

  • Doctor profiles and schedules
  • Specialty and department management
  • Availability tracking

3. Appointment System

  • Online booking
  • Schedule management
  • Reminders and notifications

4. Medical Records

  • Electronic Health Records (EHR)
  • Prescription management
  • Lab test results
  • Treatment history

5. Billing and Insurance

  • Invoice generation
  • Insurance claim processing
  • Payment tracking

6. Pharmacy & Inventory

  • Medicine stock management
  • Supplier information
  • Prescription fulfillment

7. Laboratory Management

  • Test orders and results
  • Lab technician assignments
  • Report generation

Database Design

Core Entity-Relationship Diagram:

Patient ←→ Appointment ←→ Doctor
↓           ↓           ↓
MedicalRecord  Billing  Department
↓           ↓           ↓
Prescription ← Medicine  LabTest

Implementation Code

1. Domain Models (JPA Entities)

Patient Entity:

@Entity
@Table(name = "patients")
public class Patient {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String firstName;
@Column(nullable = false)
private String lastName;
@Column(unique = true)
private String email;
private String phone;
private Date dateOfBirth;
private String gender;
private String address;
private String bloodGroup;
private String emergencyContact;
@OneToMany(mappedBy = "patient")
private List<Appointment> appointments;
@OneToMany(mappedBy = "patient")
private List<MedicalRecord> medicalRecords;
// Constructors, getters, setters
public Patient() {}
public Patient(String firstName, String lastName, String email) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
}

Doctor Entity:

@Entity
@Table(name = "doctors")
public class Doctor {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
private String email;
private String phone;
private String specialization;
private String qualification;
private Integer yearsOfExperience;
private String licenseNumber;
@Enumerated(EnumType.STRING)
private Department department;
@OneToMany(mappedBy = "doctor")
private List<Appointment> appointments;
@OneToMany(mappedBy = "doctor")
private List<MedicalRecord> medicalRecords;
public enum Department {
CARDIOLOGY, NEUROLOGY, ORTHOPEDICS, PEDIATRICS, 
DERMATOLOGY, ONCOLOGY, RADIOLOGY, EMERGENCY
}
// Constructors, getters, setters
}

Appointment Entity:

@Entity
@Table(name = "appointments")
public class Appointment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "patient_id")
private Patient patient;
@ManyToOne
@JoinColumn(name = "doctor_id")
private Doctor doctor;
private Date appointmentDate;
private Time appointmentTime;
private Integer duration; // in minutes
@Enumerated(EnumType.STRING)
private AppointmentStatus status;
private String reason;
private String notes;
public enum AppointmentStatus {
SCHEDULED, CONFIRMED, IN_PROGRESS, COMPLETED, CANCELLED, NO_SHOW
}
// Constructors, getters, setters
}

Medical Record Entity:

@Entity
@Table(name = "medical_records")
public class MedicalRecord {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "patient_id")
private Patient patient;
@ManyToOne
@JoinColumn(name = "doctor_id")
private Doctor doctor;
private Date visitDate;
private String diagnosis;
private String symptoms;
private String treatment;
private String notes;
@OneToMany(mappedBy = "medicalRecord")
private List<Prescription> prescriptions;
// Constructors, getters, setters
}

Prescription Entity:

@Entity
@Table(name = "prescriptions")
public class Prescription {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "medical_record_id")
private MedicalRecord medicalRecord;
private String medicineName;
private String dosage;
private String frequency;
private Integer duration; // in days
private String instructions;
// Constructors, getters, setters
}

2. Repository Layer (Spring Data JPA)

Patient Repository:

@Repository
public interface PatientRepository extends JpaRepository<Patient, Long> {
Optional<Patient> findByEmail(String email);
List<Patient> findByLastNameContainingIgnoreCase(String lastName);
@Query("SELECT p FROM Patient p WHERE p.dateOfBirth BETWEEN :startDate AND :endDate")
List<Patient> findPatientsByBirthDateRange(@Param("startDate") Date startDate, 
@Param("endDate") Date endDate);
Long countByDateOfBirthAfter(Date date);
}

Appointment Repository:

@Repository
public interface AppointmentRepository extends JpaRepository<Appointment, Long> {
List<Appointment> findByDoctorAndAppointmentDate(Doctor doctor, Date date);
List<Appointment> findByPatientAndStatus(Patient patient, Appointment.AppointmentStatus status);
List<Appointment> findByAppointmentDateBetween(Date startDate, Date endDate);
@Query("SELECT a FROM Appointment a WHERE a.doctor = :doctor AND a.appointmentDate = :date AND a.status = 'SCHEDULED'")
List<Appointment> findScheduledAppointmentsByDoctorAndDate(@Param("doctor") Doctor doctor, 
@Param("date") Date date);
}

3. Service Layer

Patient Service:

@Service
@Transactional
public class PatientService {
@Autowired
private PatientRepository patientRepository;
public Patient registerPatient(Patient patient) {
// Validate patient data
if (patientRepository.findByEmail(patient.getEmail()).isPresent()) {
throw new IllegalArgumentException("Patient with this email already exists");
}
return patientRepository.save(patient);
}
public Optional<Patient> getPatientById(Long id) {
return patientRepository.findById(id);
}
public List<Patient> searchPatients(String keyword) {
return patientRepository.findByLastNameContainingIgnoreCase(keyword);
}
public Patient updatePatient(Long id, Patient patientDetails) {
Patient patient = patientRepository.findById(id)
.orElseThrow(() -> new RuntimeException("Patient not found"));
patient.setFirstName(patientDetails.getFirstName());
patient.setLastName(patientDetails.getLastName());
patient.setPhone(patientDetails.getPhone());
patient.setAddress(patientDetails.getAddress());
return patientRepository.save(patient);
}
public void deletePatient(Long id) {
Patient patient = patientRepository.findById(id)
.orElseThrow(() -> new RuntimeException("Patient not found"));
patientRepository.delete(patient);
}
}

Appointment Service:

@Service
@Transactional
public class AppointmentService {
@Autowired
private AppointmentRepository appointmentRepository;
@Autowired
private PatientRepository patientRepository;
@Autowired
private DoctorRepository doctorRepository;
public Appointment scheduleAppointment(Appointment appointment) {
// Validate appointment constraints
validateAppointment(appointment);
return appointmentRepository.save(appointment);
}
private void validateAppointment(Appointment appointment) {
// Check if doctor is available
List<Appointment> existingAppointments = appointmentRepository
.findScheduledAppointmentsByDoctorAndDate(
appointment.getDoctor(), 
appointment.getAppointmentDate()
);
for (Appointment existing : existingAppointments) {
if (isTimeOverlap(existing, appointment)) {
throw new IllegalArgumentException("Doctor is not available at this time");
}
}
}
private boolean isTimeOverlap(Appointment existing, Appointment newAppointment) {
// Implement time overlap logic
return false; // Simplified
}
public List<Appointment> getDoctorAppointments(Long doctorId, Date date) {
Doctor doctor = doctorRepository.findById(doctorId)
.orElseThrow(() -> new RuntimeException("Doctor not found"));
return appointmentRepository.findByDoctorAndAppointmentDate(doctor, date);
}
public void cancelAppointment(Long appointmentId) {
Appointment appointment = appointmentRepository.findById(appointmentId)
.orElseThrow(() -> new RuntimeException("Appointment not found"));
appointment.setStatus(Appointment.AppointmentStatus.CANCELLED);
appointmentRepository.save(appointment);
}
}

4. REST Controller Layer

Patient Controller:

@RestController
@RequestMapping("/api/patients")
@CrossOrigin(origins = "http://localhost:3000")
public class PatientController {
@Autowired
private PatientService patientService;
@GetMapping
public ResponseEntity<List<Patient>> getAllPatients() {
List<Patient> patients = patientService.getAllPatients();
return ResponseEntity.ok(patients);
}
@GetMapping("/{id}")
public ResponseEntity<Patient> getPatientById(@PathVariable Long id) {
Patient patient = patientService.getPatientById(id)
.orElseThrow(() -> new RuntimeException("Patient not found"));
return ResponseEntity.ok(patient);
}
@PostMapping
public ResponseEntity<Patient> createPatient(@RequestBody Patient patient) {
Patient savedPatient = patientService.registerPatient(patient);
return ResponseEntity.status(HttpStatus.CREATED).body(savedPatient);
}
@PutMapping("/{id}")
public ResponseEntity<Patient> updatePatient(@PathVariable Long id, 
@RequestBody Patient patientDetails) {
Patient updatedPatient = patientService.updatePatient(id, patientDetails);
return ResponseEntity.ok(updatedPatient);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deletePatient(@PathVariable Long id) {
patientService.deletePatient(id);
return ResponseEntity.noContent().build();
}
@GetMapping("/search")
public ResponseEntity<List<Patient>> searchPatients(@RequestParam String keyword) {
List<Patient> patients = patientService.searchPatients(keyword);
return ResponseEntity.ok(patients);
}
}

Appointment Controller:

@RestController
@RequestMapping("/api/appointments")
public class AppointmentController {
@Autowired
private AppointmentService appointmentService;
@PostMapping
public ResponseEntity<Appointment> scheduleAppointment(@RequestBody Appointment appointment) {
Appointment savedAppointment = appointmentService.scheduleAppointment(appointment);
return ResponseEntity.status(HttpStatus.CREATED).body(savedAppointment);
}
@GetMapping("/doctor/{doctorId}")
public ResponseEntity<List<Appointment>> getDoctorAppointments(
@PathVariable Long doctorId,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") Date date) {
List<Appointment> appointments = appointmentService.getDoctorAppointments(doctorId, date);
return ResponseEntity.ok(appointments);
}
@PutMapping("/{id}/cancel")
public ResponseEntity<Void> cancelAppointment(@PathVariable Long id) {
appointmentService.cancelAppointment(id);
return ResponseEntity.ok().build();
}
@GetMapping("/patient/{patientId}")
public ResponseEntity<List<Appointment>> getPatientAppointments(@PathVariable Long patientId) {
// Implementation
return ResponseEntity.ok(Collections.emptyList());
}
}

5. Configuration and Main Application

Application Properties:

# application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/hospital_db
spring.datasource.username=root
spring.datasource.password=password
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
server.port=8080

Main Application Class:

@SpringBootApplication
public class HospitalManagementSystemApplication {
public static void main(String[] args) {
SpringApplication.run(HospitalManagementSystemApplication.class, args);
}
@Bean
public CommandLineRunner demoData(PatientRepository patientRepo, 
DoctorRepository doctorRepo) {
return args -> {
// Initialize sample data
Patient patient1 = new Patient("John", "Doe", "[email protected]");
patient1.setPhone("123-456-7890");
patient1.setDateOfBirth(Date.from(LocalDate.of(1985, 5, 15)
.atStartOfDay(ZoneId.systemDefault()).toInstant()));
patientRepo.save(patient1);
Doctor doctor1 = new Doctor();
doctor1.setFirstName("Sarah");
doctor1.setLastName("Smith");
doctor1.setSpecialization("Cardiology");
doctor1.setDepartment(Doctor.Department.CARDIOLOGY);
doctor1.setLicenseNumber("MED123456");
doctorRepo.save(doctor1);
};
}
}

Advanced Features Implementation

1. Billing System

Billing Service:

@Service
public class BillingService {
@Autowired
private BillingRepository billingRepository;
public Bill generateBill(Long appointmentId, List<ServiceCharge> charges) {
Appointment appointment = // fetch appointment
Bill bill = new Bill();
bill.setAppointment(appointment);
bill.setBillDate(new Date());
bill.setServices(charges);
double total = charges.stream()
.mapToDouble(ServiceCharge::getAmount)
.sum();
bill.setTotalAmount(total);
return billingRepository.save(bill);
}
public void processPayment(Long billId, Payment payment) {
Bill bill = billingRepository.findById(billId)
.orElseThrow(() -> new RuntimeException("Bill not found"));
bill.setPaymentStatus(PaymentStatus.PAID);
bill.setPaymentDate(new Date());
billingRepository.save(bill);
}
}

2. Reporting Module

Report Service:

@Service
public class ReportService {
public HospitalStatistics generateMonthlyReport(int year, int month) {
HospitalStatistics stats = new HospitalStatistics();
stats.setTotalPatients(patientRepository.countByRegistrationMonth(year, month));
stats.setTotalAppointments(appointmentRepository.countByMonth(year, month));
stats.setRevenue(billingRepository.getMonthlyRevenue(year, month));
return stats;
}
}

Security Implementation

Security Configuration:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeHttpRequests(authz -> authz
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/patients/**").hasAnyRole("DOCTOR", "ADMIN")
.requestMatchers("/api/doctors/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.httpBasic();
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails doctor = User.builder()
.username("doctor")
.password(passwordEncoder().encode("password"))
.roles("DOCTOR")
.build();
UserDetails admin = User.builder()
.username("admin")
.password(passwordEncoder().encode("admin"))
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(doctor, admin);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

Testing

Patient Service Test:

@SpringBootTest
class PatientServiceTest {
@Autowired
private PatientService patientService;
@Autowired
private PatientRepository patientRepository;
@Test
void testRegisterPatient() {
Patient patient = new Patient("Test", "User", "[email protected]");
Patient savedPatient = patientService.registerPatient(patient);
assertNotNull(savedPatient.getId());
assertEquals("Test", savedPatient.getFirstName());
}
@Test
void testRegisterPatientWithDuplicateEmail() {
Patient patient1 = new Patient("John", "Doe", "[email protected]");
patientService.registerPatient(patient1);
Patient patient2 = new Patient("Jane", "Doe", "[email protected]");
assertThrows(IllegalArgumentException.class, () -> {
patientService.registerPatient(patient2);
});
}
}

Frontend Integration (Basic Example)

Simple HTML/JavaScript Client:

<!DOCTYPE html>
<html>
<head>
<title>Hospital Management System</title>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="app">
<h1>Patient Registration</h1>
<form id="patientForm">
<input type="text" id="firstName" placeholder="First Name" required>
<input type="text" id="lastName" placeholder="Last Name" required>
<input type="email" id="email" placeholder="Email" required>
<button type="submit">Register Patient</button>
</form>
<div id="patientList"></div>
</div>
<script>
const API_BASE = 'http://localhost:8080/api';
// Register patient
document.getElementById('patientForm').addEventListener('submit', async (e) => {
e.preventDefault();
const patient = {
firstName: document.getElementById('firstName').value,
lastName: document.getElementById('lastName').value,
email: document.getElementById('email').value
};
try {
const response = await axios.post(`${API_BASE}/patients`, patient);
alert('Patient registered successfully!');
loadPatients();
} catch (error) {
alert('Error registering patient: ' + error.response.data.message);
}
});
// Load patients
async function loadPatients() {
try {
const response = await axios.get(`${API_BASE}/patients`);
const patients = response.data;
const patientList = document.getElementById('patientList');
patientList.innerHTML = '<h2>Patients</h2>' +
patients.map(p => `<div>${p.firstName} ${p.lastName} - ${p.email}</div>`).join('');
} catch (error) {
console.error('Error loading patients:', error);
}
}
// Initial load
loadPatients();
</script>
</body>
</html>

Deployment Considerations

Docker Configuration:

FROM openjdk:17-jdk-slim
VOLUME /tmp
COPY target/hospital-management-system-1.0.0.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

docker-compose.yml:

version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/hospital_db
depends_on:
- db
db:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=password
- MYSQL_DATABASE=hospital_db
volumes:
- db_data:/var/lib/mysql
volumes:
db_data:

Best Practices

  1. Data Validation: Always validate input data
  2. Exception Handling: Implement global exception handling
  3. Logging: Use SLF4J for comprehensive logging
  4. Security: Implement proper authentication and authorization
  5. Documentation: Use Swagger for API documentation
  6. Testing: Maintain high test coverage
  7. Monitoring: Implement health checks and metrics

Conclusion

Building a Hospital Management System in Java requires careful planning and implementation of various modules. This guide provides:

  • Modular architecture for scalability
  • RESTful APIs for integration
  • Comprehensive entity relationships
  • Security implementation
  • Testing strategies

The system can be extended with additional features like:

  • Real-time notifications
  • Mobile app integration
  • AI-powered diagnostics
  • Telemedicine capabilities
  • Advanced analytics and reporting

By following this structure, you can create a robust, maintainable, and scalable Hospital Management System that meets the complex needs of modern healthcare facilities.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper