この例では、Date オブジェクトを Long オブジェクトに変換するメソッドと、Long から Date に逆変換するメソッドという 2 つの型コンバータ メソッドを定義しています。Room は Long オブジェクトを永続化する方法を認識しているため、こうしたコンバータを使用して Date オブジェクトを永続化できます。
これらの型コンバータを定義すると、プリミティブ型を使用する場合と同様に、エンティティと DAO でカスタム型を使用できます。
Kotlin
@EntitydataclassUser(privatevalbirthday:Date?)@DaointerfaceUserDao{@Query("SELECT * FROM user WHERE birthday = :targetDate")funfindUsersBornOnDate(targetDate:Date):List<User>}
Java
@EntitypublicclassUser{privateDatebirthday;}@DaopublicinterfaceUserDao{@Query("SELECT * FROM user WHERE birthday = :targetDate")List<User>findUsersBornOnDate(DatetargetDate);}
[[["わかりやすい","easyToUnderstand","thumb-up"],["問題の解決に役立った","solvedMyProblem","thumb-up"],["その他","otherUp","thumb-up"]],[["必要な情報がない","missingTheInformationINeed","thumb-down"],["複雑すぎる / 手順が多すぎる","tooComplicatedTooManySteps","thumb-down"],["最新ではない","outOfDate","thumb-down"],["翻訳に関する問題","translationIssue","thumb-down"],["サンプル / コードに問題がある","samplesCodeIssue","thumb-down"],["その他","otherDown","thumb-down"]],["最終更新日 2025-07-27 UTC。"],[],[],null,["# Referencing complex data using Room\n\nRoom provides functionality for converting between primitive and boxed types\nbut doesn't allow for object references between entities. This document\nexplains how to use type converters and why Room doesn't support object\nreferences.\n\nUse type converters\n-------------------\n\nSometimes, you need your app to store a custom data type in a single database\ncolumn. You support custom types by providing *type converters* , which are\nmethods that tell Room how to convert custom types to and from known types that\nRoom can persist. You identify type converters by using the\n[`@TypeConverter`](/reference/androidx/room/TypeConverter) annotation.\n| **Note:** Room 2.3 and higher includes a default type converter for persisting enums. Existing type converters take precedence over the default, but if you have not already defined a type converter for enums then you don't need to define one.\n\nSuppose you need to persist instances of [`Date`](/reference/java/util/Date) in\nyour Room database. Room doesn't know how to persist `Date` objects, so you need\nto define type converters: \n\n### Kotlin\n\n```kotlin\nclass Converters {\n @TypeConverter\n fun fromTimestamp(value: Long?): Date? {\n return value?.let { Date(it) }\n }\n\n @TypeConverter\n fun dateToTimestamp(date: Date?): Long? {\n return date?.time?.toLong()\n }\n}\n```\n\n### Java\n\n```java\npublic class Converters {\n @TypeConverter\n public static Date fromTimestamp(Long value) {\n return value == null ? null : new Date(value);\n }\n\n @TypeConverter\n public static Long dateToTimestamp(Date date) {\n return date == null ? null : date.getTime();\n }\n}\n```\n\nThis example defines two type converter methods: one that converts a `Date`\nobject to a `Long` object, and one that performs the inverse conversion from\n`Long` to `Date`. Because Room knows how to persist `Long` objects, it can use\nthese converters to persist `Date` objects.\n\nNext, you add the [`@TypeConverters`](/reference/androidx/room/TypeConverters)\nannotation to the `AppDatabase` class so that Room knows about the converter\nclass that you have defined: \n\n### Kotlin\n\n```kotlin\n@Database(entities = [User::class], version = 1)\n@TypeConverters(Converters::class)\nabstract class AppDatabase : RoomDatabase() {\n abstract fun userDao(): UserDao\n}\n```\n\n### Java\n\n```java\n@Database(entities = {User.class}, version = 1)\n@TypeConverters({Converters.class})\npublic abstract class AppDatabase extends RoomDatabase {\n public abstract UserDao userDao();\n}\n```\n\nWith these type converters defined, you can use your custom type in your\nentities and DAOs just as you would use primitive types: \n\n### Kotlin\n\n```kotlin\n@Entity\ndata class User(private val birthday: Date?)\n\n@Dao\ninterface UserDao {\n @Query(\"SELECT * FROM user WHERE birthday = :targetDate\")\n fun findUsersBornOnDate(targetDate: Date): List\u003cUser\u003e\n}\n```\n\n### Java\n\n```java\n@Entity\npublic class User {\n private Date birthday;\n}\n\n@Dao\npublic interface UserDao {\n @Query(\"SELECT * FROM user WHERE birthday = :targetDate\")\n List\u003cUser\u003e findUsersBornOnDate(Date targetDate);\n}\n```\n\nIn this example, Room can use the defined type converter everywhere because you\nannotated `AppDatabase` with `@TypeConverters`. However, you can also scope type\nconverters to specific entities or DAOs by annotating your `@Entity` or `@Dao`\nclasses with `@TypeConverters`.\n\n### Control type converter initialization\n\nOrdinarily, Room handles instantiation of type converters for you. However,\nsometimes you might need to pass additional dependencies to your type converter\nclasses, which means that you need your app to directly control initialization\nof your type converters. In that case, annotate your converter class with\n[`@ProvidedTypeConverter`](/reference/androidx/room/ProvidedTypeConverter): \n\n### Kotlin\n\n```kotlin\n@ProvidedTypeConverter\nclass ExampleConverter {\n @TypeConverter\n fun StringToExample(string: String?): ExampleType? {\n ...\n }\n\n @TypeConverter\n fun ExampleToString(example: ExampleType?): String? {\n ...\n }\n}\n```\n\n### Java\n\n```java\n@ProvidedTypeConverter\npublic class ExampleConverter {\n @TypeConverter\n public Example StringToExample(String string) {\n ...\n }\n\n @TypeConverter\n public String ExampleToString(Example example) {\n ...\n }\n}\n```\n\nThen, in addition to declaring your converter class in `@TypeConverters`, use\nthe\n[`RoomDatabase.Builder.addTypeConverter()`](/reference/androidx/room/RoomDatabase.Builder#addTypeConverter(java.lang.Object))\nmethod to pass an instance of your converter class to the `RoomDatabase`\nbuilder: \n\n### Kotlin\n\n```kotlin\nval db = Room.databaseBuilder(...)\n .addTypeConverter(exampleConverterInstance)\n .build()\n```\n\n### Java\n\n```java\nAppDatabase db = Room.databaseBuilder(...)\n .addTypeConverter(exampleConverterInstance)\n .build();\n```\n\nUnderstand why Room doesn't allow object references\n---------------------------------------------------\n\n**Key takeaway:**\nRoom disallows object references between entity classes. Instead, you must\nexplicitly request the data that your app needs.\n\nMapping relationships from a database to the respective object model is a common\npractice and works very well on the server side. Even when the program loads\nfields as they're accessed, the server still performs well.\n\nHowever, on the client side, this type of lazy loading isn't feasible because\nit usually occurs on the UI thread, and querying information on disk in the UI\nthread creates significant performance problems. The UI thread typically has\nabout 16 ms to calculate and draw an activity's updated layout, so even if a\nquery takes only 5 ms, it's still likely that your app will run out of time to\ndraw the frame, causing noticeable visual glitches. The query could take even\nmore time to complete if there's a separate transaction running in parallel, or\nif the device is running other disk-intensive tasks. If you don't use lazy\nloading, however, your app fetches more data than it needs, creating memory\nconsumption problems.\n\nObject-relational mappings usually leave this decision to developers so that\nthey can do whatever is best for their app's use cases. Developers usually\ndecide to share the model between their app and the UI. This solution doesn't\nscale well, however, because as the UI changes over time, the shared model\ncreates problems that are difficult for developers to anticipate and debug.\n\nFor example, consider a UI that loads a list of `Book` objects, with each book\nhaving an `Author` object. You might initially design your queries to use lazy\nloading to have instances of `Book` retrieve the author. The first retrieval of\nthe `author` field queries the database. Some time later, you realize that you\nneed to display the author name in your app's UI, as well. You can access this\nname easily enough, as shown in the following code snippet: \n\n### Kotlin\n\n```kotlin\nauthorNameTextView.text = book.author.name\n```\n\n### Java\n\n```java\nauthorNameTextView.setText(book.getAuthor().getName());\n```\n\nHowever, this seemingly innocent change causes the `Author` table to be queried\non the main thread.\n\nIf you query author information ahead of time, it becomes difficult to change\nhow data is loaded if you no longer need that data. For example, if your app's\nUI no longer needs to display `Author` information, your app effectively loads\ndata that it no longer displays, wasting valuable memory space. Your app's\nefficiency degrades even further if the `Author` class references another table,\nsuch as `Books`.\n\nTo reference multiple entities at the same time using Room, you instead create a\nPOJO that contains each entity, then write a query that joins the corresponding\ntables. This well-structured model, combined with Room's robust query\nvalidation capabilities, allows your app to consume fewer resources when loading\ndata, improving your app's performance and user experience."]]