greendao是如何實現查詢操作的,多線程是否安全?

 

想要了解GreenDao如何進行數據庫CRUD操作,那麼可以看源碼。

查詢:

先看最簡單最容記得查詢方法queryBuilder().list()。

/** Executes the query and returns the result as a list containing all entities loaded into memory. */
    public List<T> list() {
        checkThread();
        Cursor cursor = dao.getDatabase().rawQuery(sql, parameters);
        return daoAccess.loadAllAndCloseCursor(cursor);
    }

通過sql語句進行查詢,然後返回一個遊標。根據這個遊標進行處理。查看下方法實現

/** Reads all available rows from the given cursor and returns a list of entities. */
    protected List<T> loadAllFromCursor(Cursor cursor) {
        int count = cursor.getCount();
        if (count == 0) {
            return new ArrayList<T>();
        }
        List<T> list = new ArrayList<T>(count);
        CursorWindow window = null;
        boolean useFastCursor = false;
        if (cursor instanceof CrossProcessCursor) {
            window = ((CrossProcessCursor) cursor).getWindow();
            if (window != null) { // E.g. Robolectric has no Window at this point
                if (window.getNumRows() == count) {
                    cursor = new FastCursor(window);
                    useFastCursor = true;
                } else {
                    DaoLog.d("Window vs. result size: " + window.getNumRows() + "/" + count);
                }
            }
        }

        if (cursor.moveToFirst()) {
            if (identityScope != null) {
                identityScope.lock();
                identityScope.reserveRoom(count);
            }

            try {
                if (!useFastCursor && window != null && identityScope != null) {
                    loadAllUnlockOnWindowBounds(cursor, window, list);
                } else {
                    do {
                        list.add(loadCurrent(cursor, 0, false));
                    } while (cursor.moveToNext());
                }
            } finally {
                if (identityScope != null) {
                    identityScope.unlock();
                }
            }
        }
        return list;
    }

看到這裏,發現和我們自己寫SQLite原生查詢的時候處理遊標差不多。

它首先判斷cursor是否爲空,接着新建List,再從遊標中讀取數據存進list中然後返回,整體理解起來不難。那麼它轉化成我們需要的對象是在loadCurrent()這個方法中。

final protected T loadCurrent(Cursor cursor, int offset, boolean lock)

 

所以總體思路是這樣:

我們調用實體類Dao的queryBuilder().list()方法,接着它使用SQL語句進行數據庫查詢,返回一個cursor,再對cursor進行解析,解析cursor的方法是

final protected T loadCurrent(Cursor cursor, int offset, boolean lock)

,存進List中返回給我們。

 

再看看queryBuilder().listLazy()

該方法返回一個LazyList<T>,這個LazyList是實現了List接口的,我們得到這個LazyList時,操作相關的數據是的方法有構造方法和get方法。

看下他實現的的方法。構造方法

 LazyList(InternalQueryDaoAccess<E> daoAccess, Cursor cursor, boolean cacheEntities) {
        this.cursor = cursor;
        this.daoAccess = daoAccess;
        size = cursor.getCount();
        if (cacheEntities) {
            entities = new ArrayList<E>(size);
            for (int i = 0; i < size; i++) {
                entities.add(null);
            }
        } else {
            entities = null;
        }
        if (size == 0) {
            cursor.close();
        }

        lock = new ReentrantLock();
    }

 

 

 

@Override
    public E get(int location) {
        if (entities != null) {
            E entity = entities.get(location);
            if (entity == null) {
                lock.lock();
                try {
                    entity = entities.get(location);
                    if (entity == null) {
                        entity = loadEntity(location);
                        entities.set(location, entity);
                        // Ignore FindBugs: increment of volatile is fine here because we use a lock
                        loadedCount++;
                        if (loadedCount == size) {
                            cursor.close();
                        }
                    }
                } finally {
                    lock.unlock();
                }
            }
            return entity;
        } else {
            lock.lock();
            try {
                return loadEntity(location);
            } finally {
                lock.unlock();
            }
        }
    }

可以看到LazyList中的get方法是線程安全的,裏面使用了雙重檢驗。提高了多線程的安全性和效率。裏面使用了loadEntity()方法來解析遊標數據,查看源碼

可以發現最終也調用了loadCurrent()方法來解析cursor並封裝成一個實體類返回給get方法。最後封裝成一個LazyList返回給用戶。

 

總體思路是這樣:

用戶調用queryBuilder().LazyList()方法,該方法使用SQL語句進行數據庫查詢並返回一個遊標,接着遊標層層傳遞,在LazyList的get方法中進行數據解析,解析cursor的方法是loadEntity(location),但是該方法調用了loadCurrent方法,所以最終解析cursor數據的方法是loadCurrent,然後封裝成實體類返回,裝載進LazyList返回給用戶,完成查詢。

 

 

再看看迭代查詢,queryBuilder().listIterator()

該方法返回一個迭代器,在迭代器中獲取數據使用到next()方法,判斷是否還有數據則使用hasNext()方法,接下來看看源碼

層層進入源碼,最終 發現進入的是LazyList

看看next()方法,非常容易理解,裏面通過一個get()方法來進行數據的獲取

@Override
        public E next() {
            if (index >= size) {
                throw new NoSuchElementException();
            }
            E entity = get(index);
            index++;
            if (index == size && closeWhenDone) {
                close();
            }
            return entity;
        }

點進去發現和我們的Lislazy方法一毛一樣!!  也是最終通過loadCurrent()方法解析遊標數據進行實體類封裝返回給用戶。

其實除了list方法,其餘的三個查詢方法都用到了LazyList類,最終調用的步驟也是一致。不過和list的區別就是惰性加載(按需加載),而且需要開發者手動關閉連接。

總體思路:

用戶調用該方法,然後它new 一個迭代器,通過LazyList類的get方法加載數據封裝實體存進迭代器返回給用戶。

 

 

總結:

list方法和listLazy(),listLazyUncached(),listIterator()區別就是:後者需要關閉連接(因爲有遊標的引用),後者是惰性加載,即使用到該數據纔會加載進內存,而前者則是全部加載進緩存,開銷比後者大。

後者多線程安全。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章