Skip to content

Commit 30616c1

Browse files
authored
Merge pull request #101 from digma-ai/feature/db-profiles
Feature/db profiles
2 parents b49ec6b + 91b2851 commit 30616c1

15 files changed

+173
-202
lines changed

docker-compose.yml

Lines changed: 16 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -17,58 +17,24 @@ services:
1717
timeout: 5s
1818
retries: 5
1919

20-
app:
21-
build: ./
22-
environment:
23-
OTEL_SERVICE_NAME: "PetClinic"
24-
OTEL_EXPORTER_OTLP_ENDPOINT: "http://host.docker.internal:5050"
25-
# Logs are disabled by default
26-
OTEL_LOGS_EXPORTER: "otlp"
27-
# Digma entries
28-
CODE_PACKAGE_PREFIXES: "org.springframework.samples.petclinic"
29-
DEPLOYMENT_ENV: "SAMPLE_ENV"
30-
SPRING_PROFILES_ACTIVE: postgres
31-
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/petclinic
32-
SPRING_DATASOURCE_USERNAME: postgres
33-
SPRING_DATASOURCE_PASSWORD: postgres
34-
healthcheck:
35-
test: [ "CMD", "curl", "-f", "http://localhost:9753/" ]
36-
interval: 20s
37-
timeout: 10s
38-
retries: 4
39-
start_period: 5s
20+
mysql:
21+
image: mysql:8.0
22+
container_name: petclinic-mysql
4023
ports:
41-
- "9753:9753"
42-
entrypoint: java -jar -javaagent:/opentelemetry-javaagent.jar -Dotel.javaagent.extensions=/digma-otel-agent-extension.jar app.jar
43-
depends_on:
44-
postgres:
45-
condition: service_healthy
46-
extra_hosts:
47-
- "host.docker.internal:host-gateway"
48-
49-
tester:
50-
build: ./
24+
- "3306:3306"
5125
environment:
52-
OTEL_SERVICE_NAME: "PetClinicTester"
53-
OTEL_EXPORTER_OTLP_ENDPOINT: "http://host.docker.internal:5050"
54-
# Logs are disabled by default
55-
OTEL_LOGS_EXPORTER: "otlp"
56-
PETSHOP_URL: "http://app:9753"
57-
# Digma entries
58-
CODE_PACKAGE_PREFIXES: "org.springframework.samples.petclinic"
59-
DEPLOYMENT_ENV: "SAMPLE_ENV"
60-
SPRING_PROFILES_ACTIVE: postgres
61-
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/petclinic
62-
SPRING_DATASOURCE_USERNAME: postgres
63-
SPRING_DATASOURCE_PASSWORD: postgres
64-
entrypoint: java -cp app.jar -Dloader.main=petclinic.client.ClientTester org.springframework.boot.loader.PropertiesLauncher
65-
depends_on:
66-
app:
67-
condition: service_healthy
68-
postgres:
69-
condition: service_healthy
70-
extra_hosts:
71-
- "host.docker.internal:host-gateway"
26+
MYSQL_ROOT_PASSWORD: root
27+
MYSQL_DATABASE: patient_records
28+
MYSQL_USER: patient_user
29+
MYSQL_PASSWORD: patient_pass
30+
volumes:
31+
- mysql_data:/var/lib/mysql
32+
healthcheck:
33+
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
34+
interval: 10s
35+
timeout: 5s
36+
retries: 5
7237

7338
volumes:
7439
postgres_data:
40+
mysql_data:

src/main/java/org/springframework/samples/petclinic/clinicactivity/ClinicActivityController.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.springframework.beans.factory.InitializingBean;
1111
import org.springframework.beans.factory.annotation.Autowired;
1212
import org.springframework.beans.factory.annotation.Qualifier;
13+
import org.springframework.context.annotation.Profile;
1314
import org.springframework.http.HttpStatus;
1415
import org.springframework.http.ResponseEntity;
1516
import org.springframework.jdbc.core.JdbcTemplate;
@@ -22,6 +23,7 @@
2223

