diff --git a/.jpb/jpb-settings.xml b/.jpb/jpb-settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9e8fa78703d266a57b04865c38a859b6f17e8e9f
--- /dev/null
+++ b/.jpb/jpb-settings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+    <component name="JpaPluginProjectSettings">
+        <option name="lastSelectedLanguage" value="Kotlin"/>
+    </component>
+</project>
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index b68ab8b6df49eda8685df3a8eb6ed342a564d750..5bf465c9fe3856729b6f1a245449f0eccc8b2a89 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,11 +1,22 @@
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
-	id("org.springframework.boot") version "3.0.5"
+	id("org.springframework.boot") version "3.0.6"
 	id("io.spring.dependency-management") version "1.1.0"
+	id("org.jetbrains.kotlin.plugin.allopen") version "1.8.21"
 	kotlin("jvm") version "1.7.22"
 	kotlin("plugin.spring") version "1.7.22"
-    kotlin("plugin.serialization") version "1.8.21"
+	kotlin("plugin.jpa") version "1.7.22"
+	kotlin("plugin.serialization") version "1.8.21"
+}
+
+allOpen {
+	annotation("javax.persistence.Entity")
+	annotation("javax.persistence.Embeddable")
+	annotation("javax.persistence.MappedSuperclass")
+	annotation("jakarta.persistence.Entity")
+	annotation("jakarta.persistence.Embeddable")
+	annotation("jakarta.persistence.MappedSuperclass")
 }
 
 group = "de.fraunhofer.iem"
