GreenDAO代码生成器:自动化DAO类生成与定制化配置

GreenDAO代码生成器:自动化DAO类生成与定制化配置

GreenDAO代码生成器采用MVC架构与FreeMarker模板引擎,实现了高度可定制的DAO类、实体类和ContentProvider的自动化生成。通过Schema配置定义数据库结构和实体关系,结合模板引擎的灵活性和KEEP智能保留机制,开发者可以生成符合企业标准的ORM代码,显著提升Android应用开发效率和数据访问性能。

DaoGenerator架构与模板引擎原理

GreenDAO的代码生成器采用经典的MVC架构模式,将数据模型(Model)、模板视图(View)和生成控制(Controller)分离,通过FreeMarker模板引擎实现高度可定制的代码生成机制。

核心架构设计

DaoGenerator的架构遵循分层设计原则,主要包含以下核心组件:

mermaid

FreeMarker模板引擎集成

GreenDAO采用FreeMarker作为模板引擎,通过以下方式实现集成:

模板配置初始化

private Configuration getConfiguration(String probingTemplate) throws IOException {
    Configuration config = new Configuration(Configuration.VERSION_2_3_29);
    config.setClassForTemplateLoading(getClass(), "/");
    
    // 多环境适配:IDE开发环境与Gradle构建环境
    try {
        config.getTemplate(probingTemplate);
    } catch (TemplateNotFoundException e) {
        File dir = new File("src/main/resources/");
        if (!dir.exists()) {
            dir = new File("DaoGenerator/src/main/resources/");
        }
        if (dir.exists() && new File(dir, probingTemplate).exists()) {
            config.setDirectoryForTemplateLoading(dir);
            config.getTemplate(probingTemplate);
        } else {
            throw e;
        }
    }
    return config;
}

模板数据模型构建

代码生成过程中,DaoGenerator构建统一的数据模型传递给模板引擎:

private void generate(Template template, File outDirFile, String javaPackage, 
                     String javaClassName, Schema schema, Entity entity, 
                     Map<String, Object> additionalObjectsForTemplate) throws Exception {
    
    Map<String, Object> root = new HashMap<>();
    root.put("schema", schema);      // Schema对象
    root.put("entity", entity);      // Entity对象
    
    if (additionalObjectsForTemplate != null) {
        root.putAll(additionalObjectsForTemplate); // 附加对象
    }
    
    // 处理KEEP sections保留机制
    if (entity != null && entity.getHasKeepSections()) {
        checkKeepSections(file, root);
    }
    
    Writer writer = new FileWriter(file);
    template.process(root, writer);  // 模板渲染
}

模板语法与特性

GreenDAO的模板文件(.ftl)使用FreeMarker语法,支持丰富的模板特性:

1. 变量插值与条件判断

<#if entity.active>
import ${schema.defaultJavaPackageDao}.${schema.prefix}DaoSession;
import org.greenrobot.greendao.DaoException;
</#if>

2. 循环遍历与列表处理

<#list entity.properties as property>
    private ${property.javaTypeInEntity} ${property.propertyName};
</#list>

3. 宏定义与代码复用

<#macro multiIndexes>
{
<#list entity.multiIndexes as index>
    @Index(value = "${index.orderSpec}"<#if index.nonDefaultName>, name = "${index.name}"</#if>)<#sep>,
</#list>
}
</#macro>

4. 类型映射与转换

<#assign toBindType = {
    "Boolean":"Long", "Byte":"Long", "Short":"Long", 
    "Int":"Long", "Long":"Long", "Float":"Double", 
    "Double":"Double", "String":"String", "ByteArray":"Blob" 
}/>

KEEP Sections智能保留机制

GreenDAO实现了独特的代码保留机制,允许开发者在生成的代码中保留自定义内容:

private void checkKeepSections(File file, Map<String, Object> root) {
    if (file.exists()) {
        String contents = new String(DaoUtil.readAllBytes(file));
        
        // 正则匹配保留区域
        Matcher matcher = patternKeepIncludes.matcher(contents);
        if (matcher.matches()) {
            root.put("keepIncludes", matcher.group(1)); // 保留imports
        }
        
        matcher = patternKeepFields.matcher(contents);
        if (matcher.matches()) {
            root.put("keepFields", matcher.group(1));   // 保留字段
        }
        
        matcher = patternKeepMethods.matcher(contents);
        if (matcher.matches()) {
            root.put("keepMethods", matcher.group(1));  // 保留方法
        }
    }
}

