Pull to refresh

Comments 83

На мой взгляд, и я думаю, многие согласятся, можно достичь результата быстрее и с меньшим количеством потенциальных ошибок, если использовать какой-нибудь ORM-фреймворк.
Я читал несколько статей про ORM-фрейморвки, и несмотря на ряд проблем, описываемых там, думаю когда нибудь перейти на них. А может и Google к тому времени включит какой нибудь фреймворк в свой официальный SDK.

В последнем моём проекте попробовал GreenDAO и меня ждало разочарование. Переписал всё вручную в итоге. Возможно, есть и более удачные фреймворки

А что не устроило? Потому что как раз думал мигрировать с ormlite на greendao на одном legacy-проекте.

Пользуясь случаем, а что не устроило в ormlite?
Пользовался ormlite в связке с h2.db. Все красиво написано, удобно пользоваться, написанием sql запросов текстом можно не морочиться. Вплоть до момента, когда надо обновлять таблицу. Нет ни версии БД, ни каких-то продуманных инструментов из коробки для обновления ее структуры. Пришлось писать дополнительную табличку с версией БД, и свои методы по обновлению, которые исполняют сырые запросы.

Самое основное — это скорость. В Ormlite вовсю используется рефлексия, что на андроиде довольно дорого. Ну и не развивается уже давно. Это расстраивает.

Убогая кодогенерация и плохая поддержка генерации ключей в таблице.
По поводу кодогенерации: если у вас есть какой-нибудь объект, который вы хотите записать в базу, то после добавления аннотаций GreenDAO модифицирует класс добавляя ненужные геттеры/сеттеры. Об иммутабельности класса можно забыть.
Далее, поле id должно быть обязательно объявлено @Nullable для того, чтобы GreenDAO смог обновить его после вставки в базу, т.е. о примитивных типах можно забыть.

Я уже мигрировал. И что могу сказать — да, greendao не идеален. Модификации прямо моих классов, конечно, раздражают, как и непримитивный Long для ID. Но он дал приложению прирост производительности основных запросов в 2(sic!) раза. Так что с парой ограничений и косяков можно смириться. А насчёт иммутабельности… Тема, конечно, забавная, но для модели с 40 полями если на каждую модификацию новый объект делать, GC обидится. Иммутабельность для тех, кто пишет очередную читалку новостей и пьет смузи :) Ну, или предпочитает красоту и "правильность" кода пользовательскому опыту.

Мне что-то подсказывает, что то, что GreenDAO дал вам прирост в 2 раза — не то, чтобы его преимущество. Скорее просто где-то вы не использовали prepared statements, где-то — не оптимизировали like запрос как вхождение в range. К слову, примерно 20% прирост производительности на голом SQLite дает кэширвание индексов курсора. Так что тут вопрос скорее в том, что GreenDAO за вас сделал базовые оптимизации (но это не точно).
О, идея!
Испльзовать ORM-фреймворк — отличная методология создания свой БД.
Сначала пишешь при коннекте «DBI:itsmysql:database=test;host=localhost» — устраняешь все ошибки, затем другие и другие ошибки.
Как минимум есть великолепный realm который выполняет все функции БД, со всеми связями и праймари ключами…
И создает инстанс себя в каждом потоке, угу.
>Почему в книгах и в статьях не описываются инструменты для проектирования архитектуры базы данных и какие-нибудь паттерны для работы с базами данных на этапе их создания я честно говоря не знаю.

Между «я не знаю» и «этого нет» на самом деле очень большая разница. Этой теме уже лет 20, как минимум, и книг написано полно. Вы где-то не там ищете видимо.
Наверное мне стоило уточнить, что я говорю про книги по программированию под Android
А какая разница? Вы же сами говорите про проектирование архитектуры базы и паттерны?

