Android
область
Поиск…
Вступление
База данных Realm Mobile является альтернативой SQLite. База данных Realm Mobile намного быстрее, чем ORM, и часто быстрее, чем сырой SQLite.
Выгоды
Автономная функциональность, быстрые запросы, безопасная потоковая передача, кросс-платформенные приложения, шифрование, реактивная архитектура.
замечания
Когда вы используете Realm, вы должны помнить, что вы не должны передавать объекты RealmObjects, RealmResults и Realm между потоками. Если вам нужен запрос по данному потоку, откройте экземпляр Realm в этом потоке. При завершении потока вы должны закрыть Царство.
ПРАВОВОЕ ПРИМЕЧАНИЕ . Вы понимаете, что Программное обеспечение может содержать криптографические функции, которые могут быть подвержены экспортным ограничениям, и вы представляете и гарантируете, что вы не находитесь в стране, на которую распространяется экспортное ограничение или эмбарго Соединенных Штатов, включая Кубу, Иран, Север Кореи, Судана, Сирии или региона Крыма и что вы не находитесь в списке запрещенных лиц Департамента торговли, Непроверенных Сторон или связаны с Ограниченной организацией.
Добавление королевства в ваш проект
Добавьте следующую зависимость для вашего уровня проекта build.gradle
файла.
dependencies {
classpath "io.realm:realm-gradle-plugin:3.1.2"
}
Добавьте в верхнюю часть файла build.gradle
уровне приложения build.gradle
.
apply plugin: 'realm-android'
Завершите синхронизацию градиента, и теперь вы добавили Realm в качестве зависимости от вашего проекта!
Realm требует первоначального вызова с 2.0.0 перед его использованием. Вы можете сделать это в своем классе Application
или в методе onCreate
первого действия.
Realm.init(this); // added in Realm 2.0.0
Realm.setDefaultConfiguration(new RealmConfiguration.Builder().build());
Модели царства
Модели Realm должны расширять базовый класс RealmObject
, они определяют схему базовой базы данных.
Поддерживаемые типы полей: boolean
, byte
, short
, int
, long
, float
, double
, String
, Date
, byte[]
, ссылки на другие RealmObject
s и RealmList<T extends RealmModel>
.
public class Person extends RealmObject {
@PrimaryKey //primary key is also implicitly an @Index
//it is required for `copyToRealmOrUpdate()` to update the object.
private long id;
@Index //index makes queries faster on this field
@Required //prevents `null` value from being inserted
private String name;
private RealmList<Dog> dogs; //->many relationship to Dog
private Person spouse; //->one relationship to Person
@Ignore
private Calendar birthday; //calendars are not supported but can be ignored
// getters, setters
}
Если вы добавите (или удалите) новое поле в свой RealmObject (или добавите новый класс RealmObject или удалите уже существующий), потребуется перенастройка . Вы можете либо установить deleteIfMigrationNeeded()
в свой RealmConfiguration.Builder
, либо определить необходимую миграцию. Миграция также требуется при добавлении (или удалении) @Required
или @Index
или @PrimaryKey
.
Отношения должны устанавливаться вручную, они НЕ автоматические на основе первичных ключей.
Начиная с 0.88.0, также можно использовать публичные поля вместо частных полей / геттеров / сеттеров в классах RealmObject.
Также возможно реализовать RealmModel
вместо расширения RealmObject
, если класс также аннотируется с помощью @RealmClass
.
@RealmClass
public class Person implements RealmModel {
// ...
}
В этом случае такие методы, как person.deleteFromRealm()
или person.addChangeListener()
, заменяются RealmObject.deleteFromRealm(person)
и RealmObject.addChangeListener(person)
.
Ограничения заключаются в RealmObject
, что RealmObject
может быть расширен только RealmObject
, и нет поддержки final
, volatile
и transient
полей.
Важно, чтобы управляемый класс RealmObject мог быть изменен только в транзакции. Управляемый объект RealmObject не может быть передан между потоками.
Список примитивов (RealmList )
В настоящее время Realm не поддерживает сохранение списка примитивов. Он находится в списке задач ( GitHub issue # 575 ), но пока что это обходной путь.
Создайте новый класс для вашего примитивного типа, это использует Integer, но измените его на все, что вы хотите сохранить.
public class RealmInteger extends RealmObject {
private int val;
public RealmInteger() {
}
public RealmInteger(int val) {
this.val = val;
}
// Getters and setters
}
Теперь вы можете использовать это в своем RealmObject
.
public class MainObject extends RealmObject {
private String name;
private RealmList<RealmInteger> ints;
// Getters and setters
}
Если вы используете GSON
для заполнения вашего RealmObject
, вам нужно будет добавить адаптер настраиваемого типа.
Type token = new TypeToken<RealmList<RealmInteger>>(){}.getType();
Gson gson = new GsonBuilder()
.setExclusionStrategies(new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getDeclaringClass().equals(RealmObject.class);
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
})
.registerTypeAdapter(token, new TypeAdapter<RealmList<RealmInteger>>() {
@Override
public void write(JsonWriter out, RealmList<RealmInteger> value) throws IOException {
// Empty
}
@Override
public RealmList<RealmInteger> read(JsonReader in) throws IOException {
RealmList<RealmInteger> list = new RealmList<RealmInteger>();
in.beginArray();
while (in.hasNext()) {
list.add(new RealmInteger(in.nextInt()));
}
in.endArray();
return list;
}
})
.create();
примерочных с-ресурсы
try (Realm realm = Realm.getDefaultInstance()) {
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
//whatever Transaction that has to be done
}
});
//No need to close realm in try-with-resources
}
Ресурс Try with resources может использоваться только от KITKAT (minSDK 19)
Сортированные запросы
Чтобы отсортировать запрос, вместо использования findAll()
вы должны использовать findAllSorted()
.
RealmResults<SomeObject> results = realm.where(SomeObject.class)
.findAllSorted("sortField", Sort.ASCENDING);
Замечания:
sort()
возвращает полностью новые данные RealmResults, которые были отсортированы, но обновление этого RealmResults сбрасывает его. Если вы используете sort()
, вы всегда должны повторно сортировать его в своем RealmChangeListener
, удалите RealmChangeListener
из предыдущих RealmResults
и добавьте его в возвращаемые новые RealmResults
. Использование sort()
в RealmResults
возвращаемом асинхронным запросом, который еще не загружен, завершится с ошибкой.
findAllSorted()
всегда возвращает результаты, отсортированные по полю, даже если он обновляется. Рекомендуется использовать findAllSorted()
.
Асинхронные запросы
Каждый синхронный метод запроса (например, findAll()
или findAllSorted()
) имеет асинхронный аналог ( findAllAsync()
/ findAllSortedAsync()
).
Асинхронные запросы выгружают оценку RealmResults
в другой поток. Чтобы получить эти результаты в текущем потоке, текущий поток должен быть потоком петлителя (чтение: асинхронные запросы обычно работают только с потоком пользовательского интерфейса).
RealmChangeListener<RealmResults<SomeObject>> realmChangeListener; // field variable
realmChangeListener = new RealmChangeListener<RealmResults<SomeObject>>() {
@Override
public void onChange(RealmResults<SomeObject> element) {
// asyncResults are now loaded
adapter.updateData(element);
}
};
RealmResults<SomeObject> asyncResults = realm.where(SomeObject.class).findAllAsync();
asyncResults.addChangeListener(realmChangeListener);
Использование Realm с RxJava
Для запросов Realm предоставляет метод realmResults.asObservable()
. Наблюдение результатов возможно только на потоках петлителя (как правило, в потоке пользовательского интерфейса).
Чтобы это работало, ваша конфигурация должна содержать следующее
realmConfiguration = new RealmConfiguration.Builder(context) //
.rxFactory(new RealmObservableFactory()) //
//...
.build();
Впоследствии вы можете использовать свои результаты как наблюдаемые.
Observable<RealmResults<SomeObject>> observable = results.asObservable();
Для асинхронных запросов вы должны фильтровать результаты с помощью isLoaded()
, чтобы вы получили событие только в том случае, когда запрос был выполнен. Этот filter()
не требуется для синхронных запросов ( isLoaded()
всегда возвращает true
при запросах синхронизации).
Subscription subscription = RxTextView.textChanges(editText).switchMap(charSequence ->
realm.where(SomeObject.class)
.contains("searchField", charSequence.toString(), Case.INSENSITIVE)
.findAllAsync()
.asObservable())
.filter(RealmResults::isLoaded) //
.subscribe(objects -> adapter.updateData(objects));
Для записи вам следует либо использовать метод executeTransactionAsync()
, либо открыть экземпляр Realm в фоновом потоке, выполнить транзакцию синхронно, а затем закрыть экземпляр Realm.
public Subscription loadObjectsFromNetwork() {
return objectApi.getObjects()
.subscribeOn(Schedulers.io())
.subscribe(response -> {
try(Realm realmInstance = Realm.getDefaultInstance()) {
realmInstance.executeTransaction(realm -> realm.insertOrUpdate(response.objects));
}
});
}
Основное использование
Настройка экземпляра
Чтобы использовать Realm, вам сначала нужно получить его экземпляр. Каждый экземпляр Realm отображается в файл на диске. Самый простой способ получить экземпляр:
// Create configuration
RealmConfiguration realmConfiguration = new RealmConfiguration.Builder(context).build();
// Obtain realm instance
Realm realm = Realm.getInstance(realmConfiguration);
// or
Realm.setDefaultConfiguration(realmConfiguration);
Realm realm = Realm.getDefaultInstance();
Метод Realm.getInstance()
создает файл базы данных, если он не был создан, иначе открывается файл. Объект RealmConfiguration
контролирует все аспекты создания Realm - будь то inMemory()
данных inMemory()
, имя файла Realm, если Realm должен быть очищен, если требуется миграция, исходные данные и т. Д.
Обратите внимание, что вызовы Realm.getInstance()
подсчитываются по ссылке (каждый вызов увеличивает счетчик), а счетчик уменьшается, когда realm.close()
.
Закрытие экземпляра
В фоновых потоках очень важно закрыть экземпляр Realm, если он больше не используется (например, транзакция завершена и завершение потока завершено). Невозможность закрыть все экземпляры Realm в фоновом потоке приводит к фиксации версии и может привести к значительному увеличению размера файла.
Runnable runnable = new Runnable() {
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
// ...
} finally {
if(realm != null) {
realm.close();
}
}
};
new Thread(runnable).start(); // background thread, like `doInBackground()` of AsyncTask
Стоит отметить, что выше уровня API 19 можно заменить этот код следующим:
try(Realm realm = Realm.getDefaultInstance()) {
// ...
}
модели
Следующим шагом будет создание ваших моделей. Здесь может быть задан вопрос: «Какова модель?». Модель представляет собой структуру, которая определяет свойства объекта, хранящегося в базе данных. Например, в следующем мы моделируем книгу.
public class Book extends RealmObject {
// Primary key of this entity
@PrimaryKey
private long id;
private String title;
@Index // faster queries
private String author;
// Standard getters & setter
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
Обратите внимание, что ваши модели должны расширять класс RealmObject. Основной ключ также указывается аннотацией @PrimaryKey
. Первичные ключи могут быть нулевыми, но только один элемент может иметь null
в качестве первичного ключа. Также вы можете использовать аннотацию @Ignore
для полей, которые не должны сохраняться на диске:
@Ignore
private String isbn;
Вставка или обновление данных
Чтобы сохранить объект книги в экземпляр базы данных Realm, вы можете сначала создать экземпляр своей модели, а затем сохранить его в базе данных с помощью метода copyToRealm
. Для создания или обновления вы можете использовать copyToRealmOrUpdate
. (Более быстрая альтернатива - это новая добавленная insertOrUpdate()
).
// Creating an instance of the model
Book book = new Book();
book.setId(1);
book.setTitle("Walking on air");
book.setAuthor("Taylor Swift")
// Store to the database
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
realm.insertOrUpdate(book);
}
});
Обратите внимание, что все изменения в данных должны происходить в транзакции. Другой способ создания объекта - использовать следующий шаблон:
Book book = realm.createObject(Book.class, primaryKey);
...
Запрос базы данных
Все книги:
RealmResults<Book> results = realm.where(Book.class).findAll();
Все книги с идентификатором более 10:
RealmResults<Book> results = realm.where(Book.class) .greaterThan("id", 10) .findAll();
Книги
'Taylor Swift'
или'%Peter%'
:RealmResults<Book> results = realm.where(Book.class) .beginGroup() .equalTo("author", "Taylor Swift") .or() .contains("author", "Peter") .endGroup().findAll();
Удаление объекта
Например, мы хотим удалить все книги Тейлора Свифта:
// Start of transaction
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
// First Step: Query all Taylor Swift books
RealmResults<Book> results = ...
// Second Step: Delete elements in Realm
results.deleteAllFromRealm();
}
});