Генерация классов из БД с помощью DataGrip

  • Tutorial

В этой небольшой заметке будет показано, как написать DataGrip расширение для генерации кода (в данном случае POCO (C#) классов) на основе таблиц из почти любой БД (SQL Server, Oracle, DB2, Sybase, MySQL, PostgreSQL, SQLite, Apache Derby, HyperSQL, H2).


Предисловие



DataGrip это относительно новая IDE от JetBrains для работы с разными СУБД имеющая некоторый API для расширения функционала. Задача — используя его написать генератор POCO (C#) классов.
Если вы не хотите читать все это, а желаете просто начать генерировать классы, то вот ссылка на репозиторий со скриптом.


Написание скрипта


DataGrip позволяет расширить свою функциональность с помощью скриптов (Scripted Extensions). Поддерживаются языки Groovy, Clojure и JavaScript. Документация на сайте об этом довольно краткая, но в распоряжении есть примеры и архив с API в виде исходного кода на Java. Исходный код можно найти в <DataGrip_installation_dir>/lib/src/src_database-openapi.zip. Примеры можно посмотреть в самой IDE в панели Files -> Scratches. Так же DataGrip поддерживает скрипты для экспорта данных в различные форматы (extractors, в данной статье рассмотрены не будут), примеры для форматов csv, json и html тоже находятся в панели Scratches.


Итак, для написания скрипта мы будем использовать Clojure, за основу был взят пример генератора POJO из IDE.
Подсветки синтаксиса и автодополнения для Clojure в DataGrip конечно же нет, поэтому можно использовать любой другой редактор.
Для начала настроим маппинги типов из БД на C# типы и объявим некоторые константы.


Код
(def usings "using System;")
(def default-type "string")
(def type-mappings
    [
        [["bit"] "bool"]
        [["tinyint"] "byte"]
        [["uniqueidentifier"] "Guid"]
        [["int"] "int"]
        [["bigint"] "long"]
        [["char"] "char"]
        [["varbinary" "image"] "byte[]" true] ; cannot be null
        [["double" "float" "real"] "double"]
        [["decimal" "money" "numeric" "smallmoney"] "decimal"]
        [["datetime" "timestamp" "date" "time"] "DateTime"]
        [["datetimeoffset"] "DateTimeOffset"]
    ])
(def new-line "\r\n")

Далее напишем функцию которая приводит строку к PascalCase.


Код
(defn- poco-name [name]
    (apply str (map clojure.string/capitalize (re-seq #"(?:[A-Z]+)?[a-z\d]*" name))))

Матчинг типа из БД на тип в C# основываясь на маппингах которые мы определили ранее.


Код
(defn- poco-type [data-type is-null]
    (let [spec                (.. data-type getSpecification toLowerCase)
          spec-matches?       (fn [pattern] (= (re-find #"^\w+" spec) pattern))
          mapping-matches?    (fn [[ps t n]] (when (some spec-matches? ps) [t n]))
          [type cant-be-null]  (some mapping-matches? type-mappings)
          nullable-type       (if (and type (not cant-be-null) is-null) (str type "?") type)]
        (or nullable-type default-type)))

Функция которая получает все столбцы из таблицы, вызывает функцию матчинга и собирает нужный нам объект для дальнейшего сохранения. Здесь мы используем методы из API, например com.intellij.database.util.DasUtil/getColumns, все эти методы можно посмотреть в архиве src_database-openapi.zip упомянутом выше.


Код
(defn- field-infos [table]
    (let [columns    (com.intellij.database.util.DasUtil/getColumns table)
          field-info (fn [column] {:name (poco-name (.getName column))
                                   :type (poco-type (.getDataType column) (not (.isNotNull column)))})]
        (map field-info columns)))

Генерация текста для свойств и классов, ничего особенного просто конкатенация строк. А так же функция записи этого текста в файл.


Код
(defn- property-text [field-info]
    (let [type  (:type field-info)
          name  (:name field-info)]
        (str "  public " type " " name " { get; set; } " new-line)))

(defn- poco-text [class-name fields]
    (apply str (flatten
        [usings new-line new-line
         "public class " class-name " " new-line "{" new-line
         (interpose new-line (interleave (map property-text fields)))
         "}" new-line])))

(defn- generate-poco [directory table]
    (let [class-name (poco-name (.getName table))
          fields     (field-infos table)
          file       (java.io.File. directory (str class-name ".cs"))
          text       (poco-text class-name fields)]
        (com.intellij.openapi.util.io.FileUtil/writeToFile file text)))

И наконец функция открытия диалога выбора директории для сохранения файлов и функция которая определяет выбранные таблицы и запускает генерацию.


Код
(defn- generate-pocos [directory]
    (let [table? (partial instance? com.intellij.database.model.DasTable)]
        (doseq [table (filter table? SELECTION)]
            (generate-poco directory table))))

(.chooseDirectoryAndSave FILES
                         "Choose directory"
                         "Choose where to generate POCOs to"
                         (proxy [com.intellij.util.Consumer] []
                            (consume [directory]
                                (generate-pocos directory)
                                (.refresh FILES directory))))

Установка скрипта



Полный код скрипта на GitHub.
Для установки надо просто скопировать файл Generate POCO.clj Generate POCO.groovy в IDE > Files > Scratches > Extensions > DataGrip > schema.
И в контекстном меню таблиц появится соотвествующий пункт подменю в разделе Scripted Extensions.


Результат


Из следующей таблицы


Было
CREATE TABLE Users
(
    Id INT PRIMARY KEY NOT NULL IDENTITY,
    first_name NVARCHAR(255),
    Last_Name NVARCHAR(255),
    Email VARCHAR(255) NOT NULL,
    UserGuid UNIQUEIDENTIFIER,
    Age TINYINT NOT NULL,
    Address NVARCHAR(MAX),
    photo IMAGE,
    Salary MONEY,
    ADDITIONAL_INFO NVARCHAR(42)
);

будет сгенерирован следующий класс:


Стало
using System;

public class Users 
{
  public int Id { get; set; } 

  public string FirstName { get; set; } 

  public string LastName { get; set; } 

  public string Email { get; set; } 

  public Guid? UserGuid { get; set; } 

  public byte Age { get; set; } 

  public string Address { get; set; } 

  public byte[] Photo { get; set; } 

  public decimal? Salary { get; set; } 

  public string AdditionalInfo { get; set; } 
}

Заключение


Вот таким довольно простым способом можно генерировать C# классы которые описывают ваши таблицы в БД и сделать жизнь чуть менее рутинной. Конечно же, вы не ограничены только POCO классами C#, можно написать генератор чего угодно, для другого языка, фреймворка и т.д.

Share post

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 13

    0
    планируется ли поддержка субд firebird ???
      +1
      К ней есть jdbс драйвер, так что одключиться можно уже сейчас
      0
      Кажется в Intellij IDEA Ultimate есть встроенный модуль для работы с БД и достаточно мощный. Работаю с Hibernate и позволяет на основе таблицы сгенерировать сущность для Hibernate и сама в конфиги прописывает. Тоже достаточно удобно и лишнюю IDE не надо качать
        0
        Ага, когда у вас куча дата сорсов(разных) c кучей разный вариантов типа PROD/PAR/UAT/DEV и несколько проектов… держите все в одной идеи, удачи
          +1
          ORM cущности обычно генерят на DEV окружении.
          Держать разные схемы данных на PROD/PAR/UAT/DEV окружениях не лучшая практика.

            0
            Генерация, конечно, хорошо, но причем здесь она? Я привел реальный юзкейз, когда плагина к идеи маловато, и лучше всем скопом держать все в отдельной сущности(читай 0xDBE).
        +1
        Всегда использовал Linq2Db который и классы сгенерирует и LINQ разберет по лучше EF
          0
          Было бы круто, если бы в буфер просто результирующий poco скопировать можно было
            0
            Дело в том, что можно выбрать сразу много таблиц, в этом случае файлы, наверное, удобнее.
            Но я думаю, что есть API и для работы с буфером.
            0
            Мы удалили Clojure по дефолту, если кто-то все-таки хочет писать на этом языке скрипты (или использовать скрипт из поста), то в папку lib проинсталлированного DataGrip нужно добавить файлы:

            Clojure 1.9.0
            repo1.maven.org/maven2/org/clojure/clojure/1.9.0/clojure-1.9.0.jar
            central.maven.org/maven2/org/clojure/spec.alpha/0.1.143/spec.alpha-0.1.143.jar
            central.maven.org/maven2/org/clojure/core.specs.alpha/0.1.24/core.specs.alpha-0.1.24.jar
            clojars.org/repo/org/clojars/ato/clojure-jsr223/1.5.1/clojure-jsr223-1.5.1.jar

            Clojure 1.8.0
            repo1.maven.org/maven2/org/clojure/clojure/1.8.0/clojure-1.8.0.jar
            clojars.org/repo/org/clojars/ato/clojure-jsr223/1.5.1/clojure-jsr223-1.5.1.jar
              0

              Грусть какая, что рекомендуете использовать вместо clojure?

                0

                В общем, переписал на groovy.

                  +1
                  Да, мы рекомендуем груви. Спасибо большое! Надеюсь, вы не против, приведу здесь сылку: github.com/olsh/datagrip-poco-generator

            Only users with full accounts can post comments. Log in, please.