2012-02-28 14:21 8339人阅读 评论(2) 举报
 分类:
android应用开发(22) 

版权声明:本文为博主原创文章,未经博主允许不得转载。

本文参考了这篇文章:http://www.ibm.com/developerworks/cn/opensource/os-cn-sqlite/

简介: SQLite 是一款非常流行的嵌入式数据库,它支持 SQL 查询,并且只用很少的内存。Android在运行时集成了 SQLite,所以每个 Android 应用程序都可以使用 SQLite 数据库。对数熟悉 SQL 的开发人员来时,使用 SQLite 相当简单。可以,由于 JDBC 不适合手机这种内存受限设备,所以 Android 开发人员需要学习新的 API 来使用 SQLite。本文主要讲解 SQLite 在 Android 环境中的基本使用。

SQLite 介绍

SQLite 一个非常流行的嵌入式数据库,它支持 SQL 语言,并且只利用很少的内存就有很好的性能。此外它还是开源的,任何人都可以使用它。许多开源项目((Mozilla, PHPPython)都使用了 SQLite。

SQLite 由以下几个组件组成:SQL 编译器、内核、后端以及附件。SQLite 通过利用虚拟机和虚拟数据库引擎(VDBE),使调试、修改和扩展 SQLite 的内核变得更加方便。

图 1. SQLite 内部结构

SQLite 基本上符合 SQL-92 标准,和其他的主要 SQL 数据库没什么区别。它的优点就是高效,Android 运行时环境包含了完整的 SQLite。

SQLite 和其他数据库最大的不同就是对数据类型的支持,创建一个表时,可以在 CREATE TABLE 语句中指定某列的数据类型,但是你可以把任何数据类型放入任何列中。当某个值插入数据库时,SQLite 将检查它的类型。如果该类型与关联的列不匹配,则 SQLite 会尝试将该值转换成该列的类型。如果不能转换,则该值将作为其本身具有的类型存储。比如可以把一个字符串(String)放入 INTEGER 列。SQLite称这为“弱类型”(manifest typing.)。

此外,SQLite 不支持一些标准的 SQL 功能,特别是外键约束(FOREIGN KEY constrains),嵌套 transcaction 和 RIGHT OUTER JOIN 和 FULL OUTER JOIN, 还有一些 ALTER TABLE 功能。

除了上述功能外,SQLite 是一个完整的 SQL 系统,拥有完整的触发器,交易等等。

Android 集成了 SQLite 数据库

Android 在运行时(run-time)集成了 SQLite,所以每个 Android 应用程序都可以使用 SQLite 数据库。对于熟悉 SQL 的开发人员来时,在 Android 开发中使用 SQLite 相当简单。但是,由于 JDBC 会消耗太多的系统资源,所以 JDBC 对于手机这种内存受限设备来说并不合适。因此,Android 提供了一些新的 API 来使用 SQLite 数据库,Android开发中,程序员需要学使用这些 API。

数据库存储在 data/< 项目文件夹 >/databases/ 下。

Android 开发中使用 SQLite 数据库

Activites 可以通过 Content Provider 或者 Service 访问一个数据库。下面会详细讲解如果创建数据库,添加数据和查询数据库。

创建数据库

Android 不自动提供数据库。在 Android 应用程序中使用 SQLite,必须自己创建数据库,然后创建表、索引,填充数据。Android 提供了 SQLiteOpenHelper 帮助你创建一个数据库,你只要继承 SQLiteOpenHelper 类,就可以轻松的创建数据库。SQLiteOpenHelper类根据开发应用程序的需要,封装了创建和更新数据库使用的逻辑。SQLiteOpenHelper 的子类,至少需要实现三个方法:

1.  构造函数,调用父类 SQLiteOpenHelper 的构造函数。这个方法需要四个参数:上下文环境(例如,一个 Activity),数据库名字,一个可选的游标工厂(通常是 Null),一个代表你正在使用的数据库模型版本的整数。

2. onCreate()方法,它需要一个 SQLiteDatabase 对象作为参数,根据需要对这个对象填充表和初始化数据。

3. onUpgrage()方法,它需要三个参数,一个SQLiteDatabase 对象,一个旧的版本号和一个新的版本号,这样你就可以清楚如何把一个数据库从旧的模型转变到新的模型。

下面示例代码展示了如何继承 SQLiteOpenHelper 创建数据库(AbstractDBOpenHelper继承了SQLiteOpenHelper,在onCreate中创建了一张note表。):

  1. /**
  2. * 摘要的DBOpenHelper
  3. * @author haozi
  4. *
  5. */
  6. public class AbstractDBOpenHelper extends SQLiteOpenHelper {
  7. public AbstractDBOpenHelper(Context context, String name,
  8. CursorFactory factory, int version) {
  9. super(context, name, factory, version);
  10. // TODO Auto-generated constructor stub
  11. }
  12. @Override
  13. public void onCreate(SQLiteDatabase db) {
  14. // TODO 创建数据库,对数据库进行操作
  15. // 创建了一张note表
  16. String sql = "create table note( id integer primary key autoincrement, " +                                                 // 笔记的id
  17. "ntitle varchar(100), " +                          // 笔记的标题
  18. "ncontent varchar(600), " +                        // 笔记的内容
  19. "course_id integer, " +                            // 课程id
  20. "ndate date, " +                                   // 日期
  21. "ntime time, " +                                   // 时间
  22. "deletable integer default 0" +                    // 可否删除(1表示可以,0表示不可以)
  23. // 执行建表语句                                   " );";
  24. db.execSQL(sql);
  25. }
  26. @Override
  27. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  28. // TODO 更改数据库版本的操作
  29. }
  30. @Override
  31. public void onOpen(SQLiteDatabase db) {
  32. // TODO 每次成功打开数据库后首先被执行
  33. super.onOpen(db);
  34. }
  35. }