模板文件组织结构

GreenDAO的模板文件采用模块化设计,每个模板负责生成特定类型的代码:

模板文件生成内容主要功能
entity.ftl实体类生成数据实体类,包含字段、getter/setter、关系映射
dao.ftlDAO类生成数据访问对象,包含CRUD操作和查询方法
dao-master.ftlDaoMaster生成数据库主类,负责数据库创建和升级
dao-session.ftlDaoSession生成会话管理类,管理DAO实例和事务
dao-unit-test.ftl单元测试生成对应的单元测试类
content-provider.ftlContentProvider生成内容提供者相关代码

代码生成流程

整个代码生成过程遵循严格的流程控制:

mermaid

高级模板特性

动态注解生成

<#assign entityAttrs = []>
<#if schema.name != "default">
    <#assign entityAttrs = entityAttrs + ["schema = \"${schema.name}\""]>
</#if>
<#if entity.active>
    <#assign entityAttrs = entityAttrs + ["active = true"]>
</#if>
@Entity<#if (entityAttrs?size > 0)>(${entityAttrs?join(", ")})</#if>

关系映射处理

<#list entity.toOneRelations as toOne>
    <#if toOne.useFkProperty>
    @ToOne(joinProperty = "${toOne.fkProperties[0].propertyName}")
    <#else>
    @ToOne
    </#if>
    private ${toOne.targetEntity.className} ${toOne.name};
</#list>

这种架构设计使得GreenDAO的代码生成器既保持了高度的灵活性,又确保了生成代码的质量和一致性。模板引擎的使用让开发者可以轻松定制生成的代码结构,而KEEP机制则保证了自定义代码在重新生成时不会丢失。

Schema配置与实体生成策略

GreenDAO的Schema配置是整个代码生成过程的核心,它定义了数据库的结构、版本控制以及实体类的生成策略。通过Schema对象,开发者可以精细控制数据库的各个方面,从基础的表结构到复杂的实体关系映射。

Schema基础配置

Schema是GreenDAO代码生成器的根配置对象,每个Schema代表一个数据库版本。创建Schema时需要指定三个核心参数:

// 创建Schema实例
Schema schema = new Schema(2, "com.example.dao");
schema.setDefaultJavaPackageDao("com.example.dao");
schema.enableKeepSectionsByDefault();

Schema配置参数说明:

参数类型描述默认值
versionint数据库版本号必需参数
defaultJavaPackageString实体类的默认包名必需参数
nameStringSchema名称"default"
defaultJavaPackageDaoStringDAO类的包名同实体包名
defaultJavaPackageTestString测试类的包名同DAO包名

实体定义与属性映射

实体是Schema的核心组成部分,每个实体对应数据库中的一个表。GreenDAO提供了丰富的属性类型支持:

// 创建用户实体
Entity user = schema.addEntity("User");
user.setDbName("users"); // 设置表名

// 添加主键属性
PropertyBuilder idBuilder = user.addIdProperty();
idBuilder.autoincrement(); // 自增主键

// 添加字符串属性
PropertyBuilder nameBuilder = user.addStringProperty("name");
nameBuilder.notNull().unique(); // 非空且唯一

// 添加日期属性
PropertyBuilder createTimeBuilder = user.addDateProperty("createTime");
createTimeBuilder.notNull(); // 非空约束

// 添加数值属性
PropertyBuilder ageBuilder = user.addIntProperty("age");
ageBuilder.index(); // 创建索引

属性类型映射表:

Java类型数据库类型PropertyType枚举说明
booleanINTEGERBoolean布尔值
byteINTEGERByte字节
shortINTEGERShort短整型
intINTEGERInt整型
longINTEGERLong长整型
floatREALFloat单精度浮点
doubleREALDouble双精度浮点
StringTEXTString字符串
byte[]BLOBByteArray字节数组
DateINTEGERDate日期时间

