Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
/.claude/
/data/
/.apt_generated/
.kotlin/

/src/main/java/codegen
/src/main/kotlin/codegen
Expand Down
22 changes: 21 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Doma CodeGen Plugin is a Gradle plugin that generates Java, Kotlin, and SQL file
./gradlew :codegen:test
./gradlew :codegen-h2-test:test
./gradlew :codegen-tc-test:test
./gradlew :codegen-template-test:test
```

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

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

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

- **codegen-template-test**: Custom template demonstration module
- Tests code generation with custom FreeMarker templates
- Uses H2 in-memory database with custom template directory
- Single configuration: `customTemplate`
- Demonstrates how to override default templates with custom ones

### Key Components

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

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

### Development Notes

Expand All @@ -94,6 +102,10 @@ domaCodeGen {
url = "jdbc:h2:mem:example"
user = "sa"
password = ""

// Optional: Use custom templates
templateDir = file("src/main/resources/custom-templates")

entity {
packageName = "com.example.entity"
}
Expand All @@ -104,6 +116,14 @@ domaCodeGen {
}
```

### Custom Templates

The plugin supports custom FreeMarker templates to override default code generation:
- Place custom templates in a directory (e.g., `src/main/resources/custom-templates/`)
- Configure `templateDir` property in `domaCodeGen` configuration
- Template files: `entity.ftl`, `dao.ftl`, `selectById.sql.ftl`, `lib.ftl`
- See `codegen-template-test` module for a complete example

### Important Notes

- The plugin is published to Gradle Plugin Portal (not Maven Central)
Expand Down
17 changes: 0 additions & 17 deletions codegen-tc-test/src/main/java/org/domaframework/doma/Main.java

This file was deleted.

18 changes: 18 additions & 0 deletions codegen-template-test/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/.classpath
/.gradle/
/.project
/.settings/
/bin/
/build/
/target/
/.metadata
/.idea/
.factorypath
/.claude/
/data/
/.apt_generated/

/src/main/java/com/example/template/entity/
/src/main/java/com/example/template/dao/
/src/main/resources/META-INF/com/example/template/dao/
/src/test/java/com/example/template/dao/
81 changes: 81 additions & 0 deletions codegen-template-test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Template Test Module

This module demonstrates how to use custom FreeMarker templates with the Doma CodeGen Plugin.

## Overview

The `codegen-template-test` module shows how to:
- Configure custom template directories
- Create custom FreeMarker templates for entities, DAOs, and SQL files
- Override default code generation behavior

## Configuration

The module uses custom templates located in `src/main/resources/custom-templates/`:

```kotlin
domaCodeGen {
register("customTemplate") {
// ... database configuration ...

// Use custom templates
templateDir = file("src/main/resources/custom-templates")
}
}
```

## Custom Templates

### Entity Template (`entity.ftl`)
The custom entity template adds:
- Serializable interface implementation (`implements java.io.Serializable`)
- Custom helper method `getEntityName()` that returns the entity class name
- Custom copyright header with generation timestamp
- Custom author information via lib.ftl

### DAO Template (`dao.ftl`)
The custom DAO template adds:
- `@MultiInsert` annotation for batch insert operations
- Custom method `insertMulti(List<Entity>)` for bulk inserts
- Custom copyright header with generation timestamp
- Custom author information via lib.ftl

### SQL Template (`selectById.sql.ftl`)
The custom SQL template:
- Explicitly lists all columns instead of using SELECT *
- Includes formatted SQL with proper indentation

### Shared Template (`lib.ftl`)
Contains shared utilities:
- Custom copyright header with project name and generation timestamp
- Author information ("Custom Template Generator")

## Testing

Run tests with:
```bash
./gradlew :codegen-template-test:test
```

The tests verify:
- Code generation completes successfully
- Custom headers are present in generated files ("This file was generated by Doma-CodeGen with custom templates")
- Entities implement Serializable interface
- Entities have custom `getEntityName()` method
- DAOs have custom `insertMulti(List<Entity>)` method
- All expected files are created

## Database Schema

The module uses an H2 in-memory database with three tables:
- `department` - Department master table with various column types
- `employee` - Employee table with foreign key and version column
- `product` - Product catalog table demonstrating different data types

## Notes

- The generated code includes custom headers showing it was created with custom templates
- Templates are placed directly in `src/main/resources/custom-templates/` (entity.ftl, dao.ftl, etc.)
- Templates can access all properties from EntityDesc and DaoDesc classes
- The lib.ftl file contains shared template utilities like copyright headers and author information
- Custom templates override the default Doma templates when `templateDir` is configured
76 changes: 76 additions & 0 deletions codegen-template-test/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
plugins {
java
alias(libs.plugins.doma.compile)
id("org.domaframework.doma.codegen")
}

java {
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
}

repositories {
mavenLocal()
mavenCentral()
}

dependencies {
implementation(libs.doma.core)
annotationProcessor(libs.doma.processor)

testImplementation(platform(libs.junit.bom))
testImplementation(libs.junit.jupiter.api)
testRuntimeOnly(libs.junit.jupiter.engine)
testRuntimeOnly(libs.junit.platform.launcher)
testImplementation(libs.h2)

domaCodeGen(libs.h2)
}

val initScript = file("init_h2.sql")

domaCodeGen {
register("customTemplate") {
url = "jdbc:h2:mem:template_test;INIT=RUNSCRIPT FROM 'file:${initScript.absolutePath}'"
user = "sa"
password = ""
schemaName = "PUBLIC"

// Use custom templates
templateDir = file("src/main/resources/custom-templates")

// Generate both entity and dao
entity {
packageName = "com.example.template.entity"
}
dao {
packageName = "com.example.template.dao"
}
}
}

