دسترسی به داده ها با استفاده از اتاق DAO

هنگامی که از کتابخانه ماندگاری اتاق برای ذخیره داده های برنامه خود استفاده می کنید، با تعریف اشیاء دسترسی به داده یا DAO با داده های ذخیره شده تعامل دارید. هر DAO شامل روش هایی است که دسترسی انتزاعی به پایگاه داده برنامه شما را ارائه می دهد. در زمان کامپایل، Room به طور خودکار پیاده سازی های DAO هایی را که شما تعریف کرده اید تولید می کند.

با استفاده از DAO برای دسترسی به پایگاه داده برنامه خود به جای سازندگان پرس و جو یا پرس و جوهای مستقیم، می توانید جداسازی نگرانی ها را که یک اصل مهم معماری است، حفظ کنید. DAO ها همچنین هنگام آزمایش برنامه خود دسترسی به پایگاه داده را برای شما آسان تر می کنند.

آناتومی یک DAO

شما می توانید هر DAO را به عنوان یک رابط یا یک کلاس انتزاعی تعریف کنید. برای موارد استفاده اولیه، معمولاً از یک رابط استفاده می کنید. در هر صورت، همیشه باید DAO های خود را با @Dao حاشیه نویسی کنید. DAO ها ویژگی ندارند، اما یک یا چند روش را برای تعامل با داده های پایگاه داده برنامه شما تعریف می کنند.

کد زیر نمونه ای از یک DAO ساده است که روش هایی را برای درج، حذف و انتخاب اشیاء User در پایگاه داده اتاق تعریف می کند:

کاتلین

@Dao
interface UserDao {
    @Insert
    fun insertAll(vararg users: User)

    @Delete
    fun delete(user: User)

    @Query("SELECT * FROM user")
    fun getAll(): List<User>
}

جاوا

@Dao
public interface UserDao {
    @Insert
    void insertAll(User... users);

    @Delete
    void delete(User user);

    @Query("SELECT * FROM user")
    List<User> getAll();
}

دو نوع روش DAO وجود دارد که تعاملات پایگاه داده را تعریف می کند:

  • روش‌های راحتی که به شما امکان می‌دهند بدون نوشتن کد SQL، ردیف‌ها را در پایگاه داده خود درج، به‌روزرسانی و حذف کنید.
  • روش های پرس و جو که به شما امکان می دهد پرس و جوی SQL خود را برای تعامل با پایگاه داده بنویسید.

بخش‌های زیر نحوه استفاده از هر دو نوع روش DAO را برای تعریف تعاملات پایگاه داده‌ای که برنامه شما به آن نیاز دارد نشان می‌دهد.

روش های راحتی

Room حاشیه نویسی های راحتی را برای تعریف روش هایی ارائه می دهد که درج، به روز رسانی و حذف ساده را بدون نیاز به نوشتن دستور SQL انجام می دهند.

اگر نیاز به تعریف درج‌ها، به‌روزرسانی‌ها یا حذف‌های پیچیده‌تری دارید، یا اگر نیاز به پرس‌وجویی از داده‌ها در پایگاه داده دارید، به جای آن از روش پرس و جو استفاده کنید.

درج کنید

حاشیه‌نویسی @Insert به شما امکان می‌دهد روش‌هایی را تعریف کنید که پارامترهای خود را در جدول مناسب در پایگاه داده وارد می‌کنند. کد زیر نمونه هایی از روش های @Insert معتبر را نشان می دهد که یک یا چند شیء User را در پایگاه داده وارد می کنند:

کاتلین

@Dao
interface UserDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertUsers(vararg users: User)

    @Insert
    fun insertBothUsers(user1: User, user2: User)

    @Insert
    fun insertUsersAndFriends(user: User, friends: List<User>)
}

جاوا

@Dao
public interface UserDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    public void insertUsers(User... users);

    @Insert
    public void insertBothUsers(User user1, User user2);

    @Insert
    public void insertUsersAndFriends(User user, List<User> friends);
}