接下来讨论具体如何创建表、插入数据、删除表等等。调用 getReadableDatabase() 或getWriteableDatabase() 方法,你可以得到 SQLiteDatabase 实例,具体调用那个方法,取决于你是否需要改变数据库的内容:

  1. // 得到可以修改数据表(增,删,改)的sqliteDataBase实例
  2. database = dbOpenHelper.getWritableDatabase();
  3. // 得到只读(查)的sqliteDataBase实例
  4. database  = dbOpenHelper.getReadableDatabase();
 

给表添加数据

上面的代码,已经创建了数据库和表,现在需要给表添加数据。有两种方法可以给表添加数据。

像上面创建表一样,你可以使用 execSQL() 方法执行 INSERT, UPDATE, DELETE 等语句来更新表的数据。execSQL() 方法适用于所有不返回结果的 SQL 语句。例如:

  1. /**
  2. * 保存一条笔记
  3. *
  4. * @param note
  5. *
  6. * @project:note
  7. *
  8. * @author haozi on 2012-2-27
  9. */
  10. public void save(Note note){
  11. String sql = "insert into note(ntitle, ncontent, course_id, ndate, ntime, deletable) values(?,?,?,date('now'),time('now'),0);";
  12. db = dbHelper.getWritableDatabase();
  13. // 开始事务
  14. db.beginTransaction();
  15. try{
  16. db.execSQL(sql, new Object[]{note.getTitle(), note.getContent(), note.getCourse_id()});
  17. db.setTransactionSuccessful();
  18. }catch(Exception e){
  19. System.out.println("插入笔记出现错误!");
  20. }
  21. // 结束事务
  22. db.endTransaction();
  23. db.close();
  24. }

另一种方法是使用 SQLiteDatabase 对象的 insert(), update(),delete() 方法。这些方法把 SQL 语句的一部分作为参数。示例如下:

  1. /**
  2. * 更新笔记
  3. *
  4. * @param note
  5. *
  6. * @project:note
  7. *
  8. * @author haozi on 2012-2-27
  9. */
  10. public void update(Note note){
  11. db = dbHelper.getWritableDatabase();
  12. // 开始事务处理
  13. db.beginTransaction();
  14. ContentValues cv = new ContentValues();
  15. cv.put("ntitle", note.getTitle());
  16. cv.put("ncontent", note.getContent());
  17. cv.put("course_id", note.getId());
  18. db.update("note", cv, "id=?", new String[]{""+note.getId()});
  19. db.setTransactionSuccessful();
  20. // 结束事务处理
  21. db.endTransaction();
  22. db.close();
  23. }

 

查询数据库

类似 INSERT, UPDATE, DELETE,有两种方法使用 SELECT 从 SQLite 数据库检索数据。

