Как я создаю базу данных для своих приложений

Всем привет! Меня зовут Олег и я программист-любитель под Android. Любитель потому что в данный момент я зарабатываю деньги программированием в совсем другом направлении. А это хобби, которому я посвящаю свое свободное время. К сожалению у меня нет знакомых программистов под Android и все свои базовые знания я черпаю либо из книг, либо из интернета. Во всех тех книжках и статьях в интернете, которые я читал, созданию базы данных для приложения отводится крайне мало места и по сути все описание сводится к созданию класса являющегося наследником SQLiteOpenHelper и последующему внедрению SQL кода в Java код. Если не считать, что мы получаем плохо читаемый код (а если в нашем приложении появляется больше 10 таблиц, то вспоминать все эти взаимосвязи между таблицами тот еще ад), то в принципе жить можно конечно, но как-то совершенно не хочется.
Забыл сказать самое главное, можно сказать что это моя проба пера тут. И так поехали.

О вечном вопросе: почему?
Почему в книгах и в статьях, посвященных программированию под Android, не описываются инструменты для проектирования архитектуры базы данных и какие-нибудь паттерны для работы с базами данных на этапе их создания я честно говоря не знаю. Казалось бы добавить всего пару страниц в книгу или написать отдельную статью (как делаю это я сейчас) проще простого — но нет. В этой статье, я кратко пройдусь по инструментам, которые я использую в своей работе и более подробно по коду который отвечает за начальное создание БД, который с моей точки зрения выглядит более читаемым и удобным.

Если в нашем приложении больше 5 таблиц, то уже было бы не плохо использовать какой-нибудь инструмент для визуального проектирования архитектуры БД. Поскольку для меня это хобби, то и использую я абсолютно бесплатный инструмент под названием Oracle SQL Developer Data Modeler (скачать его можно тут).


Данная программа позволяет визуально рисовать таблицы, и строить взаимосвязи с ними. Многие ошибки проектирования архитектуры БД можно избежать при таком подходе проектирования (это я уже вам говорю как профессиональный программист БД). Выглядит это примерно так:



Спроектировав саму архитектуру, приступаем к более нудной части, заключающийся в созданий sql кода для создания таблиц. Для помощи в этом вопросе, я уже использую инструмент под названием SQLiteStudio (его в свою очередь можно скачать тут тут).



Данный инструмент является аналогом таких известных продуктов как SQL Naviagator, Toad etc. Но как следует из названия, заточен он под работу с SQLite. Он позволяет визуально создать БД и получить DDL код создаваемых таблиц. Кстати, он также позволяет создавать представления (View), которые вы тоже при желании можете использовать в своем приложении. Не знаю насколько правильный подход использования представлений в программах для Android, но в одном из своих приложений я использовал их.



Собственно говоря я больше не каких сторонних инструментов не использую, и дальше начинается магия с Android Studio. Как я уже писал выше, если начать внедрять SQL код в Java код, то на выходе мы получим плохочитаемый, а значит и плохо расширяемый код. Поэтому я выношу все SQL инструкции во внешние файлы, которые у меня находятся в директории assets. В Android Studio выглядит это примерно так:


О директориях db и data
Внутри директории assets я создал две директории db_01 и data_01. Цифры в названиях директорий соответствуют номеру версии моей БД с которой я работаю. В директории db у меня хранятся сами SQL скрипты создания таблиц. А в директории data хранятся данные необходимые для начального заполнения таблиц.

Теперь давайте посмотрим на код внутри моего DBHelper который я использую в своих проектах. Сначала переменные класса и конструктор (тут без каких либо неожиданностей):

    private static final String TAG = "RoadMap4.DBHelper";

    String mDb = "db_";
    String mData = "data_";

    Context mContext;
    int mVersion;

    public DBHelper(Context context, String name,  int version) {
        super(context, name, null, version);

        mContext = context;
        mVersion = version;
    }


Теперь метод onCreate и тут становится уже интереснее:

    @Override
    public void onCreate(SQLiteDatabase db) {
        ArrayList<String> tables = getSQLTables();
        for (String table: tables){
            db.execSQL(table);
        }

        ArrayList<HashMap<String, ContentValues>> dataSQL = getSQLDatas();
        for (HashMap<String, ContentValues> hm: dataSQL){
            for (String table: hm.keySet()){
                Log.d(TAG, "insert into " + table + " " + hm.get(table));
                long rowId = db.insert(table, null, hm.get(table));
            }
        }

    }


