В этом уроке рассмотрим, как использовать конвертеры типов данных, чтобы Room мог сохранять не только поля-примитивы.

 


Полный список уроков курса:


 

 

Иногда ваши Entity объекты могут содержать поля, которые не являются примитивами, и не могут быть сохранены в БД.

В качестве примера рассмотрим класс работника. У него вполне может быть поле, в котором мы хотим перечислить его хобби. Используем для этого поле hobbies с типом List<String> 

@Entity()
public class Employee {

   @PrimaryKey
   public long id;

   public String name;

   public int salary;

   public List<String> hobbies;

}

Если мы попытаемся сейчас скомпилировать проект, то получим ошибку: Cannot figure out how to save this field into database. You can consider adding a type converter for it.

Room справедливо замечает, что понятия не имеет, как ему такое поле сохранить в базу, и предлагает использовать type converter.

Ок, давайте создадим конвертер. Он должен уметь конвертировать List<String> в какой-нибудь простой тип, который может быть сохранен в базу, например, String. Также конвертер должен уметь конвертировать в обратную сторону, т.е. из String в List<String>, чтобы Room мог прочесть данные из базы в поле Entity объекта.

 

Создаем конвертер:

public class HobbiesConverter {

   @TypeConverter
   public String fromHobbies(List<String> hobbies) {
       return hobbies.stream().collect(Collectors.joining(","));
   }

   @TypeConverter
   public List<String> toHobbies(String data) {
       return Arrays.asList(data.split(","));
   }

}

Первый метод преобразует List<String> в String. Второй - наоборот. Оба метода помечаем аннотацией TypeConverter.

 

Осталось указать этот конвертер для поля hobbies. Это делается аннотацией TypeConverters с указанием класса конвертера.

@Entity()
public class Employee {

   @PrimaryKey
   public long id;

   public String name;

   public int salary;

   @TypeConverters({HobbiesConverter.class})
   public List<String> hobbies;

}

Теперь Room будет знать, что для поля hobbies он может использовать конвертер HobbiesConverter.

 

Конвертер также можно указать для всего Entity объекта. Это может быть полезно, если у вас в Entity несколько полей требуют конвертеры. Вы создаете один класс, там прописываете все необходимые методы преобразования полей, и указываете этот класс для всего Entity.

@Entity()
@TypeConverters({EmployeeConverter.class})
public class Employee {

   @PrimaryKey
   public long id;

   public String name;

   public int salary;

   public List<String> hobbies;

}

 

 

Бывают случаи, когда преобразование может быть необходимо не только для Entity объекта. Рассмотрим пример.

Есть Entity класс

@Entity()
public class Employee {

   @PrimaryKey
   public long id;

   public String name;

   public int salary;

   public long birthday;

}

У работника все поля являются простыми, и Room без проблем может их сохранить/прочесть. Этим полям не нужны конвертеры.

 

Но что если мы хотим в Dao сделать так:

@Dao
public interface EmployeeDao {

   @Query("SELECT * FROM employee WHERE birthday = :birthdayDate")
   Employee getByDate(Date birthdayDate);

}

Т.е. нам для поиска по полю birthday (с типом long) удобнее использовать объект Date.

При попытке собрать проект получаем ошибку: Query method parameters should either be a type that can be converted into a database column or a List / Array that contains such type. You can consider adding a Type Adapter for this.

Room сообщает, что типы не совпадают и снова предлагает использовать конвертеры.

 

Создаем конвертер:

public class DateConverter {

   @TypeConverter
   public Long dateToTimestamp(Date date) {
       if (date == null) {
           return null;
       } else {
           return date.getTime();
       }
   }

}

В нашем случае необходимо Date конвертировать в long, чтобы Room мог выполнить query запрос. Создаем для этого метод dateToTimestamp.

Обратная конвертация нам не нужна. У Room нет необходимости конвертировать long в Date. Объект Employee будет содержать дату в формате long.

 

Конвертер прописываем в Dao, прямо для конкретного параметра конкретного метода

@Dao
public interface EmployeeDao {

   @Query("SELECT * FROM employee WHERE birthday = :birthday")
   Employee getByDate(@TypeConverters({DateConverter.class}) Date birthday);

}

Теперь Room конвертирует Date в long и запрос будет выполнен.

 


Также конвертер можно прописать для всего метода, а не отдельного параметра

@Dao
public interface EmployeeDao {

   @Query("SELECT * FROM employee WHERE birthday BETWEEN :birthdayFrom and :birthdayTo")
   @TypeConverters({DateConverter.class})
   Employee getByDate(Date birthdayFrom, Date birthdayTo);

}

В этом случае Room сможет использовать конвертер для преобразования всех параметров метода.

 

 

Если же прописать конвертер для Dao, то он будет доступен всем методам этого Dao

@Dao
@TypeConverters({DateConverter.class})
public interface EmployeeDao {

   ...

}

 

 

Ну и самое глобальное решение - прописать конвертер для Database

@Database(entities = {Employee.class}, version = 1)
@TypeConverters({DateConverter.class})
public abstract class AppDatabase extends RoomDatabase {
   public abstract EmployeeDao employeeDao();
}

В этом случае Room сможет использовать его во всех Entity и Dao.

Если у вас несколько конвертеров, указывайте их через запятую.

 


Присоединяйтесь к нам в Telegram:

- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование 

- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня

- тут можно посмотреть, над какими уроками я сейчас работаю, и о чем будут следующие уроки 




Language

Социальные сети

 

Telegram канал



Android чат в Telegram



Группа ВКонтакте



Страница в Facebook