Я знаю примерно один существенный фактор, который реально влияет именно на проектирование, когда мы говорим про Андроид — что у вас обычно очень мало ресурсов. И в общем-то довольно узкий выбор самих СУБД.
Как бы правильно выразить мою мысль… Вот откройте любую книгу или статью посвященную работе с БД под Android. Во всех книгах и статьях разбираются простые примеры (что логично) с внедрением SQL кода в код написанный на Java. С моей точки зрения, это как минимум дискуссионый вопрос.
Сам подход создания таким образом БД очень не удобный. Причем ладно я, Я зарабатываю на хлеб программированием на PL/SQL и я знаю инструменты для работы с БД и как они работают. Я могу найти способы как использовать уже имеющиеся знания для помощи при написания приложения (правильный или не правильный у меня подход это другой разговор). Собственно говоря, этой теме как раз и посвященна моя статья.
А вот новичку в программировании вынос мозга при проектировании своего приложения (при внедрения sql кода в java код), которое использует БД, гарантирован (это мое имхо).

То есть эта статья, не что иное как выработка подхода проектирования БД и внедрения его в приложение, которое будет работать на устройстве.
Нет, я вполне понял вашу мысль. Просто как бы… считать, что человек станет программистом, прочитав одну книгу о разработке под Андроид — это несколько наивно. Не станет. Если ему нужно будет разрабатывать базу данных — нужно будет почитать книгу по разработке баз данных.

И не просто прочитать, а попрактиковаться, а иначе будет как у меня в одном проекте, когда новые «работники», придя в проект, начали заявлять «а у вас тут база данных ненормализованная».

Тему «куда деть SQL при разработке на Java» тоже можно долго обсуждать, она тоже необъятная, и от нее можно будет прийти как к ORM, так и к инструментам типа JOOQ, QueryDSL и прочим, но тоже не сразу, и предварительно понимая некоторые другие базовые вещи — например, как сопровождать приложение, и какого рода изменения в SQL-запросах в нем возможны. И это все равно тема далеко не для одной книги.

Да согласен, но есть одно но. Книги по программированию под Android в отличие от книг БД по сути проводят читателя по всей технологической цепочке создания ПО: от простого Hello world и до публикации в маркете. Моя статья не о том как правильно программировать, ниже мне вполне справедливо накидали замечаний по коду, моя статья скорей о том с помощью каких инструментов и как следствие подходов к написанию самого кода можно себе немного облегчить жизнь.

Так никтож не против ) Я лишь комментировал конкретную вещь о том, почему про это не пишут в книгах. Потому что это заслуживает отдельной книги, а время у авторов не бесконечное.

Просто тема внедрения SQL в java (а на самом деле и не в java вовсе — в большинстве других языков есть все теже самые проблемы, решаемые примерно также, плюс-минус особенности языка) — она не имеет одного наилучшего решения.

Что до вашего конкретного примера — то я бы посмотрел наверное на liquibase. Не буквально, и не прямо в том виде, в каком его обычно используют в проектах (не на Андроид), а скорее на заложенные в нем идеи, в том числе — возможность написания java-миграций. Тем более он open source, можно и в код заглянуть.
Спасибо! Обязательно посмотрю :). Что то мне подсказывает, что по тем советам и критике что мне пишут в комментариях скоро появится очередная реинкарнация моего проекта :)
Я с этих уроков начинал свое знакомство с Android :) Но опять таки основная проблема что там инжектят SQL код на моменте создания БД в Java код…
Как вы боритесь с миграцими? особенно если апдейт идет через несколько версий? (с версии 3 до версии 6 например) К сожалению в более менее среднем приложении этот вопрос всплывает не редко и это один из самых больных вопросов.
1) SQLiteStudio при изменении структуры таблицы сам генерит скрипт для изменения таблицы
"

2) Что касается миграции с версии 3 на версию 6 — это хороший вопрос… пока не готов на него ответить. Спасибо что подняли его, буду думать :)
На скрине добавили одно поле и с генерировался такой монстр вместо
ALTER TABLE xxx add column aaaaa BLOB;

Представь, что размер таблицы 5гб а места на диске всего свободно 3…
а если есть индексы/триггера — студия их восстанавливает?

по-моему скрипты миграции нужно писать руками и обрабатывать все исключительные ситуации
иначе есть риск, что ваше приложение у конечного пользователя сломается.