2324
@RestController
2425
@RequestMapping("/api/clinic-activity")
26+
@Profile({"postgres", "mysql"})
2527
public class ClinicActivityController implements InitializingBean {
2628

2729
private static final Logger logger = LoggerFactory.getLogger(ClinicActivityController.class);

src/main/java/org/springframework/samples/petclinic/clinicactivity/ClinicActivityDataService.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import org.slf4j.LoggerFactory;
66
import org.springframework.beans.factory.annotation.Autowired;
77
import org.springframework.beans.factory.annotation.Qualifier;
8+
import org.springframework.context.annotation.Profile;
89
import org.springframework.samples.petclinic.model.ClinicActivityLog;
910
import org.springframework.stereotype.Service;
1011
import org.springframework.transaction.PlatformTransactionManager;
@@ -35,6 +36,7 @@
3536
import java.util.HashMap;
3637

3738
@Service
39+
@Profile({"postgres", "mysql"})
3840
public class ClinicActivityDataService {
3941

4042
private static final Logger logger = LoggerFactory.getLogger(ClinicActivityDataService.class);

src/main/java/org/springframework/samples/petclinic/config/DatabaseConfig.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77
import org.springframework.context.annotation.Bean;
88
import org.springframework.context.annotation.Configuration;
99
import org.springframework.context.annotation.Primary;
10+
import org.springframework.context.annotation.Profile;
1011
import org.springframework.jdbc.core.JdbcTemplate;
1112

1213
import javax.sql.DataSource;
1314

1415
@Configuration
16+
@Profile({"postgres", "mysql"})
1517
public class DatabaseConfig {
1618

1719
@Primary
@@ -23,6 +25,7 @@ public DataSource postgresDataSource() {
2325

2426
@Bean(name = "mysqlDataSource")
2527
@ConfigurationProperties("app.datasource.mysql")
28+
@Profile("mysql")
2629
public DataSource mysqlDataSource() {
2730
return DataSourceBuilder.create().type(HikariDataSource.class).build();
2831
}
@@ -34,6 +37,7 @@ public JdbcTemplate postgresJdbcTemplate(@Qualifier("postgresDataSource") DataSo
3437
}
3538

3639
@Bean(name = "mysqlJdbcTemplate")
40+
@Profile("mysql")
3741
public JdbcTemplate mysqlJdbcTemplate(@Qualifier("mysqlDataSource") DataSource dataSource) {
3842
return new JdbcTemplate(dataSource);
3943
}

src/main/java/org/springframework/samples/petclinic/config/MySqlConfig.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
55
import org.springframework.context.annotation.Bean;
66
import org.springframework.context.annotation.Configuration;
7+
import org.springframework.context.annotation.Profile;
78
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
89
import org.springframework.orm.jpa.JpaTransactionManager;
910
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
@@ -22,6 +23,7 @@
2223
transactionManagerRef = "mysqlTransactionManager",
2324
basePackages = {"org.springframework.samples.petclinic.patientrecords"}
2425
)
26+
@Profile("mysql")
2527
public class MySqlConfig {
2628

2729
@Bean(name = "mysqlEntityManagerFactory")

src/main/java/org/springframework/samples/petclinic/config/PostgresConfig.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import org.springframework.context.annotation.Bean;
66
import org.springframework.context.annotation.Configuration;
77
import org.springframework.context.annotation.Primary;
8+
import org.springframework.context.annotation.Profile;
89
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
910
import org.springframework.orm.jpa.JpaTransactionManager;
1011
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
@@ -27,6 +28,7 @@
2728
"org.springframework.samples.petclinic.clinicactivity"
2829
}
2930
)
31+
@Profile({"postgres", "mysql"})
3032
public class PostgresConfig {
3133

3234
@Primary

src/main/java/org/springframework/samples/petclinic/patientrecords/PatientRecordController.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.springframework.beans.factory.InitializingBean;
1111
import org.springframework.beans.factory.annotation.Autowired;
1212
import org.springframework.beans.factory.annotation.Qualifier;
13+
import org.springframework.context.annotation.Profile;
1314
import org.springframework.http.HttpStatus;
1415
import org.springframework.http.ResponseEntity;
1516
import org.springframework.jdbc.core.JdbcTemplate;
@@ -21,6 +22,7 @@
2122

2223
@RestController
2324
@RequestMapping("/api/patient-records")
25+
@Profile("mysql")
2426
public class PatientRecordController implements InitializingBean {
2527

2628
private static final Logger logger = LoggerFactory.getLogger(PatientRecordController.class);

src/main/java/org/springframework/samples/petclinic/patientrecords/PatientRecordDataService.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import org.slf4j.LoggerFactory;
66
import org.springframework.beans.factory.annotation.Autowired;
77
import org.springframework.beans.factory.annotation.Qualifier;
8+
import org.springframework.beans.factory.annotation.Value;
9+
import org.springframework.context.annotation.Profile;
810
import org.springframework.samples.petclinic.model.PatientRecord;
911
import org.springframework.stereotype.Service;
1012
import org.springframework.transaction.PlatformTransactionManager;
@@ -14,6 +16,7 @@
1416
import org.springframework.transaction.support.DefaultTransactionDefinition;
1517
import org.springframework.jdbc.core.JdbcTemplate;
1618

19+
import jakarta.annotation.PostConstruct;
1720
import java.time.LocalDateTime;
1821
import java.time.ZoneId;
1922
import java.util.ArrayList;
@@ -23,6 +26,7 @@
2326
import java.util.concurrent.TimeUnit;
2427

2528
@Service
29+
@Profile("mysql")
2630
public class PatientRecordDataService {
2731

2832
private static final Logger logger = LoggerFactory.getLogger(PatientRecordDataService.class);
@@ -32,6 +36,9 @@ public class PatientRecordDataService {
3236
private final JdbcTemplate mysqlJdbcTemplate;
3337
private final PlatformTransactionManager mysqlTransactionManager;
3438

39+
@Value("${app.patient.records.auto-init:false}")
40+
private boolean autoInitEnabled;
41+
3542
// List of veterinary treatment types
3643
private static final List<String> TREATMENT_TYPES = List.of(
3744
"Annual Wellness Exam", "Vaccination (Rabies)", "Vaccination (DHPP)", "Vaccination (FVRCP)",
@@ -53,6 +60,35 @@ public PatientRecordDataService(PatientRecordRepository repository,
5360
this.mysqlTransactionManager = mysqlTransactionManager;
5461
}
5562

63+
@PostConstruct
64+
public void initializeSchema() {
65+
if (autoInitEnabled) {
66+
logger.info("Auto-initialization enabled - creating patient_records table if it doesn't exist");
67+
try {
68+
String createTableSql =
69+
"CREATE TABLE IF NOT EXISTS patient_records (" +
70+
"id INT AUTO_INCREMENT PRIMARY KEY," +
71+
"treatment_type VARCHAR(255) NOT NULL," +
72+
"patient_weight INT NOT NULL," +
73+
"visit_date TIMESTAMP NOT NULL," +
74+
"treatment_completed BOOLEAN NOT NULL," +
75+
"medical_notes TEXT," +
76+
"INDEX idx_treatment_type (treatment_type)," +
77+
"INDEX idx_visit_date (visit_date)," +
78+
"INDEX idx_treatment_completed (treatment_completed)" +
79+
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci";
80+
81+
mysqlJdbcTemplate.execute(createTableSql);
82+
logger.info("Patient records table initialized successfully");
83+
} catch (Exception e) {
84+
logger.error("Failed to initialize patient records schema", e);
85+
throw new RuntimeException("Failed to initialize patient records schema: " + e.getMessage(), e);
86+
}
87+
} else {
88+
logger.info("Auto-initialization disabled - patient_records table must be created manually");
89+
}
90+
}
91+
5692
@Transactional("mysqlTransactionManager")
5793
public void cleanupPatientRecords() {
5894
logger.info("Received request to clean up all patient records.");

src/main/java/org/springframework/samples/petclinic/patientrecords/PatientRecordUiController.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import org.springframework.beans.factory.annotation.Autowired;
44
import org.springframework.beans.factory.annotation.Qualifier;
5+
import org.springframework.context.annotation.Profile;
56
import org.springframework.jdbc.core.JdbcTemplate;
67
import org.springframework.stereotype.Controller;
78
import org.springframework.ui.Model;
@@ -10,6 +11,7 @@
1011

1112
@Controller
1213
@RequestMapping("/patient-records")
14+
@Profile("mysql")
1315
public class PatientRecordUiController {
1416

1517
private final JdbcTemplate mysqlJdbcTemplate;
Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,55 @@
1-
# database init, supports mysql too
2-
database=mysql
3-
spring.datasource.url=${MYSQL_URL:jdbc:mysql://localhost/petclinic}
4-
spring.datasource.username=${MYSQL_USER:petclinic}
5-
spring.datasource.password=${MYSQL_PASS:petclinic}
6-
# SQL is written to be idempotent so this is safe
1+
database=postgres
2+
3+
# Multi-Database Configuration - PostgreSQL + MySQL
4+
# This is the ONLY profile that enables both databases
5+
# PostgreSQL: Main petclinic data (owners, pets, visits, vets) + clinic activity
6+
# MySQL: Patient records ONLY (separate medical records system)
7+
8+
# Primary DataSource - PostgreSQL (Main PetClinic Data + Clinic Activity)
9+
app.datasource.postgres.jdbcUrl=jdbc:postgresql://localhost:5442/petclinic
10+
app.datasource.postgres.username=${POSTGRES_USER:postgres}
11+
app.datasource.postgres.password=${POSTGRES_PASS:postgres}
12+
app.datasource.postgres.driverClassName=org.postgresql.Driver
13+
app.datasource.postgres.maximumPoolSize=20
14+
app.datasource.postgres.minimumIdle=5
15+
app.datasource.postgres.connectionTimeout=20000
16+
app.datasource.postgres.idleTimeout=300000
17+
app.datasource.postgres.maxLifetime=1200000
18+
19+
# Secondary DataSource - MySQL (Patient Records ONLY)
20+
app.datasource.mysql.jdbcUrl=${MYSQL_URL:jdbc:mysql://localhost:3306/patient_records}
21+
app.datasource.mysql.username=${MYSQL_USER:patient_user}
22+
app.datasource.mysql.password=${MYSQL_PASS:patient_pass}
23+
app.datasource.mysql.driverClassName=com.mysql.cj.jdbc.Driver
24+
app.datasource.mysql.maximumPoolSize=10
25+
app.datasource.mysql.minimumIdle=2
26+
app.datasource.mysql.connectionTimeout=20000
27+
app.datasource.mysql.idleTimeout=300000
28+
app.datasource.mysql.maxLifetime=1200000
29+
30+
# Schema and Data Initialization - PostgreSQL (Main PetClinic)
731
spring.sql.init.mode=always
32+
spring.sql.init.continue-on-error=false
33+
spring.sql.init.separator=;
34+
35+
# MySQL Patient Records - AUTO INITIALIZATION ENABLED
36+
# Patient records schema will be created automatically on startup
37+
app.patient.records.auto-init=true
38+
app.patient.records.enabled=${PATIENT_RECORDS_ENABLED:true}
39+
40+
# JPA/Hibernate Settings - PostgreSQL (Main PetClinic Data)
41+
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
42+
spring.jpa.hibernate.ddl-auto=none
43+
spring.jpa.show-sql=false
44+
spring.jpa.properties.hibernate.format_sql=false
45+
spring.jpa.properties.hibernate.jdbc.batch_size=25
46+
spring.jpa.properties.hibernate.order_inserts=true
47+
spring.jpa.properties.hibernate.order_updates=true
48+
spring.jpa.properties.hibernate.jdbc.batch_versioned_data=true
49+
50+
# Application Architecture Settings
51+
app.database.primary=postgres
52+
app.database.secondary=mysql
53+
app.database.mode=multi_database
54+
app.database.primary.purpose=petclinic_data_and_clinic_activity
55+
app.database.secondary.purpose=patient_records_only

0 commit comments

Comments
 (0)