1 .使用 rawQuery() 直接调用 SELECT 语句:

  1. /**
  2. * 查找全部的笔记记录
  3. *
  4. * @return
  5. *
  6. * @project:note
  7. *
  8. * @author haozi on 2012-2-27
  9. */
  10. public ArrayList<Note> findAll(){
  11. ArrayList<Note> notes = new ArrayList<Note>();
  12. db = dbHelper.getReadableDatabase();
  13. String sql = "select * from note;";
  14. Cursor cursor = db.rawQuery(sql, null);
  15. while(cursor.moveToNext()){
  16. Note note = new Note();
  17. note.setId(cursor.getInt(cursor.getColumnIndex("id")));
  18. note.setTitle(cursor.getString(cursor.getColumnIndex("ntitle")));
  19. note.setContent(cursor.getString(cursor.getColumnIndex("ncontent")));
  20. note.setCourse_id(cursor.getInt(cursor.getColumnIndex("course_id")));
  21. note.setDate(cursor.getString(cursor.getColumnIndex("ndate")));
  22. note.setTime(cursor.getString(cursor.getColumnIndex("ntime")));
  23. int deletable = cursor.getInt(cursor.getColumnIndex("deletable"));
  24. if(deletable == 1){
  25. note.setDeletable(true);
  26. }else{
  27. note.setDeletable(false);
  28. }
  29. notes.add(note);
  30. }
  31. db.close();
  32. cursor.close();
  33. return notes;
  34. }

使用游标

不管你如何执行查询,都会返回一个 Cursor,这是 Android 的SQLite 数据库游标,使用游标,你可以:

通过使用 getCount() 方法得到结果集中有多少记录;

通过 moveToFirst(), moveToNext(), 和isAfterLast() 方法遍历所有记录;

通过 getColumnNames() 得到字段名;

通过 getColumnIndex() 转换成字段号;

通过 getString(),getInt() 等方法得到给定字段当前记录的值;

通过 requery() 方法重新执行查询得到游标;

通过 close() 方法释放游标资源;

例如,上面查找表中所有笔记的方法还可以这样写:

  1. /**
  2. * 查找全部的笔记记录
  3. *
  4. * @return
  5. *
  6. * @project:note
  7. *
  8. * @author haozi on 2012-2-27
  9. */
  10. public ArrayList<Note> findAll(){
  11. ArrayList<Note> notes = new ArrayList<Note>();
  12. db = dbHelper.getReadableDatabase();
  13. String sql = "select * from note;";
  14. Cursor cursor = db.rawQuery(sql, null);
  15. cursor.moveToFirst();
  16. while(!cursor.isAfterLast()){
  17. Note note = new Note();
  18. note.setId(cursor.getInt(cursor.getColumnIndex("id")));
  19. note.setTitle(cursor.getString(cursor.getColumnIndex("ntitle")));
  20. note.setContent(cursor.getString(cursor.getColumnIndex("ncontent")));
  21. note.setCourse_id(cursor.getInt(cursor.getColumnIndex("course_id")));
  22. note.setDate(cursor.getString(cursor.getColumnIndex("ndate")));
  23. note.setTime(cursor.getString(cursor.getColumnIndex("ntime")));
  24. int deletable = cursor.getInt(cursor.getColumnIndex("deletable"));
  25. if(deletable == 1){
  26. note.setDeletable(true);
  27. }else{
  28. note.setDeletable(false);
  29. }
  30. notes.add(note);
  31. cursor.moveToNext();
  32. }
  33. db.close();
  34. cursor.close();
  35. return notes;
  36. }
2012-02-28 14:21 8339人阅读 评论(2) 举报
 分类:
android应用开发(22) 

版权声明:本文为博主原创文章,未经博主允许不得转载。

本文参考了这篇文章:http://www.ibm.com/developerworks/cn/opensource/os-cn-sqlite/

简介: SQLite 是一款非常流行的嵌入式数据库,它支持 SQL 查询,并且只用很少的内存。Android在运行时集成了 SQLite,所以每个 Android 应用程序都可以使用 SQLite 数据库。对数熟悉 SQL 的开发人员来时,使用 SQLite 相当简单。可以,由于 JDBC 不适合手机这种内存受限设备,所以 Android 开发人员需要学习新的 API 来使用 SQLite。本文主要讲解 SQLite 在 Android 环境中的基本使用。

