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