Android SqlDelight详解和Demo例子.docx
《Android SqlDelight详解和Demo例子.docx》由会员分享,可在线阅读,更多相关《Android SqlDelight详解和Demo例子.docx(31页珍藏版)》请在冰豆网上搜索。
AndroidSqlDelight详解和Demo例子
AndroidSqlDelight详解和Demo例子
一、简介
SQLDelight和SqlBrite是Square公司推出的一个Android平台数据库解决方案。
在了解这个两个东西前,必须先得有Andorid的Sqlite的知识(SqliteDatabaseandSqliteDataHelper),AutoValue
要分清他们两个的功能:
SqlDelight
是用来把Sql语句生成Java对象的
SqlBrite
才是Sqlite操作,结合Rxjava进行响应式数据库操作。
SqlBrite教程
二、SqlDelight
SqlDelight是从SQL语句来生成JAVA对象。
这样的好处是,所有SQL语句都位于同一个位置,通过查看SQL语句可以清楚的了解需要实现的功能和数据库的结构。
SqlDelight添加了对SQL语句的编译时验证、表名字和列名字的代码自动完成功能。
让编写SQL语句更加快捷。
在编译的时候,根据SQL语句生成Java模型接口和builder来把数据行和Java对象实现转换。
2.1导包
在项目build.gradle的dependencies添加:
classpath'com.squareup.sqldelight:
gradle-plugin:
0.6.0'
在module的build.gralde头部添加apply
applyplugin:
'com.squareup.sqldelight'
重新Sync即可。
2.2使用
SqlDelight可以根据建表的SQL语句自动生成Javamodelinterface,interface的每个方法就是这张表的每一列。
1.编写sq语句
在main目录创建sqldelight目录,然后按照包名创建目录,main/sqldelight/com/xxx/xxx/sq文件(一定得是正确的目录,不然构建失败)
这里编写创建表和查询的语句;
createtableuser(
_idINTEGERNOTNULLPRIMARYKEYAUTOINCREMENT,
nameTEXTNOTNULL,
ageINTEGERNOTNULL
);
--其他的语句通过标识符来引用。
在生成的Java对象中会包含
--一个该标识符的常亮引用这个语句。
--查询use表,百分号(%)代表零个、一个或多个数字或字符,||连接两个不同的字符串,得到一个新的字符串。
select_by_name:
select*fromuser;
2.生成接口Module
编写完毕sql语句之后,make一下moduel,即可在build/generated/source/sqldelight/包名/看到xxxModule接口文件(xxx就是你的sq文件的名称)。
可以看到接口大概是这样的。
publicinterfaceUserModel{
StringTABLE_NAME="user";
String_ID="_id";
StringNAME="name";
StringAGE="age";
StringCREATE_TABLE=""
+"createtableuser(\r\n"
+"_idINTEGERNOTNULLPRIMARYKEYAUTOINCREMENT,\r\n"
+"nameTEXTNOTNULL,\r\n"
+"ageINTEGERNOTNULL\r\n"
+")";
long_id();
@NonNull
Stringname();
longage();
interfaceCreator{
Tcreate(long_id,@NonNullStringname,longage);
}
finalclassMapperimplementsRowMapper{
privatefinalFactoryuserModelFactory;
publicMapper(FactoryuserModelFactory){
this.userModelFactory=userModelFactory;
}
@Override
publicTmap(@NonNullCursorcursor){
returnuserModelFactory.creator.create(
cursor.getLong(0),
cursor.getString
(1),
cursor.getLong
(2)
);
}
}
finalclassMarshal{
protectedfinalContentValuescontentValues=newContentValues();
Marshal(@NullableUserModelcopy){
if(copy!
=null){
this._id(copy._id());
this.name(copy.name());
this.age(copy.age());
}
}
publicContentValuesasContentValues(){
returncontentValues;
}
publicMarshal_id(long_id){
contentValues.put("_id",_id);
returnthis;
}
publicMarshalname(Stringname){
contentValues.put("name",name);
returnthis;
}
publicMarshalage(longage){
contentValues.put("age",age);
returnthis;
}
}
finalclassFactory{
publicfinalCreatorcreator;
publicFactory(Creatorcreator){
this.creator=creator;
}
/**
*@deprecatedUsecompiledstatements(
*/
@Deprecated
publicMarshalmarshal(){
returnnewMarshal(null);
}
/**
*@deprecatedUsecompiledstatements(
*/
@Deprecated
publicMarshalmarshal(UserModelcopy){
returnnewMarshal(copy);
}
publicSqlDelightStatementselect_by_name(){
returnnewSqlDelightStatement(""
+"select*fromuser",
newString[0],Collections.singleton("user"));
}
publicMapperselect_by_nameMapper(){
returnnewMapper(this);
}
}
}
从这个接口可以看到重要的有下面四个类和接口:
Mapper
把Cursor映射为Java对象
Marshal
把Java对象转换为ContentValues,好方便插入数据库
接口Creator
里面定义了一个方法create用来创建我们的Model类型(这里用的是泛型T)
Factory
里面包含一个实现了Creator对象的creator,如果有列值需要转换的话还要包括相应的ColumnAdapter,它的构造函数传入Creator和ColumnAdapter这些参数,marshal方法返回Mode的Marshal(通过它的asContentValues获取对应的ContentValues)
3.定义Bean类
这里利用AtuoValue重写接口,因为SqlDelight生成的接口适合AutoValue无缝接入的,AutoValue的作用的是,自动生成bean代码,AutoValue请点击我。
使用需要导包
//基础AutoValue
apt'com.google.auto.value:
auto-value:
1.4-rc3'
provided'com.google.auto.value:
auto-value:
1.4-rc3'
//AutoValue的扩展,为每个AutoValue注释对象创建一个简单的GsonTypeAdapterFactory。
//
apt'com.ryanharter.auto.value:
auto-value-gson:
0.4.6'
provided'com.ryanharter.auto.value:
auto-value-gson:
0.4.6'
//AutoValue的扩展,支持Android的Parcelable
apt'com.ryanharter.auto.value:
auto-value-parcel:
0.2.5'
//可选择TypeAdapter支持
compile'com.ryanharter.auto.value:
auto-value-parcel-adapter:
0.2.5'
这时候User的bean为
@AutoValue
publicabstractclassUserimplementsUserModel{
publicstaticfinalFactoryFACTORY=newFactory<>(newCreator(){
@Override
publicUsercreate(long_id,@NonNullStringname,longage){
//AutoValue_User需要先make一下module
returnnewAutoValue_User(_id,name,age);
}
});
publicstaticfinalRowMapperSELECT_ALL_MAPPER=FACTORY.select_by_nameMapper();
}
4.使用
然后写一个Activity来测试看看(Sqlite相关知识请XX,点我看教程):
publicclassMainActivityextendsAppCompatActivity{
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//创建获取名字为tp的数据库
SQLiteDatabasesqLiteDatabase=newStuDBHelp(this,"tp",null,1).getWritableDatabase();
//插入数据
sqLiteDatabase.insert("user",null,createContentValues(0,"天平",21));
sqLiteDatabase.insert("user",null,createContentValues(1,"逼辉",23));
Log.e("@@","数据数量:
:
"+getAllUsers(sqLiteDatabase).size());
}
publicListgetAllUsers(SQLiteDatabasedb){
Listresult=newArrayList<>();
//try-with-resources写法,括号里面的资源需要继承AutoCloseable,作用是可以自动关闭对象
try(Cursorcursor=db.rawQuery(User.FACTORY.select_by_name().statement,newString[0])){
while(cursor.moveToNext()){
result.add(User.SELECT_ALL_MAPPER.map(cursor));
}
}
returnresult;
}
publicContentValuescreateContentValues(intid,Stringname,intage){
ContentValuescontentValues=newContentValues();
contentValues.put("_id",id);
contentValues.put("name",name);
contentValues.put("age",age);
returncontentValues;
}
classStuDBHelpextendsSQLiteOpenHelper{
publicStuDBHelp(Contextcontext,Stringname,SQLiteDatabase.CursorFactoryfactory,intversion){
super(context,name,factory,version);
}
//第一次创建数据库的调用的方法
@Override
publicvoidonCreate(SQLiteDatabasedb){
//db.execSQL(User.CREATE_TABLE);
//创建表
db.execSQL(User.CREATE_TABLE);
}
@Override
publicvoidonUpgrade(SQLiteDatabasedb,intoldVersion,intnewVersion){
}
}
}
这时候你会看到输出:
数据数量:
:
2
2.3sq语句参数
.sq文件使用与SQLite完全相同的语法,包括SQLite绑定args(官方文档点我)。
如果语句包含绑定args,将在Factory上生成类型安全方法,该方法返回包含查询字符串,查询args和要查询的表的字段的SqlDelightStatement。
上面的例子中,我把sq文件里面的查询语句添加where条件,变成:
select_by_name:
select*fromuserwherename='天平';
然后重新make一下module,然后修改查询的getAllUsers方法:
publicListgetAllUsers(SQLiteDatabasedb){
Listresult=newArrayList<>();
//创建SqlDelightStatement对象,里面有查询字符串和参数
SqlDelightStatementquery=User.FACTORY.select_by_name("天平");
//try_with_resources写法,括号里面的资源需要继承AutoCloseable,作用是可以自动关闭对象
try(Cursorcursor=db.rawQuery(User.FACTORY.select_by_name("天平").statement,query.args)){
while(cursor.moveToNext()){
result.add(User.SELECT_ALL_MAPPER.map(cursor));
}
}
returnresult;
}
此时可以看到输出:
数据数量:
:
1
如果是数组就用IN:
select_by_names:
select*fromuserwherenamein?
;
publicListgetAllUsers(SQLiteDatabasedb){
Listresult=newArrayList<>();
SqlDelightStatementquery=User.FACTORY.select_by_name(newString[]{"天平","逼辉"});
try(Cursorcursor=db.rawQuery(query.statement,query.args)){
while(cursor.moveToNext()){
result.add(User.SELECT_ALL_MAPPER.map(cursor));
}
}
returnresult;
}
还能用运算符,例如查询user表的name字段是天开头的或结尾的:
--查询use表,百分号(%)代表零个、一个或多个数字或字符,||连接两个不同的字符串,得到一个新的字符串。
?
1标识参数为同一个的意思
select_by_name:
select*fromuserwhere
namelike'%'||?
1
or
namelike?
1||'%';
SqlDelightStatementquery=User.FACTORY.select_by_name("天");
Cursorcursor=db.rawQuery(query.statement,query.args)
2.4插入、更新、删除
执行多次的插入,更新和删除都需要make一下module,SQLDelight会为为它们生成一个类型安全类。
例如在User.sq添加一句:
--更新name为参数1的年龄为参数2
update_age:
updateusersetage=?
wherename=?
;
重新make一下module会看到UserModel有一个Update_age类:
finalclassUpdate_ageextendsSqlDelightCompiledStatement.Update{
publicUpdate_age(SQLiteDatabasedatabase){
super("user",pileStatement(""
+"updateusersetage=?
wherename=?
"));
}
publicvoidbind(longage,@NonNullStringname){
program.bindLong(1,age);
program.bindString(2,name);
}
}
更新的时候(在上面的栗子的MainActivity的onCreate修改):
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//创建获取名字为tp的数据库
SQLiteDatabasesqLiteDatabase=newStuDBHelp(this,"tp",null,1).getWritableDatabase();
//插入数据
sqLiteDatabase.insert("user",null,createContentValues(0,"天平",21));
sqLiteDatabase.insert("user",null,createContentValues(1,"逼辉",23));
//创建Update_age对象
User.Update_ageupdateAge;
updateAge=newUser.Update_age(sqLiteDatabase);
//设置修改的值
updateAge.bind(66,"天平");
updateAge.bind(99,"逼辉");
//执行
updateAge.program.execute();
//执行,返回修改的行数,这个方法只修改最后一个
//updateAge.program.executeUpdateDelete();
for(Useruser:
getAllUsers(sqLiteDatabase)){
Log.e("@@","user:
"+user.toString());
}
}
这时候会看到输出:
user:
User{_id=0,name=天平,age=66}
user:
User{_id=1,name=逼辉,age=99}
ps:
注意:
如果是bind了多个,executeUpdateDelete只能修改最后那个。
如果需要同时修改需要使用execute,如上面的代码
2.5多个结果
选择返回多个结果列的话,要为查询生成的结果自定义模型、映射程序和工厂方法。
例如:
在User.sq底部添加语句:
--根据age分组,group_concat链接所有的相同age的名字(默认使用逗号链接)
names_for_age:
selectage,group_concat(name)
fromuser
groupbyage;
在Userbean里面添加一个类和静态常量:
@AutoValue
publicabstractclassUserimplementsUserModel{
//
publicstaticfinalFactoryFACTORY=newFactory<>(newCreator(){
@Override
publicUsercreate(long_id,@NonNullStringname,longage){
returnnewAutoV