Skip to content

Commit 557c11a

Browse files
authored
Merge pull request #236 from domaframework/test/add-template-test-by-claude
Add codegen-template-test module with custom template support
2 parents bb9b529 + 64ea928 commit 557c11a

File tree

14 files changed

+556
-18
lines changed

14 files changed

+556
-18
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
/.claude/
1212
/data/
1313
/.apt_generated/
14+
.kotlin/
1415

1516
/src/main/java/codegen
1617
/src/main/kotlin/codegen

CLAUDE.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Doma CodeGen Plugin is a Gradle plugin that generates Java, Kotlin, and SQL file
2323
./gradlew :codegen:test
2424
./gradlew :codegen-h2-test:test
2525
./gradlew :codegen-tc-test:test
26+
./gradlew :codegen-template-test:test
2627
```
2728

2829
## Code Quality
@@ -42,7 +43,7 @@ This is a Gradle composite build with the following structure:
4243

4344
- **Root project** (`doma-codegen-plugin`): Contains build configuration and release management
4445
- Uses `pluginManagement` with `includeBuild("codegen")` to include the plugin project
45-
- Includes test modules: `codegen-h2-test` and `codegen-tc-test`
46+
- Includes test modules: `codegen-h2-test`, `codegen-tc-test`, and `codegen-template-test`
4647

4748
- **codegen**: Main plugin implementation (included build)
4849
- Plugin entry point: `CodeGenPlugin.java`
@@ -62,6 +63,12 @@ This is a Gradle composite build with the following structure:
6263
- Supports both Java and Kotlin code generation
6364
- Two configurations: `java` and `kotlin`
6465

66+
- **codegen-template-test**: Custom template demonstration module
67+
- Tests code generation with custom FreeMarker templates
68+
- Uses H2 in-memory database with custom template directory
69+
- Single configuration: `customTemplate`
70+
- Demonstrates how to override default templates with custom ones
71+
6572
### Key Components
6673

6774
1. **Code Generators**: Transform database metadata into Java/Kotlin code using FreeMarker templates
@@ -75,6 +82,7 @@ This is a Gradle composite build with the following structure:
7582

7683
4. **Template System**: FreeMarker templates in `/codegen/src/main/resources/`
7784
- Customizable via `templateDir` configuration
85+
- See `codegen-template-test` module for custom template examples
7886

7987
### Development Notes
8088

@@ -94,6 +102,10 @@ domaCodeGen {
94102
url = "jdbc:h2:mem:example"
95103
user = "sa"
96104
password = ""
105+
106+
// Optional: Use custom templates
107+
templateDir = file("src/main/resources/custom-templates")
108+
97109
entity {
98110
packageName = "com.example.entity"
99111
}
@@ -104,6 +116,14 @@ domaCodeGen {
104116
}
105117
```
106118

119+
### Custom Templates
120+
121+
The plugin supports custom FreeMarker templates to override default code generation:
122+
- Place custom templates in a directory (e.g., `src/main/resources/custom-templates/`)
123+
- Configure `templateDir` property in `domaCodeGen` configuration
124+
- Template files: `entity.ftl`, `dao.ftl`, `selectById.sql.ftl`, `lib.ftl`
125+
- See `codegen-template-test` module for a complete example
126+
107127
### Important Notes
108128

109129
- The plugin is published to Gradle Plugin Portal (not Maven Central)

codegen-tc-test/src/main/java/org/domaframework/doma/Main.java

Lines changed: 0 additions & 17 deletions
This file was deleted.

codegen-template-test/.gitignore

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/.classpath
2+
/.gradle/
3+
/.project
4+
/.settings/
5+
/bin/
6+
/build/
7+
/target/
8+
/.metadata
9+
/.idea/
10+
.factorypath
11+
/.claude/
12+
/data/
13+
/.apt_generated/
14+
15+
/src/main/java/com/example/template/entity/
16+
/src/main/java/com/example/template/dao/
17+
/src/main/resources/META-INF/com/example/template/dao/
18+
/src/test/java/com/example/template/dao/

