1. Project setup#

The thinxar/clinic reference service uses plain Groovy Gradle with dependency versions hoisted into a sibling deps.gradle. This keeps version bumps to one file and avoids drift between the Palmyra core artifact and its extensions.

Prerequisites#

  • Java 21+
  • Gradle 8.x
  • MariaDB / MySQL / PostgreSQL / Oracle / DB2

Layout#

service/
  build.gradle
  deps.gradle        ← versions + dependency bundles
  settings.gradle
  src/main/java/...  com.your.app.AppMain, controller/, entity/, model/, pojo/, handler/, ...
  src/main/resources/application.yaml

deps.gradle#

Centralise versions and pre-compose reusable dependency bundles:

def versions = [
    spring_boot : '3.3.4',
    palmyra     : '1.4.4',
    mariadb     : '3.4.0',
    lombok      : '1.18.34'
]

def palmyra = [
    spring : "com.palmyralabs.palmyra:palmyra-spring:${versions.palmyra}",
    extn   : [
        pwdmgmt : "com.palmyralabs.palmyra.extn:palmyra-dbpwd-mgmt:${versions.palmyra}",
        aclmgmt : "com.palmyralabs.palmyra.extn:palmyra-dbacl-mgmt:${versions.palmyra}"
    ]
]

ext {
    versions = versions
    palmyra  = palmyra
}

build.gradle#

plugins {
    id 'org.springframework.boot'          version '3.3.4'
    id 'io.spring.dependency-management'   version '1.1.6'
    id 'java'
    id 'application'
}

apply from: 'deps.gradle'

java { toolchain { languageVersion = JavaLanguageVersion.of(21) } }

application { mainClass = 'com.your.app.AppMain' }

repositories {
    mavenLocal()
    mavenCentral()
    maven { url 'https://repo.palmyralabs.com/releases' }
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-security'

    implementation palmyra.spring
    implementation palmyra.extn.pwdmgmt
    implementation palmyra.extn.aclmgmt

    compileOnly     "org.projectlombok:lombok:${versions.lombok}"
    annotationProcessor "org.projectlombok:lombok:${versions.lombok}"

    runtimeOnly    "org.mariadb.jdbc:mariadb-java-client:${versions.mariadb}"
}

settings.gradle#

rootProject.name = 'your-app'

SpringBoot entrypoint#

Import PalmyraSpringConfiguration and enable JPA repositories / entity scanning alongside it. Palmyra and JPA coexist — JPA handles entities you own end-to-end; Palmyra publishes a thin CRUD API over the same schema.

package com.your.app;

@SpringBootApplication
@Import(PalmyraSpringConfiguration.class)
@EnableJpaRepositories
@EntityScan
public class AppMain {
    public static void main(String[] args) {
        SpringApplication.run(AppMain.class, args);
    }
}

application.yaml#

spring:
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: validate   # never 'update' in prod — migrations own the schema
  datasource:
    url: jdbc:mariadb://localhost:3306/clinic
    username: dbuser
    password: dbuser
    driverClassName: org.mariadb.jdbc.Driver

server:
  port: 8080
  servlet:
    context-path: /api     # every handler mounts under /api/...

Run ./gradlew bootRun. The app boots on :8080 with the /api context path; the next steps add the entities, models, and handlers that make it answer actual requests.