هر پارامتر برای یک متد @Insert باید یا نمونه‌ای از یک کلاس موجودیت داده Room باشد که با @Entity حاشیه‌نویسی شده است یا مجموعه‌ای از نمونه‌های کلاس موجودیت داده که هر کدام به یک پایگاه داده اشاره می‌کنند. هنگامی که یک متد @Insert فراخوانی می شود، Room هر نمونه موجودیت ارسال شده را در جدول پایگاه داده مربوطه درج می کند.

اگر متد @Insert یک پارامتر واحد دریافت کند، می‌تواند مقدار long را برگرداند که همان rowId جدید آیتم درج شده است. اگر پارامتر یک آرایه یا مجموعه است، در عوض یک آرایه یا مجموعه ای از مقادیر long را با هر مقدار به عنوان rowId برای یکی از موارد درج شده برگردانید. برای کسب اطلاعات بیشتر در مورد بازگرداندن مقادیر rowId ، به مستندات مرجع برای حاشیه‌نویسی @Insert و مستندات SQLite برای جداول ردیف مراجعه کنید.

به روز رسانی

حاشیه نویسی @Update به شما امکان می دهد روش هایی را تعریف کنید که ردیف های خاصی را در جدول پایگاه داده به روز می کنند. مانند روش‌های @Insert ، روش‌های @Update نمونه‌های موجودیت داده را به عنوان پارامتر می‌پذیرند. کد زیر نمونه‌ای از یک روش @Update را نشان می‌دهد که تلاش می‌کند یک یا چند شیء User را در پایگاه داده به‌روزرسانی کند:

کاتلین

@Dao
interface UserDao {
    @Update
    fun updateUsers(vararg users: User)
}

جاوا

@Dao
public interface UserDao {
    @Update
    public void updateUsers(User... users);
}

Room از کلید اصلی برای تطبیق نمونه‌های موجودیت تصویب شده با ردیف‌های پایگاه داده استفاده می‌کند. اگر ردیفی با همان کلید اصلی وجود نداشته باشد، Room هیچ تغییری ایجاد نمی کند.

یک متد @Update می‌تواند به‌صورت اختیاری مقدار int را برگرداند که نشان‌دهنده تعداد ردیف‌هایی است که با موفقیت به‌روزرسانی شده‌اند.

حذف کنید

حاشیه نویسی @Delete به شما امکان می دهد روش هایی را تعریف کنید که ردیف های خاصی را از جدول پایگاه داده حذف می کند. مانند روش های @Insert ، روش های @Delete نمونه های موجودیت داده را به عنوان پارامتر می پذیرند. کد زیر نمونه‌ای از روش @Delete را نشان می‌دهد که تلاش می‌کند یک یا چند شیء User را از پایگاه داده حذف کند:

کاتلین

@Dao
interface UserDao {
    @Delete
    fun deleteUsers(vararg users: User)
}

جاوا

@Dao
public interface UserDao {
    @Delete
    public void deleteUsers(User... users);
}

Room از کلید اصلی برای تطبیق نمونه‌های موجودیت تصویب شده با ردیف‌های پایگاه داده استفاده می‌کند. اگر ردیفی با همان کلید اصلی وجود نداشته باشد، Room هیچ تغییری ایجاد نمی کند.

یک متد @Delete می‌تواند به صورت اختیاری یک مقدار int را برگرداند که نشان‌دهنده تعداد ردیف‌هایی است که با موفقیت حذف شده‌اند.

روش های پرس و جو

حاشیه نویسی @Query به شما امکان می دهد عبارات SQL را بنویسید و آنها را به عنوان روش های DAO در معرض دید قرار دهید. از این روش‌های جستجو برای جستجوی داده‌ها از پایگاه داده برنامه خود یا زمانی که نیاز به درج، به‌روزرسانی و حذف پیچیده‌تر دارید، استفاده کنید.

اتاق درخواست‌های SQL را در زمان کامپایل تأیید می‌کند. این بدان معناست که اگر در پرس و جو شما مشکلی وجود داشته باشد، به جای شکست در زمان اجرا، یک خطای کامپایل رخ می دهد.