@@ -23,7 +34,8 @@ repositories {
 }
 
 dependencies {
-	implementation("org.springframework.boot:spring-boot-starter-data-r2dbc")
+	implementation("org.springframework.boot:spring-boot-starter-data-jpa")
+	implementation("org.springframework.boot:spring-boot-starter-validation")
 	implementation("org.springframework.boot:spring-boot-starter-security")
 	implementation("org.springframework.boot:spring-boot-starter-webflux")
 	implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
@@ -32,12 +44,10 @@ dependencies {
 	implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactive")
 	implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
 	implementation("org.gitlab4j:gitlab4j-api:6.0.0-rc.1")
-    implementation("org.eclipse.jgit:org.eclipse.jgit:6.5.0.202303070854-r")
-    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
-	// for deployment replace with io.r2dbc:r2dbc-postgresql
-	implementation("io.r2dbc:r2dbc-h2")
+	implementation("org.eclipse.jgit:org.eclipse.jgit:6.5.0.202303070854-r")
+	implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
 	developmentOnly("org.springframework.boot:spring-boot-devtools")
-	runtimeOnly("com.h2database:h2")
+	runtimeOnly("org.postgresql:postgresql")
 	testImplementation("org.springframework.boot:spring-boot-starter-test")
 	testImplementation("org.springframework.security:spring-security-test")
 	testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4")
diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/DataProviderApplication.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/DataProviderApplication.kt
index 8953f77e2ec40f9f5a889971fce14cb5bf5a704d..4b84022572a922582ec86c2b03484617cb575209 100644
--- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/DataProviderApplication.kt
+++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/DataProviderApplication.kt
@@ -3,20 +3,6 @@ package de.fraunhofer.iem.dataprovider
 import org.springframework.boot.autoconfigure.SpringBootApplication
 import org.springframework.boot.runApplication
 
-// Taken from https://spring.io/guides/tutorials/spring-webflux-kotlin-rsocket/
-//@Configuration
-//class Config {
-//
-//	@Bean
-//	fun initializer(connectionFactory: ConnectionFactory): ConnectionFactoryInitializer {
-//		val initializer = ConnectionFactoryInitializer()
-//		initializer.setConnectionFactory(connectionFactory)
-//		val populator = CompositeDatabasePopulator()
-//		populator.addPopulators(ResourceDatabasePopulator(ClassPathResource("./sql/schema.sql")))
-//		initializer.setDatabasePopulator(populator)
-//		return initializer
-//	}
-//}
 
 @SpringBootApplication
 class DataProviderApplication
diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/gitlab/Repository.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/gitlab/Repository.kt
new file mode 100644
index 0000000000000000000000000000000000000000..fb409de39884cd3ec58f0a4f13d1c09b04502750
--- /dev/null
+++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/gitlab/Repository.kt
@@ -0,0 +1,43 @@
+package de.fraunhofer.iem.dataprovider.gitlab
+
+import de.fraunhofer.iem.dataprovider.toolResult.ToolRun
+import jakarta.persistence.*
+import java.util.*
+
+@Entity
+@Table(name = "repository")
+class Repository {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    @Column(name = "id", nullable = false)
+    var id: UUID? = null
+
+    @OrderBy("time_stamp DESC")
+    @OneToMany(mappedBy = "repository", cascade = [CascadeType.ALL], orphanRemoval = true)
+    var toolRuns: MutableList<ToolRun> = mutableListOf()
+
+    @Column(name = "name")
+    var name: String? = null
+
+    @Column(name = "repo_id")
+    var repoId: Long? = null
+
+    @Enumerated
+    @Column(name = "platform")
+    var platform: Platform? = null
+
+    @Column(name = "url")
+    var url: String? = null
+
+    fun addToolResults(toolRuns: Collection<ToolRun>) {
+        toolRuns.forEach {
+            this.addToolResult(it)
+        }
+    }
+
+    fun addToolResult(toolRun: ToolRun) {
+        toolRuns.add(toolRun)
+        toolRun.repository = this
+    }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/gitlab/RepositoryRepository.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/gitlab/RepositoryRepository.kt
new file mode 100644
index 0000000000000000000000000000000000000000..23fa8211136a14ab2b5cc0f127af6219a7f20326
--- /dev/null
+++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/gitlab/RepositoryRepository.kt
@@ -0,0 +1,13 @@
+package de.fraunhofer.iem.dataprovider.gitlab;
+
+import org.springframework.data.jpa.repository.JpaRepository
+import java.util.*
+
+interface RepositoryRepository : JpaRepository<Repository, UUID> {
+
+
+    fun existsByRepoIdAndPlatform(repoId: Long, platform: Platform): Boolean
+
+
+    fun findByRepoIdAndPlatform(repoId: Long, platform: Platform): Repository?
+}
\ No newline at end of file
diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/sarif/SarifExtensions.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/sarif/SarifExtensions.kt
index 46b4dba60980920739e7dab92ee1f9ce6dc93f73..bf90606284b159bcb5d01d5cb6cdec6868a3b90a 100644
--- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/sarif/SarifExtensions.kt
+++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/sarif/SarifExtensions.kt
@@ -1,62 +1,69 @@
 package de.fraunhofer.iem.dataprovider.sarif
 
 import de.fraunhofer.iem.dataprovider.toolResult.*
+import de.fraunhofer.iem.dataprovider.toolResult.Rule
+import de.fraunhofer.iem.dataprovider.toolResult.Tool
 import kotlinx.serialization.decodeFromString
 import kotlinx.serialization.json.Json
 import java.io.IOException
 import java.nio.file.Path
+import java.sql.Timestamp
+import java.time.Instant
+import java.util.*
 
-fun Rule.asDbObject(): RuleDb {
-    return RuleDb(
-        ruleId = this.id,
-        name = this.name,
-        shortDescription = this.shortDescription.text,
-        helpUri = this.helpUri
-    )
+fun de.fraunhofer.iem.dataprovider.sarif.Rule.asDbObject(): Rule {
+    val rule = Rule()
+    rule.sarifRuleId = this.id
+    rule.shortDescription = this.shortDescription.text
+    return rule
 }
 
-fun Tool.asDbObject(): ToolDb {
-    return ToolDb(
-        fullName = this.driver.fullName,
-        name = this.driver.name,
-        rules = this.driver.rules.map { it.asDbObject() },
-        downloadUri = this.driver.downloadUri,
-        guid = this.driver.guid,
-        informationUri = this.driver.informationUri,
-        language = this.driver.language,
-        organization = this.driver.organization,
-        semanticVersion = this.driver.semanticVersion,
-        version = this.driver.version,
-    )
-}
 
-fun Location.asDbObject(): ResultLocationDb {
-    return ResultLocationDb(
-        artifactLocationUri = this.physicalLocation.artifactLocation.uri,
-        endColumn = this.physicalLocation.region.endColumn,
-        endLine = this.physicalLocation.region.endLine,
-        startColumn = this.physicalLocation.region.startColumn,
-        startLine = this.physicalLocation.region.startLine,
-    )
+fun de.fraunhofer.iem.dataprovider.sarif.Tool.asDbObject(): Tool {
+    val tool = Tool()
+    tool.addRules(this.driver.rules.map { it.asDbObject() })
+    tool.name = this.driver.fullName
+    tool.version = this.driver.version
+    return tool
 }
 
-fun Result.asDbObject(): ToolResultDb {
-    return ToolResultDb(
-        level = this.level,
-        locations = this.locations.map { it.asDbObject() },
-        message = this.message.text,
-        ruleId = this.ruleId
-    )
+fun Location.asDbObject(): ResultLocation {
+    val resultLocation = ResultLocation()
+    resultLocation.endColumn = this.physicalLocation.region.endColumn
+    resultLocation.endLine = this.physicalLocation.region.endLine
+    resultLocation.startColumn = this.physicalLocation.region.startColumn
+    resultLocation.startLine = this.physicalLocation.region.startLine
+    resultLocation.artifactLocation = this.physicalLocation.artifactLocation.uri
+    return resultLocation
 }
 
-fun Sarif.asDbObject(): List<ToolResultsDb> {
+fun Result.asDbObject(rules: Collection<Rule>): ToolResult {
+    val toolResult = ToolResult()
+
+    when (this.level.uppercase(Locale.getDefault())) {
+        "WARNING" -> toolResult.level = Level.Warning
+        "ERROR" -> toolResult.level = Level.Error
+        "NOTE" -> toolResult.level = Level.Note
+        else -> toolResult.level = Level.None
+    }
+
+    rules
+        .filter { it.sarifRuleId == this.ruleId }
+        .forEach { it.addResultLocation(toolResult) }
+
+    toolResult.message = this.message.text
+    toolResult.addLocations(this.locations.map { it.asDbObject() })
 
+    return toolResult
+}
 
+fun Sarif.asDbObject(): List<ToolRun> {
     return this.runs.map { run ->
-        ToolResultsDb(
-            results = run.results.map { it.asDbObject() },
-            tool = run.tool.asDbObject(),
-        )
+        val tr = ToolRun()
+        tr.tool = run.tool.asDbObject()
+        tr.addToolResults(run.results.map { it.asDbObject(tr.tool!!.rules) })
+        tr.timeStamp = Timestamp.from(Instant.now())
+        tr
     }
 }
 
diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/TaskManager.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/TaskManager.kt
index 7a50404ec5fd737e36b5768918c6734683b7c0b1..c6488980bf5f51bf85325a6779cdebbe2c598b30 100644
--- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/TaskManager.kt
+++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/TaskManager.kt
@@ -1,8 +1,14 @@
 package de.fraunhofer.iem.dataprovider.taskManager
 
 import de.fraunhofer.iem.dataprovider.gitlab.GitConfiguration
+import de.fraunhofer.iem.dataprovider.gitlab.RepositoryRepository
 import de.fraunhofer.iem.dataprovider.logger.getLogger
-import de.fraunhofer.iem.dataprovider.taskManager.tasks.*
+import de.fraunhofer.iem.dataprovider.sarif.Sarif
+import de.fraunhofer.iem.dataprovider.taskManager.tasks.CloneGitTask
+import de.fraunhofer.iem.dataprovider.taskManager.tasks.DetektTask
+import de.fraunhofer.iem.dataprovider.taskManager.tasks.GetGitlabProjectTask
+import de.fraunhofer.iem.dataprovider.taskManager.tasks.GitRepository
+import de.fraunhofer.iem.dataprovider.toolResult.ToolRunRepository
 import jakarta.annotation.PreDestroy
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
@@ -21,17 +27,23 @@ sealed class TaskDone : Event() {
     abstract val taskId: UUID
 }
 
-class GitCloneDone(override val taskId: UUID, val outputDirectory: String) : TaskDone()
-class GetGitlabProjectDone(override val taskId: UUID, val gitProject: GitProject) : TaskDone()
+class GitCloneDone(override val taskId: UUID, val repoId: UUID, val outputDirectory: String) : TaskDone()
+class GetGitlabProjectDone(override val taskId: UUID, val repoId: UUID, val gitRepository: GitRepository) : TaskDone()
 class ProcessTaskDone(override val taskId: UUID, val message: String) : TaskDone()
 
+class DetektDone(override val taskId: UUID, val sarif: Sarif) : TaskDone()
+
 /**
  * The Task Manager takes tasks and distributes them to
  * underlying workers. Internally it uses a channel
  * to manage incoming tasks.
  */
 @Component
-class TaskManager(private val config: Config) {
+class TaskManager(
+    private val config: Config,
+    private val repositoryRepository: RepositoryRepository,
+    private val toolRunRepository: ToolRunRepository
+) {
 
     // The used default dispatcher is ok for CPU-bound workloads. However,
     // if they block for a long time it's better to use a custom thread
@@ -83,13 +95,22 @@ class TaskManager(private val config: Config) {
 
             when (event) {
                 is RepoChangedEvent -> {
-                    ioWorker.addTask(GetGitlabProjectTask(event.repoId, event.gitConfiguration, ::addEvent))
+                    ioWorker.addTask(
+                        GetGitlabProjectTask(
+                            event.repoId,
+                            event.gitConfiguration,
+                            ::addEvent,
+                            repositoryRepository
+                        )
+                    )
                 }
 
                 is GetGitlabProjectDone -> {
-                    worker.addTask(
+
+                    ioWorker.addTask(
                         CloneGitTask(
-                            event.gitProject,
+                            event.gitRepository,
+                            event.repoId,
                             ::addEvent,
                             config.gitProjectPath
                         )
@@ -99,8 +120,17 @@ class TaskManager(private val config: Config) {
                 is GitCloneDone -> {
                     // TODO: analysis results path should be unique or best not placed in the git folder
                     val analysisResultsPath = Paths.get(event.outputDirectory, "tool-results").toString()
-                    worker.addTask(OdcTask(event.outputDirectory, analysisResultsPath, ::addEvent))
-                    worker.addTask(DetektTask(event.outputDirectory, analysisResultsPath, ::addEvent))
+//                    worker.addTask(OdcTask(event.outputDirectory, analysisResultsPath, ::addEvent))
+                    worker.addTask(
+                        DetektTask(
+                            event.outputDirectory,
+                            analysisResultsPath,
+                            ::addEvent,
+                            event.repoId,
+                            repositoryRepository,
+                            toolRunRepository
+                        )
+                    )
                 }
 
 
diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/CloneGitTask.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/CloneGitTask.kt
index 3cd1753c6daa2a269936d10a7f4b0755c2fd440b..187f4aeba00b6632449585554e92bc1bcacd5960 100644
--- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/CloneGitTask.kt
+++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/CloneGitTask.kt
@@ -1,33 +1,45 @@
 package de.fraunhofer.iem.dataprovider.taskManager.tasks
 
+import de.fraunhofer.iem.dataprovider.gitlab.Platform
+import de.fraunhofer.iem.dataprovider.gitlab.Repository
 import de.fraunhofer.iem.dataprovider.taskManager.Event
 import de.fraunhofer.iem.dataprovider.taskManager.GitCloneDone
 import org.eclipse.jgit.api.Git
 import java.nio.file.Paths
+import java.util.*
 
 
-data class GitProject(val name: String, val uri: String)
+data class GitRepository(val name: String, val uri: String, val id: Long, val platform: Platform)
+
+fun GitRepository.toDbObject(): Repository {
+    val repo = Repository()
+    repo.name = this.name
+    repo.repoId = this.id
+    repo.platform = this.platform
+    return repo
+}
 
 class CloneGitTask(
-    private val gitProject: GitProject,
+    private val gitRepository: GitRepository,
+    private val repoDbId: UUID,
     override val responseChannel: suspend (task: Event) -> Unit,
     private val outputPath: String,
 ) : Task() {
 
     override suspend fun execute() {
 
-        val outputDirName = "${gitProject.name}-${taskID}"
+        val outputDirName = "${gitRepository.name}-${taskID}"
         val outputDirectory = Paths.get(outputPath, outputDirName)
-        logger.info("Cloning ${gitProject.name} into $outputDirectory")
+        logger.info("Cloning ${gitRepository.name} into $outputDirectory")
 
         val git: Git = Git.cloneRepository()
-            .setURI(gitProject.uri)
+            .setURI(gitRepository.uri)
             .setDirectory(outputDirectory.toFile())
             .call()
         git.close()
 
-        responseChannel(GitCloneDone(taskID, outputDirectory.toString()))
+        responseChannel(GitCloneDone(taskID, repoDbId, outputDirectory.toString()))
 
-        logger.info("Finished cloning ${gitProject.name}")
+        logger.info("Finished cloning ${gitRepository.name}")
     }
 }
\ No newline at end of file
diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/DetektTask.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/DetektTask.kt
index 852c39d2e2489a664fb707a75f30c457683917b0..18d46f475692b94bf787c7a36b30bafa89c504ea 100644
--- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/DetektTask.kt
+++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/DetektTask.kt
@@ -1,37 +1,32 @@
 package de.fraunhofer.iem.dataprovider.taskManager.tasks
 
-import de.fraunhofer.iem.dataprovider.sarif.getSarifFromFilePath
+
+import de.fraunhofer.iem.dataprovider.gitlab.RepositoryRepository
+import de.fraunhofer.iem.dataprovider.sarif.Sarif
+import de.fraunhofer.iem.dataprovider.taskManager.DetektDone
 import de.fraunhofer.iem.dataprovider.taskManager.Event
-import de.fraunhofer.iem.dataprovider.taskManager.ProcessTaskDone
+import de.fraunhofer.iem.dataprovider.toolResult.ToolRunRepository
 import org.springframework.core.io.ClassPathResource
 import org.springframework.core.io.Resource
+import java.nio.file.Path
 import java.nio.file.Paths
+import java.util.*
 
 
-class DetektTask(projectPath: String, outputPath: String, override val responseChannel: suspend (task: Event) -> Unit) :
-    ProcessTask() {
+class DetektTask(
+    projectPath: String, outputPath: String, override val responseChannel: suspend (task: Event) -> Unit,
+    override val repoId: UUID,
+    override val repository: RepositoryRepository,
+    override val toolRunRepository: ToolRunRepository
+) : SarifTask() {
 
     private val resource: Resource = ClassPathResource("scripts/detekt.sh")
     override val flags: Array<String> = arrayOf(resource.file.absolutePath, projectPath, outputPath)
-
     override val execPath: String = "/bin/sh"
+    override val resultPath: Path = Paths.get(outputPath, "detekt", "report.sarif")
 
-    private val resultPath = Paths.get(outputPath, "detekt", "report.sarif")
-    override suspend fun handleProcessReturn(p: Process) {
-        logger.info(resource.toString())
-
-        val returnMessage = "Odc finished with exit code ${p.exitValue()}"
-//        val output = String(p.inputStream.readAllBytes())
-//        logger.info("Process output $output")
-
-        val sarifResult = getSarifFromFilePath(resultPath)
-
-        sarifResult.runs.forEach {
-            it.results.forEach {
-                logger.info("run in sarif result message: ${it.message}")
-            }
-        }
 
-        responseChannel(ProcessTaskDone(taskID, returnMessage))
+    override suspend fun sendResult(sarif: Sarif) {
+        responseChannel(DetektDone(this.taskID, sarif))
     }
 }
\ No newline at end of file
diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/GitlabTask.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/GitlabTask.kt
index 7ac7a188107d48f3a1271a2e64b88b21590d1b61..3a465cbee48b05ad7ffa0f8b09f1539bdcb77a57 100644
--- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/GitlabTask.kt
+++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/GitlabTask.kt
@@ -1,6 +1,8 @@
 package de.fraunhofer.iem.dataprovider.taskManager.tasks
 
 import de.fraunhofer.iem.dataprovider.gitlab.GitConfiguration
+import de.fraunhofer.iem.dataprovider.gitlab.Platform
+import de.fraunhofer.iem.dataprovider.gitlab.RepositoryRepository
 import de.fraunhofer.iem.dataprovider.taskManager.Event
 import de.fraunhofer.iem.dataprovider.taskManager.GetGitlabProjectDone
 import org.gitlab4j.api.GitLabApi
@@ -8,7 +10,8 @@ import org.gitlab4j.api.GitLabApi
 
 class GetGitlabProjectTask(
     private val repoId: Long, private val gitlabConfiguration: GitConfiguration,
-    override val responseChannel: suspend (task: Event) -> Unit
+    override val responseChannel: suspend (task: Event) -> Unit,
+    private val repository: RepositoryRepository
 ) : Task() {
 
 
@@ -20,10 +23,17 @@ class GetGitlabProjectTask(
         val project = gitlabApi.projectApi.getProject(repoId)
         logger.info(project.toString())
         val projectUri = project.httpUrlToRepo
-        val gitProject = GitProject(project.path, projectUri)
 
-        responseChannel(GetGitlabProjectDone(taskID, gitProject))
+        val gitRepository = GitRepository(project.path, projectUri, repoId, Platform.OPEN_CODE)
+
+        var repo = repository.findByRepoIdAndPlatform(gitRepository.id, gitRepository.platform)
+        if (repo == null) {
+            logger.info("Repository ${gitRepository.id} ${gitRepository.platform} doesn't exist in database.")
+            repo = repository.save(gitRepository.toDbObject())
+        }
 
         logger.info("Retrieved project ${project.path} and url $projectUri")
+
+        responseChannel(GetGitlabProjectDone(taskID, repo.id!!, gitRepository))
     }
 }
diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/ProcessTask.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/ProcessTask.kt
index 46c6a45a2de05871de9a25cb286d004b50c29193..b9fe3691b0d954200e131faa1e01cdea0969c338 100644
--- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/ProcessTask.kt
+++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/ProcessTask.kt
@@ -1,10 +1,18 @@
 package de.fraunhofer.iem.dataprovider.taskManager.tasks
 
+import de.fraunhofer.iem.dataprovider.gitlab.RepositoryRepository
+import de.fraunhofer.iem.dataprovider.sarif.Sarif
+import de.fraunhofer.iem.dataprovider.sarif.asDbObject
+import de.fraunhofer.iem.dataprovider.sarif.getSarifFromFilePath
+import de.fraunhofer.iem.dataprovider.toolResult.ToolRunRepository
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
+import org.springframework.transaction.annotation.Transactional
 import java.nio.file.Files
+import java.nio.file.Path
 import java.nio.file.Paths
+import java.util.*
 
 sealed class ProcessTask : Task() {
     protected abstract val flags: Array<String>
@@ -33,4 +41,35 @@ sealed class ProcessTask : Task() {
     }
 
     abstract suspend fun handleProcessReturn(p: Process)
+
+}
+@Transactional
+sealed class SarifTask : ProcessTask() {
+    protected abstract val repoId: UUID
+    protected abstract val repository: RepositoryRepository
+    protected abstract val toolRunRepository: ToolRunRepository
+    protected abstract val resultPath: Path
+
+
+    override suspend fun handleProcessReturn(p: Process) {
+
+        // TODO: check process exit codes. Current problem, if an analysis
+        //  has findings it might return an exit code != 0 even tho the process finished correctly
+
+        logger.info("Handle Process return in $javaClass")
+        val sarifResult = getSarifFromFilePath(resultPath)
+        val repo = repository.findById(repoId)
+        val sarifDb = sarifResult.asDbObject()
+        val results = toolRunRepository.saveAll(sarifDb)
+
+        repo.ifPresent {
+//            Hibernate.initialize(it.toolRuns) // TODO: this fails because the session seems to be closed and add won't work because toolRuns are loaded lazily
+            it.addToolResults(results)
+            repository.save(it)
+        }
+        sendResult(sarifResult)
+    }
+
+    abstract suspend fun sendResult(sarif: Sarif)
+
 }
diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolRessults.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolRessults.kt
deleted file mode 100644
index 924f5a964644117e7ec22c66415b5f0e6012f87a..0000000000000000000000000000000000000000
--- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolRessults.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-package de.fraunhofer.iem.dataprovider.toolResult
-
-import org.springframework.data.annotation.Id
-import org.springframework.data.relational.core.mapping.Table
-
-@Table("TOOL_RESULTS")
-data class ToolResultsDb(
-    val results: List<ToolResultDb>,
-    val tool: ToolDb,
-    @Id var id: String? = null
-)
-
-data class ToolResultDb(
-    val level: String,
-    val locations: List<ResultLocationDb>,
-    val message: String,
-    val ruleId: String,
-)
-
-data class ResultLocationDb(
-    val artifactLocationUri: String,
-    val endColumn: Int,
-    val endLine: Int,
-    val startColumn: Int,
-    val startLine: Int,
-)
-
-data class ToolDb(
-    val fullName: String,
-    val name: String,
-    val rules: List<RuleDb>,
-    val downloadUri: String? = null,
-    val guid: String? = null,
-    val informationUri: String? = null,
-    val language: String? = null,
-    val organization: String? = null,
-    val semanticVersion: String? = null,
-    val version: String? = null,
-)
-
-data class RuleDb(
-    val ruleId: String,
-    val name: String,
-    val shortDescription: String,
-    val helpUri: String? = null,
-)
diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolResultsPersistenceService.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolResultsPersistenceService.kt
deleted file mode 100644
index 785e693aa9db9b2ec3a55d5276587d418fc061a2..0000000000000000000000000000000000000000
--- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolResultsPersistenceService.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package de.fraunhofer.iem.dataprovider.toolResult
-
-import de.fraunhofer.iem.dataprovider.sarif.Sarif
-import org.springframework.stereotype.Service
-
-@Service
-class ToolResultsPersistenceService(val toolResultsRepository: ToolResultsRepository) {
-
-    suspend fun save(sarif: Sarif) {
-//        toolResultsRepository.save(sarif.asDbObject())
-    }
-
-}
\ No newline at end of file
diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolResultsRepository.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolResultsRepository.kt
deleted file mode 100644
index 4d2c3d26edf4884df317ff081cfe9aee8d7c9b3e..0000000000000000000000000000000000000000
--- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolResultsRepository.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package de.fraunhofer.iem.dataprovider.toolResult
-
-import org.springframework.data.repository.kotlin.CoroutineCrudRepository
-
-interface ToolResultsRepository : CoroutineCrudRepository<ToolResultsDb, String> {
-}
\ No newline at end of file
diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolRun.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolRun.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ed71bb931b55d70852c927aecc7ea62325be1338
--- /dev/null
+++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolRun.kt
@@ -0,0 +1,218 @@
+package de.fraunhofer.iem.dataprovider.toolResult
+
+import de.fraunhofer.iem.dataprovider.gitlab.Repository
+import jakarta.persistence.*
+import org.hibernate.Hibernate
+import org.hibernate.annotations.JdbcTypeCode
+import org.hibernate.type.SqlTypes
+import java.sql.Timestamp
+import java.util.*
+
+enum class Level {
+    Warning, Error, Note, None
+}
+
+enum class Kind {
+    pass, open, informational, notApplicable
+}
+
+@Entity
+@Table(name = "tool_run")
+class ToolRun {
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    @Column(name = "id", nullable = false)
+    var id: UUID? = null
+
+
+    @OneToOne(orphanRemoval = true, cascade = [CascadeType.ALL])
+    @JoinColumn(name = "tool_id")
+    var tool: Tool? = null
+
+    @JdbcTypeCode(SqlTypes.TIMESTAMP)
+    @Column(name = "time_stamp")
+    var timeStamp: Timestamp? = null
+
+    @ManyToOne(fetch = FetchType.EAGER)
+    var repository: Repository? = null
+
+    @OneToMany(mappedBy = "toolRun", cascade = [CascadeType.ALL], orphanRemoval = true)
+    var toolResults: MutableSet<ToolResult> = mutableSetOf()
+
+    fun addToolResults(toolResults: Collection<ToolResult>) {
+        toolResults.forEach {
+            this.addToolResult(it)
+        }
+    }
+
+    fun addToolResult(toolResult: ToolResult) {
+        toolResults.add(toolResult)
+        toolResult.toolRun = this
+    }
+}
+
+@Entity
+@Table(name = "tool")
+class Tool {
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    @Column(name = "id", nullable = false)
+    var id: UUID? = null
+
+
+    @Column(name = "name", length = 500)
+    var name: String? = null
+
+    @Column(name = "version")
+    var version: String? = null
+
+    @OneToMany(mappedBy = "tool", cascade = [CascadeType.ALL], orphanRemoval = true)
+    var rules: MutableSet<Rule> = mutableSetOf()
+
+    fun addRules(rules: Collection<Rule>) {
+        rules.forEach {
+            this.addRule(it)
+        }
+    }
+
+    fun addRule(rule: Rule) {
+        rules.add(rule)
+        rule.tool = this
+    }
+}
+
+
+@Entity
+@Table(name = "tool_result")
+class ToolResult {
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    @Column(name = "id", nullable = false)
+    var id: UUID? = null
+
+    @ManyToOne
+    var rule: Rule? = null
+
+    @OneToMany(mappedBy = "toolResult", cascade = [CascadeType.ALL], orphanRemoval = true)
+    var resultLocations: MutableSet<ResultLocation> = mutableSetOf()
+
+    fun addLocations(locations: Collection<ResultLocation>) {
+        locations.forEach {
+            this.addLocation(it)
+        }
+    }
+
+    fun addLocation(location: ResultLocation) {
+        resultLocations.add(location)
+        location.toolResult = this
+    }
+
+    @Enumerated
+    @Column(name = "level")
+    var level: Level? = null
+
+    @Enumerated
+    @Column(name = "kind")
+    var kind: Kind? = null
+
+    @Column(name = "message", columnDefinition = "TEXT")
+    var message: String? = null
+
+    @ManyToOne
+    var toolRun: ToolRun? = null
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || Hibernate.getClass(this) != Hibernate.getClass(other)) return false
+        other as ToolResult
+
+        return id != null && id == other.id
+    }
+
+    override fun hashCode(): Int = javaClass.hashCode()
+}
+
+@Entity
+@Table(name = "result_location")
+class ResultLocation {
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    @Column(name = "id", nullable = false)
+    var id: UUID? = null
+
+    @ManyToOne
+    var toolResult: ToolResult? = null
+
+    @Column(name = "artifact_location", columnDefinition = "TEXT")
+    var artifactLocation: String? = null
+
+    @Column(name = "end_column")
+    var endColumn: Int? = null
+
+    @Column(name = "end_line")
+    var endLine: Int? = null
+
+    @Column(name = "start_column")
+    var startColumn: Int? = null
+
+    @Column(name = "start_line")
+    var startLine: Int? = null
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || Hibernate.getClass(this) != Hibernate.getClass(other)) return false
+        other as ResultLocation
+
+        return id != null && id == other.id
+    }
+
+    override fun hashCode(): Int = javaClass.hashCode()
+}
+
+@Entity
+@Table(name = "rule")
+class Rule {
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    @Column(name = "id", nullable = false)
+    var id: UUID? = null
+
+
+    @Column(name = "sarif_rule_id")
+    var sarifRuleId: String? = null
+
+    @Column(name = "short_description", columnDefinition = "TEXT")
+    var shortDescription: String? = null
+
+    @OneToMany(mappedBy = "rule", cascade = [CascadeType.ALL], orphanRemoval = true)
+    var toolResults: MutableSet<ToolResult> = mutableSetOf()
+
+    fun addResultLocations(toolResults: Collection<ToolResult>) {
+        toolResults.forEach {
+            this.addResultLocation(it)
+        }
+    }
+
+    fun addResultLocation(toolResult: ToolResult) {
+        toolResults.add(toolResult)
+        toolResult.rule = this
+    }
+
+    @ElementCollection
+    @CollectionTable(name = "rule_messages", joinColumns = [JoinColumn(name = "owner_id")])
+    @Column(name = "message", length = 1000)
+    var messages: MutableList<String> = mutableListOf()
+
+    @ManyToOne
+    var tool: Tool? = null
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || Hibernate.getClass(this) != Hibernate.getClass(other)) return false
+        other as Rule
+
+        return id != null && id == other.id
+    }
+
+    override fun hashCode(): Int = javaClass.hashCode()
+}
\ No newline at end of file
diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolRunRepository.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolRunRepository.kt
new file mode 100644
index 0000000000000000000000000000000000000000..4a907621bbee277cc305a2a2751e59a93c5a8dac
--- /dev/null
+++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/toolResult/ToolRunRepository.kt
@@ -0,0 +1,6 @@
+package de.fraunhofer.iem.dataprovider.toolResult
+
+import org.springframework.data.jpa.repository.JpaRepository
+import java.util.*
+
+interface ToolRunRepository : JpaRepository<ToolRun, UUID>
\ No newline at end of file
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 10edc1c6cfd14c290796fb23bd18e44afdc98647..2601e3563a0a2f2763ffab9c06935584370e73a5 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -3,8 +3,16 @@ OPENCODE_GITLAB_URL=https://gitlab.opencode.de/
 OPENCODE_GITLAB_TOKEN=${OPENCODE_TOKEN}
 GITHUB_URL=https://gitlab.opencode.de/
 GITHUB_TOKEN=${GITHUB_TOKEN}
-spring.r2dbc.url=${R2DBC_URL}
-
 GIT_PROJECT_PATH=${GIT_PROJECT_PATH}
 ODC_OUTPUT_PATH=${ODC_OUTPUT_PATH}
+spring.datasource.url=jdbc:postgresql://localhost:5432/dataprovider
+spring.datasource.username=sa
+spring.datasource.password=password
+spring.jpa.generate-ddl=true
+spring.jpa.show-sql=true
+spring.jpa.hibernate.ddl-auto=update
+spring.jpa.properties.hibernate.format_sql=true
+spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
+spring.jpa.properties.hibernate.bytecode.use_reflection_optimizer=false
+spring.jpa.open-in-view=false
 #logging.level.root=DEBUG
\ No newline at end of file
diff --git a/src/main/resources/db/docker-compose.yml b/src/main/resources/db/docker-compose.yml
new file mode 100644
index 0000000000000000000000000000000000000000..977879021d81bc7f7e64ad0a58252fbcc9c5b2fa
--- /dev/null
+++ b/src/main/resources/db/docker-compose.yml
@@ -0,0 +1,11 @@
+version: '3.7'
+services:
+  db:
+    image: postgres:latest
+    restart: always
+    environment:
+      - POSTGRES_USER=sa
+      - POSTGRES_PASSWORD=password
+      - POSTGRES_DB=dataprovider
+    ports:
+      - 5432:5432
\ No newline at end of file