для этого в sqlite придумана PRAGMA user_version
по нему можно ветвится в коде миграции и делать нужные модификации базы в онлайне.
ну во первых мне сложно представить приложение с БД размером 5 гб на телефоне :)
ну и во вторых если вам надо добавить колонку в конец таблицы то да, Ваш вариант правильней. Если же Вам надо добавить колонку внутри других колонок то такой вариант уже не прокатит.
А в чем польза определенного порядка колонок? Вы же с ними по названию работаете, а не по порядковому номеру.
Работаю да — по названию. А определенный порядок читаемости колонок удобен при просмотре как структуры таблицы так и его содержимого в какой нибудь ide среде. Если у Вас колонок больше 7 то близкие по сущности колонки лучше группировать рядом друг с другом.
По индексу на колонку можно сослаться в конструкции order by 2
менять позиции колонок — бомба замедленного действия.
могут сломаться клиенты где «зашито» что-то вроде select * from…
из полезного я помню только один случай реорганизации:
в oracle если колонки не заполнены(null) и находятся в конце, то они не занимают место в блоке.
т.е. разместив редко заполняемые поля в конец — можно сэкономить на диске.

В order by указывается индекс колонки из выборки, порядок колонок из базы тут имеет значение только при выборке всех колонок через звездочку.
менять позиции колонок — бомба замедленного действия.
работать с индексами а не с именами — бомба замедленного действия.
Работать с индексами колонок это потенциальная проблема. Указывать конструкцию order by индекс мало того что это потенциальная проблема, там еще и ухудшение читаемости кода когда у вас в выборке скажем 10 колонок, и вы делаете скажем order by 7

По идее, если надо мигрировать через несколько версий, поочередно будут происходить миграции 3->4->5->6

Да, это очевидно. Я для своих проектов использую подобное. Но с недавних пор начал задумываться над подобным стилем, что привел автор: хелпер + скрипты в ассетах. Вот и стало интересно как он это делает. В моем подходе я использую массив миграций, соответственно если если первая версия базы имеет номер 1, но ее можно привести к индексу массива: версия — 1, соответственно для миграции с n на m надо взять срез n...m-1. В подходе автора не совсем очевидно как это взять. А так было бы полезно адаптировать под свои нужды.

В подходе автора это не то что не очевидно, а вообще не реализовано… Но теперь благодаря идее Bringoff автор понял куда надо двигаться, за что он крайне благодарен и Вам и Bringoff. Правильно поставленный вопрос много-го стоит
Навскидку, несколько замечаний по коду:

  • BufferedReader желательно закрывать в секции finally или использовать try-with-resource
  • Контенкация строк в цикле — зло. В Java строки неизменяемые и это плодит новые объекты при каждой итерации. Используйте StringBuilder или StringBuffer.
  • printStackTrace() в секции catch — имеет смысл только при отладке. Если исключение возникнет при работе готового приложения — есть вероятность, что вы об этом и не узнаете. Кидайте в лог или ловите/обработывайте все сразу выше.
  • … файлы имеют расширение sql, внутри не sql код ... — подумайте, какого будет человеку который будет поддерживать ваш код. В sql-файлах ожидаются скрипты, не нужно пихать в него сырые табличные данные. Имхо (могу ошибаться), лучше уж — csv или какой-нибудь dat-файл.
  • Использование «вложенных» коллекций в довольно простых случаях (ArrayList<HashMap>). Java — это объектно-ориентированный язык, лучше создать информативный объект враппер.
  • Обилие magic numbers — сразу бросаются в глаза индексы массивов, но и строковых литералов хватает. Выносите в константы, читаемость только улучшиться.
  • Ну и классическое — раздутые методы с кучей вложенных операторов, if (flag == true), неочевидные названия переменных и прочие ошибки начинающих.