پرس و جوهای ساده

کد زیر متدی را تعریف می کند که از یک کوئری ساده SELECT برای برگرداندن تمام اشیاء User در پایگاه داده استفاده می کند:

کاتلین

@Query("SELECT * FROM user")
fun loadAllUsers(): Array<User>

جاوا

@Query("SELECT * FROM user")
public User[] loadAllUsers();

بخش‌های زیر نحوه اصلاح این مثال را برای موارد استفاده معمولی نشان می‌دهد.

زیر مجموعه ای از ستون های جدول را برگردانید

بیشتر اوقات، شما فقط نیاز دارید که زیرمجموعه ای از ستون ها را از جدولی که پرس و جو می کنید برگردانید. به عنوان مثال، UI شما ممکن است به جای تمام جزئیات مربوط به آن کاربر، فقط نام و نام خانوادگی یک کاربر را نمایش دهد. برای صرفه جویی در منابع و ساده سازی اجرای پرس و جو، فقط فیلدهایی را که نیاز دارید پرس و جو کنید.

Room به شما امکان می دهد یک شی ساده را از هر یک از پرس و جوهای خود بازگردانید تا زمانی که بتوانید مجموعه ستون های نتیجه را بر روی شی برگشتی نگاشت کنید. به عنوان مثال، می توانید شی زیر را برای نگه داشتن نام و نام خانوادگی یک کاربر تعریف کنید:

کاتلین

data class NameTuple(
    @ColumnInfo(name = "first_name") val firstName: String?,
    @ColumnInfo(name = "last_name") val lastName: String?
)

جاوا

public class NameTuple {
    @ColumnInfo(name = "first_name")
    public String firstName;

    @ColumnInfo(name = "last_name")
    @NonNull
    public String lastName;
}

سپس، می توانید آن شی ساده را از متد query خود برگردانید:

کاتلین

@Query("SELECT first_name, last_name FROM user")
fun loadFullName(): List<NameTuple>

جاوا

@Query("SELECT first_name, last_name FROM user")
public List<NameTuple> loadFullName();

Room می داند که پرس و جو مقادیری را برای ستون های first_name و last_name برمی گرداند و این مقادیر را می توان در فیلدهای کلاس NameTuple نگاشت. اگر پرس و جو ستونی را برمی گرداند که روی فیلدی در شیء برگشتی نگاشت نمی شود، Room یک هشدار نمایش می دهد.

پارامترهای ساده را به یک پرس و جو منتقل کنید

اغلب اوقات، متدهای DAO شما نیاز به پذیرش پارامترها دارند تا بتوانند عملیات فیلتر کردن را انجام دهند. اتاق از استفاده از پارامترهای متد به عنوان پارامترهای bind در جستارهای شما پشتیبانی می کند.

به عنوان مثال، کد زیر متدی را تعریف می کند که همه کاربران بالای یک سن خاص را برمی گرداند:

کاتلین

@Query("SELECT * FROM user WHERE age > :minAge")
fun loadAllUsersOlderThan(minAge: Int): Array<User>

جاوا

@Query("SELECT * FROM user WHERE age > :minAge")
public User[] loadAllUsersOlderThan(int minAge);

همانطور که در کد زیر نشان داده شده است، همچنین می توانید چندین پارامتر را ارسال کنید یا به یک پارامتر چندین بار در یک کوئری ارجاع دهید:

کاتلین

@Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge")
fun loadAllUsersBetweenAges(minAge: Int, maxAge: Int): Array<User>

@Query("SELECT * FROM user WHERE first_name LIKE :search " +
       "OR last_name LIKE :search")
fun findUserWithName(search: String): List<User>

جاوا

@Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge")
public User[] loadAllUsersBetweenAges(int minAge, int maxAge);

@Query("SELECT * FROM user WHERE first_name LIKE :search " +
       "OR last_name LIKE :search")
public List<User> findUserWithName(String search);

مجموعه ای از پارامترها را به یک پرس و جو ارسال کنید

