Database Configuration & Oracle Wallet
Overview: This document explains how database connectivity is configured, including datasource properties, Oracle Wallet integration, and connection pooling strategies for different environments.
Table of Contents
- Datasource Configuration Overview
- Configuration by Environment
- Oracle Wallet Integration
- Connection Pooling (HikariCP)
- Database Selection
- Troubleshooting
Datasource Configuration Overview
Datasource in Spring Boot
The datasource is the bridge between Java application and database:
Application Code
↓
DataSource Bean (from spring.datasource.*)
↓
JDBC Driver (Oracle, H2, PostgreSQL, etc.)
↓
Physical Database
Configuration Sources
| Source | Configured In | Examples |
|---|---|---|
| Base | application.yml |
Placeholder values, driver class |
| Profile Override | application-{profile}.yml |
H2 for test, Oracle for prod |
| Environment Variables | .env or fly.toml secrets |
Actual DB credentials |
Configuration by Environment
Development (Local)
Configuration Chain:
application.yml (base: ${DB_URL} placeholder)
↓
application-{no-profile}.yml (none - uses base)
↓
Environment Variables from .env (actual values)
application.yml:
spring:
datasource:
url: ${DB_URL} # Placeholder
username: ${DB_USER} # Placeholder
password: ${DB_PASS} # Placeholder
driver-class-name: oracle.jdbc.OracleDriver # Default driverFrom .env:
# Oracle with Wallet
DB_URL=jdbc:oracle:thin:@service_name
DB_USER=admin
DB_PASS=password
TNS_ADMIN=/path/to/walletResult at Runtime:
datasource.url = "jdbc:oracle:thin:@service_name"
datasource.username = "admin"
datasource.password = "password"
Testing
Configuration Chain:
application.yml (base)
↓
application-test.yml (complete override)
↓
Environment Variables (not needed - H2 hardcoded)
application-test.yml:
spring:
datasource:
url: jdbc:h2:mem:ssp;MODE=Oracle;DATABASE_TO_UPPER=true;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
driver-class-name: org.h2.Driver
username: sa
password: # EmptyWhy H2? - ✅ In-memory (no setup needed) -
✅ Oracle-compatible (MODE=Oracle) - ✅ Fresh
database per test run - ✅ No credentials needed - ✅ Fast (no
network latency)
Production (Fly.io)
Configuration Chain:
application.yml (base)
↓
application-prod.yml (overrides)
↓
fly.toml (env section + secrets)
application-prod.yml:
spring:
datasource:
url: ${DB_URL} # From fly secrets
username: ${DB_USER} # From fly secrets
password: ${DB_PASS} # From fly secrets
driver-class-name: oracle.jdbc.OracleDriver
hikari:
connection-timeout: 30000
validation-timeout: 5000
maximum-pool-size: 5
minimum-idle: 2
max-lifetime: 240000
idle-timeout: 180000
connection-test-query: SELECT 1 FROM DUAL
leak-detection-threshold: 60000
keepalive-time: 120000fly.toml (public):
[env]
SPRING_PROFILES_ACTIVE = "prod"fly secrets (encrypted vault):
DB_URL=jdbc:oracle:thin:@prod_db_service
DB_USER=admin
DB_PASS=***
TNS_ADMIN=/etc/oracle_walletResult at Runtime:
datasource.url = "jdbc:oracle:thin:@prod_db_service"
datasource.username = "admin"
datasource.password = "***"
datasource.hikari.maximum-pool-size = 5 # Limited for Fly.io 1GB RAM
Oracle Wallet Integration
What is Oracle Wallet?
Oracle Wallet
↓
Encrypted file directory containing:
- sqlnet.ora (connection parameters)
- tnsnames.ora (service names)
- cwallet.sso (encrypted credentials)
↓
Enables secure credential-less connections to Oracle Autonomous DB
How Wallet-Based Connection Works
Application
↓
Sets: TNS_ADMIN=/path/to/wallet
↓
JDBC Driver reads wallet files
↓
Driver finds service name in tnsnames.ora
↓
Driver retrieves encrypted credentials from cwallet.sso
↓
Driver connects to Oracle Autonomous DB (no plaintext password in code!)
Configuration for Wallet
Environment Variable
In .env (local dev):
TNS_ADMIN=/Users/yourname/Downloads/Wallet_mydbIn fly.toml secrets (production):
fly secrets set TNS_ADMIN="/etc/oracle_wallet"JDBC URL Format
Without Wallet (local Oracle):
DB_URL=jdbc:oracle:thin:@localhost:1521/xeWith Wallet (Oracle Autonomous DB):
DB_URL=jdbc:oracle:thin:@mydb_high # Service name from tnsnames.oraOr with explicit wallet path (less common):
DB_URL=jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=tcps)(HOST=...)))(CONNECT_DATA=(SERVICE_NAME=...)))Wallet File Structure
Wallet_mydb/
├── sqlnet.ora # Connection params
├── tnsnames.ora # Service name mappings
├── cwallet.sso # Encrypted credentials
├── ewallet.p12 # PKCS12 keystore (if using mTLS)
├── keystore.jks # Java keystore (if using mTLS)
└── truststore.jks # Java truststore
Setting Up Wallet in Fly.io
1. Download Wallet from Oracle Cloud Console
# Download from Oracle Autonomous Database connection dialog
# Extract to local machine
unzip Wallet_mydb.zip -d /path/to/wallet2. Create Secret in Fly
fly secrets set TNS_ADMIN="/etc/oracle_wallet"
fly secrets set ORACLE_WALLET_PASSWORD="wallet_password" # If password-protected3. Copy Wallet into Docker Image
Dockerfile:
FROM maven:3.9-eclipse-temurin-11 as builder
WORKDIR /app
COPY . .
RUN mvn clean package -DskipTests
FROM eclipse-temurin:11-jre-slim
WORKDIR /app
COPY --from=builder /app/target/inventory-service-*.jar app.jar
# Copy Oracle Wallet
COPY oracle_wallet /etc/oracle_wallet
# Set TNS_ADMIN environment variable
ENV TNS_ADMIN=/etc/oracle_wallet
ENTRYPOINT ["java", "-jar", "app.jar"]4. Deploy
fly deployRuntime: - Fly injects TNS_ADMIN secret - Dockerfile copies wallet to /etc/oracle_wallet - JDBC driver finds tnsnames.ora and cwallet.sso - Connection succeeds without plaintext credentials
Connection Pooling (HikariCP)
What is Connection Pooling?
Without Pool (slow):
Request → Create connection → Execute query → Close connection
(Each request = ~100-500ms overhead)
With Pool (fast):
Request → Get from pool → Execute query → Return to pool
(Each request = ~0-5ms overhead, reuse connections)
HikariCP Configuration
In application-prod.yml:
spring:
datasource:
hikari:
connection-timeout: 30000 # Max wait for available connection
validation-timeout: 5000 # Max wait for validation query
maximum-pool-size: 5 # Max idle connections to maintain
minimum-idle: 2 # Min idle connections to maintain
max-lifetime: 240000 # Max connection age (4 minutes)
idle-timeout: 180000 # Idle before eviction (3 minutes)
connection-test-query: SELECT 1 FROM DUAL # Keep-alive test
leak-detection-threshold: 60000 # Warn if connection not returned after 60s
keepalive-time: 120000 # Ping idle connections every 2 minutesWhy These Settings for Fly.io?
| Setting | Value | Reason |
|---|---|---|
| maximum-pool-size | 5 | Fly.io has limited RAM (512MB-1GB); 5 concurrent connections is reasonable |
| minimum-idle | 2 | Maintain ready connections without wasting memory |
| max-lifetime | 240000 (4 min) | Oracle ADB closes idle connections at ~5 min; refresh before that |
| idle-timeout | 180000 (3 min) | Evict truly idle connections; keep pool lean |
| connection-test-query | SELECT 1 FROM DUAL |
Test query valid for Oracle (H2 would use
SELECT 1) |
| keepalive-time | 120000 (2 min) | Prevent connection timeout from idle; keep alive with pings |
Common Pool Issues
Issue: “ORA-17008: Closed connection”
Cause: Oracle ADB closes idle connections; pool doesn’t refresh before timeout
Solution: Configure max-lifetime < Oracle timeout
max-lifetime: 240000 # 4 minutes (less than Oracle's 5-minute timeout)
keepalive-time: 120000 # Ping every 2 minutesIssue: “Connection pool exhausted”
Cause: All max-pool-size connections in use; no available for new requests
Debug:
leak-detection-threshold: 60000 # Log if connection held > 60 secondsSolution: 1. Reduce pool size if it’s leaking connections 2. Increase max-pool-size if application needs more concurrency 3. Monitor actual usage and adjust accordingly
Issue: “too many connections to the database”
Cause: Multiple application instances; each creates pool
Solution: If scaling horizontally, reduce pool size per instance
maximum-pool-size: 3 # If running 3+ instancesDatabase Selection
H2 vs Oracle vs PostgreSQL
| Aspect | H2 | Oracle | PostgreSQL |
|---|---|---|---|
| Setup | None (in-memory) | Wallet + TNS config | docker run postgres |
| Cost | Free | Autonomous DB ($$$) | Free (open source) |
| Performance | Fast (in-memory) | Fast (network) | Fast |
| SQL Compatibility | Partial (MODE=Oracle) | Full Oracle | PostgreSQL |
| Best For | Testing | Production | Local dev/staging |
Decision Tree
What environment?
├── Local Development
│ ├── Need to test Oracle behavior? → Use Oracle with wallet
│ └── Just need working DB? → Use PostgreSQL or H2
│
├── Testing (CI/CD)
│ └── Use H2 (in-memory, fast, isolated)
│
└── Production
└── Use Oracle Autonomous DB (most secure, scalable)
Example: Switch from H2 to PostgreSQL Locally
Create application-dev.yml:
spring:
datasource:
url: jdbc:postgresql://localhost:5432/inventory_db
driver-class-name: org.postgresql.Driver
username: postgres
password: postgresActivate:
export SPRING_PROFILES_ACTIVE=dev
mvn spring-boot:runStart PostgreSQL:
docker run --name postgres \
-e POSTGRES_PASSWORD=postgres \
-p 5432:5432 \
postgres:latestTroubleshooting
Issue: “java.sql.SQLException: Connection refused”
Cause: Database not reachable
Debug:
# Check DB_URL is set
echo $DB_URL
# Test connection manually (if Oracle)
sqlplus $DB_USER/$DB_PASS@$DB_URLSolution: 1. Verify database is running 2. Check DB_URL is correct (hostname, port, service name) 3. Check firewall allows connection 4. For Oracle Autonomous DB, ensure wallet is accessible
Issue: “ORA-01017: invalid username/password; logon denied”
Cause: Database credentials wrong
Debug:
# Verify credentials
echo $DB_USER $DB_PASS
# Test with sqlplus (Oracle)
sqlplus $DB_USER/$DB_PASS@$DB_URLSolution: 1. Update .env or fly secrets with correct credentials 2. Restart application to pick up new values 3. For Oracle Autonomous DB, ensure TNS_ADMIN points to correct wallet
Issue: “Unexpected Error: java.lang.UnsatisfiedLinkError: no sqljdbc_auth in java.library.path”
Cause: SQL Server native auth; doesn’t apply to Oracle
Solution: Not applicable for Oracle JDBC
Issue: “The database has not been locked for 1 day and is not available”
Cause: Oracle Autonomous DB locked due to 5 days of no activity (Always Free tier)
Debug:
# Check cloud console
# Database state in Oracle Cloud ConsoleSolution: 1. Open database in Oracle Cloud Console 2. Click “Start” to reopen 3. Wait for database to become available 4. Retry connection
Issue: “H2 database locked (test failure)”
Cause: Multiple test processes accessing same H2 DB
Debug:
# Check if tests are running in parallel
ps aux | grep javaSolution: 1. Configure H2 to allow multiple connections:
# application-test.yml
spring:
datasource:
url: jdbc:h2:mem:ssp;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE- Or disable parallel test execution:
mvn test -DthreadCount=1Summary
| Environment | Database | Config File | Credentials | Connection Pool |
|---|---|---|---|---|
| Local Dev | Oracle/PostgreSQL | application.yml + .env | .env | Default (no pooling config) |
| Testing | H2 | application-test.yml | Hardcoded | None (in-memory) |
| Staging | Oracle | application-staging.yml | fly secrets | Custom pool config |
| Production | Oracle Autonomous | application-prod.yml | fly secrets | HikariCP (5 max, 4-min lifetime) |