codegen-template-test/README.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Template Test Module
2+
3+
This module demonstrates how to use custom FreeMarker templates with the Doma CodeGen Plugin.
4+
5+
## Overview
6+
7+
The `codegen-template-test` module shows how to:
8+
- Configure custom template directories
9+
- Create custom FreeMarker templates for entities, DAOs, and SQL files
10+
- Override default code generation behavior
11+
12+
## Configuration
13+
14+
The module uses custom templates located in `src/main/resources/custom-templates/`:
15+
16+
```kotlin
17+
domaCodeGen {
18+
register("customTemplate") {
19+
// ... database configuration ...
20+
21+
// Use custom templates
22+
templateDir = file("src/main/resources/custom-templates")
23+
}
24+
}
25+
```
26+
27+
## Custom Templates
28+
29+
### Entity Template (`entity.ftl`)
30+
The custom entity template adds:
31+
- Serializable interface implementation (`implements java.io.Serializable`)
32+
- Custom helper method `getEntityName()` that returns the entity class name
33+
- Custom copyright header with generation timestamp
34+
- Custom author information via lib.ftl
35+
36+
### DAO Template (`dao.ftl`)
37+
The custom DAO template adds:
38+
- `@MultiInsert` annotation for batch insert operations
39+
- Custom method `insertMulti(List<Entity>)` for bulk inserts
40+
- Custom copyright header with generation timestamp
41+
- Custom author information via lib.ftl
42+
43+
### SQL Template (`selectById.sql.ftl`)
44+
The custom SQL template:
45+
- Explicitly lists all columns instead of using SELECT *
46+
- Includes formatted SQL with proper indentation
47+
48+
### Shared Template (`lib.ftl`)
49+
Contains shared utilities:
50+
- Custom copyright header with project name and generation timestamp
51+
- Author information ("Custom Template Generator")
52+
53+
## Testing
54+
55+
Run tests with:
56+
```bash
57+
./gradlew :codegen-template-test:test
58+
```
59+
60+
The tests verify:
61+
- Code generation completes successfully
62+
- Custom headers are present in generated files ("This file was generated by Doma-CodeGen with custom templates")
63+
- Entities implement Serializable interface
64+
- Entities have custom `getEntityName()` method
65+
- DAOs have custom `insertMulti(List<Entity>)` method
66+
- All expected files are created
67+
68+
## Database Schema
69+
70+
The module uses an H2 in-memory database with three tables:
71+
- `department` - Department master table with various column types
72+
- `employee` - Employee table with foreign key and version column
73+
- `product` - Product catalog table demonstrating different data types
74+
75+
## Notes
76+
77+
- The generated code includes custom headers showing it was created with custom templates
78+
- Templates are placed directly in `src/main/resources/custom-templates/` (entity.ftl, dao.ftl, etc.)
79+
- Templates can access all properties from EntityDesc and DaoDesc classes
80+
- The lib.ftl file contains shared template utilities like copyright headers and author information
81+
- Custom templates override the default Doma templates when `templateDir` is configured
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
plugins {
2+
java
3+
alias(libs.plugins.doma.compile)
4+
id("org.domaframework.doma.codegen")
5+
}
6+
7+
java {
8+
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
9+
}
10+
11+
repositories {
12+
mavenLocal()
13+
mavenCentral()
14+
}
15+
16+
dependencies {
17+
implementation(libs.doma.core)
18+
annotationProcessor(libs.doma.processor)
19+
20+
testImplementation(platform(libs.junit.bom))
21+
testImplementation(libs.junit.jupiter.api)
22+
testRuntimeOnly(libs.junit.jupiter.engine)
23+
testRuntimeOnly(libs.junit.platform.launcher)
24+
testImplementation(libs.h2)
25+
26+
domaCodeGen(libs.h2)
27+
}
28+
29+
val initScript = file("init_h2.sql")
30+
31+
domaCodeGen {
32+
register("customTemplate") {
33+
url = "jdbc:h2:mem:template_test;INIT=RUNSCRIPT FROM 'file:${initScript.absolutePath}'"
34+
user = "sa"
35+
password = ""
36+
schemaName = "PUBLIC"
37+
38+
// Use custom templates
39+
templateDir = file("src/main/resources/custom-templates")
40+
41+
// Generate both entity and dao
42+
entity {
43+
packageName = "com.example.template.entity"
44+
}
45+
dao {
46+
packageName = "com.example.template.dao"
47+
}
48+
}
49+
}
50+
51+
tasks {
52+
compileJava {
53+
dependsOn("domaCodeGenCustomTemplateAll")
54+
}
55+
56+
compileTestJava {
57+
dependsOn(compileJava)
58+
}
59+
60+
test {
61+
useJUnitPlatform()
62+
}
63+
64+
val deleteSrc = register("deleteSrc") {
65+
doLast {
66+
delete("src/main/java/com/example/template/entity")
67+
delete("src/main/java/com/example/template/dao")
68+
delete("src/main/resources/META-INF/com/example/template/dao")
69+
delete("src/test/java/com/example/template/dao")
70+
}
71+
}
72+
73+
clean {
74+
dependsOn(deleteSrc)
75+
}
76+
}