高级实体配置策略

GreenDAO支持多种高级配置选项,满足复杂业务场景需求:

// 实体继承配置
user.setSuperclass("BaseEntity"); // 设置父类
user.implementsInterface("Serializable", "Parcelable"); // 实现接口

// 代码生成控制
user.setSkipGeneration(false); // 是否生成实体类
user.setSkipCreationInDb(true); // 是否跳过表创建
user.setConstructors(true); // 是否生成构造函数

// 文档和代码注入
user.setJavaDoc("用户实体类,包含用户基本信息");
user.setCodeBeforeClass("@Entity\npublic class User extends BaseEntity {");

// 自定义导入
user.addImport("java.util.List");
user.addImport("android.os.Parcelable");

索引与约束配置

索引和约束是保证数据完整性和查询性能的关键:

// 创建复合索引
Index compositeIndex = new Index();
compositeIndex.addProperty(nameBuilder.getProperty());
compositeIndex.addProperty(ageBuilder.getProperty());
compositeIndex.makeUnique(); // 唯一索引
user.addIndex(compositeIndex);

// 属性级约束配置
PropertyBuilder emailBuilder = user.addStringProperty("email");
emailBuilder.notNull().unique().index(); // 非空、唯一、索引

// 自定义数据库类型
PropertyBuilder salaryBuilder = user.addDoubleProperty("salary");
salaryBuilder.dbType("DECIMAL(10,2)"); // 自定义数据库类型

关系映射策略

GreenDAO支持多种实体关系映射,包括一对一、一对多和多对多关系:

mermaid

关系配置示例:

// 一对多关系:用户-订单
Entity order = schema.addEntity("Order");
PropertyBuilder userIdBuilder = order.addLongProperty("userId");
ToOne userToOne = order.addToOne(user, userIdBuilder.getProperty(), "user");

// 多对多关系:订单-商品(通过连接表)
Entity product = schema.addEntity("Product");
Entity orderItem = schema.addEntity("OrderItem");

PropertyBuilder orderIdBuilder = orderItem.addLongProperty("orderId");
PropertyBuilder productIdBuilder = orderItem.addLongProperty("productId");

ToMany orderToProducts = order.addToMany(product, orderItem, 
    orderIdBuilder.getProperty(), productIdBuilder.getProperty());

代码生成模板定制

GreenDAO使用FreeMarker模板引擎生成代码,开发者可以自定义模板以满足特定需求:

实体生成流程: mermaid

最佳实践建议

  1. 版本管理策略:每次数据库结构变更时递增Schema版本号,确保平滑升级
  2. 命名规范:保持Java属性名和数据库列名的一致性,使用驼峰命名法
  3. 索引优化:为频繁查询的字段创建索引,但避免过度索引影响写入性能
  4. 关系设计:根据业务需求选择适当的关系类型,避免过度规范化
  5. 模板定制:根据需要自定义代码生成模板,保持代码风格一致性

通过合理的Schema配置和实体生成策略,GreenDAO能够自动生成高效、类型安全的数据库访问代码,显著提升Android应用的开发效率和运行性能。

自定义模板与代码生成定制化

GreenDAO的代码生成器采用FreeMarker模板引擎,为开发者提供了高度灵活的定制化能力。通过自定义模板,开发者可以完全控制生成的DAO类、实体类和其他相关代码的结构和内容,满足特定项目的架构需求。

模板文件结构与配置

GreenDAO的模板文件位于DaoGenerator/src-template/目录下,包含以下核心模板:

  • entity.ftl - 实体类生成模板
  • dao.ftl - DAO类生成模板
  • dao-master.ftl - DaoMaster类生成模板
  • dao-session.ftl - DaoSession类生成模板
  • dao-unit-test.ftl - 单元测试类生成模板
  • content-provider.ftl - ContentProvider生成模板

模板引擎的配置在DaoGenerator.java中实现,使用FreeMarker的Configuration类进行模板加载和处理:

private Configuration getConfiguration(String probingTemplate) throws IOException {
    Configuration config = new Configuration(Configuration.VERSION_2_3_29);
    config.setClassForTemplateLoading(getClass(), "/");
    // ... 模板加载逻辑
    return config;
}