Логически он разделен на два цикла, в первом цикле я получаю список SQL — инструкций для создания БД и затем выполняю их, во втором цикле я уже заполняю созданные ранее таблицы начальными данными. И так, шаг первый:

    private ArrayList<String> getSQLTables() {
        ArrayList<String> tables = new ArrayList<>();
        ArrayList<String> files = new ArrayList<>();

        AssetManager assetManager = mContext.getAssets();
        String dir = mDb + mVersion;

        try {
            String[] listFiles = assetManager.list(dir);
            for (String file: listFiles){
                files.add(file);
            }

            Collections.sort(files, new QueryFilesComparator());

            BufferedReader bufferedReader;
            String query;
            String line;

            for (String file: files){
                Log.d(TAG, "file db is " + file);
                bufferedReader = new BufferedReader(new InputStreamReader(assetManager.open(dir + "/" + file)));
                query = "";
                while ((line = bufferedReader.readLine()) != null){
                    query = query + line;
                }
                bufferedReader.close();
                tables.add(query);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

        return tables;
    }

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

    private class QueryFilesComparator implements Comparator<String>{

        @Override
        public int compare(String file1, String file2) {
            Integer f2 = Integer.parseInt(file1.substring(0, 2));
            Integer f1 = Integer.parseInt(file2.substring(0, 2));
            return f2.compareTo(f1);
        }
    }

С заполнением таблиц все веселей. Таблицы у меня заполняются не только жестко заданными значениями, но также значениями из ресурсов и UUID ключами (я надеюсь когда-нибудь прийти к сетевой версии своей программы, что бы мои пользователи могли работать с общими данными). Сама структура файлов с начальными данными выглядит так:


Несмотря на то, что файлы у меня имеют расширение sql, внутри не sql код а вот такая штука:

prioritys
pri_id:UUID:UUID
pri_object:string:object_task
pri_name:string:normal
pri_color:color:colorGreen
pri_default:int:1
prioritys
pri_id:UUID:UUID
pri_object:string:object_task
pri_name:string:hold
pri_color:color:colorBlue
pri_default:int:0
prioritys
pri_id:UUID:UUID
pri_object:string:object_task
pri_name:string:important
pri_color:color:colorRed
pri_default:int:0
prioritys
pri_id:UUID:UUID
pri_object:string:object_project
pri_name:string:normal
pri_color:color:colorGreen
pri_default:int:1
prioritys
pri_id:UUID:UUID
pri_object:string:object_project
pri_name:string:hold
pri_color:color:colorBlue
pri_default:int:0
prioritys
pri_id:UUID:UUID
pri_object:string:object_project
pri_name:string:important
pri_color:color:colorRed
pri_default:int:0

Структура файла такая: я выполняю вызов функции split(":") применительно к строчке и если получаю что ее размер равен 1 то значит это название таблицы, куда надо записать данные. Иначе это сами данные. Первое поле это название поля в таблице. Второе поле тип, по которому я определяю что мне надо в это самое поле записать. Если это UUID — это значит мне надо сгенерировать уникальное значение UUID. Если string значит мне надо из ресурсов вытащить строковое значение. Если color, то опять-таки, из ресурсов надо вытащить код цвета. Если int или text, то я просто преобразую данное значение в int или String без каких либо телодвижений. Сам код выглядит вот так:

    private ArrayList<HashMap<String, ContentValues>> getSQLDatas() {
        ArrayList<HashMap<String, ContentValues>> data = new ArrayList<>();
        ArrayList<String> files = new ArrayList<>();

        AssetManager assetManager = mContext.getAssets();
        String dir = mData + mVersion;

        try {
            String[] listFiles = assetManager.list(dir);
            for (String file: listFiles){
                files.add(file);
            }

            Collections.sort(files, new QueryFilesComparator());

            BufferedReader bufferedReader;
            String line;

            int separator = 0;
            ContentValues cv = null;
            String[] fields;

            String nameTable = null;
            String packageName = mContext.getPackageName();
            boolean flag = false;

            HashMap<String, ContentValues> hm;

            for (String file: files){
                Log.d(TAG, "file db is " + file);
                bufferedReader = new BufferedReader(new InputStreamReader(assetManager.open(dir + "/" + file)));
                while ((line = bufferedReader.readLine()) != null){
                    fields = line.trim().split(":");
                    if (fields.length == 1){
                        if (flag == true){
                            hm = new HashMap<>();
                            hm.put(nameTable, cv);
                            data.add(hm);
                        }
                        // наименование таблицы
                        nameTable = line.trim();
                        cv = new ContentValues();
                        continue;
                    } else {
                        if (fields[1].equals("UUID")){
                            cv.put(fields[0], UUID.randomUUID().toString());
                        } else if (fields[1].equals("color") || fields[1].equals("string")){
                            int resId = mContext.getResources().getIdentifier(fields[2], fields[1], packageName);
                            Log.d(TAG, fields[1] + "  " + resId);
                            switch (fields[1]){
                                case "color":
                                    cv.put(fields[0], resId);
                                    break;
                                case "string":
                                    cv.put(fields[0], mContext.getString(resId));
                                    break;
                                default:
                                    break;
                            }
                        } else if (fields[1].equals("text")){
                            cv.put(fields[0], fields[2]);
                        } else if (fields[1].equals("int")){
                            cv.put(fields[0], Integer.parseInt(fields[2]));
                        }
                    }
                    flag = true;

                }
                bufferedReader.close();

            }


        } catch (IOException e) {
            e.printStackTrace();
        }


        return data;
    }

Внезапно
Уже вставив код в данную статью заметил две проблемы. Во первых если в конце файла не будет пустой строчке то я не добавлю ContentValues в свой массив. Во вторых, задумался о проблеме, если мне потребуется вставить данные в таблицу, в которой есть внешний ключ. С ходу нечего не придумал, буду думать на досуге как это лучше реализовать.

Ну и в качестве постскриптума: я повторюсь сказав что я любитель в программировании под Android, что пол-беды. Вторая беда, что в моем окружении нет программистов под Android и собственно говоря не с кем не посоветоваться не устроить мозговой штурм как лучше что-то сделать. Приходится идти методом научного тыка, по пути наступая на грабли. Иногда бывает больно, но в целом круто. Проект над которым я сейчас работаю, уже переживает 4 реинкарнацию. Поэтому просьба не стреляйте в пианиста, я играю как умею. Если напишите как сделать лучше, буду благодарен и рад.

Комментарии 83

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

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

          0

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                              0

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

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

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

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

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

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

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

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

                                0

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

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

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

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

                                    может Oracle SQL Developer Data Modeler?
                                      0
                                      Да именно он
                                      0
                                      Спасибо за статью :) Тоже есть не большой опыт работы разработчиком PL/SQL и так же присматриваюсь в сторону Java и Android.
                                        0
                                        Вместо потенциальных проблем с чтением таблиц и запоминанием связей (которые решаются опытом или тем же Data Modeler), получили вполне очевидные проблемы с нечитабельными файлами, их парсингом и обработкой ошибок. К тому же, насколько понимаю, будет проблематично ссылаться на таблицы/столбцы (как-то надо сохранять их названия?).
                                        Есть стандартный способ работы с бд, который все знают, а с вашим придется разбираться.
                                        Ну, и, как заметили, если очень хочется, лучше использовать orm.
                                          0
                                          Так я работаю стандартно с БД. Я просто навесил на эту работу свой примитивный враппер что бы мне легче было жить.
                                          0
                                          Хорошая утилита для визуализации и администрирования БД MySQL https://www.mysql.com/products/workbench/
                                          Также можно использовать такое ПО как Dia для визуализации связей БД.
                                            0
                                            А причем тут MySql Workbench? Workbench я использую для работы с Mysql.
                                          • НЛО прилетело и опубликовало эту надпись здесь
                                              0
                                              Я по возможности смотрю чужой код. Но тут та проблема, что надо знать куда смотреть. Я сейчас делаю свой проект, и как узнать из какого проетка на гите можно узнать где решаются подобные задачи…
                                              0
                                              Проектировал много баз данных.
                                              Для себя сделал вывод.
                                              На этапе проектирования структуры базы данных действительно очень полезно использовать графические инструменты, какие угодно. Даже тот же MS ACess неплохо может справиться.
                                              Но создавать объекты в БД лучше ручками, написав запросы.
                                              Все это авто создание объектов — после него дольше править.
                                                0
                                                SQLIteStudio достаточно чисто создает код на мой взгляд…
                                                  0
                                                  Но хороший редактор в котором ты видишь список полей и быстро можешь добавить нужные с ручным выбором типов еще лучше :)
                                                  0
                                                  Мои 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);
                                                      }
                                                  }
                                                  

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


                                                            Чаще всего потому, что вычислительная мощность мобильных девайсов сильно уступает мощности компьютеров, потому, смысла в проектировании сложной БД особо не возникает, посколько это может оказать негативный эффект на перформанс.
                                                              0
                                                              Беспорно, вычислительная мощность уступает. Но тут разговор не о размере БД а о ее структуре. К примеру то приложение которое я сейчас делаю имеет 10 таблиц. И работать со SQL кодом в Java коде при создании таблиц это не айс…
                                                                0
                                                                На данный момент работаю над приложением, в котором больше 20 таблиц, некоторые из них имеют отношения many-to-many, между некоторыми — несколько many-to-many. Для того, чтобы все нормально выглядело, достаточно было написать свой QueryBuilder на 500-600 строк, который делает sql-запросы выглядеть нормально. Для того, чтобы в Вашем приложении не было каши — следует не использовать сторонние тулзы, а писать структурированный код, который позволит читающему его нормально разобраться, а не сломать глаза, мозг и прострелить себе обе ноги.
                                                                  0
                                                                  А можно где то посмотреть на ваш QueryBuilder?
                                                                    0
                                                                    QB в 500-600 строк… это фантастика
                                                                    … хотя нужно уточнить длину строк и скрин
                                                                      0
                                                                      Где-то здесь, посмотрите в data-layer
                                                                        0
                                                                        Спасибо! До дома доберусь обязательно гляну!
                                                                      0
                                                                      PS
                                                                      Пока ехал домой с работы, осознал какой не тривиальный должен быть интерфейс программы в которой в БД лежит 20 таблиц, даже без отношений many_to_many…
                                                                        0
                                                                        не поверите, интерфейс тривиальный, просто я использую API моей «любимой» компании Microsoft
                                                                          0
                                                                          Не поверите, как я ломаю себе голову сейчас, над своей программой как лучше и удобнее сделать :)

                                                                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                Самое читаемое