Android Sqlite 框架GreenDao的源码分析笔记
GreenDao是一个开源的项目,有助于开发者与存储在Sqlite中的数据打交道。Sqlite是关系型数据库。然而,用Sqlite开发需要一些额外的工作。写SQL与解析查询的结果是一件相当繁琐的工作。GreenDao将会替你做这些额外的工作。它将Java对象映射到数据库的表(经常被称作ORM),通过以这种方式,你可以使用简单的面向对象的API来存储、更新、删除以及查询Java对象。节省时间,将精力聚焦于真正的问题上面。(翻译自GreenDao的官方的概要)
源码分析的步骤
- Async包下的代码,主要是数据库异步操作的对象封装
- Converter包下的代码,主要是数据库与实体对象相互转换的接口
- IdentityScope包下的代码,主要是实体对象的跟踪、管理以及缓存
- Internal包下得代码,主要是数据库的配置、快速游标的实现,优化后的HashMap,Sql的帮助类,为指定的表创建Sql语句的帮助类
- Query包下得代码,对Sql的增删改查做了抽象类以及实体类的封装
- GreenDao直接向上层暴露接口的代码,主要包括:Dao、DaoMaster、DaoSession
数据库异步操作对象的封装
- AsyncDaoException
Dao异步操作的异常类的封装
- AsyncOperation
异步操作排队进入队列等待执行
- AsyncOperationExecutor
封装了异步操作的线程池以及相关的事件的回调
- AsyncOperationListener
异步操作的事件回调,函数的参数是一个异步操作的对象
- AsyncSession
实体类操作的异步的接口,队列处理将会在后台的线程中
AsyncDaoException的剖析
- 成员变量:failedOperation,类型是AsyncOperation,也就是说当Dao操作sql的时候,抛出异常,需要维护是哪一个异步操作抛出的异常。
- 继承关系:继承DaoException,DaoException本质上是运行时的异常。
- 扩展方法:getFailedOperation,向外界暴露了AsyncOperation,知道是哪一步出现了运行时的异常。
AsyncOperation的剖析
- 成员变量:FLAG_MERGE_TX,标志当前是否合并事务,默认是1。
- 成员变量:type,所属的类型是OperationType,实际上也就是sql的操作的类型。
- 成员变量:dao,所属的类型是AbstractDao(所有的Dao的抽象类,实现增删改查实体对象的操作)。
- 成员变量:database,所属的类型是SQLiteDatabase。
- 成员变量:timeStart以及timeComplete标志的是异步操作的完成的开始时间以及结束的时间。
- 成员变量:completed,布尔值的类型,主要是线程的等待与挂起的时候将会用到。每一次检查当前的异步操作是否是执行完毕。
- 成员变量:mergedOperationsCount以及sequenceNumber对应的是合并操作的事务的数量以及当前的异步操作的序号。
核心的方法讲解
- 构造函数:AsyncOperation,在这个构造函数中,参数包括异步操作的类型、Dao的抽象类、SqliteDataBase的参数、相关的参数param以及最后一个参数标志当前是否允许合并操作事务。
- 成员变量的相关的getter以及setter的方法不再进行赘述。
- 成员方法:getResult以及waitForCompletion的作用是获取异步执行的操作的结果,如果当前的异步操作没有结束,进入锁等待。
- 成员方法:reset,也就是将相关的成员变量恢复到最原始的数值。
AsyncOperationExecutor的剖析
成员变量:
- executorService,类型是ExecutorService,也就是Java中的线程池。
- queue,类型是BlockingQueue队列,类型强制为AsyncOperation。实际上也就是异步操作的存储的队列。
- maxOperationCountToMerge,类型是整数类型,表示的是等待合并的最大的异步操作的对象的数量。
- executorRunning,类型是布尔值的类型,表示的是当前的线程池是否处于执行任务的状态。
- listener与listenerMainThread均是异步操作的观察者的回调,只是listenerMainThread是在主线程中回调。
- waitForMergeMillis,等待合并的时间。
- countOperationsEnqueued与countOperationsCompleted,对应的是异步操作进入队列的数目与异步操作已经完成的数目。
- handlerMainThread,主线程中处理的Handler。
- lastSequenceNumber,对应的是上一次的异步操作的序号。
核心的方法详解:
- AsyncOperationExecutor,构造函数,构造函数中,创建BlockingQueue的队列。初始化允许最大允许合并的异步操作的数目是50,允许最大合并超时的时间是50ms。
- enqueue,参数是异步操作AsyncOperation,在函数内部将当前的AsyncOperation进入队列。同时如果当前的线程池不是处于运行的状态,开启线程池的运行的状态。
- run,在while循环中,从队列中逐一的去除异步操作的对象AsyncOperation。如果两次在队列中取出的对象都是空指针,就在逻辑上将当前的线程池停止运行。当取出了异步操作的对象,判断当前的操作是否允许合并事务操作处理,如果允许,(等待50ms,等待另外一次异步操作的合并,因为一次事务是昂贵的,而判断是否支持合并的条件是两个异步操作是都支持合并的同时所隶属的数据库是同一个数据库)。
- mergeTxAndExecute,也就是两个异步操作合并事务并且进行执行。
核心代码块
try {
for (int i = 0; i < mergedOps.size(); i++) {
//从合并的列表中取出异步操作的对象
AsyncOperation operation = mergedOps.get(i);
//执行异步操作的对象
executeOperation(operation);
if (operation.isFailed()) {
//如果当前的操作失败,回滚
break;
}
//如果当前的对象已经是数组中的最后一个异步执行的任务
if (i == mergedOps.size() - 1) {
//再从全局的队列中检查,当前是否有新的任务进入
AsyncOperation peekedOp = queue.peek();
//如果当前合并处理的最大的操作数目还没有达到,并且当前的异步操作与新取出来的操作是能够合并的
if (i < maxOperationCountToMerge && operation.isMergeableWith(peekedOp)) {
AsyncOperation removedOp = queue.remove();
//同时 为了避免peek 与 remove取出的对象不是同一个对象 需要做异步检查
if (removedOp != peekedOp) {
throw new DaoException("Internal error: peeked op did not match removed op");
}
mergedOps.add(removedOp);
} else {
//如果代码走到这里,结束当前的事务
db.setTransactionSuccessful();
success = true;
break;
}
}
}
} finally {
try {
db.endTransaction();
} catch (RuntimeException e) {
success = false;
}
}
AsyncOperationListener的剖析
成员方法
onAsyncOperationCompleted,AsyncOperation操作完成以后回调。
AsyncSession的剖析
文档英文注释
* Asynchronous interface to entity operations. All operations will enqueued a @link {@link AsyncOperation} and return
* immediately (fine to call on the UI/main thread). The queue will be processed in a (single) background thread. The
* processing order is the call order of the operations. It's possible to start multiple AsyncSessions that will
* execute
* concurrently.
英文注释翻译如下:
这是一个异步的接口针对实体对象的操作。所有的操作将会将会进入队列,并且立即返回到对应的UI线程与主线程中。处理操作的顺序依赖这些操作被调度的顺序。有可能启动多个异步的会话,那样就会并发的执行。
主要职责
- Facade to AsyncOperationExecutor: prepares operations and delegates work to AsyncOperationExecutor.
- 相对于AsyncOperationExecutor,这是一层壳子。准备操作,同时将具体的工作托管给AsyncOperationExecutor。
成员变量
- daoSession,所属的类型是AbstractDaoSession,可以理解为这是对Sql的异步操作线程池的外部包装。
- executor,所属的类型是AsyncOperationExecutor,也就是维护一个线程池的变量。
- sessionFlags,所属的类型是整数的类型,按照其成员变量的注解,将这个变量设置给所有的异步操作的对象。
成员方法
- getMaxOperationCountToMerge(),获取最大允许的合并的异步操作的数目。
- setMaxOperationCountToMerge,给线程池设置最大允许合并的异步操作的数目。
- getWaitForMergeMillis,获取事务合并等待的最大允许的毫秒数。
- setWaitForMergeMillis,设置书屋合并等待的最大的允许的毫秒数。
- 其中的setListener与getListener分别是获取与设置异步操作的回调的观察者。
- waitForCompletion,等待在线程池中执行的异步任务完全执行成功的回调。
- insert,分为两类,一类是单个的异步的任务,同时携带sessionFlag,一类是批量异步任务的插入,同样支持携带sessionFlag的参数。后面的update,delete等sql操作均可以以此类推。
DB的数值与实体对象的转换
- PropertyConverter
数据库的属性字段与Java对象相互映射的抽象的接口
PropertyConverter剖析
- convertToEntityProperty,成员方法,参数是泛型,隶属于sql的属性字段,字面意思看是将数据库的字段映射为Java对象中的属性
- convertToDatabaseValue,成员方法,参数是泛型,隶属于Java对象的属性字段,字面伊苏是将Java对象的属性字段映射为数据库的属性字段。
对象作用于范围限定
- IdentityScope,被GreenDao内部需要的实体对象作用域的一般性的接口。作用域的限定让GreenDao重复使用Java对象。
- IdentityScopeLong,实体对象的上下文,提供作用域,在作用域的内部,实体对象将会被跟踪与管理。
- IdentityScopeObject,实体对象的上下文,提供作用域,在作用域的内部,实体对象将会被跟踪与管理。
- IdentityScopeType,这个很好理解,前面既然提到实体对象的跟踪与管理存在一个作用域,那么就单独提供一个枚举类,列举作用域分为两类,一类是一次会话,一次是没有任何作用域的限定。
IdentityScope
成员方法
- get,通过键key,获取对应的数值。
- put,存储键值对。
- getNoLock,通过键key,不加锁获取对应的数值。
- putNoLock,不加锁的存储键值对。
- detach,将指定的键值对拆开、分离。
- remove,参数是Key,移除指定键所对应的数值
- remove,参数是实现了Iterable
- lock与unlock分别表示的是加锁与不加锁。
- reserveRoom,保留指定数目的存储空间。
IdentityScopeLong
成员变量
- map,对应的对象的类型是LongHashMap,也就是GreenDao为存储键是Long类型而优化后的HashMap。
- lock,Java内部的互斥锁。
成员方法正如前面在接口中说的一样,主要分插入与删除在加锁不加锁上的不同的处理。
GreenDao内部的一些辅助类
- DaoConfig,DaoConfig存储了GreenDao所需要的必须的数据。同时,这个对象自身是由AbstractDaoMaster持有。这个类将会从Dao的类中获取所需要的信息
- FastCursor,实现了游标Cursor的接口。
- LongHashMap,针对键为Long类型而设计的简约的HashMap。
- SqlUtils,创建由GreenDao内部使用的Sql语句的工具类。
- TableStatements,针对指定的表创建Sql语句的工具类。
DaoConfig
成员变量:
- db,所属的类型是SqliteDataBase。
- tablename,数据库的表的名称。
- properties,数据库的字段属性。
- allColumns, 指定的表的所有的字段的名称
- pkColumns,指定的表的主键的字段的名称的数组
- nonPkColumns,指定的表的非主键的字段的名称的数组
- identityScope,实现了IdentityScope接口的对象,也就是实体对象的存储类。
成员方法:
- DaoConfig,构造函数,参数是SQliteDataBase与继承了AbstractDao的类的Class的对象。职责:1、初始化表名 2、初始化表字段的数组(通过对参数中的Class用反射) 3、初始化主键字段与非主键字段。(Property字段中维护了一个是否是主键的字段)。
- reflectProperties,对指定的Class应用反射,内部有一个内部类Properties,本质上是对这个内部类应用反射。
- DaoConfig,另外一个构造函数,参数是DaoConfig,做对象的拷贝,但是已经在指定的作用域的缓存对象不做拷贝。
- initIdentityScope, 初始化对象的存储的作用域,根据当前的表的主键是整数还是非整数,分别选择用IdentityScopeLong与IdentityScopeObject来进行对象在指定的会话的存储。
FastCursor
成员变量:
- window,所属的类型是sql中的CursorWindow,操纵筛选结果集
- position,当前的游标所指向的位置。
- count,当前的结果集的数目。
成员方法:
- FastCursor, 初始化CursorWindow以及position以及Count。
- 实现了Cursor的相关的接口。
LongHashMap
记住原理就行,HashMap的原理是数组与链表的结合再加上合理的Hash算法。
SqlUtils(工具类)
成员方法
- appendProperty,构造一个StringBuilder,内部的参数是StringBuilder,表的前缀、相关的表字段对应的Property属性字段
- appendColumn,直接将表字段的名称粘贴上去。
- appendPlaceholders,添加位置的字段?
- 后面还有一些创建插入、查询的Sql的语句的工具方法,不再进行赘述。
TableStatements(表Sql语句查询的工具类)
成员变量:
- tablename、allColumns、pkColumns 分别对应的是表的名称、所有的字段的名称的数组、所有的主键的字段的数组
- insertStatement、insertOrReplaceStatement、updateStatement、deleteStatement 增删改查的Sql语句的可执行的对象。
- selectAll、selectByKey、selectByRowId、selectKeys 几种不同语句的select的字符串。
成员函数:
- TableStatements,参数有表明,字段名的数组,主键名的数组。
- getInsertStatement, 获取可执行的插入执行语句的对象
- 其余的成员方法依旧是创建相关的执行语句的对象,不再进行赘述。
与查询相关的GreenDao的类的封装
- AbstractQuery,一个可重复行返回查询的Java对象的列表的查询语句的抽象类。
- AbstractQueryData,AbstractQuery所对应的数据层。
- AbstractQueryWithLimit,继承了AbstractQuery,同时加上了查询的开始位置以及偏移量的限定。
- CloseableListIterator,这是一个列表的迭代器,需要被关闭,释放底层的资源,比如数据库的游标等。
- CountQuery,很简单,指定的查询语句结果集的查询的数目。
- CursorQuery,返回一个Android中的游标。值得注意的是,游标的使用通常是一件麻烦的事情。GreenDao通过使用实体类提供了一个较高的层次的抽象。这个类与一般性的Cursor的代码搭配使用还是很有用的。
- DeleteQuery,这是一个针对删除实体对象可重复使用的查询类。
- Join,允许与其他的相关的类型的实体类做查询,在做Join的实体对象上允许使用where。
- LazyList,这是一个线程安全、不可修改的集合。可以读取通过底层游标访问的实体对象。当操作完毕后,确保对其进行关闭。这个集合既可被缓存,也可以不用。缓存的集合将对象缓存在内存中,避免多次加载实体对象。当查询完所有的对象,这个集合将会自动的关闭游标。然而,为了避免产生游标的内存泄露,程序不应当依赖这种特性。
- Query,返回实体对象的可重复查询。
- QueryBuilder,构造客户端的实体对象的查询,通过使用约束条件以及相关的参数,而不是通过Sql语句。通过使用相关类的queryBuilder来生成。Java中的对象的相关的属性是被Field对象所引用在”Properties”这个自动生成的Dao的内部类中。这样做的好处,在编译的时候就能够检查,防止类型出错。
- WhereCollector,内部使用的类,来手机Where条件。
- WhereCondition,在查询的时候,将where条件进行对象处理化。使用Property对象在DaoClass中创建新的where条件。
AbstractQuery
成员变量
- dao,所属的类型是AbstractDao。
- daoAccess,所属的类型是InternalQueryDaoAccess,内部查询Dao的访问的对象。
- sql,查询的Sql语句的字符串。
- parameters,查询所携带的相关的参数。
- ownerThread,当前的操作所隶属的线程。
成员方法
- toStringArray,参数是Java对象的数组,将对象数组转换为字符串数组,调用的是对象的toString方法。
- AbstractQuery,构造函数,初始化Dao,Dao的访问的对象,运行线程以及相关的参数。
- setParameter,使用指定的索引设置参数,指定的所以就是在构建查询时候被添加的位置。
- checkThread,检查当前的线程与构造函数中参数的线程是否是同一个线程,如果不是,抛出一个方法只能在主线程中调用的异常
AbstractQueryData
成员变量
- sql,字符串类型,查询Sql语句
- dao,类型是AbstractDao。
- initialValues,初始化数值的字符串的数组。
- queriesForThreads,针对多线程操作的查询操作,类型是SparseArray
成员方法
- AbstractQueryData,构造函数,初始化sql,dao,initialValues。构造SparseArray的对象,内部的泛型是WeakReference。
- forCurrentThread,这个是优化后的一个函数的版本,如果当前的运行的线程与参数query的主线程是一个线程,那么操作起来会比较快。仔细分析一下:1、如果当前两个操作的线程是同一个线程,那么直接用Java中的System.arraycopy,将其中的一个query的查询的参数直接拷贝到另外查询的对象的参数上面去,然后直接返回。 2、如果两个操作的线程不相同,排除线程的id的不相同的情况,通过线程的id编号,从SparseArray对象中取出当前的线程的id所对应的引用的对象,如果引用获取到查询的对象,与第一步一样,做参数的拷贝,然后直接返回,如果不存在对象,重新创建一个新的查询的对象,同时将其放入引用的集合中。
- 对引用对象的结合中的集合数据进行回收。
AbstractQueryWithLimit
成员变量
- limitPosition,限制查询的开始的位置。
- offsetPosition, 限制查询结果集的最大游标的偏移量。
成员方法
- AbstractQueryWithLimit,构造函数,一方面调用父类的构造方法,另一方面初始化limitPosition与offsetPosition的数值。
- setParameter, 参数是整型的索引与Java对象类型的参数。索引的所用是在指定的位置被添加当构造查询语句的时候。在这里面要注意的地方是,标准where条件优先,然后是做join操作的where条件。
- Date与布尔值转换为整数再来进行处理。
- setLimit,设置返回结果集的最大的查询数目。
- setOffset,设置返回结果集的偏移量。
CloseableListIterator
成员方法
- 由于是继承了ListIterator与Closeable,所以具备这两个类的相关的方法。
CountQuery
成员变量
- queryData,所隶属的类型是QueryData,注意这个类是CountQuery内部定义的内部类。这个类同样是继承了AbstractQueryData,只是在createQuery的方法中,返回的对象是CountQuery.
成员方法
- count,返回结果集的查询的数目。直接做简单的select操作。
CursorQuery
成员方法
- queryData,类型是CursorQuery的内部类QueryData。在QueryData的内部方法createQuery,返回的是一个CursorQuery的对象。
DeleteQuery
成员方法
- executeDeleteWithoutDetachingEntities,做删除逻辑的时候,判断如果当前的数据库已经被当前的线程加锁了,直接调用db执行删除操作。如果还没有加锁,在一次事务中完成此次的删除的操作。
Join
成员变量
- sourceTablePrefix,源表的前缀字符串。
- daoDestination,类型是AbstractDao,目标数据库。
- joinPropertySource,源表做join操作的属性。
- tablePrefix,表的前缀。
- whereCollector,where条件的聚合器。
成员方法:
- Join,构造函数中,参数是源表的表名的前缀、目标表做join操作的property字段、目标的Dao、目标表做join操作的property字段以及where条件的聚合器的初始化。
- where,使用逻辑上的and将指定的whereCondition对象添加到where簇中。
- whereOr,使用逻辑上的or将指定的whereCondition添加到where簇中。
LazyList
成员变量
- daoAccess,内部Dao查询访问的抽象的对象。
- cursor,Sql游标的类型。
- entities,实体对象的集合。
- size,集合的尺寸的大小。
- lock,Java中的互斥锁。
- loadedCount,已经装载的数目。
成员方法
- LazyList,构造函数中,初始化游标、访问Dao的对象、互斥锁以及集合缓存的列表的对象。
- loadRemaining,装载之前尚未添加的剩下的集合的对象。
- 重点看一下其中的get的方法。
if (entities != null) {
//从缓存的集合中取出指定的所有的对象
E entity = entities.get(location);
//如果指定的集合中没有对象
if (entity == null) {
//开始加锁
lock.lock();
try {
//再做一次检测
entity = entities.get(location);
if (entity == null) {
//如果依旧没有缓存的对象
//利用游标,从指定的索引处获取对象
entity = loadEntity(location);
//同时做缓存的操作
entities.set(location, entity);
//加载数目进行自增
loadedCount++;
//如果当前的缓存的数目达到最大的数目,将游标关闭
if (loadedCount == size) {
cursor.close();
}
}
} finally {
//锁资源进行释放
lock.unlock();
}
}
return entity;
} else {
//如果压根就不支持缓存 直接利用游标获取数据
lock.lock();
try {
return loadEntity(location);
} finally {
lock.unlock();
}
}
Query
- 对游标内部的结果集进行再次处理,暴露出相关的结合供外界调用。中间的过渡的桥梁主要是访问数据库的Dao的对象。不再赘述。
QueryBuilder(查询语句构造成对象的工具类)
成员变量
- whereCollector,where条件聚合器。
- orderBuilder,StringBuilder类型,字符串逐一的拼接。
- values,参数的集合。
- joins,做join参数的集合。
- dao,类型是AbstractDao。
- tablePrefix,表的前缀。
- limit与offset对游标与查询的结果集进行一定的限制。
成员方法的功能
-成员方法主要是构造基本sql(包括表的sql,where的sql以及join的sql),再配合查询的参数,形成最终的查询的sql。
WhereCollector
成员变量
- dao,类型是AbstractDao。
- whereConditions,where条件的集合。
- tablePrefix,表明的前缀。
成员方法
- add,将指定的whereCondition的对象以及集合依次添加到集合中。
- checkProperty,检查指定的property是否是Dao中的属性的一部分,通过反射判断。
WhereCondition
- where条件封装成为whereCondition的对象。这是一个接口
接口内部的抽象的方法
- appendTo
- appendValuesTo
AbstractDao
成员变量的代码的定义
//Android中的 sql的统一管理者
protected final SQLiteDatabase db;
//GreenDao的必要数据的配置
protected final DaoConfig config;
//数据的作用域的配置
protected IdentityScope<K, T> identityScope;
//键值为long的数据的作用域的配置
protected IdentityScopeLong<T> identityScopeLong;
//Table语句的sql构造者的对象的创建
protected TableStatements statements;
//Dao的操作的托管的对象
protected final AbstractDaoSession session;
//数据库的表的主键的序号
protected final int pkOrdinal;
核心的几个方法的代码剖析
- load
//断言主键唯一性
assertSinglePk();
if (key == null) {
return null;
}
//存在数据作用域的缓存
if (identityScope != null) {
//如果从缓存中获取到数值 直接返回
T entity = identityScope.get(key);
if (entity != null) {
return entity;
}
}
//缓存中不存在数据 创建查询的sql语句
String sql = statements.getSelectByKey();
String[] keyArray = new String[] { key.toString() };
Cursor cursor = db.rawQuery(sql, keyArray);
return loadUniqueAndCloseCursor(cursor);
- updateInTx 使用一次事务,在数据库中,根据实体对象,更新对应的数据
SQLiteStatement stmt = statements.getUpdateStatement();
db.beginTransaction();
RuntimeException txEx = null;
try {
//给statement语句加锁
synchronized (stmt) {
if (identityScope != null) {
//给数据的缓存的作用域加锁
identityScope.lock();
}
try {
for (T entity : entities) {
//for循环 同步更新数据
updateInsideSynchronized(entity, stmt, false);
}
} finally {
//释放锁资源
if (identityScope != null) {
identityScope.unlock();
}
}
}
//结束事务
db.setTransactionSuccessful();
} catch (RuntimeException e) {
txEx = e;
} finally {
try {
db.endTransaction();
} catch (RuntimeException e) {
if (txEx != null) {
throw txEx;
} else {
throw e;
}
}
}
AbstractDaoMaster
成员变量
- db 实际操作的db对象
- schemaVersion 数据库对应的版本号
- daoConfigMap,HashMap配置GreenDao的必须数据,形成DaoConfig的对象。
AbstractDaoSession
功能注解:
- 提供访问Dao,同时充当了session的缓存。为了访问Dao,直接调用DaoSession的子类的get指定的实体的Dao即可。默认的情况下,AbstractDaoSession有一个会话的缓存,但是这个session的缓存不是一般的数据的缓存来提高性能的,而是管理作用域范围内的实体对象。
成员变量
- db,实际操作的Sql对象
- entityToDao,这是一个HashMap,键是实体对象的Class,数值是AbstractDao。
成员方法
- registerDao,参数是Class与AbstractDao的对象,将其放入缓存HashMap中。
- 具体的利用AbstractDao进行增删改查的操作不再进行赘述。而这里的AbstractDao是根据参数的对象的Class的属性从缓存中获取。
InternalQueryDaoAccess
功能注解:
- 其中的参数绝大多数是Cursor,利用其成员变量AbstractDao来操纵游标获取所需要的结果集。
Property
功能注解:
- 对应的是数据库的表的一个字段的对象封装的描述。通过查询语句的构造器来创建WhereCondition的对象。
成员变量的代码的定义注解
//对应的数据库的表字段的序号
public final int ordinal;
//对应的数据类型
public final Class<?> type;
//对应的属性的名称
public final String name;
//当前这个字段是否是数据库的表的主键
public final boolean primaryKey;
//数据库表字段的定义的名称
public final String columnName;
成员方法的主要的说明
- 函数eq、notEq、between等,分别创建对应的PropertyCondition的对象。