diff --git a/opp/core/pom.xml b/opp/core/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a573bc80b75482c62550aff5fb8d734f1fb7a459
--- /dev/null
+++ b/opp/core/pom.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.example</groupId>
+ <artifactId>opp</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>core</artifactId>
+
+ <properties>
+ <maven.compiler.source>22</maven.compiler.source>
+ <maven.compiler.target>22</maven.compiler.target>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-annotations</artifactId>
+ <version>2.13.0</version>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ <version>2.13.0</version>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-core</artifactId>
+ <version>2.13.0</version>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/opp/core/src/main/java/Address.java b/opp/core/src/main/java/Address.java
new file mode 100644
index 0000000000000000000000000000000000000000..a6f7e39aff05cbf53c70460bfd93ced9920f269c
--- /dev/null
+++ b/opp/core/src/main/java/Address.java
@@ -0,0 +1,9 @@
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public record Address(
+ @JsonProperty String street,
+ @JsonProperty String house,
+ @JsonProperty String postalCode,
+ @JsonProperty String city
+) {
+}
diff --git a/opp/core/src/main/java/Gender.java b/opp/core/src/main/java/Gender.java
new file mode 100644
index 0000000000000000000000000000000000000000..986e6895e75fae933bdccdb116aecce11ffe3d05
--- /dev/null
+++ b/opp/core/src/main/java/Gender.java
@@ -0,0 +1,6 @@
+public enum Gender {
+ MALE,
+ FEMALE,
+ OTHER,
+ UNKNOWN;
+}
diff --git a/opp/core/src/main/java/Id.java b/opp/core/src/main/java/Id.java
new file mode 100644
index 0000000000000000000000000000000000000000..f2f2920f0ad5428c74386f29292602a746be7ed0
--- /dev/null
+++ b/opp/core/src/main/java/Id.java
@@ -0,0 +1,38 @@
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+import java.io.IOException;
+
+@JsonSerialize(using = Id.Serializer.class)
+public record Id<T>(String value)
+{
+
+ @Override public String toString(){ return value; }
+
+
+ public static class Serializer<T> extends StdSerializer<Id<T>>
+ {
+
+ public Serializer(){
+ this(null);
+ }
+
+ public Serializer(Class<Id<T>> cl){
+ super(cl);
+ }
+
+ @Override
+ public void serialize(
+ Id<T> id,
+ JsonGenerator json,
+ SerializerProvider provider
+ )
+ throws IOException {
+ json.writeString(id.value());
+ }
+
+ }
+
+}
diff --git a/opp/core/src/main/java/Patient.java b/opp/core/src/main/java/Patient.java
new file mode 100644
index 0000000000000000000000000000000000000000..28ad22aebcc23289e59bfbdb76db0a5bb14c27d1
--- /dev/null
+++ b/opp/core/src/main/java/Patient.java
@@ -0,0 +1,29 @@
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.time.LocalDate;
+
+public record Patient (
+ @JsonProperty
+ Id<Patient> id,
+
+ @JsonProperty
+ Gender gender,
+
+ @JsonProperty
+ String givenName,
+
+ @JsonProperty
+ String familyName,
+
+ @JsonProperty
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
+ LocalDate birthDate,
+
+ @JsonProperty
+ Address address,
+
+ @JsonProperty
+ String healthInsurance
+) {
+}
diff --git a/opp/core/src/main/java/Person.java b/opp/core/src/main/java/Person.java
new file mode 100644
index 0000000000000000000000000000000000000000..4f90e7b36c1d640d8ba30540a709b72458f0674d
--- /dev/null
+++ b/opp/core/src/main/java/Person.java
@@ -0,0 +1,26 @@
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.time.LocalDate;
+
+public record Person (
+ @JsonProperty
+ Id<Person> id,
+
+ @JsonProperty
+ Gender gender,
+
+ @JsonProperty
+ String givenName,
+
+ @JsonProperty
+ String familyName,
+
+ @JsonProperty
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
+ LocalDate birthDate,
+
+ @JsonProperty
+ Address address
+) {
+}
diff --git a/opp/core/src/main/java/Repository.java b/opp/core/src/main/java/Repository.java
new file mode 100644
index 0000000000000000000000000000000000000000..145bef94ca2a5ce7ac2be9256e9ac8fc9a4b88c8
--- /dev/null
+++ b/opp/core/src/main/java/Repository.java
@@ -0,0 +1,14 @@
+import java.util.Optional;
+
+public interface Repository {
+
+ Id<Patient> patientId();
+
+ void createPatient(Patient patient) throws Exception;
+
+ Optional<Patient> findPatient(Id<Patient> id);
+
+ Optional<Patient> deletePatient(Id<Patient> id) throws Exception;
+
+ // Hier dann die restlichen create/edit Methoden rein
+}
diff --git a/opp/jdbc-repo-impl/pom.xml b/opp/jdbc-repo-impl/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..fcb0af4f7a95afa1e2d312fd79ae3e3adfa2774d
--- /dev/null
+++ b/opp/jdbc-repo-impl/pom.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.example</groupId>
+ <artifactId>opp</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>jdbc-repo-impl</artifactId>
+
+ <properties>
+ <maven.compiler.source>22</maven.compiler.source>
+ <maven.compiler.target>22</maven.compiler.target>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>org.postgresql</groupId>
+ <artifactId>postgresql</artifactId>
+ <version>42.7.3</version>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.example</groupId>
+ <artifactId>core</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.12</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/opp/jdbc-repo-impl/src/main/java/JDBCRepository.java b/opp/jdbc-repo-impl/src/main/java/JDBCRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..81fcc8bd04edf9c6a898574e84e9f4e65d8b32c1
--- /dev/null
+++ b/opp/jdbc-repo-impl/src/main/java/JDBCRepository.java
@@ -0,0 +1,206 @@
+import java.sql.*;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.util.Optional;
+
+import static java.util.UUID.randomUUID;
+
+
+class JDBCRepository implements Repository
+{
+
+ private final Connection conn;
+
+ private JDBCRepository(Connection conn){
+ this.conn = conn;
+ }
+
+
+ // Factory method
+ static JDBCRepository instance(){
+ try {
+ var conn =
+ DriverManager.getConnection(
+ System.getProperty("pms.repo.jdbc.url"),
+ System.getProperty("pms.repo.jdbc.user"),
+ System.getProperty("pms.repo.jdbc.password")
+ );
+
+ var repo = new JDBCRepository(conn);
+ repo.setup();
+ return repo;
+
+ } catch (SQLException e){
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ private static final String CREATE_PATIENT_TABLE = """
+ CREATE TABLE IF NOT EXISTS patients(
+ id VARCHAR(50) PRIMARY KEY,
+ gender VARCHAR(10) NOT NULL,
+ givenName VARCHAR(100) NOT NULL,
+ familyName VARCHAR(100) NOT NULL,
+ birthDate DATE NOT NULL,
+ street VARCHAR(50) NOT NULL,
+ house VARCHAR(50) NOT NULL,
+ postalCode VARCHAR(50) NOT NULL,
+ city VARCHAR(50) NOT NULL,
+ healthInsurance VARCHAR(40) NOT NULL
+ );
+ """;
+
+
+ // Set up DB tables etc.
+ void setup(){
+ try (var stmt = conn.createStatement()){
+
+ stmt.execute(CREATE_PATIENT_TABLE);
+
+ } catch (SQLException e){
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ private static String quoted(String s){
+ return String.format("'%s'",s);
+ }
+
+ private static String sqlValue(Object obj){
+
+ return switch(obj){
+ case LocalDate date -> quoted(Date.valueOf(date).toString());
+ case Instant t -> quoted(Timestamp.from(t).toString());
+ case Integer n -> Integer.toString(n);
+ case Long n -> Long.toString(n);
+ case Double n -> Double.toString(n);
+ default -> quoted(obj.toString());
+ };
+
+ }
+
+
+ private static Patient readPatientFromRow(ResultSet rs) throws SQLException {
+ return new Patient(
+ new Id<>(rs.getString("id")),
+ Gender.valueOf(rs.getString("gender")),
+ rs.getString("givenName"),
+ rs.getString("familyName"),
+ rs.getDate("birthDate").toLocalDate(),
+ new Address(
+ rs.getString("street"),
+ rs.getString("house"),
+ rs.getString("postalCode"),
+ rs.getString("city")
+ ),
+ rs.getString("healthInsurance")
+ );
+ }
+
+
+ private static String insertSQL(Patient patient){
+ return
+ "INSERT INTO patients(" +
+ "id,gender,givenName,familyName,birthDate," +
+ "street,house,postalCode,city,healthInsurance" +
+ ") VALUES (" +
+ sqlValue(patient.id().value()) + "," +
+ sqlValue(patient.gender()) + "," +
+ sqlValue(patient.givenName()) + "," +
+ sqlValue(patient.familyName()) + "," +
+ sqlValue(patient.birthDate()) + "," +
+ sqlValue(patient.address().street()) + "," +
+ sqlValue(patient.address().house()) + "," +
+ sqlValue(patient.address().postalCode()) + "," +
+ sqlValue(patient.address().city()) + "," +
+ sqlValue(patient.healthInsurance()) +
+ ");";
+ }
+
+ private static String updateSQL(Patient patient){
+ return
+ "UPDATE patients SET " +
+ "gender = " + sqlValue(patient.gender()) + "," +
+ "givenName = " + sqlValue(patient.givenName()) + "," +
+ "familyName = " + sqlValue(patient.familyName()) + "," +
+ "birthDate = " + sqlValue(patient.birthDate()) + "," +
+ "street = " + sqlValue(patient.address().street()) + "," +
+ "house = " + sqlValue(patient.address().house()) + "," +
+ "postalCode = " + sqlValue(patient.address().postalCode()) + "," +
+ "city = " + sqlValue(patient.address().city()) + "," +
+ "healthInsurance = " + sqlValue(patient.healthInsurance()) + " " +
+ "WHERE id = " + sqlValue(patient.id().value()) + ";";
+ }
+
+
+
+ @Override
+ public Id<Patient> patientId(){
+
+ var id = new Id<Patient>(randomUUID().toString());
+
+ return findPatient(id).isEmpty() ? id : patientId();
+ }
+
+
+ @Override
+ public void createPatient(Patient patient) throws SQLException {
+
+ try (
+ var stmt = conn.createStatement()
+ ){
+ var sql =
+ findPatient(patient.id()).isPresent() ?
+ updateSQL(patient) :
+ insertSQL(patient);
+
+ stmt.executeUpdate(sql);
+
+ } catch (SQLException e){
+ throw new RuntimeException(e);
+ }
+
+ }
+
+
+ @Override
+ public Optional<Patient> findPatient(Id<Patient> id){
+ try (
+ var result =
+ conn.createStatement()
+ .executeQuery("SELECT * FROM patients WHERE id = " + sqlValue(id.value()) + ";")
+ ){
+ return
+ result.next() ?
+ Optional.of(readPatientFromRow(result)) :
+ Optional.empty();
+
+ } catch (SQLException e){
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ @Override
+ public Optional<Patient> deletePatient(Id<Patient> id) throws SQLException {
+
+ var patient = findPatient(id);
+
+ patient.ifPresent(
+ p -> {
+ try {
+ conn.createStatement()
+ .executeUpdate("DELETE FROM patients WHERE id = " + quoted(id.value()) + ";");
+ } catch (SQLException e){
+ throw new RuntimeException(e);
+ }
+ }
+ );
+
+ return patient;
+ }
+
+
+}
diff --git a/opp/jdbc-repo-impl/src/test/java/Tests.java b/opp/jdbc-repo-impl/src/test/java/Tests.java
new file mode 100644
index 0000000000000000000000000000000000000000..9c089048e5423e883adb3d061971dcfaaa2aa1af
--- /dev/null
+++ b/opp/jdbc-repo-impl/src/test/java/Tests.java
@@ -0,0 +1,64 @@
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.time.LocalDate;
+
+import static org.junit.Assert.*;
+import static org.junit.Assert.assertTrue;
+
+
+public final class Tests
+{
+
+ private static Repository repo = null;
+
+ private static Patient testPatient = null;
+
+ @BeforeClass
+ public static void init() throws Exception {
+
+ System.setProperty("pms.repo.jdbc.url", "jdbc:postgresql:postgres");
+ System.setProperty("pms.repo.jdbc.user", "postgres");
+ System.setProperty("pms.repo.jdbc.password", "1234");
+
+ repo = JDBCRepository.instance();
+
+ testPatient = new Patient(
+ new Id<>("1111"),
+ Gender.MALE,
+ "Hans",
+ "Guenther",
+ LocalDate.of(1999, 01, 01),
+ new Address("Musterstraße", "12", "12345", "Beispielhausen"),
+ "AOK");
+ }
+
+ @Test
+ public void testPatientSave(){
+
+ try {
+ repo.createPatient(testPatient);
+ } catch (Exception e){
+ e.printStackTrace();
+ }
+
+ assertTrue(
+ repo.findPatient(testPatient.id()).isPresent()
+ );
+ }
+
+
+ @Test
+ public void testPatientDelete(){
+
+ try {
+ repo.deletePatient(testPatient.id());
+ } catch (Exception e){
+ e.printStackTrace();
+ }
+
+ assertTrue(
+ repo.findPatient(testPatient.id()).isEmpty()
+ );
+ }
+}
diff --git a/opp/pom.xml b/opp/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7b50b5f510aafc4b75be28155a0d5309138e693d
--- /dev/null
+++ b/opp/pom.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.example</groupId>
+ <artifactId>opp</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <packaging>pom</packaging>
+ <modules>
+ <module>core</module>
+ <module>jdbc-repo-impl</module>
+ </modules>
+
+ <properties>
+ <maven.compiler.source>22</maven.compiler.source>
+ <maven.compiler.target>22</maven.compiler.target>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+
+</project>
\ No newline at end of file