How to collect data from MongoDB in Android Studio? - java

I am experimenting with the integration of MongoDB on Android using Java as the language.
I followed the guide provided by MongoDB to configure the Atlas account and the Realm to communicate with.
After that I tried implementing CRUD methods, for insertions I did not encounter any problems, while for queries I did.
In particular to get all the objects of a certain class in a certain collection.
I used this method, as suggested by the wiki (https://www.mongodb.com/docs/realm/sdk/java/quick-start-local/)
RealmResults<Contact> contacts = backgroundThreadRealm.where(Contact.class).findAll();
inserted in a class made to handle background tasks:
public class BackgroundTasks implements Runnable {
#Override
public void run() {
String realmName = "MyApp";
RealmConfiguration config = new RealmConfiguration.Builder().name(realmName).build();
Realm backgroundThreadRealm = Realm.getInstance(config);
// all Tasks in the realm
RealmResults<Contact> contacts = backgroundThreadRealm.where(Contact.class).findAll();
Log.v("Contacts", String.valueOf(contacts.size()));
backgroundThreadRealm.close();
}
}
While in the MainActivity I inserted this (looks like a battlefield, maybe I inserted stuff I won't even need, but I was experimenting):
// initialize mongodb realm
realm.init(this);
// open realm
String realmName = "MyApp";
RealmConfiguration config = new RealmConfiguration.Builder().name(realmName).build();
backgroundThreadRealm = Realm.getInstance(config);
app = new App(new AppConfiguration.Builder(appId).build());
User user = app.currentUser();
mongoClient = user.getMongoClient("mongodb-atlas");
mongoDatabase = mongoClient.getDatabase("MyApp");
MongoCollection<Document> mongoCollection = mongoDatabase.getCollection("Contacts");
FutureTask<String> task = new FutureTask(new BackgroundTasks(), "test");
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.execute(task);
#Override
protected void onDestroy() {
super.onDestroy();
// the ui thread realm uses asynchronous transactions, so we can only safely close the realm
// when the activity ends and we can safely assume that those transactions have completed
backgroundThreadRealm.close();
}
I get no exceptions but the Log:
Log.v("Contacts", String.valueOf(contacts.size()));
results in 0.
Yet I have these contacts in the DB (they have different IDs):
And the related model in java:
#RealmClass
public class Contact extends RealmObject implements Serializable {
#PrimaryKey
private String nameSurname;
private int age;
// Drawable resource ID
private int imageResourceId;
public Contact() {
}
public Contact(String name, String surname, int age, int imageResourceId) {
this.nameSurname = name+" "+surname;
this.age = age;
this.imageResourceId = imageResourceId;
}
// In addition all the getters and setters
Can you help me?
It would also help to understand when it's appropriate to make synchronous and asynchronous calls, because I guess I've confused the implementations in general.
I'd like to use synchronous calls to get all the objects in the DB and then display them on the app, but it seems ill-advised online so I tried asynchronous, although I'm sure I did something wrong..
Thanks

Related

Java - Asynchronous multi-database with connection poll

I am developing a program that, based on a configuration file, allows different types of databases (e.g., YAML, MySQL, SQLite, and others to be added in the future) to be used to store data.
Currently it is all running on the main thread but I would like to start delegating to secondary threads so as not to block the execution of the program.
For supported databases that use a connection, I use HikariCP so that the process is not slowed down too much by opening a new connection every time.
The main problem is the multitude of available databases. For example, for some databases it might be sufficient to store the query string in a queue and have an executor check it every X seconds; if it is not empty it executes all the queries. For others, however, it is not, because perhaps they require other operations (e.g., YAML files that use a key-value system with a map).
What I can't do is something "universal", that doesn't give problems with the order of queries (cannot just create a Thread and execute it, because then one fetch thread might execute before another insertion thread and the data might not be up to date) and that can return data on completion (in the case of get functions).
I currently have an abstract Database class that contains all the get() and set(...) methods for the various data to be stored. Some methods need to be executed synchronously (must be blocking) while others can and should be executed asynchronously.
Example:
public abstract class Database {
public abstract boolean hasPlayedBefore(#Nonnull final UUID uuid);
}
public final class YAMLDatabase extends Database {
#Override
public boolean hasPlayedBefore(#Nonnull final UUID uuid) { return getFile(uuid).exists(); }
}
public final class MySQLDatabase extends Database {
#Override
public boolean hasPlayedBefore(#Nonnull final UUID uuid) {
try (
final Connection conn = getConnection(); // Get a connection from the poll
final PreparedStatement statement = conn.prepareStatement("SELECT * FROM " + TABLE_NAME + " WHERE UUID= '" + uuid + "';");
final ResultSet result = statement.executeQuery()
) {
return result.isBeforeFirst();
} catch (final SQLException e) {
// Notifies the error
Util.sendMessage("Database error: " + e.getMessage() + ".");
writeLog(e, uuid, "attempt to check whether the user is new or has played before");
}
return true;
}
}
// Simple example class that uses the database
public final class Usage {
private final Database db;
public Usage(#Nonnull final Database db) { this.db = db; }
public User getUser(#Nonnull final UUID uuid) {
if(db.hasPlayedBefore(uuid))
return db.getUser(uuid); // Sync query
else {
// Set default starting balance
final User user = new User(uuid, startingBalance);
db.setBalance(uuid, startingBalance); // Example of sync query that I would like to be async
return user;
}
}
}
Any advice? I am already somewhat familiar with Future, CompletableFuture and Callback.

Hibernate session not shared between threads

I have a springboot application that implements a user referral system. One use case is that when a user signs up using a valid referral code from another user, the referrer user gets one reward point, and for every five points they get 10$ in credit. According to this, I have implemented a use case in my application that honors these business rules, and to test proper behavior under high concurrency, I've created an integration test using #DataJpaTest and spring data repositories and H2 DB as storage system. In my test, I create a first user, and then I create a certain amount of users using the first user referral code, every one of those users is created on a different thread using a thread pool executor to spawn those threads. My problem is that the users created through thread pool spawned threads don't see the first user created in the main thread, even though I'm using JpaRepository.saveAndFlush() method to save them.
Could someone give me an explanation about what's happening here? Is it because Hibernate's session is not thread-safe?
You can see my code below, the first test has been simplified to just check the amount of user's in the repository.
#DataJpaTest(includeFilters = #ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Repository.class))
public class JpaTest {
#Autowired
private TestEntityManager entityManager;
#Autowired
#Qualifier("JpaUserRepository")
private JpaUserRepository userRepository;
#Autowired
#Qualifier("JpaReferralRepository")
private ReferralRepository referralRepository;
private RegisterReferredUser registerReferredUser;
private CreateUser createUser;
private GetUser getUser;
#BeforeEach
void setUp() {
registerReferredUser = new RegisterReferredUser(referralRepository, userRepository);
createUser = new CreateUser(userRepository, referralRepository, registerReferredUser);
getUser = new GetUser(userRepository);
}
#Test
void createUser_shouldWorksProperlyUnderConcurrentExecution() throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(10);
EmailAddress referrerUserEmail = EmailAddress.of("john.doe#acme.inc");
User referrerUser = createUser.execute(new CreateUserCommand(referrerUserEmail.getValue(), null));
String referralCode = referrerUser.getReferralCode().getValue();
int maxIterations = 10;
for (int i = 0; i < maxIterations; i++) {
int emailSeed = i;
executor.submit(() -> {
createUser.execute(new CreateUserCommand(anEmailAddress(emailSeed), referralCode));
});
}
executor.shutdown();
if (!executor.awaitTermination(20, TimeUnit.SECONDS)) {
fail("Executor didn't finish in time");
}
assertThat(entityManager.getEntityManager().createQuery("from JpaUser").getResultList().size()).isEqualTo(maxIterations + 1);
// This fails: just 1 user in the repository, however, if I register users without referral (without checking the existence of the first user), users are created and this pass
}
#Test
void just_a_POC() throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(10);
userRepository.save(UserMother.aUserWithEmail("john.doe#acme.inc"));
int maxIterations = 10;
for (int i = 0; i < maxIterations; i++) {
int emailSeed = i;
executor.submit(() -> userRepository.save(UserMother.aUserWithEmail(anEmailAddress(emailSeed))));
}
executor.shutdown();
if (!executor.awaitTermination(20, TimeUnit.SECONDS)) {
fail("Executor didn't finish in time");
}
assertThat(entityManager.getEntityManager().createQuery("from JpaUser").getResultList().size()).isEqualTo(maxIterations + 1);
// This pass
}
}
In the CreateUser I have the following code:
private void assertReferralCodeIsValid(ReferralCode referralCode, EmailAddress email) {
if (!userRepository.findByReferralCode(referralCode).isPresent()) {
throw new NonExistentReferralCode(referralCode);
}
if (referralRepository.findByEmailAndCode(email, referralCode).isPresent()) {
throw new ReferralCodeAlreadyUsed(email, referralCode);
}
}
And this is the JpaUserRepository.save() method:
#Repository("JpaUserRepository")
public class JpaUserRepository implements UserRepository {
private JpaUserCrudRepository crudRepository;
public JpaUserRepository(JpaUserCrudRepository crudRepository) {
this.crudRepository = crudRepository;
}
#Override
public void save(User user) {
crudRepository.saveAndFlush(JpaUser.fromDomain(user));
}
}
Look at the isolation level configured for your transactions. Database engines usually try to serve data as fast as possible without blocking (when possible). So if all your threads read a table at the same time, they may get an "uncommited" version of the records.
If you need synchronization, you can change the isolation level, or lock the table before working on it.
More on this topic:
Spring #Transactional - isolation, propagation
https://www.baeldung.com/java-jpa-transaction-locks

java.lang.IllegalArgumentException: 'value' belongs to a different Realm

I am trying to create an Realm object and getting «java.lang.IllegalArgumentException: 'value' belongs to a different Realm.» exception.
I am using Realm.getDefaultInstance() that I've initiated in Fragment at OnCreateView.
The object I am trying to save into database uses other RealmObject as argument.
Code as follows:
#Override
public boolean onQueryTextSubmit(final String query) {
final ProductList productList =
realm.where(ProductList.class).equalTo("name", listName)
.findFirst();
ListEntry.create(realm, listId, productToAdd);
}
ListEntry.create looks like this —
public static void create(Realm realm,
final long listId,
final Product product,) {
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
Parent parent = realm.where(Parent.class).findFirst();
RealmList<ListEntry> entries = parent.getListEntries();
ListEntry listEntry = realm.createObject(ListEntry.class, decrement());
listEntry.setListId(listId);
listEntry.setProduct(product);
entries.add(listEntry);)
}
Parent.class is an abstraction I use to store list of all saved entries in Lists as it is done in Realm android's adapters example (https://github.com/realm/realm-android-adapters/blob/master/example/src/main/java/io/realm/examples/adapters/model/Parent.java).
Whenever I try to save the entry at onQueryTextSubmit it returns the error, not saving a thing.
This error message is extremely ungoogleable — thus I request your help.
Any input would be greatly appreciated!
While I were writing the questoin for SO I found out the solution — instead of passing RealmObject as argument I just put id of the object there and added the object to the private RealmList inside the Object during transaction.
So, instead of —
ListEntry.class:
#PrimaryKey
private long entryId;
private Product product;
I wrote
#PrimaryKey
private long entryId;
private RealmList<Product> productArray;
and inside a transaction —
Product product = realm.where(Product.class).equalTo("productId", productId).findFirst();
listEntry.getProductArray().add(product);
And now everything works. Zero-copy design at it's finest.
Keeping it here for those others unfortunate with the google search.

Implementing a AsyncTask with Realm

I'm migrating an app to Realm and I have an AsyncTask like this (simplified for brevity) that I used to search through a list of objects and filter it based on a search query:
private class SearchTask extends AsyncTask<String, Void> {
Realm realm;
#Override
protected List<SearchResults> doInBackground(String... params) {
String searchString = params[0];
realm = Realm.getDefaultInstance();
ArrayList<SearchResults> myFoos = FooHelper.getAllFoo(realm);
ArrayList<Foo> matches = new ArrayList<>();
for (Foo aFoo : myFoos){
if(!aFoo.getProperty().isEmpty()){
matches.add(aFoo);
}
}
realm.close();
return matches;
}
#Override
protected void onPostExecute(List<SearchResults> results) {
super.onPostExecute(results);
synchronized (SearchActivity.this){
//use search results
}
}
}
The problem is that when the results are returned they can't be accessed since they were created on another thread. The only solution I can think of is to return an array of primary keys from the async task then re-query for those again.
There has to be a better way of basically doing SEARCH on a realm. Any suggestions?
Until fairly recently realm did not support asynchronous queries at all.
You'll be happy to know this has changed but you don't do it via async task. Heres the documentation on how to do it:
https://realm.io/docs/java/latest/#asynchronous-queries

Binding data to the UI in GWT

In Silverlight, a frequently used pattern is:
Request data
Get back an empty container for the data
Asynchronously fire off a query to fill the container
When the query returns, fire an event on the container
Update the UI according to the container's contents
Can this be done in GWT?
The reason I ask is that I'm trying to make a SuggestBox that contains a list of group names and icons. First, I query Facebook to get a list of groups IDs that are close to the current String in the SuggestBox. Then, I fire off queries to get icons for each group id. The problem is that I have to return the suggestions before those queries are done. I'm not sure how to go back and insert the data after I have it. I don't want to block until the calls are complete, and there's no real way to know in advance what data to load.
I could return a widget for the suggestion that loads an image, but the suggestion must be a plain String.
What is the right approach here?
Let's assume you're using GWT RPC. You'll have some service interface that lets you fetch the groupIds for a suggestion and the icon for a specific group id.
public interface FacebookService extends RemoteService {
List<String> getFacebookGroupIds(String suggestion);
Icon getIconForGroup(String groupId);
}
You should build your own implementation of Suggestion that can display itself with either just a groupId or a groupId and an Icon.
public class FacebookGroupSuggestion implements Suggestion {
private String groupId;
private Icon icon;
public FacebookGroupSuggestion(String groupId) {
this.groupId = groupId;
}
public String getDisplayString() {
StringBuilder builder = new StringBuilder();
builder.append("<b>");
builder.append(this.groupId);
builder.append("</b>");
if (this.icon != null) {
builder.append(this.icon.toSafeHtml());
}
return builder.toString();
}
}
I'm using Icon as your own implementation of an icon, it's not a standard class.
Then, you can make your implementation of SuggestOracle to fetch the groupIds and icons asynchronously. The SuggestOracle uses a callback to inform the suggestBox that some response to a request is available. So fetch your results, and call the callback when you get them. It'll look something like this.
public class FacebookSuggestOracle extends SuggestOracle {
private FacebookServiceAsync service = GWT.create(FacebookService.class);
private Request currentRequest;
private Callback currentCallback;
#Override
public void requestSuggestions(Request request, Callback callback) {
// Save request & callback for future use.
this.currentRequest = request;
this.currentCallback = callback;
// Fetch the groupIds
service.getFacebookGroupIds(request.getQuery(), new AsyncCallback<List<String>>() {
public void onSuccess(List<String> result) {
createSuggestionsForGroupIds(result);
}
});
}
private void createSuggestionsForGroupIds(List<String> groupIds) {
List<FacebookGroupSuggestion> suggestions = new ArrayList<FacebookGroupSuggestion>();
for (String groupId : groupIds) {
suggestions.add(new FacebookGroupSuggestion(groupId));
}
Response response = new Response(suggestions);
// Tell the suggestBox to display some new suggestions
currentCallback.onSuggestionsReady(currentRequest, response);
// Fetch the icons
for (String groupId : groupIds) {
service.getIconForGroup(groupId, new AsyncCallback<Icon>() {
public void onSuccess(Icon result) {
// match the icon to the groupId in the suggestion list
// use the callback again to tell the display to update itself
}
});
}
}
}

Categories