SQLite 介绍

SQLite 一个非常流行的嵌入式数据库,它支持 SQL 语言,并且只利用很少的内存就有很好的性能。此外它还是开源的,任何人都可以使用它。许多开源项目((Mozilla, PHPPython)都使用了 SQLite。

SQLite 由以下几个组件组成:SQL 编译器、内核、后端以及附件。SQLite 通过利用虚拟机和虚拟数据库引擎(VDBE),使调试、修改和扩展 SQLite 的内核变得更加方便。

图 1. SQLite 内部结构

SQLite 基本上符合 SQL-92 标准,和其他的主要 SQL 数据库没什么区别。它的优点就是高效,Android 运行时环境包含了完整的 SQLite。

SQLite 和其他数据库最大的不同就是对数据类型的支持,创建一个表时,可以在 CREATE TABLE 语句中指定某列的数据类型,但是你可以把任何数据类型放入任何列中。当某个值插入数据库时,SQLite 将检查它的类型。如果该类型与关联的列不匹配,则 SQLite 会尝试将该值转换成该列的类型。如果不能转换,则该值将作为其本身具有的类型存储。比如可以把一个字符串(String)放入 INTEGER 列。SQLite称这为“弱类型”(manifest typing.)。

此外,SQLite 不支持一些标准的 SQL 功能,特别是外键约束(FOREIGN KEY constrains),嵌套 transcaction 和 RIGHT OUTER JOIN 和 FULL OUTER JOIN, 还有一些 ALTER TABLE 功能。

除了上述功能外,SQLite 是一个完整的 SQL 系统,拥有完整的触发器,交易等等。

Android 集成了 SQLite 数据库

Android 在运行时(run-time)集成了 SQLite,所以每个 Android 应用程序都可以使用 SQLite 数据库。对于熟悉 SQL 的开发人员来时,在 Android 开发中使用 SQLite 相当简单。但是,由于 JDBC 会消耗太多的系统资源,所以 JDBC 对于手机这种内存受限设备来说并不合适。因此,Android 提供了一些新的 API 来使用 SQLite 数据库,Android开发中,程序员需要学使用这些 API。

数据库存储在 data/< 项目文件夹 >/databases/ 下。

Android 开发中使用 SQLite 数据库

Activites 可以通过 Content Provider 或者 Service 访问一个数据库。下面会详细讲解如果创建数据库,添加数据和查询数据库。

创建数据库

Android 不自动提供数据库。在 Android 应用程序中使用 SQLite,必须自己创建数据库,然后创建表、索引,填充数据。Android 提供了 SQLiteOpenHelper 帮助你创建一个数据库,你只要继承 SQLiteOpenHelper 类,就可以轻松的创建数据库。SQLiteOpenHelper类根据开发应用程序的需要,封装了创建和更新数据库使用的逻辑。SQLiteOpenHelper 的子类,至少需要实现三个方法:

1.  构造函数,调用父类 SQLiteOpenHelper 的构造函数。这个方法需要四个参数:上下文环境(例如,一个 Activity),数据库名字,一个可选的游标工厂(通常是 Null),一个代表你正在使用的数据库模型版本的整数。

2. onCreate()方法,它需要一个 SQLiteDatabase 对象作为参数,根据需要对这个对象填充表和初始化数据。

3. onUpgrage()方法,它需要三个参数,一个SQLiteDatabase 对象,一个旧的版本号和一个新的版本号,这样你就可以清楚如何把一个数据库从旧的模型转变到新的模型。

下面示例代码展示了如何继承 SQLiteOpenHelper 创建数据库(AbstractDBOpenHelper继承了SQLiteOpenHelper,在onCreate中创建了一张note表。):

  1. /**
  2. * 摘要的DBOpenHelper
  3. * @author haozi
  4. *
  5. */
  6. public class AbstractDBOpenHelper extends SQLiteOpenHelper {
  7. public AbstractDBOpenHelper(Context context, String name,
  8. CursorFactory factory, int version) {
  9. super(context, name, factory, version);
  10. // TODO Auto-generated constructor stub
  11. }
  12. @Override
  13. public void onCreate(SQLiteDatabase db) {
  14. // TODO 创建数据库,对数据库进行操作
  15. // 创建了一张note表
  16. String sql = "create table note( id integer primary key autoincrement, " +                                                 // 笔记的id
  17. "ntitle varchar(100), " +                          // 笔记的标题
  18. "ncontent varchar(600), " +                        // 笔记的内容
  19. "course_id integer, " +                            // 课程id
  20. "ndate date, " +                                   // 日期
  21. "ntime time, " +                                   // 时间
  22. "deletable integer default 0" +                    // 可否删除(1表示可以,0表示不可以)
  23. // 执行建表语句                                   " );";
  24. db.execSQL(sql);
  25. }
  26. @Override
  27. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  28. // TODO 更改数据库版本的操作
  29. }
  30. @Override
  31. public void onOpen(SQLiteDatabase db) {
  32. // TODO 每次成功打开数据库后首先被执行
  33. super.onOpen(db);
  34. }
  35. }

