SQL基础入门
SQLite名称的由来
SQL:结构化查询语言Structured Query Language
Lite:简化的
-->SQLite:自包含(只有一个文件),无服务器,零配置,事务性SQL引擎。
安装使用步骤
1.下载sqlite3.exe
2.将sqlit3.exe拷贝到c:/windows/system32目录下
3.启动命令行窗口
4.输入命令sqlite3
使用SQLite
以 . 开头的是sqlite3软件特有的指令。
SQL语句不用.开头,但以;结束。
打开/创建 数据库的2种方式:
1.dos下运行命令:sqlite3 mytest.db 启动sqlite3 并且设定开发的数据库文件,如果不存在且建表了则会创建.
2.先运行sqlite3,进入后输入 .open mytest.db 该文件会放在命令行所在目录下, 没有该文件时会自动创建, 即使未创建任何表.
Sqlite3常用命令(区分大小写)
在sqlit>提示符下输入:
.open mytest.db打开数据库 mytest.db数据库名,一次只能打开一个库文件
.tables显示所有表
.schema tb_users显示表结构tb_users是表名.但这是
显示创建表的建表语句
。与mysql有区别,并不是显示表结构。
.databases列出连接的数据库(一个)
.exit退出本程序(退出提示符
sqlite>的命令)
.quit退出本程序
.help显示帮助
.read 文件名 :加载执行sql脚本,例如my.sql
备注: SQL语句不区分大小写,SQLite命令区分大小写.
一.数据库查询语句:
select
1. 查询所有数据:
select * from 表名;
select * from exam_books;
2.按照一定的条件查找:
select * from 表名
where
条件;
select * from exam_books where id<20;
sqlite> select * from tb_student; 查询tb_student表中的所有数据
1|zhangsan|18|male
2|lisi|28|female
3|zhangsan|18|male
4|wangwu|25|female
sqlite> select * from tb_student
where
age<20; 查询tb_student表中age<20的所有数据
1|zhangsan|18|male
3|zhangsan|18|male
sqlite> select * from tb_student where name='zhangsan'; 查询tb_student表中name字段的值是"zhangsan"的所有数据
1|zhangsan|18|male
3|zhangsan|18|male
sqlite> select * from tb_student where sex='female'; 查询tb_student表中sex是"female"的所有数据
2|lisi|28|female
4|wangwu|25|female
sqlite> select * from tb_student
where
sex='female'
and
age>26; 查询tb_student表中sex是"female",并且age>26的所有数据
2|lisi|28|female
3.范围条件查询:
select * from 表名 where 字段 between 值1 and 值2 ;
select * from exam_books where id<20
and
id>10; 不包括10,20, (10,20)
select * from exam_books where id
between
10
and
20; --包含端点, 即 id的取值范围 [10,20]
select * from exam_books where addtime
between
'2011-03-17 00:00:00'
and
'2011-03-18 00:00:00';
4.模糊查询:
select * from 表名 where 字段
like
'%条件%'; --%表示有0~多个未知字符
select * from exam_books where bookname
like
'
%
马克思%';
select * from exam_books where bookname
like
'马
_
_
';
(查询书名中第一个字是“马”,后面有两个字)
5.复合查询:
select * from 表名 where 条件
and
另一个条件;
select * from exam_questions where coursecode = '03706'
and
chapterid = 2;
6.查询个数:
select
count(*)
from 表名 where 条件; --查询有多少条 满足指定条件 的记录
select count(*) from exam_questions where coursecode = '03706' and chapterid = 2;
select count(*) from tb_student where age>21; 查询该表中有多少条age>21的记录(数据)
7.查询结果按顺序排列:
正序、倒序(
asc
/
desc
)
select * from 表名 where 条件
order by
字段 desc/asc;
select * from exam_questions where coursecode = '03706' order by chapterid; --不写asc/desc 则默认按照升序排序
sqlite> select * from tb_student ;
1|zhangsan|18|male
2|lisi|28|female
3|zhangsan|18|male
4|wangwu|25|female
sqlite> select * from tb_student
order by
age; 查询结果按照年龄的升序排序(不写asc就默认是asc升序)
1|zhangsan|18|male
3|zhangsan|18|male
4|wangwu|25|female
2|lisi|28|female
sqlite> select * from tb_student
order by
age
desc
; 查询结果按照年龄的降序排序
2|lisi|28|female
4|wangwu|25|female
1|zhangsan|18|male
3|zhangsan|18|male
8.按照limit查找某个范围内的数据:
SELECT * FROM exam_questions
order by
id asc
limit
0, 15;(代表查找出的第一页的信息)从0下标开始,查询出15条数据.
SELECT * FROM exam_questions order by id asc
limit
15, 15;(代表查找出的第二页的信息)从15下标开始,查询出15条数据.
【备注:】
limit后面的两个数字中:第一个代表偏移量(第几条数据,从0下标开始),第二个代表每页展示的数量(多少条数据)
二. 删除数据:
delete
delete from 表名 where 条件;
delete from exam_questions where id<2000;
delete from exam_questions where coursecode='00041' and chapterid=1;
sqlite> select _id,name,age from tb_student where age=43;
21|lisi|43
38|lisi|43
sqlite> delete from tb_student where _id=21; 删除该表中_id是21的记录(数据行)
sqlite> select _id,name,age from tb_student where age=43; 删除后,_id=21的记录就查询不到了
38|lisi|43
三.插入新数据:
insert
格式一:insert into 表名(字段1,字段2....) values(值1,值2....);
insert into exam_weburl(webname , weburl , info , bigtypeid) values('人人网', 'renren.com' , '这个网站不错' , 3);
insert into tb_student (name,age,sex) values('zhangsan',18,'male'); sql语句注意要以分号结尾
格式二:
insert into 表名 values(值1,值2....);
-- 要向所有的字段依次插入数据时的简化写法,要求值列表的个数,顺序,内容跟数据库中表的字段(列)要一 一对应.
sqlite>insert into tb_student values(3,'zhangsan',18,'male'); 对该表的每个列都指定值
四、更新(修改)数据:
update
update
表名
set
字段1=值1, 字段2=值2
where
条件;
update exam_weburl set info='这个网站不太好' where id=73;
update exam_weburl set webname='人人2', weburl='www.renren.com' where id=73;
sqlite> select _id,name,age from tb_student where age=42; 只查询_id,name,age字段的数据,不查询其他字段.如果是select * from...则是查询所有字段.
21|zhangsan17|42
38|zhangsan17|42
sqlite>
update
tb_student
set
name='lisi',age=43
where
age=42; 把age=42的记录(数据行)中的name字段的值更新为"lisi",age字段的值更新为43
sqlite> select _id,name,age from tb_student where age=42; 查询结果为空,因为原来age=42的记录,其age被更新为43
sqlite> select _id,name,age from tb_student where age=43;
21|lisi|43
38|lisi|43
五、创建/删除表的语句:Integer _id;
CREATE
TABLE
[if not exists]
表名 (_id
INTEGER PRIMARY KEY AUTOINCREMENT
, 字段2 数据类型2, 字段3 数据类型3...)
PRIMARY KEY:主键,能够唯一区分出一条记录(即一行数据)的字段(或字段的组合),不能为null
AUTOINCREMENT :自增长
例如:CREATE TABLE
if not exists
tb_newwords (_id INTEGER PRIMARY KEY AUTOINCREMENT , words , detail );
DROP
TABLE [
if exists
] 表名 ;
sqlite> CREATE TABLE if not exists tb_student(_id INTEGER PRIMARY KEY AUTOINCREMENT,name VARCHAR2,age INTEGER,sex VARCHAR2);
六、更新表结构的语句:
1、如需在表中添加列,请使用下列语法:
ALTER
TABLE table_name
ADD
column_name datatype
SQLite不支持删除列,修改列的数据类型.
【备注:】
DDL (data define language)
数据定义语句
create
,
drop
,alter
DML (data manipulate
[m
ə
'n
ɪ
pj
ʊ
le
ɪ
t操纵;操作]
language)
数据操纵语句 insert,delete,update,select
DCL (data control language)数据控制语句 grant, revoke.
TCL(transaction controll language)
事务控制语句. commit/rollback
学习SQL的主要重心应该放在DML语句上,而DML语句中的重点应该是在select语句上。
CRUD
:是指在做计算处理时的增加(C:Create 用Insert into关键字)、查询(R:Retrieve 用 select关键字)(重新得到数据)、更新(U:Update)和删除(D:Delete)几个单词的首字母简写。主要被用在描述数据库的基本操作。
insert into 表名 (字段1,字段2....) values(值1,值2...);
insert into 表名 values(值1,值2....); 该表中有多少列,就要有多少个值
select 字段1,字段2 ,.... from 表名 [where 条件] [order by 字段名 asc|desc] [limit 起始下标,要查出的行数];
update 表名 set 要更新的字段1=新值1,要更新的字段2=新值2,..... [where 条件];
delete from 表名 [where 条件];
SQLiteDatabase
一、SQLite:
(一)、简介:
[应用内可访问的数据]
Android通过 SQLite 数据库引擎来实现结构化数据的存储。
在一个app中,可以访问本app创建的私有数据库,在其他应用程序里不能访问。除非该数据库是放在sd卡中.
[小而快]
SQLite 数据库是一种用
C语言编写
的嵌入式数据库,它是一个轻量级的数据库,最初为嵌入式设计的。它是在一些基础简单的语句处理上要比oracle / mysql快很多,而且其对内存的要求很低,在内存中只需要几百KB的存储空间。这是Android中采用 SQLite 数据库的主要原因。
SQLite 支持
事务
处理功能,
Transaction
SQLite 处理速度
比MySQL等著名的开源数据库系统更快;
它没有服务器进程。
[跨平台]
SQLite 通过
单个的文件
保存数据库,数据库名即文件名。 该文件是
跨平台
的,可以
自由复制
。
[API]
JDBC会消耗太多系统资源,所以JDBC对于手机并不合适,因此Android提供了新的API来使用 SQLite 数据库。
二、
SQLiteDatabase
类:
SQLiteDatabase等同于JDBC中Connection和Statement的结合体。
SQLiteDatabase既代表与数据库的连接,又能用于执行sql语句操作。
(一)、操作 SQLite 数据库的步骤:【
重要
】
1、通过
静态方法
创建 SQLiteDatabase 对象,它代表与数据库的连接;
2、创建表(执行CREATE);
3、调用SQLiteDatabase 对象执行数据库操作(执行DML);
4、对查询后的结果集Cursor(游标/光标)进行处理。
(二)、操作 SQLite 数据库的步骤详细讲解:
1、创建 SQLite 数据库对象:
SQLiteDatabase
对象代表一个数据库。
SQLiteDatabase
提供了几个
静态方法
来打开一个文件对应的数据库。
1.打开
已存在
的数据库:
static SQLiteDatabase
openDatabase
(String
path
,
SQLiteDatabase.CursorFactory factory, int
flags
)
2.存在则打开,否则创建:
static SQLiteDatabase
open
OrCreate
Database
(String
path
, SQLiteDatabase.CursorFactory factory)
【备注:】方法参数说明:
1)参数中的
path
代表着数据库的路径, 如果想要放在
私有缓存路径
(/data/data/应用程序包名/cache/) 里面,则只需要传入
getCacheDir().getAbsolutePath()+File.separator+"student.db"
,
File
getCacheDir()
方法是Activity的方法
如果要放在Sd卡中,则要提供绝对路径.
2)factory代表着在创建Cursor对象时,使用的工厂类,传
null即可
;
3)
flags
代表的是创建表时的一些权限设置(
SQLiteDatabase里面的静态常量)
,多个权限之间用"|"分隔:
OPEN_READWRITE
:代表以读写方式打开数据库(常量值为:0)
OPEN_READONLY
:代表的是以只读方式打开数据库(常量值为:1)
CREATE_IF_NECESSARY
:当数据库不存在时创建数据库(0x10000000)
SQLiteDatabase.
openOrCreateDatabase(path,null)//推荐写法,等价于:
SQLiteDatabase.
openDatabase(path,null,
SQLiteDatabase
.
OPEN_READWRITE |
SQLiteDatabase
.
CREATE_IF_NECESSARY)
【核心示例代码:】
//当数据库
存在时
的打开方式:
String path =
getCacheDir
().getAbsolutePath()+ File.separator + "android_manual.db";//访问放在内部存储缓存目录中的数据库
SQLiteDatabase db =
SQLiteDatabase.
openDatabase
(path, null,
SQLiteDatabase.OPEN_READWRITE
);//读写方式打开
Cursor cursor = db.
rawQuery
("select * from android_basic order by id desc
limit 0 , 15
",null);//执行原始查询语句,注意:在java代码中,sql语句不写分号;
raw的意思是:原始,原生
//当数据库
不存在时
的打开方式:
String path = SDCardHelper.
getSDCardBaseDir
() + File.separator + "words.db";//
SDCardHelper.getSDCardBaseDir()是
自定义的工具类的静态方法, 用于获取SD卡的根目录
SQLiteDatabase db =
SQLiteDatabase.
openOrCreateDatabase
(path, null);
//创建表 (如果不存在则创建,如果存在则不创建)
db.
execSQL
("create table
if not exists
tb_words (_id integer primary key autoincrement ,english , chinese)");//参数字符串结尾不要写分号";"
2、在数据库中
创建表
:
核心示例代码:
String path = SDCardHelper.getSDCardBaseDir() + File.separator +
"qf"
+ File.separator + "words.db";//如果中间还有路径,需要在程序中提前创建出来
SQLiteDatabase db = SQLiteDatabase.
openOrCreateDatabase
(path, null);
db.
execSQL
("create table tb_words (_id integer primary key autoincrement ,english , chinese)");//
参数字符串结尾不要写分号";"
【备注:】SQLite的数据类型
其实SQLite是无类型Typelessness的。这意味着你可以保存任何类型的数据到数据库表的任何列中, 无论这列声明的数据类型是什么。对于SQLite来说对字段不指定类型是完全可以的。如:
create table dict(_id integer primary key autoincrement , word , detail
);
注意:这里一定要注意,SQLite数据库
只有在一种情况下
是要求类型匹配的,当我们建表是如create table table1(id integer primary key
AUTOINCREMENT
),则该字段必须存整数。
虽然SQLite允许忽略数据类型, 但是仍然建议在你的Create Table语句中指定数据类型. 因为数据类型对于你和其他的程序员交流, 或者你准备换掉你的数据库引擎时能起到一个提示或帮助的作用。
3、调用SQLiteDatabase 对象的方法执行数据库操作:【
重要
】
void execSQL
(String sql) ; 执行不带占位符"?"的sql语句(
update,insert,delete; create表 ,drop表
)
void execSQL
(String sql , Object[] args) 执行可带占位符"?"的sql语句(
update,insert,delete; create表 ,drop表
).注意第二个参数必须传对象数组(例如Integer数组),不能传int数组
Cursor rawQuery
( String sql , String[] args ) 执行可带占位符"?"的sql查询(
select语句
) 任何类型的字段(包括主键)都可以用"?"占位,并用字符串传值;
Cursor游标,光标.指向查询结果中的一条记录.
select _id,name,age,sex from tb_student where age>18 and sex='female' order by _id desc limit 5,15;
Cursor query
(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)执行查询功能
例如:query("tb_student",new String[]{"
_id","name","age","sex","age>? and sex=?",new String[]{"18","female"},null,null,"_id desc","5,15"
});
table: 表名;
columns:要查询出来的字段名;
selection:筛选条件,不写where;
selectionArgs:筛选条件中的所有"?"的替代数组;
orderBy:按什么字段来排序,如:想按照_id逆序排序(不要写order by) _id desc
limit: 偏移量,读取数量 例如 0,10
不需要的参数 传null
对查询结果进行操作:
当执行的是select语句,返回一个Cursor对象。Cursor类似于JDBC中的ResultSet
结果集
,默认指向第一条记录之前,内置移动游标等方法:
move(int offset) 按偏移量来移动
moveToFirst
() 将记录指针移动到第一行
moveToLast ()
将记录指针移动到最后一行
moveToNext
()
将记录指针移动到下一行 返回boolean
moveToPosition(int position)
将记录指针移动到指定的一行
moveToPrevious() 将记录指针移动到上一行
getColumnName
(int index)
根据列的索引返回其相应的列名称
getColumnIndex
(String name
) 根据列的名字返回其相应的索引
getColumnNames
() 返回一个保存有所有列名称的
字符串数组
getCount
()
返回Cursor的
行数
(即查询结果的行数)
getColumnCount
() 返回
列数
close
() 关闭游标结果集,释放资源
getType
(int columnIndex) 获取指定下标字段的
数据类型
。分别有0、1、2、3、4这几个结果。
0: 代表null
1:代表int
2:代表float
3:代表String
4:代表blob
除了以上重要的几个方法外,Android考虑到不熟悉sql语句的开发者,提供了进一步封装后的一系列方法。这几个方法参数众多,平时不建议使用,但也要掌握,在ContentProvider中会有类似的方法,要注意区分。
【备注:】ContentValues的构建方式:
ContentValues c = new ContentValues();
c.put(String key,Xxx value);
下面方法是
SQLiteDatabase中定义的:
long insert
(String table, String nullColumnHack,
ContentValues
values);//第二个参数一般传null
如果第二个参数传null,第三个参数里面必须有键值对数据;
如果第三个参数里面没有数据(只是想插入一个空行),第二个参数应随便指定一个可以为null值的列名.
返回值为该行的id,返回-1表示插入失败
int update
(String table,
ContentValues
values, String whereClause, String[] whereArgs)
values:更新指定键的值
whereClause不写where关键字
返回受该语句影响的行数
int delete
(String table, String whereClause, String[] whereArgs)
返回受该语句影响的行数
在ListView中显示用户信息,提示:
先创建数据库,创建表,向用户表中插入几条记录.
查询数据库中存储的用户信息,组装成List<MyUser>(包括_id,name,age等)并封装到
ArrayAdapter
<MyUser>中.该数组适配器会把每个MyUser对象使用toString()方法 ,装到每个条目中.
上面的案例也可以使用抽象类CursorAdapter及其子类SimpleCursorAdapter
.
类似于new SimpleAdapter(Context context,List<Map<String,Object>> data, int resource,String[ ] from, int [ ]to)
SimpleCursorAdapter是一种适配器,用于数据库的数据的适配.
//构造方法格式:
//SimpleCursorAdapter(Context context, int layout, Cursor c, String[ ] from, int[ ] to, int flags)
CursorAdapter adapter = new SimpleCursorAdapter(
this,
R.layout.item,
cursor,
new String[]{"_id","name","age"},
new int[]{R.id.tv_id, R.id.tv_name, R.id.tv_age},
CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
//如果数据源发生变化则loader中可以更新
(三)、
事务
:
数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,
要么完整地执行,要么完全地不执行。事务的目的是为了保证数据的一致性。
默认情况下,insert/update/delete等操作都是单独一个事务.不必手动开启或结束事务.
例如:
设想网上购物的一次交易,其付款过程至少包括以下几步数据库操作:
一、更新客户所购商品的库存信息
二、保存客户付款信息--可能包括与银行系统的交互
三、生成订单并且保存到数据库中
四、更新用户相关信息,例如购物数量等等
正常的情况下,这些操作将顺利进行,最终交易成功,与交易相关的所有数据库信息也成功地更新。但是,如果在这一系列过程中任何一个环节出了差错,例如在更新商品库存信息时发生异常、该顾客银行帐户存款不足等,都将导致交易失败。一旦交易失败,数据库中所有信息都必须保持交易前的状态不变,比如最后一步更新用户信息时失败而导致交易失败,那么必须保证这笔失败的交易不影响数据库的状态--库存信息没有被更新、用户也没有付款,订单也没有生成。否则,数据库的信息将会一片混乱而不可预测。
数据库事务正是用来保证这种情况下交易的平稳性和可预测性的技术
。
db.
beginTransaction
();//开始事务
try {
...需要同时完成的业务逻辑...
不是每次操作都必须写的,只有"打包"几个操作一起成功或失败时才需要手动控制.
} finally {
db.
endTransaction
();//提交事务
}
(四)、对SQLiteDatabase类进行封装:
MySQLiteDatabaseHelper ( ) 构造方法,获得一个数据库连接对象
Cursor
selectCursor
(String sql, String[] selectionArgs) 执行带占位符的select语句,查询数据,返回Cursor
int
selectCount
(String sql, String[] selectionArgs)执行带占位符的select语句,返回结果集的数据条数
boolean
updateData
(String sql, String[] bindArgs) 执行带占位符的
update、insert、delete
语句,
create, drop
,返回true或false
List<Map<String, Object>>
cursorToList
(Cursor cursor)作用:将cursor转成list集合
提示:利用getColumnNames();获取全部列名,利用int getType(int columnIndex) 获取指定下标字段的数据类型
0: 代表null ;
1:代表int ; 2:代表float ; 3:代表String; 再根据数据类型, 利用getInt等方法从游标中取值.
void closeDb()关闭数据库连接
参考代码:
1、封装
MySQLiteDatabaseHelper
类核心代码:
public class MySQLiteDatabaseHelper {
private String DBPath = Environment.getExternalStorageDirectory().getAbsolutePath();
private String DBName = "android_manual.db";//数据库名称
private SQLiteDatabase db;
/**
* @作用: 构造方法。目的是创建数据库文件、创建SQLiteDatabase实例。
*/
public MySQLiteDatabaseHelper() {
getConnection();//调用自定义的方法初始化数据库连接
}
/**
* @return void
* @作用:获得一个数据库连接对象,即SQLiteDatabase对象。
*/
public void getConnection() {
db = SQLiteDatabase.openDatabase(DBPath + File.separator + DBName, null, SQLiteDatabase.OPEN_READWRITE);//第二个参数是工厂,固定传入null
}
/**
* @param sql
* @param selectionArgs
* @return Cursor
* @作用:执行带占位符的select语句,查询数据,返回Cursor
*/
public Cursor selectCursor(String sql, String[] selectionArgs) {
return db.rawQuery(sql, selectionArgs);
}
/**
* @param sql
* @param selectionArgs
* @return int
* @作用:执行带占位符的select语句,返回结果集的个数
*/
public int selectCount(String sql, String[] selectionArgs) {
Cursor cursor = db.rawQuery(sql, selectionArgs);
int count = cursor.getCount();//结果集有多少条记录
cursor.close();
return count;
}
/**
* @param sql
* @param bindArgs
* @return boolean
* @作用:执行带占位符的update、insert、delete语句,更新数据库,返回true或false
*/
public boolean updateData(String sql, String[] bindArgs) {
try {
db.execSQL(sql, bindArgs);//返回值类型为void
return true;//没出现异常,说明insert/update/delete成功
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* @param sql
* @param selectionArgs
* @return List<Map<String, Object>>
* @作用:执行带占位符的select语句,返回多条数据,放进List<Map<String,Object>>集合中。
*/
public List<Map<String, Object>> selectData(String sql, String[] selectionArgs) {
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
Cursor cursor = db.rawQuery(sql, selectionArgs);
String[] cols_names = cursor.getColumnNames();//获取每一个字段的名字
while (cursor.moveToNext()) {
Map<String, Object> map = new HashMap<String, Object>();
for (int i = 0; i < cols_names.length; i++) {
String cols_value = cursor.getString(i);//每个下标都按照字符串来获取其字段值
map.put(cols_names[i], cols_value);//把每个字段名和对应的值存到map中
}
list.add(map);
}
if (cursor != null) {
cursor.close();
}
return list;
}
/**
* 作用:将cursor转成list集合
*
* @param cursor
* @return List<Map<String, Object>>
*/
public List<Map<String, Object>> cursorToList(Cursor cursor) {
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
String[] cols_names = cursor.getColumnNames();
while (cursor.moveToNext()) {
Map<String, Object> map = new HashMap<String, Object>();
for (int i = 0; i < cols_names.length; i++) {
Object cols_value = null;
switch (cursor.getType(i)) {//获取指定下标的字段的数据类型 1:int, 2:float, 3:String, 4:Blob, 0:null
case 1://int
cols_value = cursor.getInt(cursor.getColumnIndex(cols_names[i]));
break;
case 2://float
cols_value = cursor.getFloat(cursor.getColumnIndex(cols_names[i]));
break;
case 3://String
cols_value = cursor.getString(cursor.getColumnIndex(cols_names[i]));
break;
case 4://blob
cols_value = cursor.getBlob(cursor.getColumnIndex(cols_names[i]));
break;
default://0:null
cols_value = null;
break;
}
map.put(cols_names[i], cols_value);
}
list.add(map);
}
if (cursor != null) {
cursor.close();
}
return list;
}
public void closeDb() {
if (db != null) {
db.close();
}
}
}
转载请注明原文地址: https://ju.6miu.com/read-675560.html