برخی از روش‌های DAO شما ممکن است از شما نیاز داشته باشند که تعداد متغیری از پارامترها را ارسال کنید که تا زمان اجرا مشخص نیست. اتاق متوجه می شود که یک پارامتر چه زمانی یک مجموعه را نشان می دهد و به طور خودکار آن را در زمان اجرا بر اساس تعداد پارامترهای ارائه شده گسترش می دهد.

به عنوان مثال، کد زیر روشی را تعریف می کند که اطلاعات مربوط به همه کاربران را از زیر مجموعه ای از مناطق برمی گرداند:

کاتلین

@Query("SELECT * FROM user WHERE region IN (:regions)")
fun loadUsersFromRegions(regions: List<String>): List<User>

جاوا

@Query("SELECT * FROM user WHERE region IN (:regions)")
public List<User> loadUsersFromRegions(List<String> regions);

پرس و جو از چندین جدول

ممکن است برخی از پرس و جوهای شما برای محاسبه نتیجه نیاز به دسترسی به چندین جدول داشته باشند. شما می توانید از بندهای JOIN در جستارهای SQL خود برای ارجاع به بیش از یک جدول استفاده کنید.

کد زیر روشی را تعریف می‌کند که سه جدول را به یکدیگر متصل می‌کند تا کتاب‌هایی را که در حال حاضر امانت دارند به یک کاربر خاص برگرداند:

کاتلین

@Query(
    "SELECT * FROM book " +
    "INNER JOIN loan ON loan.book_id = book.id " +
    "INNER JOIN user ON user.id = loan.user_id " +
    "WHERE user.name LIKE :userName"
)
fun findBooksBorrowedByNameSync(userName: String): List<Book>

جاوا

@Query("SELECT * FROM book " +
       "INNER JOIN loan ON loan.book_id = book.id " +
       "INNER JOIN user ON user.id = loan.user_id " +
       "WHERE user.name LIKE :userName")
public List<Book> findBooksBorrowedByNameSync(String userName);

همچنین می‌توانید اشیاء ساده را برای برگرداندن زیرمجموعه‌ای از ستون‌ها از چندین جدول متصل تعریف کنید، همانطور که در بخش Return a subset of a tables ستون‌ها بحث شد. کد زیر یک DAO را با روشی تعریف می کند که نام کاربران و نام کتاب هایی را که آنها به امانت گرفته اند برمی گرداند:

کاتلین

interface UserBookDao {
    @Query(
        "SELECT user.name AS userName, book.name AS bookName " +
        "FROM user, book " +
        "WHERE user.id = book.user_id"
    )
    fun loadUserAndBookNames(): LiveData<List<UserBook>>

    // You can also define this class in a separate file.
    data class UserBook(val userName: String?, val bookName: String?)
}

جاوا

@Dao
public interface UserBookDao {
   @Query("SELECT user.name AS userName, book.name AS bookName " +
          "FROM user, book " +
          "WHERE user.id = book.user_id")
   public LiveData<List<UserBook>> loadUserAndBookNames();

   // You can also define this class in a separate file, as long as you add the
   // "public" access modifier.
   static class UserBook {
       public String userName;
       public String bookName;
   }
}

چند نقشه را برگردانید

در اتاق 2.4 و بالاتر، می‌توانید با نوشتن روش‌های پرس و جو که یک نقشه چندگانه را برمی‌گردانند، ستون‌ها را از چندین جدول بدون تعریف کلاس داده اضافی جستجو کنید.

مثالی را از بخش Query multiple tables در نظر بگیرید. به جای بازگرداندن فهرستی از نمونه‌های یک کلاس داده سفارشی که جفت‌هایی از نمونه‌های User و Book را در خود نگه می‌دارد، می‌توانید نگاشت User و Book مستقیماً از روش جستجوی خود برگردانید:

کاتلین

@Query(
    "SELECT * FROM user" +
    "JOIN book ON user.id = book.user_id"
)
fun loadUserAndBookNames(): Map<User, List<Book>>

جاوا