接下来讨论具体如何创建表、插入数据、删除表等等。调用 getReadableDatabase() 或getWriteableDatabase() 方法,你可以得到 SQLiteDatabase 实例,具体调用那个方法,取决于你是否需要改变数据库的内容:

  1. // 得到可以修改数据表(增,删,改)的sqliteDataBase实例
  2. database = dbOpenHelper.getWritableDatabase();
  3. // 得到只读(查)的sqliteDataBase实例
  4. database  = dbOpenHelper.getReadableDatabase();
 

给表添加数据

上面的代码,已经创建了数据库和表,现在需要给表添加数据。有两种方法可以给表添加数据。

像上面创建表一样,你可以使用 execSQL() 方法执行 INSERT, UPDATE, DELETE 等语句来更新表的数据。execSQL() 方法适用于所有不返回结果的 SQL 语句。例如:

  1. /**
  2. * 保存一条笔记
  3. *
  4. * @param note
  5. *
  6. * @project:note
  7. *
  8. * @author haozi on 2012-2-27
  9. */
  10. public void save(Note note){
  11. String sql = "insert into note(ntitle, ncontent, course_id, ndate, ntime, deletable) values(?,?,?,date('now'),time('now'),0);";
  12. db = dbHelper.getWritableDatabase();
  13. // 开始事务
  14. db.beginTransaction();
  15. try{
  16. db.execSQL(sql, new Object[]{note.getTitle(), note.getContent(), note.getCourse_id()});
  17. db.setTransactionSuccessful();
  18. }catch(Exception e){
  19. System.out.println("插入笔记出现错误!");
  20. }
  21. // 结束事务
  22. db.endTransaction();
  23. db.close();
  24. }

另一种方法是使用 SQLiteDatabase 对象的 insert(), update(),delete() 方法。这些方法把 SQL 语句的一部分作为参数。示例如下:

  1. /**
  2. * 更新笔记
  3. *
  4. * @param note
  5. *
  6. * @project:note
  7. *
  8. * @author haozi on 2012-2-27
  9. */
  10. public void update(Note note){
  11. db = dbHelper.getWritableDatabase();
  12. // 开始事务处理
  13. db.beginTransaction();
  14. ContentValues cv = new ContentValues();
  15. cv.put("ntitle", note.getTitle());
  16. cv.put("ncontent", note.getContent());
  17. cv.put("course_id", note.getId());
  18. db.update("note", cv, "id=?", new String[]{""+note.getId()});
  19. db.setTransactionSuccessful();
  20. // 结束事务处理
  21. db.endTransaction();
  22. db.close();
  23. }

 

查询数据库

类似 INSERT, UPDATE, DELETE,有两种方法使用 SELECT 从 SQLite 数据库检索数据。

1 .使用 rawQuery() 直接调用 SELECT 语句:

  1. /**
  2. * 查找全部的笔记记录
  3. *
  4. * @return
  5. *
  6. * @project:note
  7. *
  8. * @author haozi on 2012-2-27
  9. */
  10. public ArrayList<Note> findAll(){
  11. ArrayList<Note> notes = new ArrayList<Note>();
  12. db = dbHelper.getReadableDatabase();
  13. String sql = "select * from note;";
  14. Cursor cursor = db.rawQuery(sql, null);
  15. while(cursor.moveToNext()){
  16. Note note = new Note();
  17. note.setId(cursor.getInt(cursor.getColumnIndex("id")));
  18. note.setTitle(cursor.getString(cursor.getColumnIndex("ntitle")));
  19. note.setContent(cursor.getString(cursor.getColumnIndex("ncontent")));
  20. note.setCourse_id(cursor.getInt(cursor.getColumnIndex("course_id")));
  21. note.setDate(cursor.getString(cursor.getColumnIndex("ndate")));
  22. note.setTime(cursor.getString(cursor.getColumnIndex("ntime")));
  23. int deletable = cursor.getInt(cursor.getColumnIndex("deletable"));
  24. if(deletable == 1){
  25. note.setDeletable(true);
  26. }else{
  27. note.setDeletable(false);
  28. }
  29. notes.add(note);
  30. }
  31. db.close();
  32. cursor.close();
  33. return notes;
  34. }

