This article and code behind it, show how to assemble a standalone, fully functional application, which as one of its main points utilizes Hibernate and Spring. You can take a look at all of the typical three layers, see how Hibernate is configured together with Spring and how classes are built and used. The best way to use this article is to read through the user stories, create your own application and come back to reivew this implementation.
Complete application code can be found on allAroundJava GitHub. Code for first version of this application is in “version-0.1” branch. We’ll develop this application further to expose its functionality over REST API.

Application’s User stories

The goal is to implement an API allowing patients to book doctor visits. Doctor, patient and booking data needs to be available between application restarts. Because we’re building a very basic API, no user roles are required, so all the stories are written from point of view of a generic user.

  • As user I can add a Doctor
  • As a user I can add a Patient
  • As user I can add a period in which doctor is available
    Acceptance Criterias:
    • Each meeting slot has a start and an end time
    • It’s ok if doctor has overlapping meeting slots
  • As user I can Book given doctor at a chosen time
    Acceptance Criterias:
    • Only available slot can be booked
    • When slot is booked an appointment is registered
    • When slot is booked, the slot is no longer available for booking

Implementation approach

The first version of application will just be a set of service methods with desired functionality. It will be verified with unit tests and a set of integration tests launching a full hibernate application and accessing the database.

Application uses the following frameworks:

  • Hibernate
  • Spring
  • JUnit
  • Mockito
  • Log4J

Application will consist of two maven modules:

  • DoctorBookingApplication – the core logic of application with unit tests on mocked objects
  • DoctorBookingAcceptanceTest – set of automated acceptance tests ran against live database to ensure end to end functionalities work as expected.

Architectural approach

The application will be built in a classical layered approach with Service, Dao and Entity layers, with each layer only being aware of a layer directly below. Entity layer is an exception here as it’s usually all over the application code. Here’s a short description of each layer:

  • Entity Layer – contains classes which have their representation in database tables. This is where classes anotated with @Entity reside.
  • Dao Layer – a data access layer ensuring entities are correctly saved and read from the database. This layer uses JPA or Hibernate API methods. Classes annotated with @Repository reside here.
  • Service Layer – a business logic layer executing a series of operations on dao classes and completing certain functionality. This is where @Service annotated classes reside. Public service methods are transactional to ensure code is executed within single transaction.

Entity Classes:

  • Patient
  • Doctor
  • ApointmentSlot – a class representing a time slot when a particular doctor is available for a visit.
  • Appointment – a class representing an appointment made by Patient to see Doctor at a particular time.

Database tables structure - hibernate mapping

Architecture diagrams

Here’s a trivial diagram of adding an appointment slot.
Persisting entities with Hibernate

And here’s how booking an appointment works.

Booking Service Functionality - bookng appointment

Hibernate Framework usage

Hibernate is used according to its purpose to communicate with the database. Because application is using Spring Boot and all entity classes are withn our control, a setup without a persistence.xml file is used. If you’d like to know a little more about sesting up Hibernate with Spring, take a look at this article.
According to advised approach we’re trying to stay with unidirectional @ManyToOne mapping to avoid unnecessary complexity. You can find @ManyToOne annotations in AppointmentSlot and Appointment entities. Relation to Doctor or Patient for these entities is unidirectional. We’d never be selecting all doctor’s or patient’s appointments without additional parameters such as dates.
In order to use LocalDate in your entities, please remember to use Hibernate version starting from 5.0. For earier versions of Hibernate, LocalDate is mapped as a binary column and comparison between dates is not possible.

Helpful Dao setup

When not using Spring Data JPA, I like defining a set of basic Dao functionalities in an abstract Dao base class. Whenever I wish to create a variation of Dao, tied to a particular entity, it’s easy to extend the base class and add additional functionality if required.


abstract class BaseDao implements Dao {
    private static final Logger log = LogManager.getLogger(BaseDao.class);
    @PersistenceContext
    EntityManager entityManager;
    private final Class aClass;

    BaseDao(Class aClass) {
        this.aClass = aClass;
    }

    @Override
    public Optional getById(Long id) {
        log.debug("Fetching {} with id {} from database", getClass(), id);
        return Optional.ofNullable(entityManager.find(aClass, id));
    }

    @Override
    public void persist(T item) {
        log.debug("Persisting {} with id {}",
                item.getClass(), item.getId());
        entityManager.persist(item);
    }

    @Override
    public void merge(T item) {
        entityManager.merge(item);
    }

    @Override
    public void delete(T item) {
        log.debug("Deleting {} with id {}",
                item.getClass(), item.getId());
        T retrievedItem = entityManager.find(aClass, item.getId());
        entityManager.remove(retrievedItem);
    }

    @Override
    public void refersh(T item) {
        entityManager.refresh(item);
    }
}

Here, the AppointmentSlot Dao class, implementing some additional functionality, querying for more specific appointment slots.


@Repository
public class AppointmentSlotDaoImpl extends BaseDao implements AppointmentSlotDao {

    public AppointmentSlotDaoImpl() {
        super(AppointmentSlot.class);
    }

    @Override
    public List getAppointmentSlotsBetween(Doctor doctor, LocalDateTime startTime, LocalDateTime endTime) {
        Query query = entityManager.createNamedQuery("appointmentSlotsBetween", AppointmentSlot.class);
        query.setParameter("startTime", startTime);
        query.setParameter("endTime", endTime);
        query.setParameter("doctor", doctor);
        return query.getResultList();
    }
}

Further development

Feel free to explore this project. We will be adding some more functionality to it as we go.
Next up we’ll expose some web methods over HTTP. Stay tuned.

To see where a little more details behind how this application is constructed, see the following articles.

Leave a Comment