tasks {
compileJava {
dependsOn("domaCodeGenCustomTemplateAll")
}

compileTestJava {
dependsOn(compileJava)
}

test {
useJUnitPlatform()
}

val deleteSrc = register("deleteSrc") {
doLast {
delete("src/main/java/com/example/template/entity")
delete("src/main/java/com/example/template/dao")
delete("src/main/resources/META-INF/com/example/template/dao")
delete("src/test/java/com/example/template/dao")
}
}

clean {
dependsOn(deleteSrc)
}
}
53 changes: 53 additions & 0 deletions codegen-template-test/init_h2.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
-- Initialization script for template test database
-- This creates sample tables to test custom template generation

-- Department table with various column types
CREATE TABLE department (
dept_id INTEGER NOT NULL PRIMARY KEY,
dept_name VARCHAR(100) NOT NULL,
location VARCHAR(200),
created_date DATE DEFAULT CURRENT_DATE,
active BOOLEAN DEFAULT TRUE,
description CLOB,
budget DECIMAL(15,2)
);

-- Employee table with relationships and versioning
CREATE TABLE employee (
emp_id INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
emp_name VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE,
dept_id INTEGER,
hire_date DATE NOT NULL,
salary DECIMAL(10,2),
version INTEGER NOT NULL DEFAULT 1,
notes TEXT,
CONSTRAINT fk_dept FOREIGN KEY (dept_id) REFERENCES department(dept_id)
);

-- Product table to test different data types
CREATE TABLE product (
product_code VARCHAR(20) NOT NULL PRIMARY KEY,
product_name VARCHAR(200) NOT NULL,
category VARCHAR(50),
price DECIMAL(10,2) NOT NULL,
stock_quantity INTEGER DEFAULT 0,
manufacturing_date DATE,
expiry_date DATE,
is_available BOOLEAN DEFAULT TRUE,
last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
product_info CLOB,
tags VARCHAR(500)
);

-- Add table comments for testing
COMMENT ON TABLE department IS 'Department master table';
COMMENT ON TABLE employee IS 'Employee information table';
COMMENT ON TABLE product IS 'Product catalog table';

-- Add column comments
COMMENT ON COLUMN department.dept_id IS 'Department unique identifier';
COMMENT ON COLUMN department.dept_name IS 'Department name';
COMMENT ON COLUMN employee.emp_id IS 'Employee ID (auto-generated)';
COMMENT ON COLUMN employee.version IS 'Version for optimistic locking';
COMMENT ON COLUMN product.product_code IS 'Product SKU code';
73 changes: 73 additions & 0 deletions codegen-template-test/src/main/resources/custom-templates/dao.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<#-- Custom dao.ftl -->
<#import "/lib.ftl" as lib>
<#if lib.copyright??>
${lib.copyright}
</#if>
<#if packageName??>
package ${packageName};
</#if>

<#list importNames as importName>
import ${importName};
</#list>
import org.seasar.doma.MultiInsert;

/**
<#if lib.author??>
* @author ${lib.author}
</#if>
*/
@Dao<#if configClassSimpleName??>(config = ${configClassSimpleName}.class)</#if>
public interface ${simpleName} {

<#if entityDesc.idEntityPropertyDescs?size gt 0>
/**
<#list entityDesc.idEntityPropertyDescs as property>
* @param ${property.name}
</#list>
* @return the <#if entityDesc.entityPrefix??>${entityDesc.entityPrefix}</#if>${entityDesc.simpleName}<#if entityDesc.entitySuffix??>${entityDesc.entitySuffix}</#if> entity
*/
@Select
<#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>);

</#if>
<#if entityDesc.idEntityPropertyDescs?size gt 0 && entityDesc.versionEntityPropertyDesc??>
/**
<#list entityDesc.idEntityPropertyDescs as property>
* @param ${property.name}
</#list>
* @param ${entityDesc.versionEntityPropertyDesc.name}
* @return the <#if entityDesc.entityPrefix??>${entityDesc.entityPrefix}</#if>${entityDesc.simpleName}<#if entityDesc.entitySuffix??>${entityDesc.entitySuffix}</#if> entity
*/
@Select(ensureResult = true)
<#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});

</#if>
/**
* @param entity
* @return affected rows
*/
@Insert
int insert(<#if entityDesc.entityPrefix??>${entityDesc.entityPrefix}</#if>${entityDesc.simpleName}<#if entityDesc.entitySuffix??>${entityDesc.entitySuffix}</#if> entity);

/**
* @param entities
* @return affected rows
*/
@MultiInsert
int insertMulti(java.util.List<<#if entityDesc.entityPrefix??>${entityDesc.entityPrefix}</#if>${entityDesc.simpleName}<#if entityDesc.entitySuffix??>${entityDesc.entitySuffix}</#if>> entities);

/**
* @param entity
* @return affected rows
*/
@Update
int update(<#if entityDesc.entityPrefix??>${entityDesc.entityPrefix}</#if>${entityDesc.simpleName}<#if entityDesc.entitySuffix??>${entityDesc.entitySuffix}</#if> entity);

/**
* @param entity
* @return affected rows
*/
@Delete
int delete(<#if entityDesc.entityPrefix??>${entityDesc.entityPrefix}</#if>${entityDesc.simpleName}<#if entityDesc.entitySuffix??>${entityDesc.entitySuffix}</#if> entity);
}
Loading