使用游标

不管你如何执行查询,都会返回一个 Cursor,这是 Android 的SQLite 数据库游标,使用游标,你可以:

通过使用 getCount() 方法得到结果集中有多少记录;

通过 moveToFirst(), moveToNext(), 和isAfterLast() 方法遍历所有记录;

通过 getColumnNames() 得到字段名;

通过 getColumnIndex() 转换成字段号;

通过 getString(),getInt() 等方法得到给定字段当前记录的值;

通过 requery() 方法重新执行查询得到游标;

通过 close() 方法释放游标资源;

例如,上面查找表中所有笔记的方法还可以这样写:

  1. /**
  2. * 查找全部的笔记记录
  3. *
  4. * @return
  5. *
  6. * @project:note
  7. *
  8. * @author haozi on 2012-2-27
  9. */
  10. public ArrayList<Note> findAll(){
  11. ArrayList<Note> notes = new ArrayList<Note>();
  12. db = dbHelper.getReadableDatabase();
  13. String sql = "select * from note;";
  14. Cursor cursor = db.rawQuery(sql, null);
  15. cursor.moveToFirst();
  16. while(!cursor.isAfterLast()){
  17. Note note = new Note();
  18. note.setId(cursor.getInt(cursor.getColumnIndex("id")));
  19. note.setTitle(cursor.getString(cursor.getColumnIndex("ntitle")));
  20. note.setContent(cursor.getString(cursor.getColumnIndex("ncontent")));
  21. note.setCourse_id(cursor.getInt(cursor.getColumnIndex("course_id")));
  22. note.setDate(cursor.getString(cursor.getColumnIndex("ndate")));
  23. note.setTime(cursor.getString(cursor.getColumnIndex("ntime")));
  24. int deletable = cursor.getInt(cursor.getColumnIndex("deletable"));
  25. if(deletable == 1){
  26. note.setDeletable(true);
  27. }else{
  28. note.setDeletable(false);
  29. }
  30. notes.add(note);
  31. cursor.moveToNext();
  32. }
  33. db.close();
  34. cursor.close();
  35. return notes;
  36. }

最新文章

  1. iOS开发系列--视图切换
  2. 如果觉得配置文件没有错,但web-dev-server总是报错,可以在hosts文件里加一行127.0.0.1 localhost
  3. hdu5037 Frog (贪心)
  4. CCF真题之相反数
  5. OGG配置
  6. 转:Linus:利用二级指针删除单向链表
  7. Webbench源代码分析(转载)
  8. 在ASP.NET Core Web API中为RESTful服务增加对HAL的支持
  9. windows 纤程
  10. css正三角倒三角
  11. Java面试题和解答(三)
  12. mysql对身份证号码进行脱敏处理
  13. 洛谷 P1162 填涂颜色
  14. 硬件电路io口控制继电器电路
  15. Android集成讯飞语音、百度语音、阿里语音识别
  16. linux 下gcc 编译结构体问题
  17. MT【31】傅里叶级数为背景的三角求和
  18. ORACLE-osi分层模型.md
  19. c++的字节对齐
  20. 远程连接软件TeamViewer

热门文章

  1. 理解MySQL(二)--数据库事务
  2. 使用DOM4J 对xml解析操作
  3. random库的使用
  4. Python中模块与包的导入(朴实易懂版的总结)
  5. @RequestBody 注意的问题
  6. INSERT: 批量插入结果集方式
  7. 关于GIS中Scale和Resolution的那些事儿
  8. webrtc笔记(1): 基于coturn项目的stun/turn服务器搭建
  9. 《GO Home Trash!》UML类图,ER图以及数据库设计
  10. 牛客OI测试赛 C 序列 思维