Java SQLite ORM

БД имеют неприятную особенность, когда нужно создавать множество запросов, контролировать связи, формировать все вручную. Для облегчения жизни программиста есть ORM (Object Relation Mapping).

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

Шаг 1

Начнем с поставления задачи.  На картинке ниже представлено 3 класса. 2  с них находятся внутри 1 (Question). Такой тип связи называется агрегацией. Создадим такие ж 3 таблички в базе и свяжем их между собой. Category — Question , Answer — Question

Diagram1

Шаг 2

На протяжении урока будем использовать базу SQLite. Создаем ее с помощью специального ПО. Можно использовать Navicat или другие подобные программы для работы с базой. Я буду использовать плагин для Firefox -> SQLite Manager. Создаем базу под именем main.sqlite. Создаем таблички:

Создание таблички question:

CREATE  TABLE "main"."question" 
("id" INTEGER PRIMARY KEY  AUTOINCREMENT  NOT NULL , "category" INTEGER, "text" VARCHAR, "img" VARCHAR)

Создание таблички category:

CREATE  TABLE "main"."category" 
("id" INTEGER PRIMARY KEY  AUTOINCREMENT  NOT NULL , "name" VARCHAR)

Создание таблички answer:

CREATE  TABLE "main"."answer" 
("id" INTEGER PRIMARY KEY  AUTOINCREMENT  NOT NULL , "id_q" INTEGER, "value" VARCHAR, "correct" BOOL)

Шаг 3

После создания табличек, их нужно заполнить данными. Заполним полноценных 2 объекта. 1 question, 1 category, 2 answer.

category:

INSERT INTO "main"."category" ("name") VALUES ("category1")
INSERT INTO "main"."category" ("name") VALUES ("category2")

question:

INSERT INTO "main"."question" ("category","text","img") VALUES (1,"One?","Not img now");
INSERT INTO "main"."question" ("category","text","img") VALUES (2,"Four?","Not img now");

answer:

INSERT INTO "main"."answer" ("id_q","value","correct") VALUES (1,"One","1");
INSERT INTO "main"."answer" ("id_q","value","correct") VALUES (1,"Two","0");
INSERT INTO "main"."answer" ("id_q","value","correct") VALUES (2,"Three","0");
INSERT INTO "main"."answer" ("id_q","value","correct") VALUES (2,"Four","1");

Шаг 4

Теперь создадим соответствующие классы в нашем проекте. Классы должны содержать сеттеры, геттеры и конструктор по умолчанию.

1

Question:

package org.knowledgechecker.entity;

import java.util.List;

public class Question {

    private int id;
    private Category category;
    private String text;
    private String img;
    private List<Answer> answers;

    public Question() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Category getCategory() {
        return category;
    }

    public void setCategory(Category category) {
        this.category = category;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public String getImg() {
        return img;
    }

    public void setImg(String img) {
        this.img = img;
    }

    public List<Answer> getAnswers() {
        return answers;
    }

    @Override
    public String toString() {
        return "Question{" +
                "id=" + id +
                ", category=" + category +
                ", text='" + text + '\'' +
                ", img='" + img + '\'' +
                ", answers=" + answers +
                '}';
    }

    public void setAnswers(List<Answer> answers) {
        this.answers = answers;
    }
}

Category:

package org.knowledgechecker.entity;

public class Category {
    private int id;
    private String name;

