Поиск…


Вступление

База данных 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();
    }
});


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow