AnnotatedSQL: schema + content provider

    Наконец дошли руки описать изменения, которые произошли в библиотеке AnnotatedSQL

    Анонс:
    1. Изменения в плагине
    2. Изменения в аннотациях схемы
    3. Что такое content provider в моем понимании
    4. Генерируем content provider по схеме

    1. Plugin


    Plugin update site
    Теперь это и полноценный плагин для Eclipse со своим update site

    Build path->Libraries->Add Library
    Аннотации теперь не надо класть в проект, плагин несет их с собой. Вам необходимо просто добавить AnnotatedSQL Libaray в проект в свойствах проекта. И у вас всегда API будет соответствующее плагину.

    Шаблоны для создания схемы, таблицы, view
    В eclipse есть такая замечательная штука как шаблоны кода. Плагин добавляет свои шаблоны для создания схемы, таблицы, view. и эти шаблоны попадают code completion, тот самый которые показывается по ctrl+space

    Например пишем Tabl нажимаем заветные кнопки и видим Table — AnnotatedSQL, выбираем и у вас шаблон для таблицы.
    Не забываем: что бы перепрыгнуть в следующее поле редактирование шаблона нажимаем клавишу «Tab», а «Enter» завершит редактирование шаблона.

    2. Обновления в схеме


    Изменилась пожалуй только одна аннотация
    Join
    Переименовал атрибуты:
    srcTable -> joinTable
    srcColumn -> joinColumn
    destTable -> onTableAlias
    destColumn -> onColumn
    надеюсь так будет понятнее. А главное надо помнить, что onTableAlias — не имя таблицы, а алиас на нее.

    А добавилось сразу несколько:

    RawJoin
    Если ON условие отличается от column1 = column2 тогда вам сюда. Вы указываете таблицу которую джойните и пишите условие руками.
    Атрибуты:
    joinTable — имя таблицы на которую джойнитесь
    onCondition — ON условие

    IgnoreColumns
    Когда нам не надо выбирать ни единого столбца из таблицы — помечаем ее такой аннотацией. Применима для From, Join, RawJoin

    RawQuery
    Запрос с параметрами в любом месте. Эта штука придумана для контент провайдера и обусловлена ограниченностью view в sql. Вьюшка это select + join, а where уже потом применяется.
    Но часто надо выполнить запрос, где уже в ON условии джойна надо использовать параметры. вот для этого оно и придумано, аля sql function. Там где надо юзать параметры естественно ставим ?
    Полученный sql запрос никуда не сторится просто лежит константой и его можно дернуть по URI(расскажу дальше)

    RawQuery можно сформировать как и SimpleView используя From, Join and RawJoin,
    а можно использую «сырой sql» через аннотацию SqlQuery

    @RawQuery(ChatListQuery.QUERY_NAME)
    public static interface ChatListQuery{
    
        String QUERY_NAME = "chatListQuery";
    
        @SqlQuery
        String CHAT_LIST_QUERY = " some sql here"
    }
    


    3. Что такое content provider в моем понимании


    Исходя из опыта разработки под Android, контент провайдер должен обладать дополнительными свойствами. И дополнительные параметры можно пропихнуть через Uri, там полно «свободного места» — fragment, parametrs все как в url. Итак, вот что обычно надо:

    no-notify
    Когда вы вставляете пачку данных в провайдер нет необходимости нотифаить UI после вставки каждой строки, а лучше занотифаить после вставки.

    alternative notify uri
    Часто есть необходимость при нотифаи одно uri занотифаить зависящие uri. Например, у вас есть UserView который вы юзаете для заполнения списка, а вставка то идет в таблицу User и вам надо нотифаить uri из view вот это и есть alternative uri.

    limit
    тривиальный лимит при запросе

    4. Генерация Content Provider по схеме


    После того, как я написал автогенерацию схемы по аннотациям, я задумался: у меня ведь есть все, что бы захватить мир генерировать контент провайдер и облегчить себе жизнь. Сказано — сделано. Пара аннотаций и у вас полноценный провайдер + немного плюшек. Ну понеслось!

    Первое — надо сказать, что по схеме генерим контент провайдер. В этом нам поможет аннотация Provider

    Provider

    name — имя класса для контент провайдреа
    authority — нам надо знать авторити контет провайдера.
    schemaClass — имя класса схемы. Используется когда генерится open helper.
    openHelperClass — можете отказаться от генерации внутреннего open helper и заставить юзать ваш.

    @Schema(className="FManagerSchema", dbName="fmanager.db", dbVersion=6)
    @Provider(authority="com.gdubina.fmanager.store.FManagerProvider", schemaClass="FManagerSchema", name="FManagerProvider", openHelperClass="CustomOpenHelper")
    public interface FManagerStore {
    


    Итак, мы сказали что хотим контент провайдер. Что дальше?
    Надо сказать по какому URI будут доступны наши entity — юзаем одноименную аннотацию URI

    URI
    вешаем на константу в table, view или query. Мне нравится нечто типа URI_CONTENT.
    Все атрибуты тут опциональны, но они есть.
    type — тип uri. Как мы знаем в контент провайдере принято определять, что возвращается по URI много записей(dir) или одна(item). Помните такой метод getType? тут тоже самое.
    onlyQuery — доступ только для выполнения query, юзабельно для view and query. При попытке выполнить insert/delete/update — приведет к exception
    column — по умолчанию "_id". Собственно когда вы юзаете URI типа parh/# контент провайдер делает выборку аля _id = uri.getLastPathSegment(). так вот вы можете переопределить по какому полю делать select
    altNotify — список uri которые надо пронатифаить еще надо пронатифаить

    Пару примеров:
    вот так мы заставили контент провайдер работать с таблицей чемпионатов. Все доступно — выборка, вставка, удаление, обновление

    @Table(ChempTable.TABLE_NAME)
    public static interface ChempTable{
    	
    	@URI
    	String CONTENT_PATH = "chemps";
    	
    	String TABLE_NAME = "chemp_table";
    
    	@PrimaryKey
    	@Column(type = Type.INTEGER)
    	String ID = "_id";
    	
    	@Column(type = Type.TEXT)
    	String TITLE = "title";
    	
    	@Column(type = Type.TEXT)
    	String CHEMP_KEY = "CHEMP_KEY";
    }
    


    А вот кусок таблицы сообщений. Как мы видим, здесь у меня два URI
    1. для доступа к самой таблице URI_CONTENT
    2. для выборки мессаджей определенного юзера URI_MESSAGES_BY_USER, как раз здесь column=Message.FROM_ID

    @Table(Message.TABLE_NAME)
    @PrimaryKey(columns = {Message.USER_ID, Message.ID})
    public static interface Message{
    	
    	@URI(altNotify={MessageChatView.URI_CONTENT, ChatListQuery.URI_CONTENT, URI_RECALC_MESSAGE_PATH})
    	String URI_CONTENT = "message";
    	
    	@URI(type=URI.Type.ITEM, altNotify={Message.URI_CONTENT, MessageChatView.URI_CONTENT, ChatListQuery.URI_CONTENT, URI_RECALC_MESSAGE_PATH}, column=Message.FROM_ID)
    	String URI_MESSAGES_BY_USER = "message_by_user";
    }
    


    Для создания простого конетп ровайдера этого достаточно. Всего две аннотации!

    Но это еще не все, что делать если на действия с таблицей надо что-то сделать? например добавили юзера — пошли закачали аватарку. в большом sql есть такая штука как триггеры — делают нечто подобное. Так вот и я обозвал свою аннотацию так само Trigger. В провайдере будет создан protected метод, который вы должны переопределить в наследники автогенеренного класса

    Trigger
    вешается на uri, но вызов метода происходит при совпадении имени таблицы
    name — имя тригера, используется для генерации имени метода
    when — когда вызывать метод до действия с таблицей или после.
    type — на какие действия реагировать — insert/delete/udapte или на все сразу

    Имя метода генерится по такому шаблону
    	on<Name><When>Inserted(ContentValues values){}
    
    	on<Name><When>Deleted(Uri uri, String selection, String[] selectionArgs){}
    
    	on<Name><When>Updated(Uri uri, ContentValues values, String selection, String[] selectionArg){}
    


    Если надо повесить несколько триггеров — юзаем аннотацию Triggers и набиваем ее триггерами.

    Пример:
    @Table(User.TABLE_NAME)
    public static interface User{
    
    	@Trigger(type=Trigger.Type.INSERT, name="user", when=When.BEFORE)
    	@URI(altNotify={SuggestionView.URI_CONTENT, MessageChatView.URI_CONTENT, ChatListQuery.URI_CONTENT})
    	String URI_CONTENT = "user";
    


    и вот так выглядит провайдер в этом случае

    public class AppProvider extends AppAutoProvider {
    	@Override
    	protected void onUserBeforeInserted(ContentValues values) {
    		.................
    	}
    


    Как правильно использовать notify?

    Если вы хотите пронотифаить uri и все альтернативные, необходимо использовать статический метод notifyUri(ContentResolver cr, Uri uri)

    Пачка статически метод из провайдера

    getContentUri(String path) — по path получаем URI
    getContentUriGroupBy(String path, String groupBy) — по path получаем URI с поддержкой группировки
    getContentUri(String path, long id) — получаем URI path/#
    getContentUri(String path, String id) — получаем URI path/#

    getContentWithLimitUri(String path, int limit) — path + limit

    getNoNotifyContentUri(String path) — по path получаем URI без нотифая
    getNoNotifyContentUri(String path, long id) — получаем URI path/# без нотифая

    P.S. библиотека прошла «боевые» испытания и уже использовалась в продакшен проектах
    P.S.S за грамматику и орфографию прошу не пинать
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 0

    Only users with full accounts can post comments. Log in, please.