codegen-template-test/init_h2.sql

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
-- Initialization script for template test database
2+
-- This creates sample tables to test custom template generation
3+
4+
-- Department table with various column types
5+
CREATE TABLE department (
6+
dept_id INTEGER NOT NULL PRIMARY KEY,
7+
dept_name VARCHAR(100) NOT NULL,
8+
location VARCHAR(200),
9+
created_date DATE DEFAULT CURRENT_DATE,
10+
active BOOLEAN DEFAULT TRUE,
11+
description CLOB,
12+
budget DECIMAL(15,2)
13+
);
14+
15+
-- Employee table with relationships and versioning
16+
CREATE TABLE employee (
17+
emp_id INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
18+
emp_name VARCHAR(100) NOT NULL,
19+
email VARCHAR(255) UNIQUE,
20+
dept_id INTEGER,
21+
hire_date DATE NOT NULL,
22+
salary DECIMAL(10,2),
23+
version INTEGER NOT NULL DEFAULT 1,
24+
notes TEXT,
25+
CONSTRAINT fk_dept FOREIGN KEY (dept_id) REFERENCES department(dept_id)
26+
);
27+
28+
-- Product table to test different data types
29+
CREATE TABLE product (
30+
product_code VARCHAR(20) NOT NULL PRIMARY KEY,
31+
product_name VARCHAR(200) NOT NULL,
32+
category VARCHAR(50),
33+
price DECIMAL(10,2) NOT NULL,
34+
stock_quantity INTEGER DEFAULT 0,
35+
manufacturing_date DATE,
36+
expiry_date DATE,
37+
is_available BOOLEAN DEFAULT TRUE,
38+
last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
39+
product_info CLOB,
40+
tags VARCHAR(500)
41+
);
42+
43+
-- Add table comments for testing
44+
COMMENT ON TABLE department IS 'Department master table';
45+
COMMENT ON TABLE employee IS 'Employee information table';
46+
COMMENT ON TABLE product IS 'Product catalog table';
47+
48+
-- Add column comments
49+
COMMENT ON COLUMN department.dept_id IS 'Department unique identifier';
50+
COMMENT ON COLUMN department.dept_name IS 'Department name';
51+
COMMENT ON COLUMN employee.emp_id IS 'Employee ID (auto-generated)';
52+
COMMENT ON COLUMN employee.version IS 'Version for optimistic locking';
53+
COMMENT ON COLUMN product.product_code IS 'Product SKU code';
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<#-- Custom dao.ftl -->
2+
<#import "/lib.ftl" as lib>
3+
<#if lib.copyright??>
4+
${lib.copyright}
5+
</#if>
6+
<#if packageName??>
7+
package ${packageName};
8+
</#if>
9+
10+
<#list importNames as importName>
11+
import ${importName};
12+
</#list>
13+
import org.seasar.doma.MultiInsert;
14+
15+
/**
16+
<#if lib.author??>
17+
* @author ${lib.author}
18+
</#if>
19+
*/
20+
@Dao<#if configClassSimpleName??>(config = ${configClassSimpleName}.class)</#if>
21+
public interface ${simpleName} {
22+
23+
<#if entityDesc.idEntityPropertyDescs?size gt 0>
24+
/**
25+
<#list entityDesc.idEntityPropertyDescs as property>
26+
* @param ${property.name}
27+
</#list>
28+
* @return the <#if entityDesc.entityPrefix??>${entityDesc.entityPrefix}</#if>${entityDesc.simpleName}<#if entityDesc.entitySuffix??>${entityDesc.entitySuffix}</#if> entity
29+
*/
30+
@Select
31+
<#if entityDesc.entityPrefix??>${entityDesc.entityPrefix}</#if>${entityDesc.simpleName}<#if entityDesc.entitySuffix??>${entityDesc.entitySuffix}</#if> selectById(<#list entityDesc.idEntityPropertyDescs as property>${property.propertyClassSimpleName} ${property.name}<#if property_has_next>, </#if></#list>);
32+
33+
</#if>
34+
<#if entityDesc.idEntityPropertyDescs?size gt 0 && entityDesc.versionEntityPropertyDesc??>
35+
/**
36+
<#list entityDesc.idEntityPropertyDescs as property>
37+
* @param ${property.name}
38+
</#list>
39+
* @param ${entityDesc.versionEntityPropertyDesc.name}
40+
* @return the <#if entityDesc.entityPrefix??>${entityDesc.entityPrefix}</#if>${entityDesc.simpleName}<#if entityDesc.entitySuffix??>${entityDesc.entitySuffix}</#if> entity
41+
*/
42+
@Select(ensureResult = true)
43+
<#if entityDesc.entityPrefix??>${entityDesc.entityPrefix}</#if>${entityDesc.simpleName}<#if entityDesc.entitySuffix??>${entityDesc.entitySuffix}</#if> selectByIdAndVersion(<#list entityDesc.idEntityPropertyDescs as property>${property.propertyClassSimpleName} ${property.name}, </#list>${entityDesc.versionEntityPropertyDesc.propertyClassSimpleName} ${entityDesc.versionEntityPropertyDesc.name});
44+
45+
</#if>
46+
/**
47+
* @param entity
48+
* @return affected rows
49+
*/
50+
@Insert
51+
int insert(<#if entityDesc.entityPrefix??>${entityDesc.entityPrefix}</#if>${entityDesc.simpleName}<#if entityDesc.entitySuffix??>${entityDesc.entitySuffix}</#if> entity);
52+
53+
/**
54+
* @param entities
55+
* @return affected rows
56+
*/
57+
@MultiInsert
58+
int insertMulti(java.util.List<<#if entityDesc.entityPrefix??>${entityDesc.entityPrefix}</#if>${entityDesc.simpleName}<#if entityDesc.entitySuffix??>${entityDesc.entitySuffix}</#if>> entities);
59+
60+
/**
61+
* @param entity
62+
* @return affected rows
63+
*/
64+
@Update
65+
int update(<#if entityDesc.entityPrefix??>${entityDesc.entityPrefix}</#if>${entityDesc.simpleName}<#if entityDesc.entitySuffix??>${entityDesc.entitySuffix}</#if> entity);
66+
67+
/**
68+
* @param entity
69+
* @return affected rows
70+
*/
71+
@Delete
72+
int delete(<#if entityDesc.entityPrefix??>${entityDesc.entityPrefix}</#if>${entityDesc.simpleName}<#if entityDesc.entitySuffix??>${entityDesc.entitySuffix}</#if> entity);
73+
}

0 commit comments

Comments
 (0)