geeendao学习小结
来源:互联网 发布:企业 如何优化 编辑:程序博客网 时间:2024/06/05 20:42
最近研究greendao框架,了解了一些东西,总结一下
greenDAO(greenDAO官网:http://greendao-orm.com/)是一个可以帮助Android开发者快速将Java对象映射到SQLite数据库的表单中的ORM解决方案,通过使用一个简单的面向对象API,开发者可以对Java对象进行存储、更新、删除和查询。
greenDAO的主要设计目标:
*最大性能(最快的Android ORM)
*易于使用API
*高度优化
*最小内存消耗
android几个ORM框架里,greendao还是比较突出的
GreenDao跟ORMLite的对比:http://blog.csdn.net/xushuaic/article/details/24434881
greendao与contentProvider ormlite的性能对比:http://blog.csdn.net/u012565107/article/details/21546829
greenDao与provider的区别:http://blog.csdn.net/u012565107/article/details/20042339
下面说下greendao的实战
通常都是从greendao在github上的DEMO开始的 https://github.com/greenrobot/greenDAO
从github上clone到本地
官方Demo里共有六个工程目录,分别为:
(1).DaoCore:库目录,即jar文件greendao-1.3.0-beta-1.jar的代码;
(2).DaoExample:android范例工程;
(3).DaoExampleGenerator:DaoExample工程的DAO类构造器,java工程;
(4).DaoGenerator:DAO类构造器,java工程;
(5).DaoTest、PerformanceTestOrmLite:其他测试相关的工程。
有3个必要的jar包
1. freemaker.jar
2. greendao-generator-1.3.0
3. greendao-1.3.7.jar
前面两个jar是为了build出dao而用的,放在DaoExampleGenerator下
而后面第三个jar给我们app使用的!
在开始之前,先说明一下整体流程,
大致上分为两个步骤,
1.产生 DAO files
2.把DAO files 加到自己的androidproject中
(1)generator --- greenDAO-generator.jar
网上使用说明很少,搜到的都是对于我这个新人来说看的云里雾里,于是本人就干脆直接看官方文档,这篇文章也是边看边写的,可能会有些混乱。
在github上下载的实例源码以及上面图中我们都会发现,会有一个generator。比如包里面会有greendao和greendao-generator,示例工程会有DaoExample和DaoExampleGenerator两个工程。之前一直不明白,看了官方文档终于理解了。
“In order to use greenDao in your project, you need to create a second project, the "generator project". 这个额外的工程是一个普通的java工程,而非android工程。它的任务是生成你希望的java对象所对应的DAO接口对象。
(2)核心的class -- greenDAO.jar
DAO的core library(greenDAO.jar)中有以下几个核心类,也是后面比用到的,先来大概了解下他们的结构吧,不然直接看他们的使用会云里雾里。
DaoMaster:一看名字就知道它是Dao中的最大的官了。它保存了sqlitedatebase对象以及操作DAO classes(注意:不是对象)。其提供了一些创建和删除table的静态方法,其内部类OpenHelper和DevOpenHelper实现了SQLiteOpenHelper并创建数据库的框架。
DaoSession:会话层。操作具体的DAO对象(注意:是对象),比如各种getter方法。
XXXDao:实际生成的某某DAO类,通常对应具体的java类,比如NoteDao等。其有更多的权限和方法来操作数据库元素。
XXXEntity:持久的实体对象。通常代表了一个数据库row的标准java properties。
引入了这个流程图,那么我们开始浅析关于这幅图。
居然GreenDao是用来帮我们简化建表、增删改查操作的,而且从上面的图中我们可以知道,原来不用GreenDao的时候,我们需要自己去写XyzEntity(实体类bean), 写完实体类之后,我们会写操作对应实体类的XyzDao。
另外GreenDao引入了两个类,
DaoSession: 操作具体DAO对象的各种getter方法。
DaoMaster: 它保存了我们的SQLiteDataBase对象及操作DAO类。还有提供一些创建和删除Table的静态方法,它的内部类OpenHelper和DevOpenHelper实现了SQLiteOpenHelper并创建数据库的框架。
接著打開ExampleDaoGenerator這個class,這隻java會幫你generate出4個files,
1. DaoMaster
2. DaoSession
3. Model
4. ModelDao
DAO就是用來負責db的一切操作, create table, insert, delete, query...etc.
model就單純是一個object而已!
這四個files都要加到我們的android project之中,
如下一个实例初始化代码:
helper =
new
DaoMaster.DevOpenHelper(
this
,
"notes-db"
,
null
);
db = helper.getWritableDatabase();
daoMaster =
new
DaoMaster(db);
daoSession = daoMaster.newSession();
noteDao = daoSession.getNoteDao();
即:先创建了一个SQLiteOpenHelper并创建连接到一个具体数据库;再根据具体的datebase创建一个master对象用于;最后通过master创建一个数据库的会话操作。
然后我们就要开始使用这些类了,根据我们上面的介绍,我们知道我们需要先从DaoMaster的内部类DevOpenHelper中得到我们想要的SQLiteDatabase对象,然后我们需要通过这个对象创建出对应的DaoMaster类对象daoMaster,有了这个daoMaster之后,我们就可以实例化一个新的会话Session, 通过这个Session再来得到相应的实体Dao, 最后操作这个Dao中的函数,就可以对数据库中相应的表进行增删改查了。(一)产制DAO等文件
先从DaoExampleGenerator工程下的DaoExampleGenerator.java讲起,运行DaoExampleGenerator.java产制VO DAO DAOMASTER DAOSEESION文件
/* * Copyright (C) 2011 Markus Junginger, greenrobot (http://greenrobot.de) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package de.greenrobot.daogenerator.gentest;import de.greenrobot.daogenerator.DaoGenerator;import de.greenrobot.daogenerator.Entity;import de.greenrobot.daogenerator.Property;import de.greenrobot.daogenerator.Schema;import de.greenrobot.daogenerator.ToMany;/** * Generates entities and DAOs for the example project DaoExample. * * Run it as a Java application (not Android). * * @author Markus */public class ExampleDaoGenerator { public static void main(String[] args) throws Exception { Schema schema = new Schema(1000, "de.greenrobot.daoexample"); addNote(schema); addCustomerOrder(schema); new DaoGenerator().generateAll(schema, "../DaoExample/src-gen"); } private static void addNote(Schema schema) { Entity note = schema.addEntity("Note"); note.addIdProperty(); note.addStringProperty("text").notNull(); note.addStringProperty("comment"); note.addDateProperty("date"); } private static void addCustomerOrder(Schema schema) { Entity customer = schema.addEntity("Customer"); customer.addIdProperty(); customer.addStringProperty("name").notNull(); Entity order = schema.addEntity("Order"); order.setTableName("ORDERS"); // "ORDER" is a reserved keyword order.addIdProperty(); Property orderDate = order.addDateProperty("date").getProperty(); Property customerId = order.addLongProperty("customerId").notNull().getProperty(); order.addToOne(customer, customerId); ToMany customerToOrders = customer.addToMany(order, customerId); customerToOrders.setName("orders"); customerToOrders.orderAsc(orderDate); }}
在main方法中,
1
Schema schema = new Schema(3, "de.greenrobot.daoexample");
该方法第一个参数用来更新数据库版本号,通过更新版本号来更新数据库。第二个参数为要生成的代码所在包的路径。路径由系统自动生成
默认Java package用来存放生成的entity、DAO文件、test代码。但也可以重新制定test文件的单独路径以及DAO文件的路径,代码:
schema.setDefaultJavaPackageTest(
"de.greenrobot.daoexample.test"
);
schema.setDefaultJavaPackageDao(
"de.greenrobot.daoexample.dao"
);
另外,还有两个flag,用来标示entity是否是activie以及是否使用keep sections。代码:
schema2.enableKeepSectionsByDefault();
schema2.enableActiveEntitiesByDefault();
Keep sections:因为entity class在每次generator执行时均会覆盖原来的程序,为了能够添加用户自定义代码到entity中,需要设置该参数。只需要把自己的代码添加到下面的KEEP[]块中间就可以了。
// KEEP INCLUDES - put your custom includes here
// KEEP INCLUDES END
...
// KEEP FIELDS - put your custom fields here
// KEEP FIELDS END
...
// KEEP METHODS - put your custom methods here
// KEEP METHODS END
然后进行建表和设置要生成代码文件的摆放路径,可以是目标工程的项目路径。
1
2
3
addNote(schema);
addCustomerOrder(schema);
new DaoGenerator().generateAll(schema, "../DaoExample/src-gen");
其中src-gen这个目录名需要在运行前手动创建,否则会报错。
如果运行后出现以下错误,则导入DaoGenerator项目的dao.ftl文件即可(或者直接使用DaoGenerator来生成DAO文件)。
1
2
3
4
5
Exception in thread "main" java.io.FileNotFoundException: Template "dao.ftl" not found.
at freemarker.template.Configuration.getTemplate(Configuration.java:742)
at freemarker.template.Configuration.getTemplate(Configuration.java:665)
at de.greenrobot.daogenerator.DaoGenerator.<init>(DaoGenerator.java:68)
at de.greenrobot.daogenerator.gentest.ExampleDaoGenerator.main(ExampleDaoGenerator.java:41)
运行后出现以下的提示说明DAO文件自动生成成功了,刷新一下DaoExample项目即可看到。
1
2
3
4
5
6
7
8
9
10
11
12
13
greenDAO Generator
Copyright 2011-2013 Markus Junginger, greenrobot.de. Licensed under GPL V3.
This program comes with ABSOLUTELY NO WARRANTY
Processing schema version 3...
Written F:\Android_Ex\work_10\DaoExample\src-gen\de\greenrobot\daoexample\NoteDao.java
Written F:\Android_Ex\work_10\DaoExample\src-gen\de\greenrobot\daoexample\Note.java
Written F:\Android_Ex\work_10\DaoExample\src-gen\de\greenrobot\daoexample\CustomerDao.java
Written F:\Android_Ex\work_10\DaoExample\src-gen\de\greenrobot\daoexample\Customer.java
Written F:\Android_Ex\work_10\DaoExample\src-gen\de\greenrobot\daoexample\OrderDao.java
Written F:\Android_Ex\work_10\DaoExample\src-gen\de\greenrobot\daoexample\Order.java
Written F:\Android_Ex\work_10\DaoExample\src-gen\de\greenrobot\daoexample\DaoMaster.java
Written F:\Android_Ex\work_10\DaoExample\src-gen\de\greenrobot\daoexample\DaoSession.java
Processed 3 entities in 204ms
运行后可以看到,DaoExample项目src-gen下面自动生成了8个文件,3个实体对象,3个dao,1个DaoMaster,1个DaoSession.
(二)创建表
1.创建一个实体类
1
Entity note = schema.addEntity("Note");
默认表名就是实体类名,也可以自定义表名
1
dao.setTableName("NoteList");
greenDAO会自动根据实体类属性创建表字段,并赋予默认值。例如在数据库方面的表名和列名都来源于实体类名和属性名。默认的数据库名称是大写使用下划线分隔单词,而不是在Java中使用的驼峰式大小写风格。例如,一个名为“CREATIONDATE”属性将成为一个数据库列“CREATION_DATE”。
设置一个自增长ID列为主键:
1
dao.addIdProperty().primaryKey().autoincrement();
设置其他各种类型的属性:
1
2
3
dao.addIntProperty("cityId");
dao.addStringProperty("infoType").notNull();//非null字段
dao.addDoubleProperty("Id");
在生成的实体类中,int类型为自动转为long类型。
如果在编译过程中出现以下错误,那么有可能是主键的类型错误所致:
1
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
在使用greenDAO时,一个实体类只能对应一个表,目前没法做到一个表对应多个实体类,或者多个表共用一种对象类型。后续的升级也不会针对这一点进行扩展。
public
boolean
isSaved(
int
ID)
{
QueryBuilder<SaveList> qb = saveListDao.queryBuilder();
qb.where(Properties.Id.eq(ID));
qb.buildCount().count();
return
qb.buildCount().count() >
0
?
true
:
false
;
}
public
List<PhotoGalleryDB> getPhotoGallery()
{
return
photoGalleryDao.loadAll();
// 获取图片相册
}
/** 通过图片id查找其目录id */
public
int
getTypeId(
int
picId)
{
QueryBuilder<PhotoGalleryDB> qb = photoGalleryDao.queryBuilder();
qb.where(Properties.Id.eq(picId));
if
(qb.list().size() >
0
)
{
return
qb.list().get(
0
).getTypeId();
}
else
{
return
-
1
;
}
}
List joes = userDao.queryBuilder()
.where(Properties.FirstName.eq(
"Joe"
))
.orderAsc(Properties.LastName)
.list();
public
List<CityInfoDB> getSupportingList(
int
cityId)
{
QueryBuilder<CityInfoDB> qb = cityInfoDao.queryBuilder();
qb.where(qb.and(Properties.CityId.eq(cityId),Properties.InfoType.eq(HBContant.CITYINFO_SL)));
qb.orderAsc(Properties.Id);
// 排序依据
return
qb.list();
}
QueryBuilder qb = userDao.queryBuilder();
qb.where(Properties.FirstName.eq(
"Joe"
),
qb.or(Properties.YearOfBirth.gt(
1970
),
qb.and(Properties.YearOfBirth.eq(
1970
), Properties.MonthOfBirth.ge(
10
))));
List youngJoes = qb.list();
picJsonDao.loadByRowId(picId);
- public Note loadNote(long id) {
- return noteDao.load(id);
- }
- /**
- * query list with where clause
- * ex: begin_date_time >= ? AND end_date_time <= ?
- * @param where where clause, include 'where' word
- * @param params query parameters
- * @return
- */
- public List<Note> queryNote(String where, String... params){
- return noteDao.queryRaw(where, params);
- }
public
void
addToPhotoTable(Photo p)
{
photoDao.insert(p);
}
- Note note = new Note(null, noteText, comment, new Date());
- noteDao.insert(note);
- Log.d("DaoExample", "Inserted new note, ID: " + note.getId());
DevOpenHelper helper =
new
DaoMaster.DevOpenHelper(
this
,
"notes-db"
,
null
);
db = helper.getWritableDatabase();
daoMaster =
new
DaoMaster(db);
daoSession = daoMaster.newSession();
noteDao = daoSession.getNoteDao();
Note note =
new
Note(
null
, noteText, comment,
new
Date());
noteDao.insert(note);
photoDao.insertOrReplace(photo);
photoDao.insertInTx(photo);
- /**
- * insert or update noteList use transaction
- * @param list
- */
- public void saveNoteLists(final List<Note> list){
- if(list == null || list.isEmpty()){
- return;
- }
- noteDao.getSession().runInTx(new Runnable() {
- @Override
- public void run() {
- for(int i=0; i<list.size(); i++){
- Note note = list.get(i);
- noteDao.insertOrReplace(note);
- }
- }
- });
- }
/** 清空相册图片列表的数据 */
public
void
clearPhoto()
{
photoDao.deleteAll();
}
public
void
deleteCityInfo(
int
cityId)
{
QueryBuilder<DBCityInfo> qb = cityInfoDao.queryBuilder();
DeleteQuery<DBCityInfo> bd = qb.where(Properties.CityId.eq(cityId)).buildDelete();
bd.executeDeleteWithoutDetachingEntities();
}
- /**
- * delete note by id
- * @param id
- */
- public void deleteNote(long id){
- noteDao.deleteByKey(id);
- Log.i(TAG, "delete");
- }
- public void deleteNote(Note note){
- noteDao.delete(note);
- }
来看看如何使用GreenDao实现CRUD
如下代码实现插入一个Note对象:
- DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "notes-db", null);
- db = helper.getWritableDatabase();
- daoMaster = new DaoMaster(db);
- daoSession = daoMaster.newSession();
- noteDao = daoSession.getNoteDao();
- Note note = new Note(null, noteText, comment, new Date());
- noteDao.insert(note);
官方推荐将取得DaoMaster对象的方法放到Application层这样避免多次创建生成Session对象。
感觉这个框架和Web的Hibernate有异曲同工之妙。
这里列出自己实际开发中的代码方便记忆:
首先是在Application层实现得到DaoMaster和DaoSession的方法
private
static
DaoMaster daoMaster;
private
static
DaoSession daoSession;
/**
* 取得DaoMaster
*
* @param context
* @return
*/
public
static
DaoMaster getDaoMaster(Context context)
{
if
(daoMaster ==
null
)
{
OpenHelper helper =
new
DaoMaster.DevOpenHelper(context, HBContant.DATABASE_NAME,
null
);
daoMaster =
new
DaoMaster(helper.getWritableDatabase());
}
return
daoMaster;
}
/**
* 取得DaoSession
*
* @param context
* @return
*/
public
static
DaoSession getDaoSession(Context context)
{
if
(daoSession ==
null
)
{
if
(daoMaster ==
null
)
{
daoMaster = getDaoMaster(context);
}
daoSession = daoMaster.newSession();
}
return
daoSession;
}
public
class
DBHelper
{
private
static
Context mContext;
private
static
DBHelper instance;
private
CityInfoDBDao cityInfoDao;
private
DBHelper()
{
}
public
static
DBHelper getInstance(Context context)
{
if
(instance ==
null
)
{
instance =
new
DBHelper();
if
(mContext ==
null
)
{
mContext = context;
}
// 数据库对象
DaoSession daoSession = HBApplication.getDaoSession(mContext);
instance.cityInfoDao = daoSession.getCityInfoDBDao();
}
return
instance;
}
/** 添加数据 */
public
void
addToCityInfoTable(CityInfo item)
{
cityInfoDao.insert(item);
}
/** 查询 */
public
List<EstateLoveListJson> getCityInfoList()
{
QueryBuilder<CityInfo> qb = cityInfoDao.queryBuilder();
return
qb.list();
}
/** 查询 */
public
List<CityInfo> getCityInfo()
{
return
cityInfoDao.loadAll();
// 查找图片相册
}
/** 查询 */
public
boolean
isSaved(
int
Id)
{
QueryBuilder<CityInfo> qb = cityInfoDao.queryBuilder();
qb.where(Properties.Id.eq(Id));
qb.buildCount().count();
return
qb.buildCount().count() >
0
?
true
:
false
;
// 查找收藏表
}
/** 删除 */
public
void
deleteCityInfoList(
int
Id)
{
QueryBuilder<CityInfo> qb = cityInfoDao.queryBuilder();
DeleteQuery<CityInfo> bd = qb.where(Properties.Id.eq(Id)).buildDelete();
bd.executeDeleteWithoutDetachingEntities();
}
/** 删除 */
public
void
clearCityInfo()
{
cityInfoDao.deleteAll();
}
/** 通过城市id查找其类型id */
public
int
getTypeId(
int
cityId)
{
QueryBuilder<CityInfo> qb = cityInfoDao.queryBuilder();
qb.where(Properties.Id.eq(cityId));
if
(qb.list().size() >
0
)
{
return
qb.list().get(
0
).getTypeId();
}
else
{
return
0
;
}
}
/** 多重查询 */
public
List<CityInfo> getIphRegionList(
int
cityId)
{
QueryBuilder<CityInfoDB> qb = cityInfoDao.queryBuilder();
qb.where(qb.and(Properties.CityId.eq(cityId), Properties.InfoType.eq(HBContant.CITYINFO_IR)));
qb.orderAsc(Properties.Id);
// 排序依据
return
qb.list();
}
}
1. 查询 -- Queries
1)你可以使用原生的SQl(raw sql)语句;
2)也可以使用推荐的方法:使用greenDAO提供的QueryBuilder的API。
3)查询还支持结果延迟加载(lazy-loading),主要为操作较大查询结果是节约内存并提高性能。
(1)QueryBuilder
使用过sql语句查询的人都会有一种感触(主要针对不是专职开发数据库并对sql不是很熟练的人),写起来复杂、不能再第一时间发现问题(只能在运行过程中验证sql的正确性)、查找bug麻烦等等。QueryBuilder的出现就是为了解决sql使用的问题,提高开发效率。
看一个略微复杂的例子,查询first name是Joe,并在1970年10月以及之后的所有人:
QueryBuilder qb = userDao.queryBuilder();
qb.where(Properties.FirstName.eq("Joe"),
qb.or(Properties.YearOfBirth.gt(1970),
qb.and(Properties.YearOfBirth.eq(1970), Properties.MonthOfBirth.ge(10))));
List youngJoes = qb.list();
不用我来解释了吧。
(2)Lazylist
greenDAO支持返回唯一查询结果(如果没有返回null) ---- 调用Query或QueryBuilder的unique()方法;
也支持返回list ---- 调用list()方法。
当不希望返回null作为结果时,则调用uniqueOrThrow()方法,当结果null时将直接抛出异常。
返回多个结果解释:
list(): 所有entity均会被加载到内存中。结果仅是一个简单的ArrayList。使用最简单。
listLazy(): 查询结果会根据需要加载到内存中。列表中的元素仅在accessed for the first time,它才会被加载并缓存。该方法必须主动关闭(Must be closed)。
listLazyUncached(): 虚拟的结果列表,任何对元素的方法实际都会到数据库中去加载数据。Must be closed
listIterator(): 根据需要迭代结果(lazily)。数据不缓存。Must be closed
在greenDAO实现中,后3中其实都使用的LazyList类。为了实现根据需要加载,其内部实现上是保存了数据库cursor的引用。这也是为何这3中方式must be closed,其就是为了释放内部cursor和迭代器(通常是在try-finally块中完成close)。
(3)Query
解释:Query类代表了一个可以被重复执行的查询。在QueryBuilder内部其实也是会定义一个Query并执行完成查询。
这将带来极大地方便,因为任何人都不希望在每次查询的时候总是写一遍query代码。同时Query还可以根据需要改变参数。如下实例代码:
使用Query对象查询名为Joe并出生在1970年的人:
Query query = userDao.queryBuilder().where(
Properties.FirstName.eq("Joe"), Properties.YearOfBirth.eq(1970))
.build();
List joesOf1970 = query.list();
然后想再查询出生在1977年之后并叫Marias的人:
query.setParameter(0, "Maria");
query.setParameter(1, 1977);
List mariasOf1977 = query.list();
注意:参数的编号是创建query所设置的顺序。
(4)多线程查询
有时我们希望在多线程中执行查询。从greenDAO 1.3版本开始,实例化Query对象均会绑定到其自己的线程,这样我们就可以安全的设置Query参数,因为其他线程无法影响到。如果其他线程使用别的线程的query(比如设置参数、执行查询)将会抛出异常。因此,我们也不需要做同步工作,而且不要加锁,因为加入相关的事务用到了该Query对象将导致死锁。
为完全地避免可能产生的死锁,1.3版本引入了forCurrentThread()方法。该方法将返回本线程内的Query实例,每次调用该方法时,参数均会被重置为最初创建时的一样。
(5)原生sql查询
推荐的方法执行原生sql语句是通过QueryBuilder和WhereCondition.StringCondition。如下代码:
Query query = userDao.queryBuilder().where(
new StringCondition("_ID IN " + "(SELECT USER_ID FROM USER_MESSAGE WHERE READ_FLAG = 0)").build();
假如QueryBuilder没有提供你需要的特性,你也可以使用queryRaw() 和 queryRawCreate() 方法。具体就不再详细介绍了。
(6)删除操作(delete query)
删除操作会删除所有满足条件的实体。为实现批量删除(bulk delete),调用QueryBuilder的 buildDelete方法并执行DeleteQuery。该部分API还不稳定,可能会改变就不介绍了。
(7)问题定位(日志)
你可能会遇到查询结果并不是预期的那样,这时候你就可以设置两个静态flag参数来打日志定位问题:
QueryBuilder. LOG_SQL = true ;QueryBuilder. LOG_VALUES = true ;
这将会将产生的sql命令以及传递的参数以日志方式输出,由此方便程序员定位问题。
更詳細的內容可以看
http://blog.csdn.net/xushuaic/article/details/24438841
http://blog.csdn.net/xushuaic/article/details/24496191
http://my.oschina.net/cheneywangc/blog/196354
http://my.oschina.net/cheneywangc/blog/196360
- geeendao学习小结
- [学习小结]Ajax小结
- 面试小结 学习小结
- 学习小结
- 学习小结
- 学习小结
- 学习小结
- 学习小结
- 学习小结
- 学习小结
- 学习小结
- 学习小结
- 学习小结
- 学习小结
- 学习小结
- 学习小结
- 学习小结
- 学习小结
- static关键字的使用和注意事项
- Python学习笔记-第六章 抽象
- UFLDL Exercise:Convolution and Pooling
- 【Java设计模式之工厂模式详解】
- BigDecimal金额比较
- geeendao学习小结
- UTM投影分带标准及投影转换
- JavaScript之入门简介
- 改变Ubuntu 12.04 Terminal 和 gedit 中tab的颜色
- OpenGL ES for ios 读书笔记
- Vijos P1102 陶陶摘苹果
- 第十三周项目6-体验文件操作(实战3-读入文件、并输出文件salary)
- 用NuGet.Server管好自家的包包
- C#WinForm中TreeView默认展开设置