DBMS Port Definition
Basic Structure
port:
PortServiceName: ## PortServiceName can be freely chosen as long as it is unique within the project
meta: dbms @datasource("datasourceName")
methods:
methodName: ## methodName should be unique within the service and meaningful
meta: method [-- description]
params:
paramName1: ParamType
paramName2: ParamType
return: ReturnType
nativeQuery: true/false
pageable: true/false (optional)
ref: referencedMethodName (optional)
query: |
SQL or JPQL query
DBMS Configuration Options
- @datasource(name): DataSource name to use (default: "main"); not required for single datasource setups
- nativeQuery: Choose between Native SQL (true) or JPQL (false). Default is true
- pageable: Whether to support pagination
- ref: Reference another method's query (useful for pagination)
- query: SQL/JPQL to execute (uses YAML multi-line syntax)
State-changing Queries (insert/update/delete)
- methodName must start with insert, update, or delete.
- return should be set to int, and the number of affected rows is returned.
Parameter Binding Methods
1. Named Parameter (Recommended)
query: |
SELECT * FROM users
WHERE name = :name AND age >= :age
2. String Substitution (At Execution Time)
query: |
SELECT * FROM ${tableName}
WHERE status = :status
3. Conditional Query (Dynamic SQL)
query: |
SELECT * FROM users
WHERE 1=1
AND name = :name /* if:name */
AND age >= :minAge /* if:minAge */
Primitive Type Return Support
DBMS Port supports direct primitive type returns for SQL aggregate functions like COUNT, MAX, MIN, SUM, and single-value queries.
Supported Primitive Types
int,integer: Integer type (COUNT, SUM results)long: Long integer type (large COUNT, SUM results)double: Floating-point type (AVG results)float: Float typestring: String type (single column query, MAX/MIN results)boolean: Boolean type
Primitive Type Return Examples
port:
UserDbPortService:
meta: dbms @datasource("main")
methods:
# COUNT query - returns int
getUserCount:
meta: method -- Get total user count
return: int
query: "SELECT COUNT(*) FROM users"
# Conditional COUNT query - returns long
getActiveUserCount:
meta: method -- Get active user count
params:
active: boolean
return: long
query: "SELECT COUNT(*) FROM users WHERE active = :active"
# MAX query - returns long
getMaxPrice:
meta: method -- Get maximum price
return: long
query: "SELECT MAX(price) FROM products WHERE active = true"
# AVG query - returns double
getAverageAge:
meta: method -- Get average age
return: double
query: "SELECT AVG(age) FROM users"
# Single value query - returns string
getUserNameById:
meta: method -- Get username by ID
params:
userId: long
return: string
query: "SELECT name FROM users WHERE id = :userId"
# Existence check - returns boolean
existsByEmail:
meta: method -- Check if email exists
params:
email: string
return: boolean
query: "SELECT CASE WHEN COUNT(*) > 0 THEN true ELSE false END FROM users WHERE email = :email"
Generated Java Code Example
@DbmsService(id="UserDbPortService", datasource="main")
public interface UserDbPortService {
Integer getUserCount();
Long getActiveUserCount(Boolean active);
Long getMaxPrice();
Double getAverageAge();
String getUserNameById(Long userId);
Boolean existsByEmail(String email);
}
DBMS Port Examples
1. Basic Query
dto:
UserSearchInput:
meta: dto
fields:
name: string
minAge: int
UserDTO:
meta: dto
fields:
id: long
name: string
email: string
age: int
port:
UserDbPortService:
meta: dbms @datasource("main")
methods:
findUsersByName:
meta: method -- Search users by name
params:
input: UserSearchInput
return: List<UserDTO>
nativeQuery: true
query: |
SELECT id, name, email, age
FROM users
WHERE name LIKE CONCAT('%', :name, '%')
AND age >= :minAge
2. Conditional Dynamic Query
port:
OrderDbPortService:
meta: dbms @datasource("order")
methods:
searchOrders:
meta: method -- Order search (dynamic conditions)
params:
input: OrderSearchDTO
return: List<OrderDTO>
nativeQuery: true
query: |
SELECT o.id, o.order_date, o.status, o.amount
FROM orders o
WHERE 1=1
AND o.status = :status /* if:status */
AND o.order_date >= :startDate /* if:startDate */
AND o.order_date <= :endDate /* if:endDate */
AND o.amount >= :minAmount /* if:minAmount */
ORDER BY o.order_date DESC
3. Paginated Query
port:
ProductDbPortService:
meta: dbms @datasource("product")
methods:
getProductList:
meta: method -- Product list (paginated)
params:
input: ProductSearchDTO
return: Page<ProductDTO>
pageable: true
nativeQuery: true
query: |
SELECT p.id, p.name, p.price, p.category
FROM products p
WHERE p.active = true
AND p.name LIKE CONCAT('%', :keyword, '%') /* if:keyword */
4. Aggregate Statistics Query
port:
StatisticsDbPortService:
meta: dbms @datasource("analytics")
methods:
getSalesStatistics:
meta: method -- Sales statistics
params:
startDate: date
endDate: date
return: List<SalesStatsDTO>
nativeQuery: true
query: |
SELECT
DATE_FORMAT(order_date, '%Y-%m') AS month,
COUNT(*) AS order_count,
SUM(amount) AS total_amount
FROM orders
WHERE order_date BETWEEN :startDate AND :endDate
GROUP BY DATE_FORMAT(order_date, '%Y-%m')
ORDER BY month DESC
5. Complex JOIN Query
port:
ReportDbPortService:
meta: dbms @datasource("report")
methods:
getUserOrderSummary:
meta: method -- Order summary by user
params:
userId: long
return: UserOrderSummaryDTO
nativeQuery: true
query: |
SELECT
u.name AS user_name,
u.email,
COUNT(o.id) AS order_count,
COALESCE(SUM(o.amount), 0) AS total_amount,
MAX(o.order_date) AS last_order_date
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.id = :userId
GROUP BY u.id, u.name, u.email
6. Real-World Example (Car Info)
dto:
CarInfoInput:
meta: dto
fields:
brand: string -- Brand name
CarInfoOutput:
meta: dto
fields:
brand: string -- Brand name
description: string -- Description
modelId: string -- Model ID
port:
CarInfoPortService:
meta: dbms @datasource("main")
methods:
findByBrand:
meta: method -- Find vehicle info by brand
params:
input: CarInfoInput
return: List<CarInfoOutput>
nativeQuery: true
query: |
SELECT
brand,
description,
model_id
FROM car_info
WHERE brand = :brand
Dynamic SQL Advanced Features
1. Block-level Conditional Control
query: |
SELECT * FROM users
WHERE 1=1
/* if:searchCriteria != null */
AND (
name LIKE CONCAT('%', :name, '%')
OR email LIKE CONCAT('%', :email, '%')
)
/* if-end */
/* if:ageRange != null */
AND age BETWEEN :minAge AND :maxAge
/* if-end */
2. Automatic Sorting (sortCode Parameter)
# If the SearchDTO has a sortCode field, an ORDER BY clause is auto-generated
# Input example: {"sortCode": "name+,age-"}
# Generated SQL: ORDER BY name ASC, age DESC
3. Query Reference (ref Feature)
port:
ItemDbPortService:
meta: dbms @datasource("item")
methods:
getItemList:
meta: method -- Item list query
params:
input: ItemSearchDTO
return: List<ItemDTO>
nativeQuery: true
query: |
SELECT id, name, price FROM items
WHERE active = true
AND name LIKE CONCAT('%', :keyword, '%') /* if:keyword */
getItemListPaged:
meta: method -- Item list query (paginated)
params:
input: ItemSearchDTO
return: Page<ItemDTO>
pageable: true
ref: getItemList # References getItemList's query for pagination
Spring Boot DataSource Configuration
Single DataSource
spring:
datasource:
url: jdbc:mysql://localhost:3306/maindb
username: user
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
Multiple DataSources (Java Configuration)
@Configuration
public class DataSourceConfig {
@Primary
@Bean("main")
@ConfigurationProperties("spring.datasource")
public DataSource mainDataSource() {
return DataSourceBuilder.create().build();
}
@Bean("order")
@ConfigurationProperties("spring.datasource.order")
public DataSource orderDataSource() {
return DataSourceBuilder.create().build();
}
@Bean("analytics")
@ConfigurationProperties("spring.datasource.analytics")
public DataSource analyticsDataSource() {
return DataSourceBuilder.create().build();
}
}
Important Notes
SQL Injection Prevention
# Good example: Using named parameters
query: |
SELECT * FROM users
WHERE name = :name -- Safe parameter binding
# Bad example: Using string substitution (security risk)
query: |
SELECT * FROM users
WHERE name = '${name}' -- SQL injection risk
Performance Optimization
- Always use pagination for large datasets
- Set appropriate indexes
- Use JOINs to prevent N+1 problems
- Arrange WHERE conditions considering index usage