Android ORM数据库框架之-greenDao(三)
最后更新于:2022-04-01 15:49:08
关于上篇说到的数据库更新问题,我正在找国外大牛的二次封装的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://docs.gechiui.com/gc-content/uploads/sites/kancloud/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,实体,替换掉模板中对应的,输出。关于具体输出的源码,实在是太长了,童鞋们自己看吧。