@Query(
    "SELECT * FROM user" +
    "JOIN book ON user.id = book.user_id"
)
public Map<User, List<Book>> loadUserAndBookNames();

هنگامی که روش پرس و جو شما یک نقشه چندگانه برمی گرداند، می توانید پرس و جوهایی بنویسید که از بندهای GROUP BY استفاده می کنند و به شما امکان می دهند از قابلیت های SQL برای محاسبات و فیلترهای پیشرفته استفاده کنید. برای مثال، می‌توانید روش loadUserAndBookNames() خود را طوری تغییر دهید که فقط کاربرانی را که سه یا چند کتاب بررسی کرده‌اند بازگرداند:

کاتلین

@Query(
    "SELECT * FROM user" +
    "JOIN book ON user.id = book.user_id" +
    "GROUP BY user.name WHERE COUNT(book.id) >= 3"
)
fun loadUserAndBookNames(): Map<User, List<Book>>

جاوا

@Query(
    "SELECT * FROM user" +
    "JOIN book ON user.id = book.user_id" +
    "GROUP BY user.name WHERE COUNT(book.id) >= 3"
)
public Map<User, List<Book>> loadUserAndBookNames();

اگر نیازی به نگاشت کل اشیاء ندارید، می‌توانید با تنظیم ویژگی‌های keyColumn و valueColumn در یک حاشیه‌نویسی @MapInfo در روش درخواست خود، نگاشت‌ها را بین ستون‌های خاص در جستجوی خود برگردانید:

کاتلین

@MapInfo(keyColumn = "userName", valueColumn = "bookName")
@Query(
    "SELECT user.name AS username, book.name AS bookname FROM user" +
    "JOIN book ON user.id = book.user_id"
)
fun loadUserAndBookNames(): Map<String, List<String>>

جاوا

@MapInfo(keyColumn = "userName", valueColumn = "bookName")
@Query(
    "SELECT user.name AS username, book.name AS bookname FROM user" +
    "JOIN book ON user.id = book.user_id"
)
public Map<String, List<String>> loadUserAndBookNames();

انواع برگشت ویژه

Room انواع خاصی از بازگشت را برای ادغام با سایر کتابخانه های API فراهم می کند.

پرس و جوهای صفحه بندی شده با کتابخانه Paging

اتاق از پرس و جوهای صفحه بندی شده از طریق ادغام با کتابخانه Paging پشتیبانی می کند. در Room 2.3.0-alpha01 و بالاتر، DAO ها می توانند اشیاء PagingSource را برای استفاده با Paging 3 برگردانند.

کاتلین

@Dao
interface UserDao {
  @Query("SELECT * FROM users WHERE label LIKE :query")
  fun pagingSource(query: String): PagingSource<Int, User>
}

جاوا

@Dao
interface UserDao {
  @Query("SELECT * FROM users WHERE label LIKE :query")
  PagingSource<Integer, User> pagingSource(String query);
}

برای اطلاعات بیشتر در مورد انتخاب پارامترهای نوع برای PagingSource ، به انتخاب انواع کلید و مقدار مراجعه کنید.

دسترسی مستقیم به مکان نما

اگر منطق برنامه شما نیاز به دسترسی مستقیم به ردیف‌های برگشتی دارد، می‌توانید روش‌های DAO خود را برای برگرداندن یک شی Cursor بنویسید، همانطور که در مثال زیر نشان داده شده است:

کاتلین

@Dao
interface UserDao {
    @Query("SELECT * FROM user WHERE age > :minAge LIMIT 5")
    fun loadRawUsersOlderThan(minAge: Int): Cursor
}

جاوا

@Dao
public interface UserDao {
    @Query("SELECT * FROM user WHERE age > :minAge LIMIT 5")
    public Cursor loadRawUsersOlderThan(int minAge);
}

منابع اضافی

برای کسب اطلاعات بیشتر در مورد دسترسی به داده ها با استفاده از اتاق DAO، به منابع اضافی زیر مراجعه کنید:

نمونه ها

Codelabs