🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
关于上篇说到的数据库更新问题,我正在找国外大牛的二次封装的github代码。找到会贴出来。 咱们这篇,小小地分析下greendao-generator的源码,和大家一起了解下,代码的生成。 咱们写的java项目代码很简单,就是个初始化Schema——>添加Entity ——>生成的过程。 - Schema 我们看下我们写的代码 ~~~ Schema schema = new Schema(2,"gl.com.greendaodemo"); ~~~ 很简单,就是版本号+生成代码包名。 我们看下Schema的部分源码。 ~~~ private final int version; private final String defaultJavaPackage; private String defaultJavaPackageDao; private String defaultJavaPackageTest; private final List<Entity> entities; private Map<PropertyType, String> propertyToDbType; private Map<PropertyType, String> propertyToJavaTypeNotNull; private Map<PropertyType, String> propertyToJavaTypeNullable; private boolean hasKeepSectionsByDefault; private boolean useActiveEntitiesByDefault; public Schema(int version, String defaultJavaPackage) { this.version = version; this.defaultJavaPackage = defaultJavaPackage; this.entities = new ArrayList<Entity>(); initTypeMappings(); } ~~~ 看得出,构造函数就是初始化了数据库版本、包名、实体list以及属性类型(initTypeMappings()来完成属性类型初始化),下面贴出这个函数的部分代码 ~~~ propertyToDbType = new HashMap<PropertyType, String>(); propertyToDbType.put(PropertyType.Boolean, "INTEGER"); propertyToDbType.put(PropertyType.Byte, "INTEGER"); propertyToDbType.put(PropertyType.Short, "INTEGER"); propertyToDbType.put(PropertyType.Int, "INTEGER"); propertyToDbType.put(PropertyType.Long, "INTEGER"); propertyToDbType.put(PropertyType.Float, "REAL"); propertyToDbType.put(PropertyType.Double, "REAL"); propertyToDbType.put(PropertyType.String, "TEXT"); propertyToDbType.put(PropertyType.ByteArray, "BLOB"); propertyToDbType.put(PropertyType.Date, "INTEGER"); ~~~ Schema的初始化看完了,接下来我们看下如何添加实体 - Entity以及addEntity ~~~ Entity people = schema.addEntity("People"); people.addStringProperty("name").primaryKey(); //名字 people.addIntProperty("age"); //年龄 ~~~ 上面是添加一个实体的过程,我们瞅瞅addEntity();函数 ~~~ public Entity addEntity(String className) { Entity entity = new Entity(this, className); entities.add(entity); return entity; } ~~~ 嗯,简单 ,就是给list添加了一个对象。。。那么,给实体添加约束的源码又是什么呢?我们以addIdProperty()为例。 ~~~ public PropertyBuilder addIdProperty() { PropertyBuilder builder = addLongProperty("id"); builder.columnName("_id").primaryKey(); return builder; } ~~~ 可以看到,这里直接将给了个_id的列并作为主键存在。上面有用到PropertyBuilder这个类,这个是干什么的? ![](https://box.kancloud.cn/2016-04-08_57076806a69e5.jpg "") ,偶,这个类就是给数据库中的字段设置约束的。看到,有自增、非空、主键等等。 ~~~ person.addStringProperty("name") ~~~ 我们看看如何给字段指定类型。上面 的哪一行代码 最终会调用 下面这个构造函数。可以看到,这里就有了字段类型了,那么字段类型又有哪些呢,还记得我们在初始化Schema的时候的代码么,没错,就是那些。但是,光那些是不够用的,greendao还支持我们自定义。请移步[官方介绍](http://greendao-orm.com/documentation/custom-types/) ~~~ public Property(Schema schema, Entity entity, PropertyType propertyType, String propertyName) { this.schema = schema; this.entity = entity; this.propertyName = propertyName; this.propertyType = propertyType; } ~~~ 接下来便是重头戏,代码生成部分 - 代码生成 - ~~~ new DaoGenerator().generateAll(schema, "/Users/mac/Desktop/GLandroidstudy/AS/greendaodemo/src/main/java-gen"); ~~~ 我们看看DaoGenerator的构造函数 ~~~ public DaoGenerator() throws IOException { System.out.println("greenDAO Generator"); System.out.println("Copyright 2011-2015 Markus Junginger, greenrobot.de. Licensed under GPL V3."); System.out.println("This program comes with ABSOLUTELY NO WARRANTY"); patternKeepIncludes = compilePattern("INCLUDES"); patternKeepFields = compilePattern("FIELDS"); patternKeepMethods = compilePattern("METHODS"); Configuration config = new Configuration(); config.setClassForTemplateLoading(this.getClass(), "/"); config.setObjectWrapper(new DefaultObjectWrapper()); templateDao = config.getTemplate("dao.ftl"); templateDaoMaster = config.getTemplate("dao-master.ftl"); templateDaoSession = config.getTemplate("dao-session.ftl"); templateEntity = config.getTemplate("entity.ftl"); templateDaoUnitTest = config.getTemplate("dao-unit-test.ftl"); templateContentProvider = config.getTemplate("content-provider.ftl"); } ~~~ 那个.ftl文件是什么呢?.ftl是Freemarker文件的后缀名,是个模版语言引擎。关于Freemarker更多介绍,自行百度。我们以entity.ftl为例,简单介绍几行。 ~~~ public class ${entity.className}<#if entity.superclass?has_content> extends ${entity.superclass} </#if><#if entity.interfacesToImplement?has_content> implements <#list entity.interfacesToImplement as ifc>${ifc}<#if ifc_has_next>, </#if></#list></#if> { <#list entity.properties as property> <#if property.notNull && complexTypes?seq_contains(property.propertyType)> /** Not-null value. */ </#if> <#if property.codeBeforeField ??> ${property.codeBeforeField} </#if> private ${property.javaTypeInEntity} ${property.propertyName}; </#list> ~~~ 上面的结果就是 ~~~ private class classname (extends supperclass )(implements interface){ private type property; ... } ~~~ 就是输出类似上面的东西,其实语法很简单,就是根据传进来的entity实体,根据entity实体的内容来讲${}部分用对应的东西替代,最后就输出成我们的文件了。好,就这么多把,我们再来看下generateAll()的代码。 ~~~ public void generateAll(Schema schema, String outDir, String outDirEntity, String outDirTest) throws Exception { long start = System.currentTimeMillis(); File outDirFile = toFileForceExists(outDir); File outDirEntityFile = outDirEntity != null? toFileForceExists(outDirEntity): outDirFile; File outDirTestFile = outDirTest != null ? toFileForceExists(outDirTest) : null; schema.init2ndPass(); schema.init3rdPass(); System.out.println("Processing schema version " + schema.getVersion() + "..."); List<Entity> entities = schema.getEntities(); for (Entity entity : entities) { generate(templateDao, outDirFile, entity.getJavaPackageDao(), entity.getClassNameDao(), schema, entity); if (!entity.isProtobuf() && !entity.isSkipGeneration()) { generate(templateEntity, outDirEntityFile, entity.getJavaPackage(), entity.getClassName(), schema, entity); } if (outDirTestFile != null && !entity.isSkipGenerationTest()) { String javaPackageTest = entity.getJavaPackageTest(); String classNameTest = entity.getClassNameTest(); File javaFilename = toJavaFilename(outDirTestFile, javaPackageTest, classNameTest); if (!javaFilename.exists()) { generate(templateDaoUnitTest, outDirTestFile, javaPackageTest, classNameTest, schema, entity); } else { System.out.println("Skipped " + javaFilename.getCanonicalPath()); } } for (ContentProvider contentProvider : entity.getContentProviders()) { Map<String, Object> additionalObjectsForTemplate = new HashMap<String, Object>(); additionalObjectsForTemplate.put("contentProvider", contentProvider); generate(templateContentProvider, outDirFile, entity.getJavaPackage(), entity.getClassName() + "ContentProvider", schema, entity, additionalObjectsForTemplate); } } generate(templateDaoMaster, outDirFile, schema.getDefaultJavaPackageDao(), "DaoMaster", schema, null); generate(templateDaoSession, outDirFile, schema.getDefaultJavaPackageDao(), "DaoSession", schema, null); long time = System.currentTimeMillis() - start; System.out.println("Processed " + entities.size() + " entities in " + time + "ms"); } ~~~ 最后都会调用上面的一段代码,上面的代码在做什么呢。显示创建几个文件夹,然后遍历List< Entity>,输出内容的 ~~~ generate(templateEntity, outDirEntityFile, entity.getJavaPackage(), entity.getClassName(), schema, entity); ~~~ 就是根据传进来的末班,包名,类名,schema,实体,替换掉模板中对应的,输出。关于具体输出的源码,实在是太长了,童鞋们自己看吧。