AnnotatedSQL: schema + content provider

в 21:28, , рубрики: android, annotatedsql, sqlite, метки: , ,

Наконец дошли руки описать изменения, которые произошли в библиотеке 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 за грамматику и орфографию прошу не пинать

Автор: hamsterksu

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js