From b12379fdc42d06524c553c7e3170a1121d76388c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marina=20M=C4=83d=C4=83ra=C8=99?=
 <m.madara@student.tudelft.nl>
Date: Sat, 29 Mar 2025 11:43:25 +0100
Subject: [PATCH 1/5] Add basic structure of projects

---
 CHANGELOG.md                                  |  1 +
 .../java/nl/tudelft/tam/model/JobHolder.java  | 42 +++++++++++++++++++
 .../java/nl/tudelft/tam/model/JobOffer.java   |  6 ++-
 .../java/nl/tudelft/tam/model/Project.java    | 41 ++++++++++++++++++
 .../tudelft/tam/model/ProjectJobOffers.java   | 29 +++++++++++++
 5 files changed, 118 insertions(+), 1 deletion(-)
 create mode 100644 src/main/java/nl/tudelft/tam/model/JobHolder.java
 create mode 100644 src/main/java/nl/tudelft/tam/model/Project.java
 create mode 100644 src/main/java/nl/tudelft/tam/model/ProjectJobOffers.java

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a2d79a036..9b8cb6d6b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Button added to switch to dark theme. @rgiedryte 
 - Add HeadTA Job Offers @toberhuber @dsavvidi
 
+- Added JobHolder and Project Entity @mmadara
 ### Changed
 
 ### Fixed
