我有数据库与表联系,我想检查是否有联系某个电话号码。
@Query("SELECT * FROM contact WHERE phone_number = :number")
Flowable<Contact> findByPhoneNumber(int number);
我有RxJava 2 Composite一次性声明,以检查是否有联系电话号码。
disposable.add(Db.with(context).getContactsDao().findByPhoneNumber(phoneNumber)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableSubscriber<Contact>() {
@Override
public void onNext(final Contact contact) {
Log.d("TAG", "phone number fined");
Conversation conversation;
if(contact != null){
conversation = Db.with(context).getConversationsDao().findBySender(contact.getContactId());
if(conversation != null){
conversation.setUpdatedAt(Utils.getDateAndTimeNow());
saveConversation(contact, conversation, context, text, phoneNumber, false);
} else {
conversation = getConversation(contact, contact.getPhoneNumber());
saveConversation(contact, conversation, context, text, phoneNumber, true);
}
} else {
conversation = Db.with(context).getConversationsDao().findByPhone(phoneNumber);
if(conversation != null){
conversation.setUpdatedAt(Utils.getDateAndTimeNow());
saveConversation(contact, conversation, context, text, phoneNumber, false);
} else {
conversation = getConversation(contact, phoneNumber);
saveConversation(contact, conversation, context, text, phoneNumber, true);
}
}
}
@Override
public void onError(Throwable t) {
Log.d("TAG", "find phone number throwable");
Toast.makeText(context, t.getLocalizedMessage(), Toast.LENGTH_LONG).show();
}
@Override
public void onComplete() {
Log.d("TAG", "onComplete");
}
}));
如果查询可以找到所需电话号码的联系人,这可以正常工作,但如果有结果,则没有任何反应。
以下是我编写的两个测试用例,它们运行正常:
@RunWith(AndroidJUnit4.class)
public class ContactsTest {
private AppDatabase db;
@Rule
public InstantTaskExecutorRule instantTaskExecutorRule =
new InstantTaskExecutorRule();
@Before
public void initDb() throws Exception {
db = Room.inMemoryDatabaseBuilder(
InstrumentationRegistry.getContext(),
AppDatabase.class)
// allowing main thread queries, just for testing
.allowMainThreadQueries()
.build();
}
@After
public void close(){
db.close();
}
@Test
public void insertAndFindTest(){
final Contact contact = new Contact();
contact.setName("Test");
contact.setPhoneNumber(555);
db.contactsDao()
.insert(contact);
db.contactsDao().findByPhoneNumber(contact.getPhoneNumber())
.test()
.assertValue(new Predicate<Contact>() {
@Override
public boolean test(@NonNull Contact savedContact) throws Exception {
if(savedContact.getPhoneNumber() == contact.getPhoneNumber()){
return true;
}
return false;
}
});
}
@Test
public void findNoValues(){
db.contactsDao().findByPhoneNumber(333)
.test()
.assertNoValues();
}
}
我如何解决这个问题?
答案 0 :(得分:21)
正如here所述,您可以在这种情况下使用Maybe
或Single
:
@Query("SELECT * FROM Users WHERE id = :userId")
Maybe<User> getUserById(String userId);
以下是发生的事情:
@Query("SELECT * FROM Users WHERE id = :userId")
Single<User> getUserById(String userId);
以下是一些情景:
它已在版本 1.0.0-alpha5 中添加。
答案 1 :(得分:7)
如果您只想使用一次实体,Single
或Maybe
就足够了。但是,如果您想观察您的查询是否已更新,您可以使用Flowable
并将您的对象包装在List
中,这样当没有结果时您将获得空列表,之后更新数据库你会在列表中得到另一个结果。
代码
@Query("SELECT * FROM contact WHERE phone_number = :number LIMIT 1")
Flowable<List<Contact>> findByPhoneNumber(int number)
我相信它在某些情况下很有用。缺点是您必须访问resultList.get(0)
答案 2 :(得分:4)
当您使用Flowable
(以及LiveData
)作为Dao
类中的返回值时,您的查询永远不会停止发送数据,因为Room正在监视表以进行数据更改。引用官方文档:
此外,如果响应是可观察的数据类型,例如 Flowable或LiveData,Room监视查询中引用的所有表 失效。
不确定处理这种情况的最佳方式是什么,但对我来说有用的是一位好的.timeout()
运营商。请查看以下测试并遵循评论:
@Test
public void shouldCompleteIfForced() throws InterruptedException {
// given
TestScheduler testScheduler = new TestScheduler();
// when asking db for non existent project
TestSubscriber<Project> test = projectDao.getProject("non existent project")
.timeout(4, TimeUnit.SECONDS, testScheduler)
.test();
// then hang forever waiting for first emission which will never happen
// as there is no such project
test.assertNoValues();
test.assertNotComplete();
test.assertNoErrors();
// when time passes and we trigger timeout() operator
testScheduler.advanceTimeBy(10, TimeUnit.SECONDS);
// then finally break stream with TimeoutException error ...
test.assertError(TimeoutException.class);
}
答案 3 :(得分:1)
我猜你也可以使用Single包装器。像:
public class QueryResult<D> {
public D data;
public QueryResult() {}
public QueryResult(D data) {
this.data = data;
}
public boolean isEmpty(){
return data != null;
}
}
并使用它:
public Single<QueryResult<Transaction>> getTransaction(long id) {
return createSingle(() -> database.getTransactionDao().getTransaction(id))
.map(QueryResult::new);
}
createAsyncSingle
:
protected <T> Single<T> createSingle(final Callable<T> func) {
return Single.create(emitter -> {
try {
T result = func.call();
emitter.onSuccess(result);
} catch (Exception ex) {
Log.e("TAG", "Error of operation with db");
}
});
}
别忘了使用IO线程。