Большое спасибо за Ваши замечания! Как мне не хватает вот таких замечаний :(
Хм. Ну, такого в вашем кода завались… его можно долго комментировать, почти что построчно:

ArrayList<HashMap<String, ContentValues>> data = new ArrayList<>();

Вообще принято писать как-то так:

List<Map<String, ContentValues>> data = new ArrayList<>();

А начать можно с того, что ваш кусок кода можно просто и логично разбить минимум на два метода — один из которых будет работать с одним файлом.

Спасибо, посмотрю обязательно.

Читал по диагонали… но имхо, для любителей SQL есть square/sqldelight, который при всех его недостатках предоставляет больше удобства чем велосипед в статье.

Я так понимаю это какой то ORM… Я обязательно посмотрю его, но опять таки, ORM это штука для уже более мнее продвинутых программистов. Моя же статья больше предназначена для начинающих, как им облегчить жизнь…
Это не ОРМ, это ОМ с тонкой прослойкой для опен хелпера для удобного использования скл. Он делает ровно то что вы хотели сделать своим кодом, только гораздо удобней, без вот этой всей порнографии, которая в посте наблюдается. Создание таблиц это конечно муторно, но это разовая операция. А вот запросы и с данными работать надо гораздо чаще.

>Моя же статья больше предназначена для начинающих, как им облегчить жизнь…

Я пока увидел только как усложнить жизнь начинающему разработчику по сравнению с обычным опен хелпером при сомнительных преимуществах. По моему опыту с начинающими программистами, любой ОРМ без скл им облегчает жизнь гораздо больше ибо нынче начинающий разработчик под андроид вообще не знает скл)) ну а для ценителей есть упомянутая мной либа, которая на этом скл и базируется.
Вот я начинающий разработчик, и лично мне с моими знаниями такой подход оказался легче. Возможно сам подход не правильный, но к сожалению в моем окружении нет людей которые могли бы сказать как правильно подходить. Ваш подход я обязательно изучу.
ORM это штука для начинающих, имхо, т.к. написать плохой и опасный код при незнании SQL гораздо легче, чем написать говнокод с использованием ORM
Oracle SQL Developer Modeler

может Oracle SQL Developer Data Modeler?
Спасибо за статью :) Тоже есть не большой опыт работы разработчиком PL/SQL и так же присматриваюсь в сторону Java и Android.
Вместо потенциальных проблем с чтением таблиц и запоминанием связей (которые решаются опытом или тем же Data Modeler), получили вполне очевидные проблемы с нечитабельными файлами, их парсингом и обработкой ошибок. К тому же, насколько понимаю, будет проблематично ссылаться на таблицы/столбцы (как-то надо сохранять их названия?).
Есть стандартный способ работы с бд, который все знают, а с вашим придется разбираться.
Ну, и, как заметили, если очень хочется, лучше использовать orm.
Так я работаю стандартно с БД. Я просто навесил на эту работу свой примитивный враппер что бы мне легче было жить.
Хорошая утилита для визуализации и администрирования БД MySQL https://www.mysql.com/products/workbench/
Также можно использовать такое ПО как Dia для визуализации связей БД.
А причем тут MySql Workbench? Workbench я использую для работы с Mysql.
UFO landed and left these words here
Я по возможности смотрю чужой код. Но тут та проблема, что надо знать куда смотреть. Я сейчас делаю свой проект, и как узнать из какого проетка на гите можно узнать где решаются подобные задачи…
Проектировал много баз данных.
Для себя сделал вывод.
На этапе проектирования структуры базы данных действительно очень полезно использовать графические инструменты, какие угодно. Даже тот же MS ACess неплохо может справиться.
Но создавать объекты в БД лучше ручками, написав запросы.
Все это авто создание объектов — после него дольше править.
SQLIteStudio достаточно чисто создает код на мой взгляд…
Но хороший редактор в котором ты видишь список полей и быстро можешь добавить нужные с ручным выбором типов еще лучше :)
Мои 5 копеек.
Если БД простая то достаточно SQLiteOpenHelper'а, можно спроектировать всё в голове и написать класс — слой доступа к ней что бы уж где-то дальше не встречались sql-запросы.
А если табличек становиться уже много и так просто не разобраться то можно спроектировать, протестировать, наполнить данными базу где-нибудь во внешнем инструменте. Я отыскал для себя SQLite Expert Professional, в ней можно как и мышкой / мастером создавать таблички / запросы так и поработать с ними потом чистыми sql-запросами. Но эта софтина платная.

image

А затем что бы не переносить скрипты создания и заполнения БД можно положить наш готовый файл БД db2.sqlite в assets/databases и подцепить его SQLiteAssetHelper'ом
public class DatabaseOpenHelper extends SQLiteAssetHelper{

    private static final String DATABASE_NAME = "DB2.sqlite";
    private static final int DATABASE_VERSION = 1;

    public DatabaseOpenHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
}