diff --git a/src/main/java/nl/tudelft/tam/model/JobHolder.java b/src/main/java/nl/tudelft/tam/model/JobHolder.java
new file mode 100644
index 000000000..86d876044
--- /dev/null
+++ b/src/main/java/nl/tudelft/tam/model/JobHolder.java
@@ -0,0 +1,42 @@
+/*
+ * TAM
+ * Copyright (C) 2021 - Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.tam.model;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import jakarta.persistence.*;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.SuperBuilder;
+
+@Data
+@Entity
+@SuperBuilder
+@NoArgsConstructor
+@AllArgsConstructor
+@Inheritance(strategy = InheritanceType.JOINED)
+public class JobHolder {
+	@Id
+	@GeneratedValue(strategy = GenerationType.IDENTITY) //TODO: Once suborganisation is implemented, change to move this annotation to SubOrganisation id
+	private Long id;
+
+	@OneToMany(mappedBy = "holder")
+	private Set<JobOffer> jobOffers = new HashSet<>();
+}
diff --git a/src/main/java/nl/tudelft/tam/model/JobOffer.java b/src/main/java/nl/tudelft/tam/model/JobOffer.java
index 05b16a1d8..ac4413970 100644
--- a/src/main/java/nl/tudelft/tam/model/JobOffer.java
+++ b/src/main/java/nl/tudelft/tam/model/JobOffer.java
@@ -44,7 +44,11 @@ public class JobOffer {
 	private Long id;
 
 	@NotNull
-	private Long editionId;
+	private Long editionId; //TODO: Remove this field
+
+	@Nullable
+	@ManyToOne
+	private JobHolder holder;
 
 	@NotBlank
 	@Builder.Default
diff --git a/src/main/java/nl/tudelft/tam/model/Project.java b/src/main/java/nl/tudelft/tam/model/Project.java
new file mode 100644
index 000000000..4dcf9c791
--- /dev/null
+++ b/src/main/java/nl/tudelft/tam/model/Project.java
@@ -0,0 +1,41 @@
+/*
+ * TAM
+ * Copyright (C) 2021 - Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.tam.model;
+
+import javax.validation.constraints.NotNull;
+
+import jakarta.persistence.*;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.SuperBuilder;
+
+// TODO: once suborganisation is implemented, change JobHolder to SubOrganisation
+@Data
+@Entity
+@SuperBuilder
+@NoArgsConstructor
+@AllArgsConstructor
+public class Project extends JobHolder {
+	@NotNull
+	private String name;
+
+	@NotNull
+	@ManyToOne
+	private TAMProgram organisation; //TODO: once Organisation is implemented, change TAMProgram to Organisation
+}
diff --git a/src/main/java/nl/tudelft/tam/model/ProjectJobOffers.java b/src/main/java/nl/tudelft/tam/model/ProjectJobOffers.java
new file mode 100644
index 000000000..6699b34f8
--- /dev/null
+++ b/src/main/java/nl/tudelft/tam/model/ProjectJobOffers.java
@@ -0,0 +1,29 @@
+/*
+ * TAM
+ * Copyright (C) 2021 - Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.tam.model;
+
+import jakarta.persistence.Entity;
+import lombok.NoArgsConstructor;
+import lombok.experimental.SuperBuilder;
+
+@Entity
+@SuperBuilder
+@NoArgsConstructor
+//@AllArgsConstructor
+public class ProjectJobOffers extends JobHolder {
+}
-- 
GitLab


From 4a9dc58e6a09da4063082d162f6453a69e5678e2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marina=20M=C4=83d=C4=83ra=C8=99?=
 <m.madara@student.tudelft.nl>
Date: Sat, 29 Mar 2025 13:39:41 +0100
Subject: [PATCH 2/5] migrations

---
 src/main/resources/migrations.yaml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/main/resources/migrations.yaml b/src/main/resources/migrations.yaml
index 13512a3b1..18f593d45 100644
--- a/src/main/resources/migrations.yaml
+++ b/src/main/resources/migrations.yaml
@@ -938,6 +938,7 @@ databaseChangeLog:
           oldColumnName: hidden
           columnDataType: bit
           tableName: job_offer
+
 - changeSet:
     id: 1740585090326-1
     author: dsav (generated)
@@ -950,4 +951,5 @@ databaseChangeLog:
                 name: offer_type
                 type: ENUM('HEAD_TA', 'TA', 'TA_OR_HEAD_TA')
                 defaultValue: 'TA_OR_HEAD_TA'
-          tableName: job_offer
\ No newline at end of file
+          tableName: job_offer
+
-- 
GitLab


From 2bda0bba47375c5153242b3fbaac019cc3ecd0b1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marina=20M=C4=83d=C4=83ra=C8=99?=
 <m.madara@student.tudelft.nl>
Date: Wed, 2 Apr 2025 12:11:08 +0200
Subject: [PATCH 3/5] Make jobholder abstract

---
 .../java/nl/tudelft/tam/model/JobHolder.java  | 10 ++++---
 .../java/nl/tudelft/tam/model/Project.java    | 11 +++++--
 .../tudelft/tam/model/ProjectJobOffers.java   | 29 -------------------
 src/main/resources/migrations.yaml            |  4 +++
 4 files changed, 19 insertions(+), 35 deletions(-)
 delete mode 100644 src/main/java/nl/tudelft/tam/model/ProjectJobOffers.java

diff --git a/src/main/java/nl/tudelft/tam/model/JobHolder.java b/src/main/java/nl/tudelft/tam/model/JobHolder.java
index 86d876044..444610edc 100644
--- a/src/main/java/nl/tudelft/tam/model/JobHolder.java
+++ b/src/main/java/nl/tudelft/tam/model/JobHolder.java
@@ -31,11 +31,13 @@ import lombok.experimental.SuperBuilder;
 @SuperBuilder
 @NoArgsConstructor
 @AllArgsConstructor
-@Inheritance(strategy = InheritanceType.JOINED)
-public class JobHolder {
+@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
+public sealed abstract class JobHolder permits Project {
+
 	@Id
-	@GeneratedValue(strategy = GenerationType.IDENTITY) //TODO: Once suborganisation is implemented, change to move this annotation to SubOrganisation id
-	private Long id;
+	public abstract Long getId();
+
+	public abstract void setId(Long id);
 
 	@OneToMany(mappedBy = "holder")
 	private Set<JobOffer> jobOffers = new HashSet<>();
diff --git a/src/main/java/nl/tudelft/tam/model/Project.java b/src/main/java/nl/tudelft/tam/model/Project.java
index 4dcf9c791..98a7ec499 100644
--- a/src/main/java/nl/tudelft/tam/model/Project.java
+++ b/src/main/java/nl/tudelft/tam/model/Project.java
@@ -25,13 +25,20 @@ import lombok.Data;
 import lombok.NoArgsConstructor;
 import lombok.experimental.SuperBuilder;
 
-// TODO: once suborganisation is implemented, change JobHolder to SubOrganisation
+/*
+ * TODO: once suborganisation is implemented, change JobHolder to SubOrganisation and add a one to one
+ * ProjectJobOffers entity
+ */
 @Data
 @Entity
 @SuperBuilder
 @NoArgsConstructor
 @AllArgsConstructor
