SQLite는 관계형 데이터베이스이므로 항목 간 관계를 정의할 수 있습니다. 대부분의 객체 관계 매핑(ORM) 라이브러리에서는 항목 객체가 서로를 참조할 수 있지만, Room은 이러한 상호 참조를 명시적으로 금지합니다. 이 결정의 기술적 근거를 알아보려면 Room에서 객체 참조를 허용하지 않는 이유 이해를 참고하세요.
다대다: 한 유형의 여러 항목이 다른 유형의 여러 항목과 연결될 수 있는 관계를 나타냅니다. 일반적으로 교차 테이블이 필요합니다.
중첩된 관계 (삽입된 객체 사용): 항목이 다른 항목을 필드로 포함하고 이 중첩된 항목이 다른 항목을 추가로 포함할 수 있는 관계를 나타냅니다. 여기서는 @Embedded 주석을 사용합니다.
두 가지 접근 방식 중에서 선택
Room에서는 2가지 방법으로 항목 간 관계를 정의하고 쿼리합니다.
다음 중 원하는 방법을 사용할 수 있습니다.
삽입된 객체가 있는 중간 데이터 클래스 또는
멀티매핑 반환 유형이 있는 관계형 쿼리 메서드
중간 데이터 클래스를 사용해야 하는 것이 아니라면 멀티매핑 반환 유형 접근 방식을 사용하는 것이 좋습니다. 이 접근 방식에 관한 자세한 내용은 멀티매핑 반환을 참고하세요.
중간 데이터 클래스 접근 방식을 사용하면 복잡한 SQL 쿼리를 작성하지 않아도 되지만, 추가 데이터 클래스가 필요하므로 코드의 복잡성이 증가할 수도 있습니다. 즉, 멀티매핑 반환 유형 접근 방식에서는 SQL 쿼리로 더 많은 작업을 실행해야 하고 중간 데이터 클래스 접근 방식에서는 코드로 더 많은 작업을 해야 합니다.
중간 데이터 클래스 접근 방식 사용
중간 데이터 클래스 접근 방식에서는 Room 항목 간 관계를 모델링하는 데이터 클래스를 정의합니다. 이 데이터 클래스는 한 항목의 인스턴스와 다른 항목의 인스턴스로 구성된 쌍을 삽입된 객체로 보유합니다. 그러면 쿼리 메서드가 앱에서 사용할 이 데이터 클래스의 인스턴스를 반환할 수 있습니다.
예를 들어 UserBook 데이터 클래스를 정의하여 특정 책을 대출한 도서관 이용자를 표시하고, 쿼리 메서드를 정의하여 데이터베이스에서 UserBook 인스턴스 목록을 검색할 수 있습니다.
Kotlin
@DaointerfaceUserBookDao{@Query("SELECT user.name AS userName, book.name AS bookName "+"FROM user, book "+"WHERE user.id = book.user_id")funloadUserAndBookNames():LiveData<List<UserBook>>
}dataclassUserBook(valuserName:String?,valbookName:String?)
자바
@DaopublicinterfaceUserBookDao{@Query("SELECT user.name AS userName, book.name AS bookName "+"FROM user, book "+"WHERE user.id = book.user_id")publicLiveData<List<UserBook>>loadUserAndBookNames();}publicclassUserBook{publicStringuserName;publicStringbookName;}
멀티매핑 반환 유형 접근 방식 사용
멀티매핑 반환 유형 접근 방식에서는 데이터 클래스를 추가로 정의하지 않아도 됩니다. 대신 원하는 매핑 구조에 기반하여 메서드의 멀티매핑 반환 유형을 정의하고 SQL 쿼리에서 항목 간 관계를 직접 정의합니다.
예를 들어 다음 쿼리 메서드는 User 및 Book 인스턴스의 매핑을 반환하여 특정 책을 대출한 도서관 이용자를 나타냅니다.
Kotlin
@Query("SELECT * FROM user"+"JOIN book ON user.id = book.user_id")funloadUserAndBookNames():Map<User,List<Book>>
자바
@Query("SELECT * FROM user"+"JOIN book ON user.id = book.user_id")publicMap<User,List<Book>>loadUserAndBookNames();
삽입된 객체 만들기
때로 개발자는 객체에 여러 필드가 포함되어 있는 경우에도 데이터베이스 로직에서 항목 또는 데이터 객체를 응집된 전체로 표현하려고 합니다. 이 경우 @Embedded 주석을 사용하여 테이블 내의 하위 필드로 분해하려는 객체를 나타낼 수 있습니다. 그러면 다른 개별 열을 쿼리하듯 삽입된 필드를 쿼리할 수 있습니다.
예를 들어 User 클래스는 Address 유형의 필드를 포함할 수 있으며 이것은 street, city, state, postCode 필드로 구성된 유형임을 나타냅니다. 구성된 열을 테이블에 별도로 저장하려면 Address 필드를 포함합니다. 이는 @Embedded로 주석이 추가된 User 클래스에 표시됩니다. 다음 코드 스니펫은 이를 보여줍니다.
이 페이지에 나와 있는 콘텐츠와 코드 샘플에는 콘텐츠 라이선스에서 설명하는 라이선스가 적용됩니다. 자바 및 OpenJDK는 Oracle 및 Oracle 계열사의 상표 또는 등록 상표입니다.
최종 업데이트: 2024-11-30(UTC)
[[["이해하기 쉬움","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"]],["최종 업데이트: 2024-11-30(UTC)"],[],[],null,["# Choose relationship types between objects\n\nBecause SQLite is a relational database, you can define relationships between\nentities. But while most object-relational mapping libraries let entity objects\nreference each other, Room explicitly forbids this. To learn about the technical\nreasoning behind this decision, see [Understand why Room doesn't allow object\nreferences](/training/data-storage/room/referencing-data#understand-no-object-references).\n\nTypes of relationships\n----------------------\n\nRoom supports the following relationship types:\n\n- [**One-to-one**](/training/data-storage/room/relationships/one-to-one): Represents a relationship where a single entity is related to another single entity.\n- [**One-to-many**](/training/data-storage/room/relationships/one-to-many): Represents a relationship where a single entity can be related to multiple entities of another type.\n- [**Many-to-many**](/training/data-storage/room/relationships/many-to-many): Represents a relationship where multiple entities of one type can be related to multiple entities of another type. This usually requires a junction table.\n- [**Nested Relationships (using embedded objects)**](/training/data-storage/room/relationships/nested): Represents a relationship where an entity contains another entity as a field, and this nested entity can further contain other entities. This uses the `@Embedded` annotation.\n\nChoose between two approaches\n-----------------------------\n\nIn Room, there are two ways to define and query a relationship between entities.\nYou can use either:\n\n- An intermediate data class with embedded objects, or\n- A relational query method with a multimap return type.\n\nIf you don't have a specific reason to use intermediate data classes, we\nrecommend using the multimap return type approach. To learn more about this\napproach, see [Return a multimap](/training/data-storage/room/accessing-data#multimap).\n\nThe intermediate data class approach lets you avoid writing complex SQL queries,\nbut it can also result in increased code complexity because it requires\nadditional data classes. In short, the multimap return type approach requires\nyour SQL queries to do more work, and the intermediate data class approach\nrequires your code to do more work.\n\n### Use the intermediate data class approach\n\nIn the intermediate data class approach, you define a data class that models the\nrelationship between your Room entities. This data class holds the pairings\nbetween instances of one entity and instances of another entity as [embedded\nobjects](#nested-objects). Your query methods can then return instances of this data class for\nuse in your app.\n\nFor example, you can define a `UserBook` data class to represent library users\nwith specific books checked out, and define a query method to retrieve a list of\n`UserBook` instances from the database: \n\n### Kotlin\n\n @Dao\n interface UserBookDao {\n @Query(\n \"SELECT user.name AS userName, book.name AS bookName \" +\n \"FROM user, book \" +\n \"WHERE user.id = book.user_id\"\n )\n fun loadUserAndBookNames(): LiveData\u003cList\u003cUserBook\u003e\u003e\n }\n\n data class UserBook(val userName: String?, val bookName: String?)\n\n### Java\n\n @Dao\n public interface UserBookDao {\n @Query(\"SELECT user.name AS userName, book.name AS bookName \" +\n \"FROM user, book \" +\n \"WHERE user.id = book.user_id\")\n public LiveData\u003cList\u003cUserBook\u003e\u003e loadUserAndBookNames();\n }\n\n public class UserBook {\n public String userName;\n public String bookName;\n }\n\n### Use the multimap return types approach\n\n| **Note:** Room only supports multimap return types in version 2.4 and higher.\n\nIn the multimap return type approach, you don't need to define any additional\ndata classes. Instead, you define a [multimap](https://en.wikipedia.org/wiki/Multimap) return type for\nyour method based on the map structure that you want and define the relationship\nbetween your entities directly in your SQL query.\n\nFor example, the following query method returns a mapping of `User` and `Book`\ninstances to represent library users with specific books checked out: \n\n### Kotlin\n\n @Query(\n \"SELECT * FROM user\" +\n \"JOIN book ON user.id = book.user_id\"\n )\n fun loadUserAndBookNames(): Map\u003cUser, List\u003cBook\u003e\u003e\n\n### Java\n\n @Query(\n \"SELECT * FROM user\" +\n \"JOIN book ON user.id = book.user_id\"\n )\n public Map\u003cUser, List\u003cBook\u003e\u003e loadUserAndBookNames();\n\nCreate embedded objects\n-----------------------\n\nSometimes, you'd like to express an entity or data object as a cohesive whole in\nyour database logic, even if the object contains several fields. In these\nsituations, you can use the [`@Embedded`](/reference/androidx/room/Embedded) annotation to represent an object\nthat you'd like to decompose into its subfields within a table. You can then\nquery the embedded fields just as you do for other individual columns.\n\nFor example, your `User` class can include a field of type `Address` that\nrepresents a composition of fields named `street`, `city`, `state`, and\n`postCode`. To store the composed columns separately in the table, include an\n`Address` field. This should appear in the `User` class annotated with\n`@Embedded`. The following code snippet demonstrates this: \n\n### Kotlin\n\n data class Address(\n val street: String?,\n val state: String?,\n val city: String?,\n @ColumnInfo(name = \"post_code\") val postCode: Int\n )\n\n @Entity\n data class User(\n @PrimaryKey val id: Int,\n val firstName: String?,\n @Embedded val address: Address?\n )\n\n### Java\n\n public class Address {\n public String street;\n public String state;\n public String city;\n\n @ColumnInfo(name = \"post_code\") public int postCode;\n }\n\n @Entity\n public class User {\n @PrimaryKey public int id;\n\n public String firstName;\n\n @Embedded public Address address;\n }\n\nThe table representing a `User` object then contains columns with the following\nnames: `id`, `firstName`, `street`, `state`, `city`, and `post_code`.\n| **Note:** Embedded fields can also include other embedded fields.\n\nIf an entity has multiple embedded fields of the same type, you can keep each\ncolumn unique by setting the [`prefix`](/reference/androidx/room/Embedded#getPrefix()) property. Room then adds the\nprovided value to the beginning of each column name in the embedded object.\n\nAdditional resources\n--------------------\n\nTo learn more about defining relationships between entities in Room, see the\nfollowing additional resources.\n\n### Videos\n\n- [What's New in Room](https://www.youtube.com/watch?v=_aJsh6P00c0) (Android Dev Summit '19)\n\n### Blogs\n\n- [Database relations with Room](https://medium.com/androiddevelopers/database-relations-with-room-544ab95e4542)"]]