    public Category() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Category{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

Answer:

package org.knowledgechecker.entity;

public class Answer {
    private int id;
    private Question question;
    private String value;
    private boolean correct;

    public Answer() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Question getQuestion() {
        return question;
    }

    public void setQuestion(Question question) {
        this.question = question;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public boolean isCorrect() {
        return correct;
    }

    public void setCorrect(boolean correct) {
        this.correct = correct;
    }

    @Override
    public String toString() {
        return "Answer{" +
                "id=" + id +
                ", idQuestion=" + idQuestion +
                ", value='" + value + '\'' +
                ", corect=" + corect +
                '}';
    }
}

Шаг 5

Подключаем нашу библиотеку ORMLite. Ее можно взять с офф. сайта по ссылке: http://ormlite.com/releases/ или, подключить с помощью Maven.

<dependency>
    <groupId>com.j256.ormlite</groupId>
    <artifactId>ormlite-core</artifactId>
    <version>4.47</version>
</dependency>

<dependency>
    <groupId>com.j256.ormlite</groupId>
    <artifactId>ormlite-jdbc</artifactId>
    <version>4.47</version>
</dependency>

<dependency>
    <groupId>org.xerial</groupId>
    <artifactId>sqlite-jdbc</artifactId>
    <version>3.7.2</version>
</dependency>

Для того чтобы проверять правильность настройки можно использовать библиотеку Junit:

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
</dependency>

Шаг 6

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

Category:

@DatabaseTable
public class Category {
    @DatabaseField(generatedId = true)
    private int id;
    @DatabaseField
    private String name;

//getters and setters...
}

@DatabaseTable — для указание на табличку с базы.  

tableName — eсли имя класса не совпадает с именем таблички

@DatabaseField — указывает что поле является столбцом.

    columnName — указать имя колонки самостоятельно. (Если имя колонки не совпадает с именем поля класса)
    Id — поле является идентификатором
    generatedId — идентификатор генерируется автоматиески
    foreign — ссылается на другой класс, который также хранит в себе таблицу

Answer:

@DatabaseTable (tableName = "answer")
public class Answer {
    @DatabaseField(generatedId = true)
    private int id;
    @DatabaseField(columnName = "id_q", foreign = true)
    private Question question;
    @DatabaseField
    private String value;
    @DatabaseField
    private boolean correct;
//getter and setters...
}

Question:

@DatabaseTable(tableName = "question")
public class Question {
    @DatabaseField(generatedId = true)
    private int id;
    @DatabaseField(columnName = "category", foreign = true)
    private Category category;
    @DatabaseField
    private String text;
    @DatabaseField
    private String img;

    @ForeignCollectionField
    private ForeignCollection<Answer> answers;

//getters and setters
}

Обратите внимание, что в 13 строке был изменен List на ForeignColliection<Answer>.

@ForeignCollectionField — получает коллекцию данных зависимой таблички.

Шаг 7

После создания сущностей и связывания их с табличками в БД создаем сервис, для управления их поведением. ORMLite дает легкий способ создания DAO (Data Accsess Object) который мы используем для построения нашего сервиса. Чтобы урок не разростался и дальше, приведу код одного из сервиса.

public class QuestionService {
    private final String url = "jdbc:sqlite:main.sqlite";
    private ConnectionSource source;
    private Dao<Question, String> dao;

    public QuestionService() throws SQLException {
        source = new JdbcConnectionSource(url);
        dao = DaoManager.createDao(source,Question.class);
    }

    public List<Question> getAll() throws SQLException {
        return dao.queryForAll();
    }
}

url — путь к нашей бд

source = new JdbcConnectionSource(url) — создаем подключение к нашей базе по url

dao = DaoManager.createDao(source,Question.class) — создание DAO для класса. Он будет использоваться
для того чтобы управлять объектом, выполнять над ним операции чтения, сохранения, удаления, изменения и т.д в базе.

dao.queryForAll() — возвращает все данные с таблички query и зависимых табличек.

Пример выполнения метода getAll() с тестового набора для класса Questions:

34_38_2

1 — это лог наших запросов, информация о работе библиотеки

2 — наш результат, который мы ждали. вывод с таблички answer есть корректным. Для вывода его в «человеческом» виде, достаточно сделать обработку этой коллекции.

В заключение, можно выполнять аналогичным образом операции CRUD (Create Read Update Delete), а также, есть пара дополнительных методов, которые добавят возможностей. Об этом, по необходимости, в следующем уроке.

Спасибо.

Урок создан: 13 ноября 2013 | Просмотров: 12153 | Автор: Олег Криль | Правила перепечатки


Добавить комментарий

Добавить комментарий

Ваш e-mail не будет опубликован.

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

  • 18 ноября 2013 в 17:22

    Аноним

    Не могу понять что данная программа делает, объясните пожалуйста :)

    • 18 ноября 2013 в 23:59

      Олег Криль

      Привет) Я начну со слов что здесь все просто) Этот кусочек кода, дает возможность установить связь между табличками в базе и объектами (Java классами). Таким образом, это даст возможность уйти от написания запросов к базе вручную и оставит время на то, чтобы мы сосредоточились на конкретной задаче

  • 30 ноября 2014 в 17:11

    test

    «opening db: ‘main.sqlite’: open failed: EROFS (Read-only file system)»
    падает при вызове getAll(). Использую с genymotion. Что я упустил?

  • 24 июня 2015 в 23:58

    Павел

    Привет, а продолжения не будет ?

  • 13 апреля 2016 в 01:36

    Богдан

    Будет ли видео урок по работе с SQLite в Java (не Андроид), желательно с использованием графики FX 8 ?

  • 13 июля 2016 в 14:15

    Дмитрий

    Конект прейдется делать в каждом сервисе???