-public class Project extends JobHolder {
+public non-sealed class Project extends JobHolder {
+	@Id
+	@GeneratedValue(strategy = GenerationType.IDENTITY)
+	private Long id;
+
 	@NotNull
 	private String name;
 
diff --git a/src/main/java/nl/tudelft/tam/model/ProjectJobOffers.java b/src/main/java/nl/tudelft/tam/model/ProjectJobOffers.java
deleted file mode 100644
index 6699b34f8..000000000
--- a/src/main/java/nl/tudelft/tam/model/ProjectJobOffers.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * TAM
- * Copyright (C) 2021 - Delft University of Technology
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-package nl.tudelft.tam.model;
-
-import jakarta.persistence.Entity;
-import lombok.NoArgsConstructor;
-import lombok.experimental.SuperBuilder;
-
-@Entity
-@SuperBuilder
-@NoArgsConstructor
-//@AllArgsConstructor
-public class ProjectJobOffers extends JobHolder {
-}
diff --git a/src/main/resources/migrations.yaml b/src/main/resources/migrations.yaml
index 18f593d45..da5b90901 100644
--- a/src/main/resources/migrations.yaml
+++ b/src/main/resources/migrations.yaml
@@ -939,6 +939,7 @@ databaseChangeLog:
           columnDataType: bit
           tableName: job_offer
 
+
 - changeSet:
     id: 1740585090326-1
     author: dsav (generated)
@@ -953,3 +954,6 @@ databaseChangeLog:
                 defaultValue: 'TA_OR_HEAD_TA'
           tableName: job_offer
 
+## TAM Improvement
+
+
-- 
GitLab


From 6aae857082594b87a8fc0643c0b619b395205b34 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marina=20M=C4=83d=C4=83ra=C8=99?=
 <m.madara@student.tudelft.nl>
Date: Sat, 19 Apr 2025 20:04:47 +0200
Subject: [PATCH 4/5] Review?

---
 src/main/java/nl/tudelft/tam/model/JobHolder.java | 15 +++++++--------
 src/main/java/nl/tudelft/tam/model/Project.java   |  2 +-
 2 files changed, 8 insertions(+), 9 deletions(-)

diff --git a/src/main/java/nl/tudelft/tam/model/JobHolder.java b/src/main/java/nl/tudelft/tam/model/JobHolder.java
index 444610edc..8047602aa 100644
--- a/src/main/java/nl/tudelft/tam/model/JobHolder.java
+++ b/src/main/java/nl/tudelft/tam/model/JobHolder.java
@@ -21,23 +21,22 @@ import java.util.HashSet;
 import java.util.Set;
 
 import jakarta.persistence.*;
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import lombok.NoArgsConstructor;
+import lombok.*;
 import lombok.experimental.SuperBuilder;
 
-@Data
+
 @Entity
 @SuperBuilder
 @NoArgsConstructor
 @AllArgsConstructor
 @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
-public sealed abstract class JobHolder permits Project {
+public abstract class JobHolder {
 
+	@Getter
+	@Setter
 	@Id
-	public abstract Long getId();
-
-	public abstract void setId(Long id);
+	@Builder.Default
+	private Long id = -1L;
 
 	@OneToMany(mappedBy = "holder")
 	private Set<JobOffer> jobOffers = new HashSet<>();
diff --git a/src/main/java/nl/tudelft/tam/model/Project.java b/src/main/java/nl/tudelft/tam/model/Project.java
index 98a7ec499..e3daf953a 100644
--- a/src/main/java/nl/tudelft/tam/model/Project.java
+++ b/src/main/java/nl/tudelft/tam/model/Project.java
@@ -34,7 +34,7 @@ import lombok.experimental.SuperBuilder;
 @SuperBuilder
 @NoArgsConstructor
 @AllArgsConstructor
-public non-sealed class Project extends JobHolder {
+public class Project extends JobHolder {
 	@Id
 	@GeneratedValue(strategy = GenerationType.IDENTITY)
 	private Long id;
-- 
GitLab


From 81d0bd8679a94be18607b2c9a7cca2baa3b77de2 Mon Sep 17 00:00:00 2001
From: Ruben Backx <r.w.backx@tudelft.nl>
Date: Sun, 20 Apr 2025 18:12:36 +0200
Subject: [PATCH 5/5] Fix JobHolder ID generation

Signed-off-by: Ruben Backx <r.w.backx@tudelft.nl>
---
 .../java/nl/tudelft/tam/model/JobHolder.java  | 17 ++--
 .../java/nl/tudelft/tam/model/Project.java    | 15 +++-
 .../java/nl/tudelft/tam/model/ProjectId.java  | 48 +++++++++++
 .../java/nl/tudelft/tam/model/TAMProgram.java |  8 ++
 .../tam/repository/JobHolderRepository.java   | 25 ++++++
 .../tam/repository/ProjectIdRepository.java   | 25 ++++++
 .../tam/repository/ProjectRepository.java     | 25 ++++++
 .../tudelft/tam/model/EditionRepository.java  | 24 ++++++
 .../nl/tudelft/tam/model/JobHolderTest.java   | 81 +++++++++++++++++++
 src/test/resources/application-h2.properties  |  6 +-
 .../resources/application-test.properties     |  6 +-
 11 files changed, 268 insertions(+), 12 deletions(-)
 create mode 100644 src/main/java/nl/tudelft/tam/model/ProjectId.java
 create mode 100644 src/main/java/nl/tudelft/tam/repository/JobHolderRepository.java
 create mode 100644 src/main/java/nl/tudelft/tam/repository/ProjectIdRepository.java
 create mode 100644 src/main/java/nl/tudelft/tam/repository/ProjectRepository.java
 create mode 100644 src/test/java/nl/tudelft/tam/model/EditionRepository.java
 create mode 100644 src/test/java/nl/tudelft/tam/model/JobHolderTest.java

diff --git a/src/main/java/nl/tudelft/tam/model/JobHolder.java b/src/main/java/nl/tudelft/tam/model/JobHolder.java
index 8047602aa..30063763d 100644
--- a/src/main/java/nl/tudelft/tam/model/JobHolder.java
+++ b/src/main/java/nl/tudelft/tam/model/JobHolder.java
@@ -21,10 +21,11 @@ import java.util.HashSet;
 import java.util.Set;
 
 import jakarta.persistence.*;
+import jakarta.validation.constraints.NotNull;
 import lombok.*;
 import lombok.experimental.SuperBuilder;
 
-
+@Data
 @Entity
 @SuperBuilder
 @NoArgsConstructor
@@ -32,12 +33,18 @@ import lombok.experimental.SuperBuilder;
 @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
 public abstract class JobHolder {
 
-	@Getter
-	@Setter
 	@Id
-	@Builder.Default
-	private Long id = -1L;
+	protected String id;
 
+	@NotNull
+	@Builder.Default
+	@ToString.Exclude
+	@EqualsAndHashCode.Exclude
 	@OneToMany(mappedBy = "holder")
 	private Set<JobOffer> jobOffers = new HashSet<>();
+
+	public JobHolder(String id) {
+		this.id = id;
+		this.jobOffers = new HashSet<>();
+	}
 }
diff --git a/src/main/java/nl/tudelft/tam/model/Project.java b/src/main/java/nl/tudelft/tam/model/Project.java
index e3daf953a..b45bc3fed 100644
--- a/src/main/java/nl/tudelft/tam/model/Project.java
+++ b/src/main/java/nl/tudelft/tam/model/Project.java
@@ -22,6 +22,7 @@ import javax.validation.constraints.NotNull;
 import jakarta.persistence.*;
 import lombok.AllArgsConstructor;
 import lombok.Data;
+import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
 import lombok.experimental.SuperBuilder;
 
@@ -34,10 +35,12 @@ import lombok.experimental.SuperBuilder;
 @SuperBuilder
 @NoArgsConstructor
 @AllArgsConstructor
+@EqualsAndHashCode(callSuper = true)
 public class Project extends JobHolder {
-	@Id
-	@GeneratedValue(strategy = GenerationType.IDENTITY)
-	private Long id;
+
+	@NotNull
+	@OneToOne
+	private ProjectId projectId;
 
 	@NotNull
 	private String name;
@@ -45,4 +48,10 @@ public class Project extends JobHolder {
 	@NotNull
 	@ManyToOne
 	private TAMProgram organisation; //TODO: once Organisation is implemented, change TAMProgram to Organisation
+
+	@PrePersist
+	private void prePersist() {
+		this.id = "project-" + projectId.getId(); // Avoid overlap with IDs of other JobHolders
+	}
+
 }
diff --git a/src/main/java/nl/tudelft/tam/model/ProjectId.java b/src/main/java/nl/tudelft/tam/model/ProjectId.java
new file mode 100644
index 000000000..a8c269fc3
--- /dev/null
+++ b/src/main/java/nl/tudelft/tam/model/ProjectId.java
@@ -0,0 +1,48 @@
+/*
+ * TAM
+ * Copyright (C) 2021 - Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.tam.model;
+
+import javax.annotation.Nullable;
+
+import jakarta.persistence.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+@Data
+@Entity
+public class ProjectId {
+
+	@Id
+	@GeneratedValue(strategy = GenerationType.IDENTITY)
+	private Long id;
+
+	@Nullable
+	@ToString.Exclude
+	@EqualsAndHashCode.Exclude
+	@OneToOne(mappedBy = "projectId")
+	private Project project;
+
+	public ProjectId() {
+	}
+
+	public ProjectId(Long id) {
+		this.id = id;
+	}
+
+}
diff --git a/src/main/java/nl/tudelft/tam/model/TAMProgram.java b/src/main/java/nl/tudelft/tam/model/TAMProgram.java
index 787607e58..27bd39f59 100644
--- a/src/main/java/nl/tudelft/tam/model/TAMProgram.java
+++ b/src/main/java/nl/tudelft/tam/model/TAMProgram.java
@@ -23,6 +23,7 @@ import java.util.Set;
 import jakarta.persistence.Entity;
 import jakarta.persistence.Id;
 import jakarta.persistence.ManyToMany;
+import jakarta.persistence.OneToMany;
 import jakarta.validation.constraints.NotNull;
 import lombok.*;
 
@@ -52,4 +53,11 @@ public class TAMProgram {
 	@ManyToMany(mappedBy = "advertisesTo")
 	private Set<TAMProgram> advertisedToBy = new HashSet<>();
 
+	@NotNull
+	@Builder.Default
+	@ToString.Exclude
+	@EqualsAndHashCode.Exclude
+	@OneToMany(mappedBy = "organisation")
+	private Set<Project> projects = new HashSet<>(); // TODO move this to organisations, and change the field type to Set<SubOrganisation> when possible
+
 }
diff --git a/src/main/java/nl/tudelft/tam/repository/JobHolderRepository.java b/src/main/java/nl/tudelft/tam/repository/JobHolderRepository.java
new file mode 100644
index 000000000..0f1734d8f
--- /dev/null
+++ b/src/main/java/nl/tudelft/tam/repository/JobHolderRepository.java
@@ -0,0 +1,25 @@
+/*
+ * TAM
+ * Copyright (C) 2021 - Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.tam.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import nl.tudelft.tam.model.JobHolder;
+
+public interface JobHolderRepository extends JpaRepository<JobHolder, String> {
+}
diff --git a/src/main/java/nl/tudelft/tam/repository/ProjectIdRepository.java b/src/main/java/nl/tudelft/tam/repository/ProjectIdRepository.java
new file mode 100644
index 000000000..40a92438f
--- /dev/null
+++ b/src/main/java/nl/tudelft/tam/repository/ProjectIdRepository.java
@@ -0,0 +1,25 @@
+/*
+ * TAM
+ * Copyright (C) 2021 - Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.tam.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import nl.tudelft.tam.model.ProjectId;
+
+public interface ProjectIdRepository extends JpaRepository<ProjectId, Long> {
+}
diff --git a/src/main/java/nl/tudelft/tam/repository/ProjectRepository.java b/src/main/java/nl/tudelft/tam/repository/ProjectRepository.java
new file mode 100644
index 000000000..49c5698eb
--- /dev/null
+++ b/src/main/java/nl/tudelft/tam/repository/ProjectRepository.java
@@ -0,0 +1,25 @@
+/*
+ * TAM
+ * Copyright (C) 2021 - Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.tam.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import nl.tudelft.tam.model.Project;
+
+public interface ProjectRepository extends JpaRepository<Project, String> {
+}
diff --git a/src/test/java/nl/tudelft/tam/model/EditionRepository.java b/src/test/java/nl/tudelft/tam/model/EditionRepository.java
new file mode 100644
index 000000000..69d4f0ce9
--- /dev/null
+++ b/src/test/java/nl/tudelft/tam/model/EditionRepository.java
@@ -0,0 +1,24 @@
+/*
+ * TAM
+ * Copyright (C) 2021 - Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.tam.model;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+// TODO change this for the real TAMEditionRepository when it exists
+public interface EditionRepository extends JpaRepository<JobHolderTest.Edition, String> {
+}
diff --git a/src/test/java/nl/tudelft/tam/model/JobHolderTest.java b/src/test/java/nl/tudelft/tam/model/JobHolderTest.java
new file mode 100644
index 000000000..f8647f287
--- /dev/null
+++ b/src/test/java/nl/tudelft/tam/model/JobHolderTest.java
@@ -0,0 +1,81 @@
+/*
+ * TAM
+ * Copyright (C) 2021 - Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.tam.model;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import application.test.TestTAMApplication;
+import jakarta.persistence.Entity;
+import nl.tudelft.tam.repository.JobHolderRepository;
+import nl.tudelft.tam.repository.ProjectIdRepository;
+import nl.tudelft.tam.repository.ProjectRepository;
+import nl.tudelft.tam.repository.TAMProgramRepository;
+
+@SpringBootTest(classes = { TestTAMApplication.class, JobHolderTest.class })
+public class JobHolderTest {
+
+	private final ProjectRepository projectRepository;
+	private final TAMProgramRepository programRepository;
+	private final ProjectIdRepository projectIdRepository;
+	private final EditionRepository editionRepository;
+	private final JobHolderRepository jobHolderRepository;
+
+	@Autowired
+	public JobHolderTest(ProjectRepository projectRepository, TAMProgramRepository programRepository,
+			ProjectIdRepository projectIdRepository, EditionRepository editionRepository,
+			JobHolderRepository jobHolderRepository) {
+		this.projectRepository = projectRepository;
+		this.programRepository = programRepository;
+		this.projectIdRepository = projectIdRepository;
+		this.editionRepository = editionRepository;
+		this.jobHolderRepository = jobHolderRepository;
+	}
+
+	@Test
+	public void jobHolderIdsCanBeAutoGeneratedOrSetManually() {
+		TAMProgram program = programRepository.save(TAMProgram.builder().id(1L).build());
+
+		ProjectId projectId = projectIdRepository.save(new ProjectId());
+		Project project = new Project(projectId, "name", program);
+		project = projectRepository.save(project);
+		assertThat(project.getId()).isEqualTo("project-" + projectId.getId());
+
+		Edition edition = new Edition("edition-1");
+		edition = editionRepository.save(edition);
+		assertThat(edition.getId()).isEqualTo("edition-1");
+
+		assertThat(jobHolderRepository.existsById("project-" + projectId.getId())).isTrue();
+		assertThat(jobHolderRepository.existsById("edition-1")).isTrue();
+	}
+
+	// TODO remove in favour of TAMEdition when it exists
+	@Entity
+	public static class Edition extends JobHolder {
+		public Edition() {
+		}
+
+		public Edition(String id) {
+			super(id);
+		}
+	}
+
+}
diff --git a/src/test/resources/application-h2.properties b/src/test/resources/application-h2.properties
index 5cd799fc3..a8cf9bff1 100644
--- a/src/test/resources/application-h2.properties
+++ b/src/test/resources/application-h2.properties
@@ -21,9 +21,11 @@
 
 spring.profiles.active=testing
 
-spring.jpa.hibernate.ddl-auto=validate
+# TODO change back to validate
+spring.jpa.hibernate.ddl-auto=create
 
 spring.liquibase.change-log=classpath:/changelog-master.yaml
-spring.liquibase.enabled=true
+# TODO change back to true
+spring.liquibase.enabled=false
 
 spring.datasource.url=jdbc:h2:mem:testdb
diff --git a/src/test/resources/application-test.properties b/src/test/resources/application-test.properties
index aa6590a48..77cd4fe27 100644
--- a/src/test/resources/application-test.properties
+++ b/src/test/resources/application-test.properties
@@ -18,8 +18,10 @@
 
 spring.profiles.active=test
 
-spring.jpa.hibernate.ddl-auto=validate
-spring.liquibase.enabled=true
+# TODO change back to validate
+spring.jpa.hibernate.ddl-auto=create
+# TODO change back to true
+spring.liquibase.enabled=false
 
 spring.datasource.url=jdbc:h2:mem:testdb
 spring.liquibase.change-log=classpath:/changelog-master.yaml
-- 
GitLab