Repository
https://github.com/realm/realm-java
What Will I Learn?
- You will learn about Queries in Realm
- contains() predicate
- beginsWith() predicate
- in() predicate
Requirements
- An Integrated Development Environment(IDE) for building Android Application(e.g Android Studio, IntelliJ)
- Android Device/Virtual Device.
- Little Experience in working with Realm Java.
- Java Programming Experience.
- Of course, willingness to learn
Resources
- Retrofit Website. https://realm.io/
- Retrofit Github. - https://github.com/realm
- Retrofit License - Apache License
Difficulty
- Intermediate
Tutorial Duration - 30 - 35Mins
Tutorial Content
In Todays tutorial, we are going to be learing about queries in Realm. An inorder to learning this, we are going to be develioping an android application that will comprise of a Realm database that will have a Person
class that will extend the RealmObject
class and will hold the name
, address
, email
and phone number
of a person object.
For this tutorial, we are going to be looking at three queries/predicates - contains
, beginsWith
and in
.
For each of this predicates, they take a first argument which is the name of a field in the database and then the second argument which be the query string
and an optional last argument to specify Case Sensitiviy
or not.
e.g - contains("name","Edet Ebenezer",Case.SENSITIVE)
The above indicates that we are looking at the name
field is it contains the a String Edet Ebenezer
and that the cases shouldn't be ignored the upper case E in Edet must match and the E in Ebenezer must also match.
The android application will have three buttons - Single Query Button
,Double Query Button
,Multiple Querry Button
and once the user clicks any of this button, the appropriate fragment will be loaded.
Outline
- Dependencied Used.
- Create Three Buttons in activity_main Layout
- Create Three Fragments.
- Add FragmentTransaction codes in MainActivity.java
- Create the Necessary Model Classes.
- Create Realm Datbase and Populate using JSON
- Show Predicate Usage in Fragments.
Depenedencies used
- implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
The ButterKnife dependency should be placed in your application level gradle file - "build.gradle" which will make the injection of views (e.g ImageView, TextView) as easy as possible which we will be seeing in this tutorial.
- implementation 'org.projectlombok:lombok:1.16.20'
annotationProcessor 'org.projectlombok:lombok:1.16.20'
The lombok dependency also is placed in the application level gradle file which makes the generator of getter and setter methods for our model classes by just adding the annotations @Getter for getters and @Setter for the setter methods.
Realm dependency
Steps
Head to your project level gradle file and add the classpath dependency:
classpath "io.realm:realm-gradle-plugin:5.1.0"
Next, head to your application level Gradle file "build.gradle" and add the realm-android plugin to the top of the file.
apply plugin:'realm-android'
Finally, refresh your Gradle dependencies.
After you have added the necessary dependencies, your application level Gradle file should look like this -
Create Three Buttons in activity_main Layout
We need to add Three Button views to our main activity layout file and then set the id's which we will be using to inject their repestive onClick
methods using ButterKnife
.
activity_layout.xml
//RelativeLayout Root View with id - mainLayout
//LinearLayout with orientation - veritcal
<Button
android:id="@+id/single_query"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:text="Single String Search" />
<Button
android:id="@+id/double_string_query"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Double String Search" />
<Button
android:id="@+id/multiple_string_query"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="30dp"
android:text="Multiple String Search" />
In the layout code above, we have inserted three buttons which are children views of a LinearLayout
with orientation of vertical
.
The root layout is a RelativeLayout
with an id of mainLayout
which we will be using in our FragmentTransaction
later in this tutorial.
The id's of the three buttons are - single_query
,double_string_query
,multiple_string_query
and thier text are - Single String Search
,Double String Search
,Multiple String Search
respectively
Create Three Fragments
We will be creating three fragments which we will be using to
To create a new blank fragment, right click on your java folder => New => Fragment => Blank Fragment.
Next, Name your fragment - SingleQuery
and then do not forget to uncheck the - Include fragment factory methods ?
and include interface callbacks
checkboxes
Repeat the process for the remaining fragment and make sure to give it a unique name as two fragments can't have the same name.
You can name the remaining fragments - DoubleStringQuery
and MultipleStringQuery
in order to follow along with the tutorial.
Add Fragment Transactions in MainActivity.java class
Next, in our mainActivity.java we will be using butterknife to inject the onClick methods for our three buttons and then we will be using FragmentTransaction to call the aappropriate fragment based on the button that was clicked.
To inject the onClick methods of the new buttons using butterknife, place your cursor on the layout name on the setContentView()
method and then on windows - alt + ins => Generate ButterKnife Injection and then select the checkboxes shown in the image below:
Injected Code
The MainActivity.java class file should now look like this
@OnClick({R.id.single_query, R.id.double_string_query, R.id.multiple_string_query})
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.single_query:
showSingleStringFragment();
break;
case R.id.double_string_query:
showDoubleStringFragment();
break;
case R.id.multiple_string_query:
showMultipleStringFragment();
break;
}
}
Note: The showSingleStringFragment()
,showDoubleStringFragment()
and showMultipleStringFragment
wasn't added by butterknife.
showSingleStringFragment()
private void showSingleStringFragment() {
SingleQuery singleQuery = new SingleQuery();
showFragment(singleQuery);
}
In this method, we create a SingleQuery
Fragment object and then pass it as an argument to the showFragment()
method.
The same step is taken in all the other methods - showDoubleStringFragment()
and showMultipleStringFragment
-
For the showDoubleStringFragment()
method, we will create a DoubleStringQuery
Fragment object and pass it as an argument to the showFragment()
method and create a MultipleStringQuery
object and pass to the showFragment()
method for the showMultipleStringFragment()
method.
showFragment()
private void showFragment(Fragment fragment) {
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction ft = fragmentManager.beginTransaction();
ft.replace(R.id.mainLayout,fragment).addToBackStack(fragment.getTag());
ft.commit();
}
Firstly, we get a FragmentManager
object and set it to the getFragmentManger()
and then we get a FragmentTransaction
object - ft
and begin a transaction with it on the FragmentManager
object - fragmentManager
.
We then call the replace method on the FragmentTransaction
object passing the id of the view we want replaced and the the fragment to replace it as the arguments.
Note: The mainLayout
id is the id of the RelativeLayout
which is the root layout for our activity_main.xml
file.
We then add the fragment to backStack using the addToBackStack()
method and getting the tag from the fragment as the argument and finally we call commit()
on the transaction.
Create the Necessary Model Classes
As explained earlier, we will be creating a Person
class that will have a name
, email
, address
and phone number
fields and then we will be using the Lombok
library to inject our Getter
and Setter
methods for the fields by using the @Getter
and @Setter
annotations as shown below:
Person.java
@Getter
@Setter
public class Person extends RealmObject {
String name;
String address;
String email;
int phone_number;
}
Create Realm Datbase and Populate using JSON
We will have to initialize and populate our realm database in our onCreate()
method of our MainActivity
java class file.
Firstly, we have to create a Realm
object at the top of our class file - private Realm realm;
and in our onCreate()
method, we add the following code -
Realm.init(this);
Realm.deleteRealm(Realm.getDefaultConfiguration());
realm = Realm.getDefaultInstance();
createRealmFromJSON();
Code Explanation
First, we initialize the Realm in our Activity, we then start with a clean slate by calling the deleteRealm()
method on the defaultConfiguration file and then we initialize our realm object to a defaultInstance
object.
We then call the createRealmFromJSON()
method.
createRealmFromJSON()
Please refer here and here for more explanations for using JSON
to create and populate Realm
database.
- You have to first create the raw android resource directory:
- right click on res folder => New => Android Resource Directory
- From resource drop-down , select raw and click OK button.
- Create JSON file
- right click on the raw folder located under res folder => New => File => persons.json
- Insert the following into the persons.json file -
[
{
"name":"Ebenezer Edet",
"address":"Garki Abuja",
"phone_number":242424,
"email":"[email protected]"
},
{
"name":"Smith Williams",
"address":"Wuse Abuja",
"phone_number":123456,
"email":"[email protected]"
},
{
"name":"Mark John",
"address":"Gwagwalada Abuja",
"phone_number":890234,
"email":"[email protected]"
},
{
"name":"Edet Lucas",
"address":"Gwarinpa Abuja",
"phone_number":345678,
"email":"[email protected]"
}
]
private void createRealmFromJSON() {
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
InputStream inputStream = getResources().openRawResource(R.raw.persons);
try {
realm.createAllFromJson(Person.class,inputStream);
} catch (IOException e) {
if (realm.isInTransaction())
realm.cancelTransaction();
}
}
});
}
Code Explanation
We execute a realm transaction on the realm variable by calling the executeTransaction()
method and override the execute()
method.
We use an InputStream object - inputStream
to get the json file resource by calling the getResources()
method and then calling the openRawResource()
method with the persons json file as an argument.
realm.createAllFromJson(Person.class,inputStream); creates a realm database of Person class and gets its realm objects from the inputStream
.
Show Predicate Usage in Fragments
SingleQuery Fragment
For our SingleQuery
Fragment, we will be receiving a single string query from the user and when the user clicks the GO
button, we then check the database for object that contains the query string using the contains()
predicate and then we will be using the beginsWith()
predicate to get objects that starts with the query string.
For the contains()
predicate , we will be using the third optional query of Case.INSENSITIVE
which ignores the case of the query string and for the beginsWith()
predicate we will be using Case.SENSITIVE
which doesnt ignore the case of the query string.
For us to be able to accept input from the user, we will be adding an EditText
view to our SingleQuery
xml layout file - single_query_fragment.xml
with the id - query_name
and we also have to add a Button
view with the id - go_btn
and then for us to display the result, we will be adding a TextView
with the id - result
which we will be inject all of them using ButterKnife
.
single_query_fragment.xml
<EditText
android:layout_marginTop="30dp"
android:id="@+id/query_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter First Name" />
<Button
android:id="@+id/go_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/query_name"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:text="GO" />
<TextView
android:id="@+id/result"
android:layout_below="@+id/go_btn"
android:layout_marginTop="30dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="Result Will appear Here"
android:textColor="#000"
android:textSize="18sp" />
SingleQuery.java
Firstly, we declear a Realm object - private Realm realm;
and then we get a defaultInstance in our onCreate()
method - realm = Realm.getDefaultInstance();
and then in the injected onClick()
method of our button with the id -go_btn
we add the following codes -
@OnClick(R.id.go_btn)
public void onViewClicked() {
String string_queryName = queryName.getText().toString();
StringBuilder toDisplay = new StringBuilder();
if (!string_queryName.isEmpty()){
RealmResults<Person> result = realm.where(Person.class)
.contains("name", string_queryName, Case.INSENSITIVE)
.findAll();
toDisplay.append("There are - "+result.size()+" Persons with a name like that\n\n");
int i = 0;
while(i < result.size()){
toDisplay.append(result.get(i).name+" with phone number : "+result.get(i).phone_number+" email : "+result.get(i).email+" and Address :"+result.get(i).address+"\n\n\n");
i++;
}
result = realm.where(Person.class)
.beginsWith("name",string_queryName,Case.SENSITIVE)
.findAll();
toDisplay.append("There are - "+result.size()+" Persons that their name starts with - "+string_queryName+"\n\n");
i = 0;
while(i < result.size()){
toDisplay.append(result.get(i).name+" with phone number : "+result.get(i).phone_number+" email : "+result.get(i).email+" and Address :"+result.get(i).address+"\n\n\n");
i++;
}
}
else
showToast("Name cannot be empty");
result.setText(toDisplay.toString());
}
Code Explanation
Firstly, once the user clicks the GO
button, we ensure that the EditText
is not empty by saving its value into a String variable -string_queryName
and then calling the isEmpty()
method inside an if statement to verify that something was entered.
Next, we create an empty StringBuilder
object - toDisplay
that we will append the result gotten from the query.
If the String is empty, inside our else
statament, we call the method - showToast()
with the argument - Name cannot be empty.
If the String is not empty, we then create a RealmResult<> object - result
which uses the contains()
predicate with the where()
statement checking the name
field for the string stored in the string_queryName
variable and then passes the Case.INSENSITIVE
optional argument telling it to ignore the case of the query string and we lastly add the findAll()
method meaning this should return a List and not just a single object.
Next, we append the size of the result
and we then use a while loop to loop through all the objects in our result
variable and then display the nessary details using result.get(i).name
to display the person's name, result.get(i).phone_number
to display the person's phone number, result.get(i).email
for the email and result.get(i).address
for the address, we then append three new line
characters.
We then use the result
object to get objects in the realm database that their name
field begins with the string_queryName
and then we use the Case.SENSITIVE
optional argument to pay attention to the case matching and then we finally use findAll()
.
The same step for the contains()
predicate is done for the beginsWith()
predicate also.
Lastly, we set the text of our TextView
with id result to the StringBuilder object - toDisplay
that now contains well formatted details of objects that match our searches from the contains()
and beginsWith()
predicate.
showToast()
private void showToast(String message) {
Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show();
}
DoubleStringQuery Fragment
For the DoubleStringQuery
Fragment, we are going to be accepting two strings from the user and then we are going to use the contain()
predicate to check if there are objects that match the strings entered using the contains()
predicate with the or()
statement.
The or()
statement allows you to join two queries together.
DoubleString.java
After declearing our realm object and initializing it in our onCreate()
method, we then inject our two EditText
s and an onClick
method for the GO
Button and add the following code to the onClick
method.
String string_queryname,string_queryname2;
string_queryname = queryName.getText().toString();
string_queryname2 = queryName2.getText().toString();
StringBuilder toDisplay = new StringBuilder();
if (!(string_queryname.isEmpty() && string_queryname2.isEmpty())){
RealmResults<Person> result = realm.where(Person.class)
.contains("name", string_queryname, Case.INSENSITIVE)
.or()
.contains("name",string_queryname2,Case.SENSITIVE)
.findAll();
toDisplay.append("There are - "+result.size()+" Persons with names like that\n\n");
int i = 0;
while(i < result.size()){
toDisplay.append(result.get(i).name+" with phone number : "+result.get(i).phone_number+" email : "+result.get(i).email+" and Address :"+result.get(i).address+"\n\n\n");
i++;
}
}
else
showToast("No Field can be left empty");
result.setText(toDisplay.toString());
Every Explanation is still the same with the SingleQueryFragment
Fragment except that we use the or()
statement here to combine two contains()
predicate, one to check for the string_queryname
value in the name
field of the Person
class ignoring cases and the other to check for the string_queryname2
value in the name
field but not ignoring cases this time.
The results are appended to the toDisplay
StringBuilder object and displayed on the result
TextView in our double_string_query_fragment
.
NB: queryName
and queryName2
are injected into this class using ButterKnife
they represent two EditText
with the id's - query_name
and query_name2
.
MultipleStringQuery Fragment
To explain the in()
predicate, we will require the user to enter multiple strings separated by commas and then click the GO
button, we will then save that string in a String
array using the spilt()
method and ,
as the delimeter, we will then pass the String array
as the second argument for the in()
predicate and then use the Case.INSENSITIVE
as the third optional argument.
MultipleStringQuery.java
String string_queryname = queryName.getText().toString();
String[] names = string_queryname.split(",");
StringBuilder toDisplay = new StringBuilder();
if (!string_queryname.isEmpty()){
RealmResults<Person> result = realm.where(Person.class)
.in("name",names,Case.INSENSITIVE)
.findAll();
toDisplay.append("There are - "+result.size()+" Persons with names like that\n\n");
int i = 0;
while(i < result.size()){
toDisplay.append(result.get(i).name+" with phone number : "+result.get(i).phone_number+" email : "+result.get(i).email+" and Address :"+result.get(i).address+"\n\n\n");
i++;
}
}
else {
showToast("Please enter a name");
}
result.setText(toDisplay.toString());
NB: The queryName
is injected using ButterKnife
which represents an EditText
which the user has to enter the names separated by commas.
We then append the result of the query to the toDisplay
StringBuilder object and then set it as the text of our result
TextView which was injected by ButterKnife
NB: We have to close the realm database on the onDestroy
method of our fragment for both the SingleQuery
, DoubleStringQuery
and MultipleStringQuery
Fragments.
@Override
public void onDestroyView() {
super.onDestroyView();
realm.close();
}
Application Execution
Curriculum
- Understanding Relationships in Realm Database using Java in Android Studio - PART 1 (One To One and One To Many Relationship)
- Understanding Relationships in Realm - PART 2(Many To Many and Inverse Relationship)
Complete Source code:
https://github.com/generalkolo/Realm-JSON/tree/master/Realm%20Queries
Hey @edetebenezer
Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!
Contributing on Utopian
Learn how to contribute on our website or by watching this tutorial on Youtube.
Want to chat? Join us on Discord https://discord.gg/h52nFrV.
Vote for Utopian Witness!
While I liked the content of your contribution, I would still like to extend one advice for your upcoming contributions:
Looking forward to your upcoming tutorials.
Your contribution has been evaluated according to Utopian policies and guidelines, as well as a predefined set of questions pertaining to the category.
To view those questions and the relevant answers related to your post, click here.
Need help? Write a ticket on https://support.utopian.io/.
Chat with us on Discord.
[utopian-moderator]
Thanks @portugalcoin for taking time out to moderate my post.
Adequate improvements will be made on subsequent changes.
You have a minor misspelling in the following sentence:
It should be separated instead of seperated.