Doma CodeGen Plugin
Overview
The Doma CodeGen Plugin is a Gradle plugin that generates Java, Kotlin, and SQL files from a database schema.
Key Benefits
Database-First Development: Generate type-safe entity and DAO classes directly from your existing database schema
Multi-Language Support: Generate both Java and Kotlin code with the same configuration
SQL Template Generation: Automatically create SQL template files for common READ operations
Testcontainers Integration: Seamlessly work with Testcontainers for database testing and code generation
Customizable: Use custom templates to control the generated code structure and style
Multiple Database Support: Works with PostgreSQL, MySQL, Oracle, H2, and other JDBC-compatible databases
Use Cases
Rapid Prototyping: Quickly bootstrap data access layers from database designs
Schema Evolution: Keep your code in sync with database schema changes
Team Development: Ensure consistent entity and DAO implementations across team members
Are you looking for documentation for Ant-based Doma-Gen?
Documentation for Ant-based Doma-Gen is available at the Doma-Gen GitHub repository.
Please note that Ant-based Doma-Gen is no longer maintained. We recommend using the Doma CodeGen Plugin described on this page instead.
Getting Started
Prerequisites
Gradle 8.0 or higher
Java 17 or higher
Access to a database (can be local, remote, or Testcontainers-based)
Step-by-Step Setup
Add the Plugin
Add the Doma CodeGen plugin to your Gradle build file:
plugins { java id("org.domaframework.doma.codegen") version "3.2.1" }
Configure Dependencies
Add the necessary JDBC driver dependency:
dependencies { // For code generation domaCodeGen("org.postgresql:postgresql:42.7.7") }
Set up Local PostgreSQL Database
Make sure you have PostgreSQL installed and running locally. Create a database and tables:
-- Connect to PostgreSQL and create database CREATE DATABASE myapp; -- Switch to the new database and create tables CREATE TABLE users ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, email VARCHAR(255) UNIQUE NOT NULL, version INTEGER NOT NULL DEFAULT 1, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE orders ( id SERIAL PRIMARY KEY, user_id INTEGER REFERENCES users(id), total_amount DECIMAL(10,2) NOT NULL, order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, version INTEGER NOT NULL DEFAULT 1 );
Configure Code Generation
Complete build.gradle.kts example:
plugins { java id("org.domaframework.doma.codegen") version "3.2.1" } dependencies { // Code generation dependencies domaCodeGen("org.postgresql:postgresql:42.7.7") } domaCodeGen { val basePackage = "com.example.myapp" register("postgresql") { // Database connection to local PostgreSQL url.set("jdbc:postgresql://localhost:5432/myapp") user.set("postgres") // Replace with your PostgreSQL username password.set("password") // Replace with your PostgreSQL password // Entity generation settings entity { packageName.set("$basePackage.entity") useAccessor.set(true) // Generate getters/setters useListener.set(true) // Generate entity listeners showDbComment.set(true) // Include database comments } // DAO generation settings dao { packageName.set("$basePackage.dao") } } }
Generate Code
Run the code generation task:
$ ./gradlew domaCodeGenPostgresqlAll
This will generate:
Entity classes in
src/main/java/com/example/myapp/entity/
DAO interfaces in
src/main/java/com/example/myapp/dao/
SQL template files in
src/main/resources/META-INF/com/example/myapp/dao/
Test classes in
src/test/java/com/example/myapp/dao/
What Gets Generated
After running the code generation, you’ll find the following files:
Entity Classes
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Integer id;
public String name;
public String email;
@Version
public Integer version;
@Column(name = "created_at")
public Timestamp createdAt;
}
DAO Interfaces
@Dao
public interface UserDao {
@Select
User selectById(Integer id);
@Insert
Result<User> insert(User entity);
@Update
Result<User> update(User entity);
@Delete
Result<User> delete(User entity);
}
SQL Templates
SELECT /*%expand*/* FROM users WHERE id = /* id */1
Database Examples
The following examples show how to configure different database types:
PostgreSQL
dependencies {
domaCodeGen("org.postgresql:postgresql:42.7.7")
}
domaCodeGen {
register("postgresql") {
url.set("jdbc:postgresql://localhost:5432/mydatabase")
user.set("myuser")
password.set("mypassword")
entity {
packageName.set("com.example.postgresql.entity")
}
dao {
packageName.set("com.example.postgresql.dao")
}
}
}
PostgreSQL with Testcontainers
dependencies {
domaCodeGen(platform("org.testcontainers:testcontainers-bom:1.21.2"))
domaCodeGen("org.postgresql:postgresql:42.7.7")
domaCodeGen("org.testcontainers:postgresql")
}
domaCodeGen {
register("postgresql") {
val initScript = file("src/main/resources/schema-postgresql.sql")
url.set("jdbc:tc:postgresql:15:///test?TC_INITSCRIPT=file:${initScript.absolutePath}")
user.set("test")
password.set("test")
entity {
packageName.set("com.example.postgresql.entity")
}
dao {
packageName.set("com.example.postgresql.dao")
}
}
}
MySQL with Testcontainers
dependencies {
domaCodeGen(platform("org.testcontainers:testcontainers-bom:1.21.2"))
domaCodeGen("mysql:mysql-connector-java:8.0.33")
domaCodeGen("org.testcontainers:mysql")
}
domaCodeGen {
register("mysql") {
val initScript = file("src/main/resources/schema-mysql.sql")
url.set("jdbc:tc:mysql:8.0:///test?TC_INITSCRIPT=file:${initScript.absolutePath}")
user.set("test")
password.set("test")
entity {
packageName.set("com.example.mysql.entity")
}
dao {
packageName.set("com.example.mysql.dao")
}
}
}
Gradle Tasks
The Doma CodeGen Plugin provides the following tasks:
domaCodeGenXxxAll - Generates all.
domaCodeGenXxxDao - Generates DAO source files.
domaCodeGenXxxDto - Reads ResultSet metadata and generate a DTO source file.
domaCodeGenXxxEntity - Generates Entity source files.
domaCodeGenXxxSql - Generates SQL files.
domaCodeGenXxxSqlTest - Generates SQL test source files.
Note that the Xxx part in each task name is replaced with the block name defined under the domaCodeGen
block.
In the usage example above, the Postgresql part corresponds to the postgresql
block.
To check all defined task names, run the tasks task:
$ ./gradlew tasks
Configuration Reference
Named Configuration
A named configuration must be defined under the domaCodeGen
block.
You can choose any name for your configuration.
Multiple configurations can be defined to support different databases or environments.
Example: Multiple Database Configurations
domaCodeGen {
register("sales") {
url.set("jdbc:postgresql://localhost:5432/sales")
user.set("sales_user")
password.set("sales_pass")
entity {
packageName.set("com.example.sales.entity")
}
dao {
packageName.set("com.example.sales.dao")
}
}
register("inventory") {
url.set("jdbc:mysql://localhost:3306/inventory")
user.set("inventory_user")
password.set("inventory_pass")
entity {
packageName.set("com.example.inventory.entity")
}
dao {
packageName.set("com.example.inventory.dao")
}
}
}
This generates separate task sets for each database:
$ ./gradlew domaCodeGenSalesAll # Generate all for sales DB
$ ./gradlew domaCodeGenInventoryAll # Generate all for inventory DB
Main Configuration Options
These options are configured at the top level of each named configuration block:
Option |
Description |
Example Values |
Default |
---|---|---|---|
url |
JDBC connection URL to your database |
|
Required |
user |
Database username for authentication |
|
Required |
password |
Database password for authentication |
|
Required |
dataSource |
Custom data source class (advanced) |
inferred from URL |
|
codeGenDialect |
Database dialect for SQL generation (advanced) |
inferred from URL |
|
catalogName |
Database catalog name to filter tables |
|
|
schemaName |
Database schema name to filter tables |
|
|
tableNamePattern |
Regex pattern to include specific tables |
|
|
ignoredTableNamePattern |
Regex pattern to exclude tables |
|
|
tableTypes |
Types of database objects to include |
|
|
versionColumnNamePattern |
Regex to identify version columns |
|
|
languageType |
Target programming language |
|
|
templateDir |
Directory containing custom FreeMarker templates |
|
|
encoding |
Text encoding for generated source files |
|
|
sourceDir |
Output directory for generated source files |
|
depends on language |
resourceDir |
Output directory for generated SQL files |
|
|
globalFactory |
entry point to customize plugin behavior |
[2] The instance of GlobalFactory |
Entity Configuration
The entity
block configures how entity classes are generated. This block must be defined within a named configuration.
Basic Example
domaCodeGen {
register("sales") {
entity {
packageName.set("com.example.sales.entity")
useAccessor.set(true) // Generate getters/setters
useListener.set(true) // Generate entity listeners
showDbComment.set(true) // Include database comments
prefix.set("Sales") // Add prefix to class names
}
}
}
Advanced Example
domaCodeGen {
register("enterprise") {
entity {
packageName.set("com.enterprise.domain.entity")
superclassName.set("com.enterprise.core.BaseEntity") // Common base class
listenerSuperclassName.set("com.enterprise.core.BaseEntityListener")
useMetamodel.set(true) // Generate metamodel classes
useMappedSuperclass.set(true) // Use @MappedSuperclass
originalStatesPropertyName.set("originalStates") // Property for @OriginalStates
showTableName.set(false) // Don't show @Table annotations
showColumnName.set(false) // Don't show @Column annotations
}
}
}
Option |
Description |
Values |
Default |
---|---|---|---|
overwrite |
where to overwrite generated entity files or not |
true |
|
overwriteListener |
allow to overwrite listeners or not |
false |
|
superclassName |
common superclass for generated entity classes |
||
listenerSuperclassName |
common superclass for generated entity listener classes |
||
packageName |
package name for generated entity class |
“example.entity” |
|
generationType |
generation type for entity identities |
[3] enum value of GenerationType |
|
namingType |
naming convention |
[4] enum value of NamingType |
|
initialValue |
initial value for entity identities |
||
allocationSize |
allocation size for entity identities |
||
showCatalogName |
whether to show catalog names or not |
false |
|
showSchemaName |
whether to show schema names or not |
false |
|
showTableName |
whether to show table names or not |
true |
|
showColumnName |
whether to show column names or not |
true |
|
showDbComment |
whether to show database comments or not |
true |
|
useAccessor |
whether to use accessors or not |
true |
|
useListener |
whether to use listeners or not |
true |
|
useMetamodel |
whether to use metamodels or not |
true |
|
useMappedSuperclass |
whether to use mapped superclasses or not |
true |
|
originalStatesPropertyName |
property to be annotated with @OriginalStates |
||
entityPropertyClassNamesFile |
file used to resolve entity property classes |
||
prefix |
prefix for entity classes |
||
suffix |
suffix for entity classes |
DAO Configuration
The dao
block configures how DAO (Data Access Object) interfaces are generated.
Basic Example
domaCodeGen {
register("sales") {
dao {
packageName.set("com.example.sales.dao")
suffix.set("Repository") // Use "Repository" instead of "Dao"
}
}
}
Option |
Description |
Values |
Default |
---|---|---|---|
overwrite |
whether to overwrite generated DAO files or not |
|
|
packageName |
package name for generated DAO classes |
“example.dao” |
|
suffix |
suffix for Dao classes |
“Dao” |
SQL Configuration
The sql
block configures how SQL template files are generated.
domaCodeGen {
register("sales") {
sql {
overwrite.set(true) // Overwrite existing SQL files
}
}
}
Note
SQL files are generated in src/main/resources/META-INF/<package>/dao/
directory.
These include basic READ operations like selectById.sql
and selectByIdAndVersion.sql
.
Option |
Description |
Values |
Default |
---|---|---|---|
overwrite |
whether to overwrite generated sql files or not |
|
SQL Test Configuration
The sqlTest
block configures generation of SQL test files and can use a different database for testing.
Example: Separate Test Database
domaCodeGen {
register("production") {
// Main database configuration
url.set("jdbc:postgresql://prod-db:5432/myapp")
user.set("prod_user")
password.set("prod_pass")
// Test database configuration
sqlTest {
url.set("jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1")
user.set("sa")
password.set("")
}
}
}
Example: Same Database for Tests
domaCodeGen {
register("development") {
url.set("jdbc:tc:postgresql:15:///test")
user.set("test")
password.set("test")
sqlTest {
// Uses same connection as main configuration
// No need to specify url, user, password again
}
}
}
Option |
Description |
Default |
---|---|---|
url |
JDBC URL for test database (can be different from main) |
Same as main configuration |
user |
Database username for test database |
Same as main configuration |
password |
Database password for test database |
Same as main configuration |
Customization
Generating Kotlin code
To generate Kotlin code, set the languageType option to LanguageType.KOTLIN
as follows:
import org.seasar.doma.gradle.codegen.desc.LanguageType
...
domaCodeGen {
register("dev") {
url.set("jdbc:postgresql://localhost:5432/mydatabase")
user.set("myuser")
password.set("mypassword")
languageType.set(LanguageType.KOTLIN)
entity {
packageName.set("org.example.entity")
}
dao {
packageName.set("org.example.dao")
}
}
}
Template Customization
The Doma CodeGen Plugin uses Apache FreeMarker templates to generate code. You can customize these templates to match your project’s coding standards and requirements.
Available Templates
The default template files can be found in the source code repository.
Template File |
Purpose |
Generated Output |
---|---|---|
entity.ftl |
Entity class generation |
Java/Kotlin entity classes with JPA annotations |
entityListener.ftl |
Entity listener generation |
Entity listener classes for lifecycle callbacks |
dao.ftl |
DAO interface generation |
DAO interfaces with basic CRUD methods |
sqlTest.ftl |
SQL test generation |
Test classes for validating SQL files |
selectById.sql.ftl |
Basic select SQL |
SQL files for selecting by primary key |
selectByIdAndVersion.sql.ftl |
Optimistic locking SQL |
SQL files for selecting with version checking |
Setting Up Custom Templates
Create Template Directory
your-project/ ├── custom-templates/ │ ├── entity.ftl │ ├── dao.ftl │ └── entityListener.ftl └── build.gradle.kts
Configure Template Directory
domaCodeGen { register("mydb") { url.set("jdbc:postgresql://localhost:5432/mydb") user.set("user") password.set("pass") templateDir.set(file("$projectDir/custom-templates")) entity { packageName.set("com.example.entity") } dao { packageName.set("com.example.dao") } } }
Customize Entity Template
Create
custom-templates/entity.ftl
to add custom annotations:<#-- Custom entity template with additional annotations --> package ${entityDesc.packageName}; import java.io.Serializable; import org.seasar.doma.*; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; /** * Entity for ${entityDesc.tableName} table. <#if entityDesc.comment??> * ${entityDesc.comment}</#if> */ @Entity<#if entityDesc.tableName??>(table = @Table(name = "${entityDesc.tableName}"))</#if> @Data // Lombok annotation @JsonIgnoreProperties(ignoreUnknown = true) // Jackson annotation public class ${entityDesc.simpleName} implements Serializable { <#list entityDesc.propertyDescs as property> <#if property.id> @Id <#if property.generationType??> @GeneratedValue(strategy = GenerationType.${property.generationType}) </#if> </#if> <#if property.version> @Version </#if> <#if property.columnName??> @Column(name = "${property.columnName}") </#if> public ${property.propertyClassName} ${property.propertyName}; </#list> }
Customize DAO Template
Create
custom-templates/dao.ftl
for custom DAO methods:<#-- Custom DAO template with additional methods --> package ${daoDesc.packageName}; import org.seasar.doma.*; import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.Optional; /** * DAO for ${daoDesc.entityDesc.simpleName}. */ @Dao<#if daoDesc.configClassName??>(config = ${daoDesc.configClassName}.class)</#if> @Transactional // Spring transaction annotation public interface ${daoDesc.simpleName} { @Select Optional<${daoDesc.entityDesc.simpleName}> selectById(${daoDesc.entityDesc.idPropertyDesc.propertyClassName} ${daoDesc.entityDesc.idPropertyDesc.propertyName}); @Select List<${daoDesc.entityDesc.simpleName}> selectAll(); @Select List<${daoDesc.entityDesc.simpleName}> selectByExample(${daoDesc.entityDesc.simpleName} example); @Insert Result<${daoDesc.entityDesc.simpleName}> insert(${daoDesc.entityDesc.simpleName} entity); @Update Result<${daoDesc.entityDesc.simpleName}> update(${daoDesc.entityDesc.simpleName} entity); @Delete Result<${daoDesc.entityDesc.simpleName}> delete(${daoDesc.entityDesc.simpleName} entity); @BatchInsert BatchResult<${daoDesc.entityDesc.simpleName}> batchInsert(List<${daoDesc.entityDesc.simpleName}> entities); }
Common Template Variables
The following variables are available in templates:
Entity Templates
entityDesc.packageName
- Package name for the entityentityDesc.simpleName
- Simple class name (e.g., “User”)entityDesc.tableName
- Database table nameentityDesc.comment
- Table comment from databaseentityDesc.propertyDescs
- List of property descriptors
DAO Templates
daoDesc.packageName
- Package name for the DAOdaoDesc.simpleName
- Simple interface name (e.g., “UserDao”)daoDesc.entityDesc
- Associated entity descriptordaoDesc.configClassName
- Doma config class name
Property Descriptors
property.propertyName
- Java property name (e.g., “userId”)property.propertyClassName
- Java type (e.g., “Integer”)property.columnName
- Database column nameproperty.id
- True if primary keyproperty.version
- True if version columnproperty.comment
- Column comment from database
Advanced Template Features
Conditional Generation
<#-- Only generate if table has a version column -->
<#if entityDesc.versionPropertyDesc??>
@Version
public ${entityDesc.versionPropertyDesc.propertyClassName} ${entityDesc.versionPropertyDesc.propertyName};
</#if>
Custom Imports Based on Properties
<#-- Import specific types based on entity properties -->
<#assign hasTimestamp = false>
<#list entityDesc.propertyDescs as property>
<#if property.propertyClassName == "java.sql.Timestamp">
<#assign hasTimestamp = true>
</#if>
</#list>
<#if hasTimestamp>
import java.sql.Timestamp;
</#if>
Troubleshooting
Common Issues and Solutions
Problem: “No suitable driver found” Error
[DOMAGEN0033] The class "org.postgresql.Driver" to which the parameter "driverClassName" refers is not found.
Solution: Make sure you’ve added the JDBC driver dependency to the domaCodeGen
configuration:
dependencies {
domaCodeGen("org.postgresql:postgresql:42.7.7")
}
Problem: Generated Code in Wrong Package
Solution: Check your package configuration:
entity {
packageName.set("com.example.entity") // Ensure this is set correctly
}
dao {
packageName.set("com.example.dao") // Ensure this is set correctly
}
Problem: Custom Templates Not Applied
Solution: Verify template directory structure and filenames:
your-project/
├── template/
│ ├── entity.ftl # Must match exact filename
│ ├── dao.ftl
│ └── entityListener.ftl
└── build.gradle.kts
domaCodeGen {
register("mydb") {
templateDir.set(file("$projectDir/template")) // Point to template directory
}
}
Best Practices
Use Testcontainers for Development
Testcontainers ensure consistent database environments across different machines:
// Preferred approach url.set("jdbc:tc:postgresql:15:///test?TC_INITSCRIPT=file:${initScript.absolutePath}")
Use Version Control for Schema Files
Keep your initialization scripts in version control:
src/main/resources/ ├── schema-postgresql.sql ├── schema-mysql.sql └── test-data.sql
Incremental Generation
Use specific tasks for faster development:
# Generate only entities (faster for schema changes) ./gradlew domaCodeGenMydbEntity # Generate only DAOs (faster for new tables) ./gradlew domaCodeGenMydbDao