模板变量与数据模型

每个模板接收一个数据模型,包含以下核心变量:

变量名类型描述
schemaSchema数据库模式对象
entityEntity实体对象(如生成实体类时)
contentProviderContentProviderContentProvider对象(可选)
实体模板变量示例

entity.ftl模板中,可以使用以下实体相关变量:

<#-- @ftlvariable name="entity" type="org.greenrobot.greendao.generator.Entity" -->
<#-- @ftlvariable name="schema" type="org.greenrobot.greendao.generator.Schema" -->

<#assign toBindType = {"Boolean":"Long", "Byte":"Long", "Short":"Long", "Int":"Long", "Long":"Long", "Float":"Double", "Double":"Double", "String":"String", "ByteArray":"Blob" }/>
<#assign toCursorType = {"Boolean":"Short", "Byte":"Short", "Short":"Short", "Int":"Int", "Long":"Long", "Float":"Float", "Double":"Double", "String":"String", "ByteArray":"Blob" }/>

自定义模板开发流程

1. 理解现有模板结构

首先分析现有模板的结构,了解GreenDAO的代码生成模式。以实体类模板为例:

mermaid

2. 创建自定义模板

复制现有模板并根据需求进行修改。例如,创建自定义实体模板:

<#-- 自定义实体模板 -->
package ${entity.javaPackage};

import org.greenrobot.greendao.annotation.*;

<#-- 自定义导入 -->
import com.example.custom.annotations.*;
import com.example.custom.utils.EntityUtils;

@Entity
@CustomEntityAnnotation(version = "1.0")
public class ${entity.className} extends BaseEntity {
    
    <#-- 自定义字段生成逻辑 -->
    <#list entity.properties as property>
    @CustomFieldAnnotation
    private ${property.javaTypeInEntity} ${property.propertyName};
    </#list>

    <#-- 自定义构造函数 -->
    public ${entity.className}() {
        super();
    }

    <#-- 自定义方法 -->
    public String toCustomJson() {
        return EntityUtils.toJson(this);
    }
}
3. 配置自定义模板路径

修改DaoGenerator.java以支持自定义模板路径:

public class CustomDaoGenerator extends DaoGenerator {
    
    private Template customTemplateEntity;
    private Template customTemplateDao;
    
    public CustomDaoGenerator() throws IOException {
        super();
        
        Configuration config = getConfiguration("custom-dao.ftl");
        customTemplateEntity = config.getTemplate("custom-entity.ftl");
        customTemplateDao = config.getTemplate("custom-dao.ftl");
    }
    
    @Override
    public void generateAll(Schema schema, String outDir) throws Exception {
        // 使用自定义模板生成代码
        generate(customTemplateEntity, outDirFile, entity.getJavaPackage(), 
                entity.getClassName(), schema, entity);
        generate(customTemplateDao, outDirFile, entity.getJavaPackageDao(), 
                entity.getClassNameDao(), schema, entity);
    }
}

高级定制化特性

KEEP区域保护机制

GreenDAO提供了智能的KEEP区域保护功能,确保自定义代码在重新生成时不被覆盖:

private void checkKeepSections(File file, Map<String, Object> root) {
    if (file.exists()) {
        String contents = new String(DaoUtil.readAllBytes(file));
        
        // 提取KEEP INCLUDES区域
        Matcher matcher = patternKeepIncludes.matcher(contents);
        if (matcher.matches()) {
            root.put("keepIncludes", matcher.group(1));
        }
        
        // 提取KEEP FIELDS区域  
        matcher = patternKeepFields.matcher(contents);
        if (matcher.matches()) {
            root.put("keepFields", matcher.group(1));
        }
        
        // 提取KEEP METHODS区域
        matcher = patternKeepMethods.matcher(contents);
        if (matcher.matches()) {
            root.put("keepMethods", matcher.group(1));
        }
    }
}

在模板中使用KEEP区域:

<#if entity.hasKeepSections>
// KEEP INCLUDES - put your custom includes here
${keepIncludes!}// KEEP INCLUDES END

