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
- Data Validation: Always validate input data
- Exception Handling: Implement global exception handling
- Logging: Use SLF4J for comprehensive logging
- Security: Implement proper authentication and authorization
- Documentation: Use Swagger for API documentation
- Testing: Maintain high test coverage
- 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.