Android 开发 框架系列 Google的ORM框架 Room
目录
自定义表名 tableName 自定义字段名@ColumnInfo
简介
Android 2017 IO大会推出了官方数据库框架:Room。Room其实就只是对原生的SQLite API进行了一层封装。不得不说google其实早应该出SQLite的ORM了,因为Android的SQLite谁用谁知道,没有任何封装的字符输入。如果不对着Demo基本上会有记不起来的一两个关键字的时候,而且完全手敲容易输入错误。当然还有很多其他的Android ORM框架例如OrmLite、GreenDao 和 Sugar。但是本着Google爸爸的东西肯定有牛逼的地方,我们还是需要学习一下怎么使用。
这里解释一下什么是ORM,对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。其实更好的说明就是,对数据库指令的二次封装。使其使用更加简单、轻松、缓解记不住指令的尴尬。
参考文档:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2017/0525/7971.html
数据库升级迁移:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2017/0728/8278.html
导入工程
这里只说明Android studio的导入
1.首先在build.gradle里添加
allprojects {
repositories {
jcenter()
google()
}
}
2.然后在添加依赖
implementation 'android.arch.persistence.room:runtime:1.0.0'
annotationProcessor "android.arch.persistence.room:compiler:1.0.0"
使用流程概况
因为创建步骤较多所以这里简单先给一个流程,给大家有一个简单的概念:
- 创建数据class,使用的注释是:@Entity
- 创建Dao 数据操作抽象class,使用的注释是:@Dao(负责提供操作数据的方法,比如基本的增加、更新、查找、删除。当然除了这些基本的,更复杂的我们将在后续深入学习)
- 创建应用程序数据库的抽象class,使用的注释是:@Database(负责创建应用数据库,组合“数据class”与“数据操作class”,还有数据库版本)
- 实例化使用数据库
一个简单的小Demo
在深入学习前,我们先来一个简单的小demo演示一下创建流程与使用。
1.首先是创建数据class,它使用关键注释是@Entity,这个class代表着一列的数据表(里面包含着主键id(这是是必需的)、内容1标题、内容2标题等等)
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey; @Entity //重点!这个是关键.数据class必需使用@Entity注释
public class MyData { @PrimaryKey //@PrimaryKey = 主键
@ColumnInfo(name = "id")
public int id; @ColumnInfo(name = "name")//这个代表了这个数据标题名称
public String name; public String content; //当然不写 @ColumnInfo(name = "content") 也是可以的,因为数据名称可以默认为变量名称。
}
2.创建Dao 数据操作抽象class,它使用关键注释是@Dao,这个class负责提供操作数据的方法(比如基本的增加、更新、查找、删除)
import java.util.List;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import androidx.room.Update; @Dao //重点! 这个是关键,数据操作的class必需使用@Dao来注释
public abstract class MyDao { //另外注意它是一个抽象类 @Insert(onConflict = OnConflictStrategy.REPLACE)
//@Insert = 插入, onConflict = 如果冲突 OnConflictStrategy.REPLACE = 如果冲突就替换
public abstract void insert(MyData... data); //添加了插入注释后,这个方法就可以当做插入数据的方法使用 @Update
public abstract void update(MyData... data);// @Update = 更新 @Delete
public abstract void delete(MyData... data);// @Delete = 删除 @Query("select * from MyData")
abstract List<MyData> getAll();
//@Query = 查询 ,这里的注释括号里的内容代表这查询的关键词,可以用于筛查想要的数据。
}
3.创建应用程序数据库的抽象class,使用的注释是:@Database(负责创建应用数据库,组合数据class与数据操作class,还有数据库版本)
/**
* 重点!应用数据库class必需使用Database注释
* entities 实体 = 我们的数据class MyData,注意它使用了{}包裹
* version = 1 数据库版本号
* exportSchema = false 导出模式
*/
@Database(entities = {MyData.class},version = 1,exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
public abstract MyDao Dao(); private static AppDatabase mAppDataBase;
public static AppDatabase getI(Context context){ //实现单例模式
if (mAppDataBase == null){
mAppDataBase = Room.databaseBuilder(context,AppDatabase.class,"data.db")//data.db 是你的数据库名称
.build();
}
return mAppDataBase;
}
}
注意!这里使用的是单例模式。 还有另外,你要记住你的数据库名称。另外数据库是允许在主线程里创建的,但是不建议在主线程里创建。如果你非要创建,可以添加属性后在UI线程中创建。
mAppDataBase = Room.databaseBuilder(context,AppDatabase.class,"data.db")
.allowMainThreadQueries()
.build();
4.在activity的子线程里实例化并且使用数据库。
public class RoomActivity extends AppCompatActivity {
private static final String TAG = "RoomActivity";
private Button mBtnGetData;
private AppDatabase appDatabase;
private TextView mTextView; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_room); /**
* 实例化应用数据库
*/
new Thread(new Runnable() {
@Override
public void run() {
appDatabase = AppDatabase.getI(RoomActivity.this);
MyDao dao = appDatabase.Dao();//得到实例化的数据操作class
MyData data = new MyData();//实例一个数据class
data.id = 101;
data.name = "橘子";
data.content = "酸酸的";
MyData data2 = new MyData();
data2.id = 102;
data2.name = "苹果";
data2.content = "脆脆的";
//因为我在MyDao的insert插入方法里写的是数组参数,所以也可以多个添加
dao.insert(data,data2);
Log.e(TAG, "数据导入完成");
}
}).start(); mTextView = (TextView)findViewById(R.id.textView);
mBtnGetData = (Button)findViewById(R.id.btn_getdata);
mBtnGetData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Thread(new Runnable() {
@Override
public void run() {
MyDao dao = appDatabase.Dao();
final StringBuffer sb = new StringBuffer();
sb.append("数据库内容是:"+"\n"+"-------------------------------\n");
List<MyData> datas = dao.getAll();//得到所有数据的List
for (MyData data : datas){
sb.append("id : "+String.valueOf(data.id)+"\n");
sb.append("name : "+data.name+"\n");
sb.append("content : "+data.content+"\n");
sb.append("-------------------------------\n");
}
runOnUiThread(new Runnable() {
@Override
public void run() {
mTextView.setText(sb.toString());
}
});
}
}).start();
}
});
}
}
以上是创建数据库、插入数据、查询数据在线程中的操作。创建数据库、插入数据和查询数据最好都在子线程里操作,尽量不要在主线程里操作数据库。另外注意!如果你在子线程里创建了数据库,那么你在主线程中就无法获取数据,一定要在子线程里获取数据。
效果图:
深入学习 @Entity使用
@Entity 是用来创建表格与表格中一列的内容的。Room 会为实体类中定义的每个字段在数据库中创建“列”。
下面这个代码块展示了如何定义一个实体
@Entity
public class MyData { @PrimaryKey //@PrimaryKey = 主键
@ColumnInfo(name = "id")
public int id; @ColumnInfo(name = "name")
public String name; public String content; @Ignore //使用@Ignore的数据将不会在数据库创建此数据的一列
public Bitmap bitmap; }
另外如果你有不需要数据库创建字段列,你可以使用@Ignore 进行注释。为了能够保存某个字段,Room必须能够对它进行操作。你可以使用public修饰符,或者你可以提供getter和setter方法。如果你选择后者,记住Room是基于JavaBeans约定的。
自定义表名 tableName 自定义字段名@ColumnInfo
在通常不添加自定义表名的属性情况下,表名是默认class名的,但是如果有需求自定义可以这样实现
@Entity (tableName = "Data")//创建自定义表名称
public class MyData {
@PrimaryKey
public int id; @ColumnInfo(name = "name")
public String name; @ColumnInfo(name = "content")
public String content;
}
另外你需要注意一点,表名对大小写是有区分的。
主键@PrimaryKey
一份数据实体,必定包含一个主键变量(或者叫主键字段) 。
自增主键 @PrimaryKey (autoGenerate = true)
如果你不想自己添加主键值,可以设定这个属性,让主键值自增
@Entity
public class MyData { @PrimaryKey (autoGenerate = true)
public int id; @ColumnInfo(name = "name")
public String name; @ColumnInfo(name = "content")
public String content; }
我们试试不添加主键内容,让它自增
MyDao dao = appDatabase.Dao();//得到实例化的数据操作class
MyData data = new MyData();//实例一个数据class
data.name = "橘子";
data.content = "酸酸的";
MyData data2 = new MyData();
data2.name = "苹果";
data2.content = "脆脆的";
dao.insert(data,data2);//因为我在MyDao的insert插入方法里写的是数组参数,所以也可以多个添加
效果图:
组合主键 primaryKeys
@Entity (primaryKeys = {"id","num"})
public class MyData { public int id;
public int num; @ColumnInfo(name = "name")
public String name;
@ColumnInfo(name = "content")
public String content; }
索引 @Index
了解索引:假如我们比喻数据库是一份字典,如果不添加索引我们去查找某个数据,数据库是逐行逐列的去查询直到整个字典被遍历完成。这样搜索必然慢一些,而这个时候我们可以添加索引,原理跟你在查字典的时候在索引页面里去查询关键信息(比如拼音或者笔画查找)然后在逐步缩小范围最终找到想要的信息并且锁定页数。
使用范围:索引并不是万能的,也有它的优点与缺点,索引可以增加更新、删除、查询的速度,但是会增加插入数据的速度。使用索引不适合使用在:1.数据量少的情况 2.有大量null值,不值得索引查询 3.频繁更新、插入的数据库,插入修改操作频繁反而更慢。
其他:索引的创建不需要添加什么标示字符串,你只需要告诉数据库需要创建索引的列,它会自动添加数据索引。
废话了这么多,我们开始创建索引:
单列索引:
@Entity (indices = {@Index("name")})//这里说明了,我们的name需要索引
public class MyData { @PrimaryKey(autoGenerate = true)
public int id; @ColumnInfo(name = "name")
public String name; @ColumnInfo(name = "content")
public String content;
}
多列索引(组合索引):
@Entity (indices = {@Index(value = {"name","content"})})//这个是索引
public class MyData { @PrimaryKey(autoGenerate = true)
public int id; @ColumnInfo(name = "name")
public String name; @ColumnInfo(name = "content")
public String content;
}
索引唯一性:
你可以通过在@Index注解下设置unique为true,即可强制实现该字段的唯一性。防止name与content内容一致
@Entity (indices = {@Index(value = {"name","content"},unique = true)})
外键 @ForeignKey
了解外键:外键是什么?按照字面解读“外部的键值”?恩,部分解释到了它的用处。它的确是关联2个列的关键功能。百度这么解释的:如果公共关键字在一个关系中是主关键字,那么这个公共关键字被称为另一个关系的外键。由此可见,外键表示了两个关系之间的相关联系。以另一个关系的外键作主关键字的表被称为主表,具有此外键的表被称为主表的从表。外键又称作外关键字。外键有2个基本特性:1.约束插入的值 (你无法插入一个没有被创建的主键)2.被主键影响(比如主键删除,主键下的全部外键会被删除)
使用范围:聊天记录、好友列表等等需要主次分类关联数据的地方.
其他:注意!Android 好像是不允许主键操作外键,比如直接从主键得到外键数据。但是它是允许关联主外键的
代码演示:
为了思维连续,我将贴全代码。
步骤一 创建数据实体class
我将分别创建 书架数据类-Classify 与 书籍数据类-Book。
书架数据类-Classify
@Entity
public class Classify { @PrimaryKey (autoGenerate = true)
public int id;//书架id public String classifyName;//书架名称
}
书籍数据类-Book 请注意,我给Book添加的外键属性。
//重点!这里写入了外键属性!声明了,书架Classify是我的主键 , 父类列 = 书架Classify里的id 子类列 = 我下面创建的classifyId变量
@Entity (foreignKeys = @ForeignKey(entity = Classify.class,parentColumns = "id",childColumns = "classifyId"))
public class Book { @PrimaryKey (autoGenerate = true)
public int bookId;//书籍id public String bookName;//书籍名称 public int classifyId;//书架id
}
步骤二 创建数据操作Dao类
也是分别2个ClassifyDao 与 BookDao
@Dao
public abstract class ClassifyDao { @Insert(onConflict = OnConflictStrategy.REPLACE)
public abstract void insert(Classify... data); @Update
public abstract void update(Classify... data); @Delete
public abstract void delete(Classify... data); @Query("select * from Classify")
public abstract List<Classify> getAll();
}
@Dao
public abstract class BookDao { @Insert(onConflict = OnConflictStrategy.REPLACE)
public abstract void insert(Book... data); @Update
public abstract void update(Book... data); @Delete
public abstract void delete(Book... data); @Query("select * from Book")
public abstract List<Book> getAll();
}
步骤三 创建应用数据库
@Database(entities = {Classify.class,Book.class},version = 1,exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
public abstract ClassifyDao classifyDao();
public abstract BookDao bookDao(); private static AppDatabase mAppDataBase;
public static AppDatabase getI(Context context){ //实现单例模式
if (mAppDataBase == null){
mAppDataBase = Room.databaseBuilder(context,AppDatabase.class,"data.db")//data.db 是你的数据库名称
.build();
}
return mAppDataBase;
}
}
老样子,我导入了2个返回操作抽象类的方法,使用了单例模式得到应用数据库实例。
步骤四 创建应用数据库实例、插入数据、查询数据
public class RoomActivity extends AppCompatActivity {
private static final String TAG = "RoomActivity";
private Button mBtnGetData,mBtnSetData;
private AppDatabase appDatabase;
private TextView mTextView; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_room);
mTextView = (TextView)findViewById(R.id.textView);
mBtnGetData = (Button)findViewById(R.id.btn_getdata);
mBtnSetData = (Button)findViewById(R.id.btn_setdata);
mBtnSetData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
/**
* 实例化应用数据库
*/
new Thread(new Runnable() {
@Override
public void run() {
appDatabase = AppDatabase.getI(RoomActivity.this);//单例模式得到应用数据库
ClassifyDao classifyDao = appDatabase.classifyDao();//得到实例化的数据操作class
Classify data = new Classify();//实例一个数据class
data.classifyName = "科幻类";
Classify data2 = new Classify();
data2.classifyName = "技术类";
classifyDao.insert(data,data2); BookDao bookDao = appDatabase.bookDao();
Book book1 = new Book();
book1.bookName = "三体";
book1.classifyId = 1;//写入主键id
Book book2 = new Book();
book2.bookName = "黑暗森林";
book2.classifyId = 1;
Book book3 = new Book();
book3.bookName = "Java从入门到精通";
book3.classifyId = 2;
Book book4 = new Book();
book4.bookName = "Android第一行代码";
book4.classifyId = 2;
bookDao.insert(book1,book2,book3,book4); Log.e(TAG, "数据导入完成");
}
}).start(); }
}); mBtnGetData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Thread(new Runnable() {
@Override
public void run() { final StringBuffer sb = new StringBuffer();
sb.append("数据库内容是:"+"\n"+"-------------------------------\n"); ClassifyDao classifyDao = appDatabase.classifyDao();
BookDao bookDao = appDatabase.bookDao();
List<Classify> classifyList = classifyDao.getAll();
List<Book> bookList = bookDao.getAll();
for (Classify classify : classifyList){
sb.append("ClassifyId:"+String.valueOf(classify.id)+"\n");
sb.append("Name:"+classify.classifyName+"\n");
sb.append("-------------------------------\n");
}
for (Book book : bookList){
sb.append("BookId:"+String.valueOf(book.bookId)+"\n");
sb.append("Name:"+book.bookName+"\n");
sb.append("ClassifyId:"+book.classifyId+"\n");
sb.append("-------------------------------\n");
} runOnUiThread(new Runnable() {
@Override
public void run() {
mTextView.setText(sb.toString());
}
});
}
}).start();
}
});
}
}
唯一需要注意的地方,我写入的主键id,因为我在书架数据class标示的id是自增,然后我又创建了2个书架,所以在书籍class里写入的主键id是1和2。到此为止,演示外键的demo代码就已经贴全了。
效果图:
外键的功能探索:
约束:
或许某些同学会这么有这些疑问,ヾ(。`Д´。) 看到目前为止外键好像并没有什么用啊。恩,这是我一开始的疑惑,特别是我发现还需要手动添加外键(原谅我之前没有接触过数据库),这不是鸡肋么。下面,我们就来演示外键的功能之一”约束“。
Classify data = new Classify();//实例一个数据class
data.classifyName = "科幻类";
Classify data2 = new Classify();
data2.classifyName = "技术类";
classifyDao.insert(data,data2); BookDao bookDao = appDatabase.bookDao();
Book book1 = new Book();
book1.bookName = "三体";
book1.classifyId = 1;
Book book2 = new Book();
book2.bookName = "黑暗森林";
book2.classifyId = 1;
Book book3 = new Book();
book3.bookName = "Java从入门到精通";
book3.classifyId = 3;
Book book4 = new Book();
book4.bookName = "Android第一行代码";
book4.classifyId = 3;
bookDao.insert(book1,book2,book3,book4);
因为,我们只创建了2个书架。按照自增id,最多自增到了“2”。所以我们这里给它一个错误的书架id “3”,看看会出现什么情况。
恩,报错了。我们看看,为什么报错了?
android.database.sqlite.SQLiteConstraintException: FOREIGN KEY constraint failed (code 787)
at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:782)
at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:788)
at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:86)
at androidx.sqlite.db.framework.FrameworkSQLiteStatement.executeInsert(FrameworkSQLiteStatement.java:51)
at androidx.room.EntityInsertionAdapter.insert(EntityInsertionAdapter.java:80)
at com.example.user.demo.room.BookDao_Impl.insert(BookDao_Impl.java:79)
at com.example.user.demo.room.RoomActivity$1$1.run(RoomActivity.java:56)
at java.lang.Thread.run(Thread.java:761)
报错的原因是我们添加了一个不存在的主键id,我们的外键功能成功的约束了。
关联操作:
外键还有一个功能,关联操作,下面我们来演示一个主键id的列被删除了,它关联的外键也被删除的功能。在代码相同的部分我就不贴了,可以参考上面已经贴出的代码。下面将贴出具体实现的代码。
在之前的Book数据class的外键属性里添加了onDelete属性:
@Entity (foreignKeys =
@ForeignKey(entity = Classify.class,
parentColumns = "id",
childColumns = "classifyId",
onDelete = CASCADE))//重点!我们添加onDelete属性为CASCADE串联.表示删除操作串联
public class Book { @PrimaryKey (autoGenerate = true)
public int bookId; public String bookName; public int classifyId;
}
下面来实现查找一个数据列的方法:
@Query("select * from Classify where id = :num ")
public abstract Classify getClassify(int num);
这里在ClassifyDao类里添加了一个根据id查找实体数据列的方法。这里你只要先稍微了解。后续我将详解解释查询功能部分。
在activity里实现:
首先,导入数据的部分不变依然是上面的操作,我们在获取数据的步骤里添加了如下代码:
mBtnGetData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Thread(new Runnable() {
@Override
public void run() { final StringBuffer sb = new StringBuffer();
sb.append("数据库内容是:"+"\n"+"-------------------------------\n"); ClassifyDao classifyDao = appDatabase.classifyDao();
BookDao bookDao = appDatabase.bookDao();
Classify classify = classifyDao.getClassify(2);//找到id为2的列
classifyDao.delete(classify);//删除这个列
List<Classify> classifyList = classifyDao.getAll();//得到全部书架数据列
List<Book> bookList = bookDao.getAll();//得到全部书籍数据列
for (Classify classifyItem : classifyList){
sb.append("ClassifyId:"+String.valueOf(classifyItem.id)+"\n");
sb.append("Name:"+classifyItem.classifyName+"\n");
sb.append("-------------------------------\n");
}
for (Book bookItem : bookList){
sb.append("BookId:"+String.valueOf(bookItem.bookId)+"\n");
sb.append("Name:"+bookItem.bookName+"\n");
sb.append("ClassifyId:"+bookItem.classifyId+"\n");
sb.append("-------------------------------\n");
} runOnUiThread(new Runnable() {
@Override
public void run() {
mTextView.setText(sb.toString());
}
});
}
}).start();
}
});
先找到id为2的列,然后在删除这个列(删除的方法请向上看之前的代码),然后在分别读出数据库里的数据。
效果图:
删除了主键书架里,id等于2 名称是技术类的书架,但是它的外键数据也被删除了。关联删除实现了。
嵌入对象 @Embedded
有时你可能想把一个entity或者一个POJOs(数据类)作为一个整体看待。这种情况下,你可以使用@Embedded注解,表示你想把一个对象分解为表的子字段。然后你就可以像其它独立字段那样查询这些嵌入的字段。通俗点的说法就是,让带有多个成员的类的每个变量都作为表中的字段。下面我就来演示一下如何添加一个class作为字段对象。
首先我们需要创建实体数据,并添加对象:
@Entity
public class Book { @PrimaryKey (autoGenerate = true)
public int bookId; public String bookName; @Embedded //重点,这里使用的是下面的内部class作为对象
public Detailed detailed;
} class Detailed{ public String author; public String press; @ColumnInfo(name = "paginal_number")
public int num; }
在activity里插入数据、查询数据:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_room);
mTextView = (TextView)findViewById(R.id.textView);
mBtnGetData = (Button)findViewById(R.id.btn_getdata);
mBtnSetData = (Button)findViewById(R.id.btn_setdata);
mBtnSetData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
/**
* 实例化应用数据库
*/
new Thread(new Runnable() {
@Override
public void run() {
appDatabase = AppDatabase.getI(RoomActivity.this);//单例模式得到应用数据库
BookDao bookDao = appDatabase.bookDao();
Book book1 = new Book();
book1.bookName = "Android第一行代码";
Detailed detailed = new Detailed();
detailed.author = "郭霖";
detailed.press = "人民邮电出版社";
detailed.num = 570;
book1.detailed = detailed;
bookDao.insert(book1); Log.e(TAG, "数据导入完成");
}
}).start();
}
}); mBtnGetData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Thread(new Runnable() {
@Override
public void run() {
final StringBuffer sb = new StringBuffer();
sb.append("数据库内容是:"+"\n"+"-------------------------------\n");
BookDao bookDao = appDatabase.bookDao();
List<Book> bookList = bookDao.getAll();
for (Book bookItem : bookList){
sb.append("BookId:"+String.valueOf(bookItem.bookId)+"\n");
sb.append("Name:"+bookItem.bookName+"\n");
sb.append("author:"+bookItem.detailed.author+"\n");
sb.append("press"+bookItem.detailed.press+"\n");
sb.append("paginal_number:"+bookItem.detailed.num+"\n");
sb.append("-------------------------------\n");
}
runOnUiThread(new Runnable() {
@Override
public void run() {
mTextView.setText(sb.toString());
}
});
}
}).start();
}
});
}
}
效果图:
深入学习@Dao
在room框架里@Dao注释的的class 都是用于数据增、删、查、更新方法的提供。另外你需要注意它必需使用接口或者抽象class来实现。
创建Dao Class
@Dao 注释的Class对具体操作那个数据class并没有要求,你可以将所有的操作方法都放如一个@Dao类,也可以分别添加多个@Dao类。
重载方法操作不同表
老样子,按照我的习惯,不厌其烦的贴全代码
步骤一 创建操作Dao
@Dao
public abstract class AllDao { @Insert(onConflict = OnConflictStrategy.REPLACE)
public abstract void insert(Fruits... data); @Insert(onConflict = OnConflictStrategy.REPLACE)
public abstract void insert(Drinks...data); @Update
public abstract void update(Fruits... data); @Update
public abstract void update(Drinks... data); @Delete
public abstract void delete(Fruits... data); @Delete
public abstract void delete(Drinks... data); @Query("select * from Fruits")
public abstract List<Fruits> getFruitsAll(); @Query("select * from Drinks")
public abstract List<Drinks> getDrinksAll(); }
我们这里重载了分别为Fruits水果 与 Drinks饮料的 数据库操作方法
步骤二 创建对应的数据class 水果与饮料
水果
@Entity
public class Fruits {
@PrimaryKey(autoGenerate = true)
public int id;
public String name;
public Fruits(String name){
this.name = name;
}
}
饮料
@Entity
public class Drinks {
@PrimaryKey(autoGenerate = true)
public int id;
public String name;
public Drinks(String name){
this.name = name;
}
}
步骤三 创建应用程序数据库的抽象class
@Database(entities = {Drinks.class,Fruits.class},version = 1,exportSchema = false)
//注意! 这里分别添加了 Drinks.class和Fruits.class
public abstract class AppDatabase extends RoomDatabase {
public abstract AllDao Dao(); private static AppDatabase mAppDataBase;
public static AppDatabase getI(Context context){ //实现单例模式
if (mAppDataBase == null){
mAppDataBase = Room.databaseBuilder(context,AppDatabase.class,"data.db")//data.db 是你的数据库名称
.build();
}
return mAppDataBase;
}
}
注意!请别忘记了导入数据class。我这里分别添加了2个Drinks.class和Fruits.class
步骤四 导入数据和获取数据
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_room);
mTextView = (TextView)findViewById(R.id.textView);
mBtnGetData = (Button)findViewById(R.id.btn_getdata);
mBtnSetData = (Button)findViewById(R.id.btn_setdata);
mBtnSetData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Thread(new Runnable() {
@Override
public void run() {
appDatabase = AppDatabase.getI(RoomActivity.this);
AllDao dao = appDatabase.Dao();
dao.insert(new Drinks("肥仔快乐水"));//使用了重载方式
dao.insert(new Drinks("解奶宝矿力"));
dao.insert(new Fruits("香蕉"));
dao.insert(new Fruits("西瓜"));
}
}).start();
}
}); mBtnGetData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Thread(new Runnable() {
@Override
public void run() {
final StringBuffer sb = new StringBuffer();
sb.append("数据库内容是:"+"\n"+"-------------------------------\n");
AllDao dao = appDatabase.Dao();
List<Drinks> DrinksList = dao.getDrinksAll();
List<Fruits> FruitsList = dao.getFruitsAll();
for (Drinks drinks : DrinksList){
sb.append("Id:"+String.valueOf(drinks.id)+"\n");
sb.append("Name:"+drinks.name+"\n");
sb.append("-------------------------------\n");
}
for (Fruits fruits : FruitsList){
sb.append("Id:"+String.valueOf(fruits.id)+"\n");
sb.append("Name:"+fruits.name+"\n");
sb.append("-------------------------------\n");
}
runOnUiThread(new Runnable() {
@Override
public void run() {
mTextView.setText(sb.toString());
}
});
}
}).start();
}
});
}
效果图:
插入 @Insert
其实@Insert可以说明的东西不多,在上面这么多demo演示下,你也应该明白了@Insert是干什么的。简单的来说就是插入一份数据。但是当然还是有一些东西可以纠结的。。。
@Insert 方法的参数:
@Insert(onConflict = OnConflictStrategy.REPLACE)
public abstract void insert(Fruits data); //导入单个数据
@Insert(onConflict = OnConflictStrategy.REPLACE)
public abstract void insert(Fruits... data); //以数组导入数据
@Insert(onConflict = OnConflictStrategy.REPLACE)
public abstract void insert(List<Fruits> data); //以list导入数据
目前,本人就验证了这3种。第三种以list导入数据的形式。请放心,本人用demo验证过,可以使用。
@Insert 的属性:
Insert只有一个属性onConflict(数据冲突策略),所以我列举一下这个属性的值:
/**
*冲突策略-替换旧数据并继续事务。
*/
int REPLACE=1;
/**
*冲突策略-回滚事务。
*/
int ROLLBACK=2;
/**
*冲突策略-中止事务。
*/
int ABORT=3;
/**
*冲突策略-使事务失败。
*/
int FAIL=4;
/**
*冲突策略-忽略冲突。
*/
int IGNORE=5;
更新 @Update
更新@update与@Insert方法的使用方法是类似的。
@update 方法的参数 (与@Insert一样可以使用单个POJO类数据,POJO数组、POJO List,作为参数)
@update 的属性(与@Insert一样使用onConflict 作为属性,并且属性值完全一致,可以参考上面的Insert)
@Update(onConflict = OnConflictStrategy.REPLACE)
public abstract void update(Fruits... data);
删除 @Delete
删除@Delete 就很简单了,会根据你导入的POJO实体,去删除表里对应的数据列,除了基本的参数一样可以设置为单个、数组、list以外,并没有其他的可以设置的属性了。
@Delete
public abstract void delete(Fruits...data);
查询 @Query
查询@Query 就复杂了许多,因为涉及到了基本的SQList语法的使用。这里我会举例几个常用的查询语法作为例子。
查询数据表里所有的数据
@Query("select * from Fruits")
public abstract List<Fruits> getAll();
解释括号中的语法:
select 代表查询结果关键字,这里写入一个 * 星号表示我们得到所有表里的数据。
from 代表从哪里查询,这里我们写入了我们的表名Fruits
使用上面的方法,我们将会得到一个表里所有数据的List。
根据id查询单列数据
@Query("select * from Fruits where id=:num")//根据id查找一列数据
public abstract Fruits queryId(int num);
解释括号中的语法:
select 代表查询结果关键字,这里写入一个 * 星号表示我们得到所有表里的数据。这个与上面一样
from 代表从哪里查询,这里我们写入了我们的表名Fruits。
where 代表查询条件 这里我们查询id = 下面方法输入的值。 注意这里的写法 :num ,这是room特有的参数写法,只要想要导入方法参数就需要在参数名前面添加:。
使用上面的方法,我们将会得到方法里输入参数值的对应id,列里的所有数据。
查询指定数据列
@Query("select id from Fruits ")//返回id字段里的所有id值数据
public abstract int[] queryIdArray();
解释括号中的语法:
select 代表查询结果关键字,这里写入一个 id 星号表示我们得到id字段里的所有数据。
使用上面的方法,我们将会得到id字段里的所有id值
查询数据数量
@Query("select count(*) from Fruits")
public abstract int queryIsEmpty();
更多数据库命令请查询:https://www.runoob.com/sqlite/sqlite-tutorial.html
更新数据库
请参考:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2017/0728/8278.html
Android 开发 框架系列 Google的ORM框架 Room的更多相关文章
- Android开发用过的十大框架
http://blog.csdn.net/u011200604/article/details/51695096 本文系多方综合与转载整合,意在Android开发中能够知道和使用一些好用的第三方支持, ...
- Android开发之最火的开源框架之一Xutils2详解(摘自开源作者官方介绍详解)
此框架说实话还是挺不错的,挺好用的,功能多,所以我也用过. 由于CSDN博客写的字数有限制,所以全文的用法打包成了markdown 文件,因为markdown真的太还用了. 全文下载地址为: http ...
- ORM框架介绍——什么是ORM框架?
1.什么是ORM?对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术.ORM框架是连接数据库的桥梁,只要提供 ...
- 【Android开发坑系列】之事件分发机制
总结一下: 事件序列的定义:从手触摸屏幕(含)到离开屏幕(含)期间所发生的一系列交互事件.主要由ACTION_DOWN.ACTION_MOVE.ACTOIN_UP.ACTION_CANCEL等组成,其 ...
- 推荐扔物线的HenCoder Android 开发进阶系列 后期接着更新
官网地址:http://hencoder.com/ 我来做一次辛勤的搬运工 HenCoder:给高级 Android 工程师的进阶手册 HenCoder Android 开发进阶: 自定义 View ...
- Android 开发 图片网络缓存加载框架Fresco
简介 Fresco是一个在Android应用程序中显示图像的强大系统. Fresco负责图像的加载和显示.它将从网络.本地存储或本地资源加载图像,图像加载完成前会显示一个占位图片.它有两个级别的缓存: ...
- C# Android 开发中使用 Sqlite.NET ORM
开发环境:VS2015 Xamarin Sqlite.NET ORM 不就相当于 Entiry Framework For Xamarin 吗? 相当于用 C# 开发安卓程序访问 Sqlite 可以使 ...
- 【Android开发坑系列】如何让Service尽可能存活
流行的思路如下[2015-11-20更新]: 1.让Service杀不死.Service的onStartCommand返回START_STICKY,同时onDestroy里面调用startServic ...
- 【Android开发坑系列】之PopupWindow
PopupWindow在4.0之前的版本有个系统级别的BUG,必须借助一段自定义的fix代码来修复.其中mPopPm就是PopupWindow实例.java.lang.NullPointerExcep ...
随机推荐
- 人群密度估计 CrowdCount
最近在看人群密度估计方面的东西,把博客看到的一些方法简单总结一下,后续继续添加. 1.论文<CrowdNet: A Deep Convolutional Network for DenseCro ...
- 微信小程序des加密、PHP des解密
最近在做对小程序传输数据进行加密,加密方法有很多,使用的是des对称加密 采用的是CBC模式, 引用的插件为tripledes.js,https://github.com/Favour1111in/h ...
- 谷歌浏览器chrome的vuejs devtools 插件的安装
(推荐方法2) 安装方法1: 需正常打开chrome商店,搜索vuejs devtools 安装.chrome://extensions/ 开发者工具-扩展程序下启用: 方法2: github下载插件 ...
- xslfo和fop使用中的一些问题
最近项目中使用fop和xslfo打印pdf,遇到一些问题记录下来: 1.表格跨行.跨列: 使用number-rows-spanned和number-columns-spanned属性 比如:<f ...
- [Mac]secureCRT私钥转换为mac ssh私钥
工作环境从win迁移到mac后,win上原来用secureCRT生成的key,在mac的iterm2中不能兼容使用,导致无法再mac下登录.报错如下: key_load_public:invalid ...
- Selenium·自动化基础
date:2018505+2018506 day05+06mor 一.安装环境 1.cmd→pip install selenium 2.将浏览器驱动放入X:\Python27下(如chromedri ...
- HttpConnection详解【转】
HttpConnection详解[转] HttpURLConnection对象 1.从Internet获取网页,发送请求,将网页以流的形式读回来. 步骤:1)创建一个URL对象:URL url ...
- ID3-C45-CART
区别:使用不同的属性选择度量. 信息增益偏向多值属性 信息增益率倾向产生不平衡的划分 基尼指数偏向多值属性,并且当类的数量很大时会有困难,还倾向于导致相等大小的分区和纯度 C4.5: 优点:产生的分类 ...
- JavaEE思维导图
- Javascript 2.8
声明函数声明参数 function multiply(A,B,...N){}; 用return可以返回一个值/字符串/数组/布尔值 变量命名的Camel记号:从第二个单词开始把每个单词的首字母大写,其 ...