// KEEP FIELDS - put your custom fields here  
${keepFields!}// KEEP FIELDS END

// KEEP METHODS - put your custom methods here
${keepMethods!}// KEEP METHODS END
</#if>
条件生成与扩展点

利用FreeMarker的条件语句实现灵活的代码生成:

<#-- 根据实体特性条件生成代码 -->
<#if entity.active>
    /** Used to resolve relations */
    @Generated
    private transient ${schema.prefix}DaoSession daoSession;
</#if>

<#-- 自定义关系处理 -->
<#if entity.toManyRelations?has_content>
    <#list entity.toManyRelations as toMany>
    @CustomToManyAnnotation
    private List<${toMany.targetEntity.className}> ${toMany.name};
    </#list>
</#if>

模板变量参考手册

核心变量表
变量类型描述示例
entity.classNameString实体类名User
entity.javaPackageString实体包名com.example.model
entity.propertiesList实体属性列表
schema.prefixString模式前缀My
schema.defaultJavaPackageDaoString默认DAO包名com.example.dao
属性变量表
变量类型描述示例
property.propertyNameString属性名userName
property.javaTypeStringJava类型String
property.dbNameString数据库列名user_name
property.primaryKeyboolean是否主键true
property.notNullboolean是否非空false

实战:创建企业级定制模板

步骤1:定义企业规范

首先确定企业级的代码规范,包括:

  • 统一的类注释格式
  • 标准的异常处理模式
  • 自定义注解的使用
  • 日志记录规范
步骤2:开发定制模板
<#-- 企业级实体模板 -->
package ${entity.javaPackage};

<#-- 企业标准导入 -->
import org.greenrobot.greendao.annotation.*;
import com.company.standard.annotation.*;
import com.company.standard.logging.Logger;
import javax.validation.constraints.*;

/**
 * ${entity.className} Entity
 * 
 * @generated
 * @enterprise-version 1.0
 * @table ${entity.dbName}
 */
@Entity
@EnterpriseEntity
public class ${entity.className} {
    
    private static final Logger LOG = Logger.getLogger(${entity.className}.class);
    
    <#list entity.properties as property>
    <#if property.javaDocField ??>
    ${property.javaDocField}
    </#if>
    <#if property.primaryKey>
    @Id<#if property.autoincrement>(autoincrement = true)</#if>
    @EnterpriseId
    </#if>
    <#if property.notNull>
    @NotNull
    @EnterpriseRequired
    </#if>
    private ${property.javaTypeInEntity} ${property.propertyName};
    </#list>

    <#-- 企业标准构造函数 -->
    public ${entity.className}() {
        LOG.debug("Creating new {} instance", getClass().getSimpleName());
    }
    
    <#-- 企业标准业务方法 -->
    public void validate() {
        // 企业级验证逻辑
    }
}
步骤3:集成构建流程

将自定义模板集成到Gradle构建流程中:

task generateEnterpriseDaos(type: JavaExec) {
    classpath = configurations.daoGenerator
    main = 'com.company.generator.EnterpriseDaoGenerator'
    args = ['src/main/enterprise-model', 'src/main/java']
}

模板调试与最佳实践

调试技巧
  1. 启用模板调试输出
// 在generate方法中添加调试输出
System.err.println("Data map for template: " + root);
  1. 使用模板验证工具
# 验证模板语法
fmpp -T src-template/entity.ftl
最佳实践
  1. 保持模板简洁:将复杂逻辑移到工具类中
  2. 版本控制:对自定义模板进行版本管理
  3. 文档化:为每个自定义模板编写使用文档
  4. 测试覆盖:为生成的代码编写单元测试

mermaid

通过GreenDAO的模板定制化功能,开发者可以创建完全符合企业标准和项目需求的ORM代码生成解决方案,大幅提升开发效率的同时保证代码质量和一致性。

ContentProvider自动生成与集成

GreenDAO的ContentProvider自动生成功能为Android开发者提供了强大的数据共享能力,通过自动生成的ContentProvider,您可以轻松地将数据库数据暴露给其他应用组件,实现跨应用数据共享和系统级集成。

ContentProvider配置与生成

