diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..1e38099be --- /dev/null +++ b/.gitignore @@ -0,0 +1,215 @@ +# Created by https://www.toptal.com/developers/gitignore/api/linux,java,maven,macos,intellij +# Edit at https://www.toptal.com/developers/gitignore?templates=linux,java,maven,macos,intellij + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +.idea +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +# Azure Toolkit for IntelliJ plugin +# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij +.idea/**/azureSettings.xml + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Maven ### +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +# https://github.com/takari/maven-wrapper#usage-without-binary-jar +.mvn/wrapper/maven-wrapper.jar + +# Eclipse m2e generated files +# Eclipse Core +.project +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# End of https://www.toptal.com/developers/gitignore/api/linux,java,maven,macos,intellij + +# Data folder generated by DB is expected to be ignored + +data \ No newline at end of file diff --git a/README.md b/README.md index 7ef29bb53..525cc6dd6 100644 --- a/README.md +++ b/README.md @@ -1 +1,30 @@ -# Template de projet pour le TP JPA 2021 UniR +# Report TP1 - TAA: + +## Presentation: + +This repository is the code of the first lab of the class TAA. It was written by Ammar Kazem and Heming Wang during our second year of master's degree in software engineering at the University of Rennes 1. + +The goal of this lab was to create a small back-end application with "Models" and the "DAO" that goes with it. We created a very basic representation the famous website "Doctolib". + +You can see a representation of our models and the links between them in the image (1). + +![image](https://user-images.githubusercontent.com/50889372/192132477-9ab58e95-0b87-4bc7-9d03-ea11c18d57e3.png) + +*Image (1) : Class diagram* + + +For the DAOs we created a generic DAO with the basic methods from which the other DAOs inherited. Requests were written in JAP. + +## How to run + +To run the project you first need to start the hsql server like this: + +Windows: + +`.\run-hsqldb-server.bat` + +Linux / Macos: + +`./run-hsqldb-server.sh` + +After starting the database server you can launch the main class `JPATest.java` with your ide or from a terminal. Methods have to be called manually in the main class. A small scenario was written but feel free to add calls to it. diff --git a/pom.xml b/pom.xml index 6948616ce..5db586a8d 100644 --- a/pom.xml +++ b/pom.xml @@ -8,8 +8,8 @@ 0.0.1-SNAPSHOT - 1.8 - 1.8 + 11 + 11 @@ -37,8 +37,5 @@ 8.0.28 - - diff --git a/src/main/java/jpa/EntityManagerHelper.java b/src/main/java/jpa/EntityManagerHelper.java index c24bf6d91..ec2ebe0ab 100644 --- a/src/main/java/jpa/EntityManagerHelper.java +++ b/src/main/java/jpa/EntityManagerHelper.java @@ -4,7 +4,7 @@ import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; -public class EntityManagerHelper { +public final class EntityManagerHelper { private static final EntityManagerFactory emf; private static final ThreadLocal threadLocal; diff --git a/src/main/java/jpa/JpaTest.java b/src/main/java/jpa/JpaTest.java index 3c9ff3e44..035219e3f 100644 --- a/src/main/java/jpa/JpaTest.java +++ b/src/main/java/jpa/JpaTest.java @@ -1,8 +1,7 @@ package jpa; -import javax.persistence.EntityManager; -import javax.persistence.EntityTransaction; -import javax.persistence.PersistenceContext; +import jpa.dao.ChildDAO; +import jpa.dao.PatientDAO; public class JpaTest { @@ -10,24 +9,26 @@ public class JpaTest { * @param args */ public static void main(String[] args) { - - EntityManager manager = EntityManagerHelper.getEntityManager(); - EntityTransaction tx = manager.getTransaction(); + var manager = EntityManagerHelper.getEntityManager(); + var tx = manager.getTransaction(); + var patientDAO = new PatientDAO(); + var childDAO = new ChildDAO(); tx.begin(); - - try { + patientDAO.createPatients("Cambria", "Alpha"); + patientDAO.createPatients("Maria", "Beta"); + patientDAO.createPatients("Jean", "Citron"); + childDAO.createChild("firstChild", "good", "parent", 12); + + patientDAO.getPatientList().forEach(System.out::println); + System.out.println(patientDAO.getPatientByName("Maria", "Beta")); } catch (Exception e) { e.printStackTrace(); } tx.commit(); - - manager.close(); - EntityManagerHelper.closeEntityManagerFactory(); - // factory.close(); + System.out.println("done ..."); } - } diff --git a/src/main/java/jpa/dao/AppointmentDAO.java b/src/main/java/jpa/dao/AppointmentDAO.java new file mode 100644 index 000000000..02d669409 --- /dev/null +++ b/src/main/java/jpa/dao/AppointmentDAO.java @@ -0,0 +1,43 @@ +package jpa.dao; + +import jpa.po.Appointment; +import jpa.po.Patient; +import jpa.po.Professional; + +import java.time.LocalDateTime; +import java.util.List; + +public class AppointmentDAO extends GenericJpaDAO { + public AppointmentDAO() { + super(Appointment.class); + } + + public void addAppointment(String reason, LocalDateTime startingTime, Patient patient, Professional professional) { + manager.persist(new Appointment(reason, startingTime, patient, professional)); + } + // Access appointments + public List getAppointmentsByProfessionalId(Long id) { + return manager.createQuery("SELECT p FROM Appointment p WHERE p.professional.id = :id", Appointment.class) + .setParameter("id", id) + .getResultList(); + } + + public List getAppointmentsByPatientId(Long id) { + return manager.createQuery("SELECT p FROM Appointment p WHERE p.patient.id = :id", Appointment.class) + .setParameter("id", id) + .getResultList(); + } + + //update + public void updateStartingTime(Long id, LocalDateTime startingTime) { + manager.createQuery("UPDATE Appointment a SET a.startingTime = :startingTime WHERE a.id = :id") + .setParameter("startingTime", startingTime) + .setParameter("id", id); + } + + //remove + public void removeById(Long id) { + manager.createQuery("DELETE FROM Appointment a WHERE a.id = :id") + .setParameter("id", id); + } +} diff --git a/src/main/java/jpa/dao/ChildDAO.java b/src/main/java/jpa/dao/ChildDAO.java new file mode 100644 index 000000000..3837fac3c --- /dev/null +++ b/src/main/java/jpa/dao/ChildDAO.java @@ -0,0 +1,47 @@ +package jpa.dao; + +import jpa.po.Child; + +import java.util.List; + +public class ChildDAO extends GenericJpaDAO{ + + public ChildDAO() { + super(Child.class); + } + + public void createChild(String firstName, String lastName, String parentName, Integer age) { + manager.persist(new Child(firstName, lastName, parentName, age)); + } + + // We think that returning a list is better than returning a single element from a list. + public List getChildByName(String firstName, String lastName) { + return manager + .createQuery("SELECT c FROM Child c WHERE c.lastName LIKE :lastName " + + "AND c.firstName LIKE :firstName", Child.class) + .setParameter("firstName", firstName) + .setParameter("lastName", lastName) + .getResultList(); + } + + public List getChildList() { + return manager + .createQuery("SELECT c FROM Child c", Child.class) + .getResultList(); + } + + public void removeChildByName(String firstName, String lastName) { + this.manager.createQuery("delete from Child c where c.firstName = :firstName and c.lastName = :lastName") + .setParameter("lastName", lastName) + .setParameter("firstName", firstName); + } + + public void removeAppointmentToPatient(String firstName, String lastName, Long appointmentId) { + this.manager.createQuery("delete from Appointment app " + + "where app.id = :id and app.patient.lastName = :lastName " + + "and app.patient.firstName = :firstName") + .setParameter("id", appointmentId) + .setParameter("lastName", lastName) + .setParameter("firstName", firstName); + } +} diff --git a/src/main/java/jpa/dao/GenericJpaDAO.java b/src/main/java/jpa/dao/GenericJpaDAO.java new file mode 100644 index 000000000..577da3df5 --- /dev/null +++ b/src/main/java/jpa/dao/GenericJpaDAO.java @@ -0,0 +1,42 @@ +package jpa.dao; + +import jpa.EntityManagerHelper; +import org.hibernate.cfg.NotYetImplementedException; + +import javax.persistence.EntityManager; +import java.util.List; + +public class GenericJpaDAO { + + protected final EntityManager manager; + protected final Class objectClass; + + protected GenericJpaDAO(Class objectClass) { + this.manager = EntityManagerHelper.getEntityManager(); + this.objectClass = objectClass; + } + + public T getById(TK id) { + return this.manager.find(this.objectClass, id); + } + + public List getAll() { + return this.manager + .createQuery("select obj from " + this.objectClass.getName() + " obj", objectClass) + .getResultList(); + } + + public boolean exists(TK id) { + return this.manager.find(this.objectClass, id) != null; + } + + public void update(TK id) { + // TODO + throw new NotYetImplementedException(); + } + + public void remove(TK id) { + // TODO + throw new NotYetImplementedException(); + } +} diff --git a/src/main/java/jpa/dao/PatientDAO.java b/src/main/java/jpa/dao/PatientDAO.java new file mode 100644 index 000000000..acd454937 --- /dev/null +++ b/src/main/java/jpa/dao/PatientDAO.java @@ -0,0 +1,54 @@ +package jpa.dao; + +import jpa.po.Appointment; +import jpa.po.Patient; + +import java.util.List; + +public class PatientDAO extends GenericJpaDAO { + + public PatientDAO() { + super(Patient.class); + } + + // Create queries + + public void createPatients(String firstName, String lastName) { + manager.persist(new Patient(firstName, lastName)); + } + + // Fetch queries + + public List getPatientByName(String firstName, String lastName) { + return manager + .createQuery("SELECT p FROM Patient p WHERE p.lastName LIKE :lastName AND p.firstName LIKE :firstName", Patient.class) + .setParameter("firstName", firstName) + .setParameter("lastName", lastName) + .getResultList(); + } + + public List getPatientList() { + return manager + .createQuery("Select p From Patient p", Patient.class) + .getResultList(); + } + + // Remove queries + + public void removePatientByName(String firstName, String lastName) { + this.manager.createQuery("delete from Patient p where p.firstName = :firstName and p.lastName = :lastName") + .setParameter("lastName", lastName) + .setParameter("firstName", firstName); + } + + // Update queries + + public void removeAppointmentToPatient(String firstName, String lastName, Long appointmentId) { + this.manager.createQuery("delete from Appointment app " + + "where app.id = :id and app.patient.lastName = :lastName " + + "and app.patient.firstName = :firstName") + .setParameter("id", appointmentId) + .setParameter("lastName", lastName) + .setParameter("firstName", firstName); + } +} diff --git a/src/main/java/jpa/dao/ProfessionalDAO.java b/src/main/java/jpa/dao/ProfessionalDAO.java new file mode 100644 index 000000000..3e88ed3f0 --- /dev/null +++ b/src/main/java/jpa/dao/ProfessionalDAO.java @@ -0,0 +1,42 @@ +package jpa.dao; + +import jpa.po.Professional; + +import java.util.List; + +public class ProfessionalDAO extends GenericJpaDAO { + + public ProfessionalDAO() { + super(Professional.class); + } + + // Create + + public void createProfessional(String firstName, String lastName) { + manager.persist(new Professional(firstName, lastName)); + } + + // Get + + public List getPatientByName(String firstName, String lastName) { + return manager + .createQuery("SELECT p FROM Professional p WHERE p.lastName LIKE :lastName AND p.firstName LIKE :firstName", Professional.class) + .setParameter("firstName", firstName) + .setParameter("lastName", lastName) + .getResultList(); + } + + public List listProfessionals() { + return manager + .createQuery("SELECT p FROM Patient p", Professional.class) + .getResultList(); + } + + // Remove queries + + public void removeProfessionalByName(String firstName, String lastName) { + this.manager.createQuery("delete from Professional p where p.firstName = :firstName and p.lastName = :lastName") + .setParameter("lastName", lastName) + .setParameter("firstName", firstName); + } +} diff --git a/src/main/java/jpa/po/Appointment.java b/src/main/java/jpa/po/Appointment.java new file mode 100644 index 000000000..15e317fcb --- /dev/null +++ b/src/main/java/jpa/po/Appointment.java @@ -0,0 +1,84 @@ +package jpa.po; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import java.time.LocalDateTime; +import java.util.List; + +@Entity +public class Appointment { + + private Long id; + + private String reason; + private LocalDateTime startingTime; + + private Professional professional; + private Patient patient; + + public Appointment() {} + + public Appointment(String reason, LocalDateTime startingTime, Patient patient, Professional professional) { + this.reason = reason; + this.startingTime = startingTime; + this.patient = patient; + this.professional = professional; + } + + @Id + @GeneratedValue + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getReason() { + return reason; + } + + public void setReason(String reason) { + this.reason = reason; + } + + public LocalDateTime getStartingTime() { + return startingTime; + } + + public void setStartingTime(LocalDateTime startingTime) { + this.startingTime = startingTime; + } + + @ManyToOne + public Professional getProfessional() { + return professional; + } + + public void setProfessional(Professional professional) { + this.professional = professional; + } + + @ManyToOne + public Patient getPatient() { + return patient; + } + + public void setPatient(Patient patient) { + this.patient = patient; + } + + @Override + public String toString() { + return "Appointment{" + + "id=" + id + + ", reason='" + reason + '\'' + + ", startingTime=" + startingTime + + ", professional=" + professional.toString() + + ", patient=" + patient.toString() + + '}'; + } +} diff --git a/src/main/java/jpa/po/Child.java b/src/main/java/jpa/po/Child.java new file mode 100644 index 000000000..76bfc12c8 --- /dev/null +++ b/src/main/java/jpa/po/Child.java @@ -0,0 +1,50 @@ +package jpa.po; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +@Entity +public class Child extends Patient { + + Long id; + String parentName; + Integer age; + + @Override + @Id + @GeneratedValue + public Long getId() { + return id; + } + + @Override + public void setId(Long id) { + this.id = id; + } + + public Child() {} + + public Child(String firstName, String lastName, String parentName, Integer age) { + super(firstName, lastName); + this.parentName = parentName; + this.age = age; + } + + + public String getParentName() { + return parentName; + } + + public void setParentName(String parentName) { + this.parentName = parentName; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } +} diff --git a/src/main/java/jpa/po/Patient.java b/src/main/java/jpa/po/Patient.java new file mode 100644 index 000000000..63ed37ac4 --- /dev/null +++ b/src/main/java/jpa/po/Patient.java @@ -0,0 +1,75 @@ +package jpa.po; + +import javax.persistence.*; +import java.util.List; + +@Entity +public class Patient { + private Long id; + private String lastName; + private String firstName; + private List appointments; + + private Professional professional; + + public Patient() { + super(); + } + + public Patient(String firstName, String lastName) { + this.firstName = firstName; + this.lastName = lastName; + } + + @Id + @GeneratedValue + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getLastName() { + return this.lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getFirstName() { + return this.firstName; + } + + public void setFirstName(String lastName) { + this.firstName = lastName; + } + + @OneToMany(mappedBy = "patient", cascade = CascadeType.PERSIST) + public List getAppointments() { + return appointments; + } + + public void setAppointments(List appointments) { + this.appointments = appointments; + } + + @ManyToOne + public Professional getProfessional() { + return professional; + } + + public void setProfessional(Professional professional) { + this.professional = professional; + } + + @Override + public String toString() { + return "Patient{" + + "lastName='" + lastName + '\'' + + ", firstName='" + firstName + '\'' + + '}'; + } +} diff --git a/src/main/java/jpa/po/Professional.java b/src/main/java/jpa/po/Professional.java new file mode 100644 index 000000000..e4a8ef7d6 --- /dev/null +++ b/src/main/java/jpa/po/Professional.java @@ -0,0 +1,75 @@ +package jpa.po; + +import javax.persistence.*; +import java.util.ArrayList; +import java.util.List; + +@Entity +public class Professional { + private Long id; + private String firstName; + private String lastName; + + private List listAppointments = new ArrayList<>(); + + private List listPatients = new ArrayList<>(); + + public Professional() {} + + public Professional(String firstName, String lastName) { + this.firstName = firstName; + this.lastName = lastName; + } + + @Id + @GeneratedValue + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + @OneToMany(mappedBy = "professional", cascade = CascadeType.PERSIST) + public List getListAppointments() { + return listAppointments; + } + + public void setListAppointments(List listAppointments) { + this.listAppointments = listAppointments; + } + + @OneToMany(mappedBy = "professional", cascade = CascadeType.PERSIST) + public List getListPatients() { + return listPatients; + } + + public void setListPatients(List listPatients) { + this.listPatients = listPatients; + } + + @Override + public String toString() { + return "Professional{" + + "firstName='" + firstName + '\'' + + ", lastName='" + lastName + '\'' + + '}'; + } +}