А как Вы решаете вопрос с внесением изменения в структуру БД при использовании SQLiteAssetHelper в последующих версиях вашей программы?
Если контент базы может изменяться в процессе работы приложения, то, очевидно, при начальной инициализации нужно копировать всю базу во внутреннюю память и открывать ее уже с помощью обычного хэлпера.
Если же структура и содержимое БД меняются только при обновлении версии приложения, то вообще никаких проблем нет.
Ок, то есть если нам надо писать данные в базу данных то мы должны копировать файл во внутреннею структуру.
Про изменение не понял. У Вас обновилась программа. Необходимо изменить структуру таблиц но при этом не потерять там данные.
Так данные могут меняться в приложении или нет? :)
Если нет, то и с обновлением нет проблем — БД лежит в assets-ах и обновляется вместе приложением. Миграцию поддерживать нет необходимости.
Тогда по первому варианту — при первом запуске копировать БД из assets-ов во внутреннюю память. Далее — проверять текущую версию БД и если она меньше, чем нужно, то последовательно применять скрипты миграции.
Или разрабатывать скрипты миграции в том же внешнем редакторе, переносить их в обновление и применять на БД, не перемещая её из assets.
Что-то я не понимаю… Что значит «применять на БД, не перемещая её из assets»? Как можно изменить БД, которая находится в assets? Она же Read Only там.
Как я понял, Arvalon имеет ввиду что если данные в БД менять не надо, то его можно держать там. Если надо менять — то тогда необходимо скопировать файл с БД уже внутрь программы…
Да, не так написал. Она сама копируется во внутреннюю память при первом обращении к ней. Это лежит на плечах SQLiteAssetHelper'а.
по-моему необходимо заняться рефакторингом кода. слишком большая вложенность, куча циклов, ArrayList в качестве возвращаемого значения. А если вы захотите изменить тип коллекции?
Вы знаете, это уже 4 версия кода в моей программе. И я все время его рефакторю. До каких то вещей я дохожу сам, до каких то вещей нет…
А не смотрели в сторону Flyway (https://flywaydb.org/documentation/api/)? Я пару раз использовал в своих андроидовских хеллоуворлдах, решает сразу все проблемы инициализации и миграции БД.
Не открывается сайт чего то, хотя гугле подтверждает что вы указали правильный путь.
Посмотрю обязательно потом. Я пока в самом начале пути и поэтому иду стандартными средствами
Почему в книгах и в статьях, посвященных программированию под Android, не описываются инструменты для проектирования архитектуры базы данных и какие-нибудь паттерны для работы с базами данных на этапе их создания я честно говоря не знаю


Чаще всего потому, что вычислительная мощность мобильных девайсов сильно уступает мощности компьютеров, потому, смысла в проектировании сложной БД особо не возникает, посколько это может оказать негативный эффект на перформанс.
Беспорно, вычислительная мощность уступает. Но тут разговор не о размере БД а о ее структуре. К примеру то приложение которое я сейчас делаю имеет 10 таблиц. И работать со SQL кодом в Java коде при создании таблиц это не айс…
На данный момент работаю над приложением, в котором больше 20 таблиц, некоторые из них имеют отношения many-to-many, между некоторыми — несколько many-to-many. Для того, чтобы все нормально выглядело, достаточно было написать свой QueryBuilder на 500-600 строк, который делает sql-запросы выглядеть нормально. Для того, чтобы в Вашем приложении не было каши — следует не использовать сторонние тулзы, а писать структурированный код, который позволит читающему его нормально разобраться, а не сломать глаза, мозг и прострелить себе обе ноги.
А можно где то посмотреть на ваш QueryBuilder?
QB в 500-600 строк… это фантастика
… хотя нужно уточнить длину строк и скрин
Спасибо! До дома доберусь обязательно гляну!
PS
Пока ехал домой с работы, осознал какой не тривиальный должен быть интерфейс программы в которой в БД лежит 20 таблиц, даже без отношений many_to_many…
не поверите, интерфейс тривиальный, просто я использую API моей «любимой» компании Microsoft
Не поверите, как я ломаю себе голову сейчас, над своей программой как лучше и удобнее сделать :)
Only those users with full accounts are able to leave comments. Log in, please.