Всем привет! Меня зовут Олег и я программист-любитель под 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 реинкарнацию. Поэтому просьба не стреляйте в пианиста, я играю как умею. Если напишите как сделать лучше, буду благодарен и рад.
Поделиться с друзьями
-->

Комментарии (71)


  1. alix_ginger
    02.04.2017 11:47
    +4

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


    1. Plesser
      02.04.2017 11:51

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


    1. serso
      02.04.2017 21:13
      +1

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


      1. Bringoff
        02.04.2017 22:02

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


        1. handbrake
          03.04.2017 07:41

          Пользуясь случаем, а что не устроило в ormlite?


          1. llerik
            03.04.2017 10:40

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


          1. Bringoff
            03.04.2017 15:18

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


    1. dmrt
      03.04.2017 11:27
      +1

      О, идея!
      Испльзовать ORM-фреймворк — отличная методология создания свой БД.
      Сначала пишешь при коннекте «DBI:itsmysql:database=test;host=localhost» — устраняешь все ошибки, затем другие и другие ошибки.


    1. dajver
      03.04.2017 12:29

      Как минимум есть великолепный realm который выполняет все функции БД, со всеми связями и праймари ключами…


  1. sshikov
    02.04.2017 13:45

    >Почему в книгах и в статьях не описываются инструменты для проектирования архитектуры базы данных и какие-нибудь паттерны для работы с базами данных на этапе их создания я честно говоря не знаю.

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


    1. Plesser
      02.04.2017 13:50
      +1

      Наверное мне стоило уточнить, что я говорю про книги по программированию под Android


      1. sshikov
        02.04.2017 14:02

        А какая разница? Вы же сами говорите про проектирование архитектуры базы и паттерны?

        Я знаю примерно один существенный фактор, который реально влияет именно на проектирование, когда мы говорим про Андроид — что у вас обычно очень мало ресурсов. И в общем-то довольно узкий выбор самих СУБД.


        1. Plesser
          02.04.2017 14:12
          +3

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

          То есть эта статья, не что иное как выработка подхода проектирования БД и внедрения его в приложение, которое будет работать на устройстве.


          1. sshikov
            02.04.2017 20:36
            +3

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

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

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


            1. Plesser
              02.04.2017 21:26

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


              1. sshikov
                02.04.2017 21:49

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

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

                Что до вашего конкретного примера — то я бы посмотрел наверное на liquibase. Не буквально, и не прямо в том виде, в каком его обычно используют в проектах (не на Андроид), а скорее на заложенные в нем идеи, в том числе — возможность написания java-миграций. Тем более он open source, можно и в код заглянуть.


                1. Plesser
                  02.04.2017 21:54

                  Спасибо! Обязательно посмотрю :). Что то мне подсказывает, что по тем советам и критике что мне пишут в комментариях скоро появится очередная реинкарнация моего проекта :)


              1. Vladal
                03.04.2017 10:40

                А посмотри урок Работа с базами данных SQLite в Android
                Может, что-то прояснится или наоборот, уже знакомо.


                1. Plesser
                  03.04.2017 10:41

                  Я с этих уроков начинал свое знакомство с Android :) Но опять таки основная проблема что там инжектят SQL код на моменте создания БД в Java код…


  1. anyd3v
    02.04.2017 15:11

    Как вы боритесь с миграцими? особенно если апдейт идет через несколько версий? (с версии 3 до версии 6 например) К сожалению в более менее среднем приложении этот вопрос всплывает не редко и это один из самых больных вопросов.


    1. Plesser
      02.04.2017 15:15

      1) SQLiteStudio при изменении структуры таблицы сам генерит скрипт для изменения таблицы
      "

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


      1. pavel_pimenov
        03.04.2017 11:52

        На скрине добавили одно поле и с генерировался такой монстр вместо
        ALTER TABLE xxx add column aaaaa BLOB;

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

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

        для этого в sqlite придумана PRAGMA user_version
        по нему можно ветвится в коде миграции и делать нужные модификации базы в онлайне.


        1. Plesser
          03.04.2017 12:05

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


          1. KamiSempai
            04.04.2017 16:24

            А в чем польза определенного порядка колонок? Вы же с ними по названию работаете, а не по порядковому номеру.


            1. Plesser
              04.04.2017 16:44

              Работаю да — по названию. А определенный порядок читаемости колонок удобен при просмотре как структуры таблицы так и его содержимого в какой нибудь ide среде. Если у Вас колонок больше 7 то близкие по сущности колонки лучше группировать рядом друг с другом.


            1. pavel_pimenov
              04.04.2017 16:46

              По индексу на колонку можно сослаться в конструкции order by 2
              менять позиции колонок — бомба замедленного действия.
              могут сломаться клиенты где «зашито» что-то вроде select * from…
              из полезного я помню только один случай реорганизации:
              в oracle если колонки не заполнены(null) и находятся в конце, то они не занимают место в блоке.
              т.е. разместив редко заполняемые поля в конец — можно сэкономить на диске.


              1. KamiSempai
                04.04.2017 17:01

                В order by указывается индекс колонки из выборки, порядок колонок из базы тут имеет значение только при выборке всех колонок через звездочку.

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


              1. Plesser
                04.04.2017 17:18

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


    1. Bringoff
      02.04.2017 18:50

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


      1. anyd3v
        02.04.2017 18:56

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


        1. Plesser
          02.04.2017 19:04

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


      1. Plesser
        02.04.2017 18:56

        кстати это решение


  1. brnovk
    02.04.2017 15:15
    +2

    Навскидку, несколько замечаний по коду:

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


    1. Plesser
      02.04.2017 15:20

      Большое спасибо за Ваши замечания! Как мне не хватает вот таких замечаний :(


      1. sshikov
        02.04.2017 20:43

        Хм. Ну, такого в вашем кода завались… его можно долго комментировать, почти что построчно:

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

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

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

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


        1. Plesser
          02.04.2017 21:27

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


  1. Alex837
    02.04.2017 21:48

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


    1. Plesser
      02.04.2017 21:52

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


      1. Alex837
        02.04.2017 23:19

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

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

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


        1. Plesser
          03.04.2017 07:42

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


      1. kaljan
        03.04.2017 07:41

        ORM это штука для начинающих, имхо, т.к. написать плохой и опасный код при незнании SQL гораздо легче, чем написать говнокод с использованием ORM


  1. Xalium
    02.04.2017 23:06

    Oracle SQL Developer Modeler

    может Oracle SQL Developer Data Modeler?


    1. Plesser
      03.04.2017 07:43

      Да именно он


  1. ls18
    03.04.2017 07:42

    Спасибо за статью :) Тоже есть не большой опыт работы разработчиком PL/SQL и так же присматриваюсь в сторону Java и Android.


  1. Sashok69
    03.04.2017 07:45

    Вместо потенциальных проблем с чтением таблиц и запоминанием связей (которые решаются опытом или тем же Data Modeler), получили вполне очевидные проблемы с нечитабельными файлами, их парсингом и обработкой ошибок. К тому же, насколько понимаю, будет проблематично ссылаться на таблицы/столбцы (как-то надо сохранять их названия?).
    Есть стандартный способ работы с бд, который все знают, а с вашим придется разбираться.
    Ну, и, как заметили, если очень хочется, лучше использовать orm.


    1. Plesser
      03.04.2017 07:49

      Так я работаю стандартно с БД. Я просто навесил на эту работу свой примитивный враппер что бы мне легче было жить.


  1. IvanGur
    03.04.2017 07:45

    Хорошая утилита для визуализации и администрирования БД MySQL https://www.mysql.com/products/workbench/
    Также можно использовать такое ПО как Dia для визуализации связей БД.


    1. Plesser
      03.04.2017 07:45

      А причем тут MySql Workbench? Workbench я использую для работы с Mysql.


  1. serg_deep
    03.04.2017 07:46

    Читайте чужой код, не обязательно общаться с другими разработчиками. Чужой код огромная кладезь знаний и новых трюков. Не знаете как сделать, лезете на гитхаб и смотрите как это делали другие. Скил качается семимильными шагами.


    1. Plesser
      03.04.2017 07:47

      Я по возможности смотрю чужой код. Но тут та проблема, что надо знать куда смотреть. Я сейчас делаю свой проект, и как узнать из какого проетка на гите можно узнать где решаются подобные задачи…


  1. alexhott
    03.04.2017 08:35

    Проектировал много баз данных.
    Для себя сделал вывод.
    На этапе проектирования структуры базы данных действительно очень полезно использовать графические инструменты, какие угодно. Даже тот же MS ACess неплохо может справиться.
    Но создавать объекты в БД лучше ручками, написав запросы.
    Все это авто создание объектов — после него дольше править.


    1. Plesser
      03.04.2017 10:13

      SQLIteStudio достаточно чисто создает код на мой взгляд…


    1. TemaAE
      03.04.2017 10:23

      Но хороший редактор в котором ты видишь список полей и быстро можешь добавить нужные с ручным выбором типов еще лучше :)


  1. Arvalon
    03.04.2017 09:42

    Мои 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);
        }
    }
    


    1. Plesser
      03.04.2017 10:15

      А как Вы решаете вопрос с внесением изменения в структуру БД при использовании SQLiteAssetHelper в последующих версиях вашей программы?


      1. subprogram
        03.04.2017 15:56

        Если контент базы может изменяться в процессе работы приложения, то, очевидно, при начальной инициализации нужно копировать всю базу во внутреннюю память и открывать ее уже с помощью обычного хэлпера.
        Если же структура и содержимое БД меняются только при обновлении версии приложения, то вообще никаких проблем нет.


        1. Plesser
          03.04.2017 15:57

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


          1. subprogram
            03.04.2017 17:01

            Так данные могут меняться в приложении или нет? :)
            Если нет, то и с обновлением нет проблем — БД лежит в assets-ах и обновляется вместе приложением. Миграцию поддерживать нет необходимости.


            1. Plesser
              03.04.2017 17:21

              Именно что могут


              1. subprogram
                03.04.2017 18:52

                Тогда по первому варианту — при первом запуске копировать БД из assets-ов во внутреннюю память. Далее — проверять текущую версию БД и если она меньше, чем нужно, то последовательно применять скрипты миграции.


                1. Arvalon
                  03.04.2017 19:08

                  Или разрабатывать скрипты миграции в том же внешнем редакторе, переносить их в обновление и применять на БД, не перемещая её из assets.


                  1. Plesser
                    03.04.2017 19:09

                    Ну я так и делаю…


                  1. subprogram
                    03.04.2017 20:04

                    Что-то я не понимаю… Что значит «применять на БД, не перемещая её из assets»? Как можно изменить БД, которая находится в assets? Она же Read Only там.


                    1. Plesser
                      03.04.2017 20:31

                      Как я понял, Arvalon имеет ввиду что если данные в БД менять не надо, то его можно держать там. Если надо менять — то тогда необходимо скопировать файл с БД уже внутрь программы…


                    1. Arvalon
                      04.04.2017 19:18

                      Да, не так написал. Она сама копируется во внутреннюю память при первом обращении к ней. Это лежит на плечах SQLiteAssetHelper'а.


  1. breakoffbrain
    03.04.2017 10:44

    по-моему необходимо заняться рефакторингом кода. слишком большая вложенность, куча циклов, ArrayList в качестве возвращаемого значения. А если вы захотите изменить тип коллекции?


    1. Plesser
      03.04.2017 10:47

      Вы знаете, это уже 4 версия кода в моей программе. И я все время его рефакторю. До каких то вещей я дохожу сам, до каких то вещей нет…


  1. kosmonaFFFt
    03.04.2017 11:50

    А не смотрели в сторону Flyway (https://flywaydb.org/documentation/api/)? Я пару раз использовал в своих андроидовских хеллоуворлдах, решает сразу все проблемы инициализации и миграции БД.


    1. Plesser
      03.04.2017 12:07

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


  1. KomarovI
    06.04.2017 14:11

    Почему в книгах и в статьях, посвященных программированию под Android, не описываются инструменты для проектирования архитектуры базы данных и какие-нибудь паттерны для работы с базами данных на этапе их создания я честно говоря не знаю


    Чаще всего потому, что вычислительная мощность мобильных девайсов сильно уступает мощности компьютеров, потому, смысла в проектировании сложной БД особо не возникает, посколько это может оказать негативный эффект на перформанс.


    1. Plesser
      06.04.2017 20:10

      Беспорно, вычислительная мощность уступает. Но тут разговор не о размере БД а о ее структуре. К примеру то приложение которое я сейчас делаю имеет 10 таблиц. И работать со SQL кодом в Java коде при создании таблиц это не айс…