在GreenDAO中,为实体类生成ContentProvider非常简单。通过调用addContentProvider()方法,您可以快速配置并生成完整的ContentProvider实现:

// 创建实体并配置ContentProvider
Entity userEntity = schema.addEntity("User");
userEntity.addIdProperty();
userEntity.addStringProperty("name");
userEntity.addStringProperty("email");

// 添加ContentProvider配置
ContentProvider contentProvider = userEntity.addContentProvider();
contentProvider.setAuthority("com.example.app.provider");
contentProvider.setBasePath("users");
contentProvider.setJavaPackage("com.example.app.provider");
配置选项详解

GreenDAO的ContentProvider生成器提供了丰富的配置选项:

配置方法说明默认值
setAuthority(String)设置ContentProvider的authority包名 + ".provider"
setBasePath(String)设置URI的基础路径空字符串
setClassName(String)设置生成的类名实体类名 + "ContentProvider"
setJavaPackage(String)设置Java包名Schema的默认包名
readOnly()设置为只读模式false

自动生成的ContentProvider结构

GreenDAO生成的ContentProvider包含完整的CRUD操作实现:

mermaid

集成到Android应用

1. 清单文件配置

生成的ContentProvider需要在AndroidManifest.xml中注册:

<application>
    <provider
        android:name=".provider.UserContentProvider"
        android:authorities="com.example.app.provider"
        android:exported="true" />
</application>
2. DaoSession初始化

在Application类中初始化DaoSession并设置到ContentProvider:

public class MyApplication extends Application {
    private DaoSession daoSession;

    @Override
    public void onCreate() {
        super.onCreate();
        
        DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "notes-db");
        Database db = helper.getWritableDb();
        daoSession = new DaoMaster(db).newSession();
        
        // 设置到ContentProvider
        UserContentProvider.daoSession = daoSession;
    }
}

ContentProvider URI模式

GreenDAO自动生成的ContentProvider支持两种URI模式:

mermaid

使用示例

查询数据
// 查询所有用户
Cursor cursor = getContentResolver().query(
    UserContentProvider.CONTENT_URI,
    null, null, null, null
);

// 查询特定ID的用户
Uri userUri = Uri.withAppendedPath(UserContentProvider.CONTENT_URI, "1");
Cursor userCursor = getContentResolver().query(
    userUri, null, null, null, null
);
插入数据
ContentValues values = new ContentValues();
values.put("name", "John Doe");
values.put("email", "john@example.com");

Uri newUserUri = getContentResolver().insert(
    UserContentProvider.CONTENT_URI, values
);
更新数据
ContentValues values = new ContentValues();
values.put("email", "newemail@example.com");

int updated = getContentResolver().update(
    userUri, values, null, null
);

高级特性

1. 只读模式

通过调用readOnly()方法,可以生成只读的ContentProvider:

ContentProvider readOnlyProvider = userEntity.addContentProvider();
readOnlyProvider.readOnly();
2. 自定义URI处理

生成的ContentProvider使用UriMatcher进行URI匹配,支持扩展自定义URI模式。

3. 数据库连接管理

ContentProvider使用共享的DaoSession实例,确保数据库连接的高效管理和线程安全。

最佳实践

  1. 权限控制:合理设置android:exported属性,控制其他应用的访问权限
  2. 性能优化:对于大数据集查询,使用分页和投影优化
  3. 错误处理:正确处理数据库操作异常和URI解析错误
  4. 安全考虑:对输入参数进行验证和清理,防止SQL注入

通过GreenDAO的ContentProvider自动生成功能,开发者可以快速实现标准化的数据访问接口,大大减少了重复代码的编写,同时保证了代码的一致性和可维护性。

总结

GreenDAO代码生成器通过其强大的架构设计和模板引擎集成,为Android开发提供了完整的ORM解决方案。从Schema配置、实体定义到ContentProvider生成,每个环节都支持高度定制化。KEEP机制确保自定义代码在重新生成时不被覆盖,而模板引擎的灵活性允许开发者创建符合特定企业标准的代码结构。这种自动化代码生成方式不仅提高了开发效率,还保证了代码质量和一致性,是Android数据库开发的强大工具。

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值