W tym wpisie przedstawiłem orientacyjny schemat bazy danych, który będzie zastosowany w mojej aplikacji. Można tam zauważyć relacje wiele-do-wielu pomiędzy tabelami tags i documents oraz tabele documents_tags, która jest tak zwaną tabelą skrzyżowań.
OrmLite niestety nie wspiera relacji wiele-do-wielu, ale sama realizacja tego na szczęście nie jest trudna.
Zacznę od pokazania encji dla tabeli documents_tags.
@DatabaseTable(tableName = "documents_tags") class DocumentTag { @DatabaseField(generatedId = true) var id: Int = _ @DatabaseField(foreign = true, canBeNull = false) var document: Document = _ @DatabaseField(foreign = true, canBeNull = false) var tag: Tag = _ }Jak widać encja zawiera tylko 3 pola:
- id – id encji, które jest wymagane
- document – klucz obcy odnoszący się do dokumentu
- tag – klucz obcy odnoszący się do tagu
Encja tag wygląda następująco:
@DatabaseTable(tableName = "tags") class Tag { @DatabaseField(generatedId = true) var id: Int = _ @DatabaseField(canBeNull = false, unique = true) var name: String = _ }Wydaję mi się, iż tutaj nie ma nic do tłumaczenia
Encja dla dokumentu wygląda natomiast tak:
@DatabaseTable(tableName = "documents") class Document { @DatabaseField(generatedId = true) var id: Int = _ @DatabaseField(canBeNull = false) var title: String = _ @DatabaseField(canBeNull = false) var createDate: Date = _ @ForeignCollectionField(eager = false) var documentTags: ForeignCollection[DocumentTag] = _ @ForeignCollectionField(eager = false) var documentFiles: ForeignCollection[DocumentFile] = _ }Tutaj istotne jest następujące pole:
@ForeignCollectionField(eager = false) var documentTags: ForeignCollection[DocumentTag] = _Jak widać jest to 'obca’ kolekcja. Co będzie zawierała kolekcja po pobraniu dokumentu przy pomocy metody queryForId?
class DocumentDao(val dao: Dao[Document, Int]) extends RuntimeExceptionDao[Document, Int](dao) { def getDocumentById(documentId: Int): Document = { dao.queryForId(documentId) } }Otóż, jak można się domyślić, kolekcja ta będzie zawieraja liste encji DocumentTag powiązanych z dokumentem. Jest tylko jeden haczyk – pobrane encje DocumentTag nie zawierają 'całych’ obiektów, a jedynie ich id. Czyli np. instancja klasy Tag w pobranej kolekcji bedzie zawierała uzupełnione id, ale pole name zawsze będzie puste.
Mając tą wiedze wystarczą dwa kroki do pobrania naszych tagów.
Po pierwsze musimy stworzyć metodę, która na podstawie listy id tagów, zwróci nam tagi.
def getTagsByIds(tagIds: List[Int]): List[Tag] = { dao.queryBuilder.where().in("id", tagIds.asJava).query().toList }A mając pobraną naszą encje możemy pobrać jej taki wywołując powyższą metodę w następujący sposób:
val tags: List[Tag] = tagDao.getTagsByIds(document.documentTags.map(t => t.tag.id).toList)Jak widać pomimo braku jawnego wsparcia dla relacji wiele-do-wielu możemy sobie z tym w miarę ładny sposób poradzić