• Как происходит связь моделей с бд в java/scala приложениях?

    @bromzh
    Drugs-driven development
    В мире Java есть много способов организации связи с БД и связи моделей с БД в частности. Есть стандарт - JPA. В мире spring есть слой совместимости с JPA, есть и иные решения. В Scala можно использовать и вышеупомянутые решения, и свои (тут уже зависит от используемого фреймворка).

    Опишу, как это устроено в JPA.

    Сперва ты описываешь связь всего приложения с БД в файле persistence.xml. В нём ты описываешь persistence-unit'ы - единицы персистентности, связь моделей с БД. Грубо говоря, можно использовать как локальные ресурсы (RESOURCE_LOCAL - связь происходит не через сервер приложений, а сторонними усилиями, связь описывается в xml-файле), так и ресурсы JTA (соединение настраивается на сервере, в приложении ты по имени получаешь нужный DataSource).

    Потом ты создаёшь классы сущностей. Создаёшь обычный (POJO) класс с аннотацией Entity, описываешь поля, геттеры и сеттеры. Аннотациями можно настраивать всякие штуки: имя таблицы в БД, имя поля, задавать связи, тип получения (LAZY/EAGER), каскадность, автогенерацию первичных ключей и т.д.

    Затем надо создать класс, который будет предоставлять интерфейс для работы с сущностями. Обычно, для каждой сущности надо сделать свой класс. Есть несколько вариантов реализации и названия этих классов. NetBeans, например, создаёт классы-фасады: один абстрактный и по-одному фасаду, наследующий абстрактный, на каждую сущность. По ссылке всё наглядно, я думаю. Каждый фасад - это бин (аннотация Stateless). В него инжектится EntityManager:
    @PersistenceContext(unitName = "AffableBeanPU")
    private EntityManager em;

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

    Ну а потом, в коде, надо просто инжектить этот фасад через аннотацию EJB и использовать его (например, для реализации REST API):
    import org.foo.example.entities.Foo;
    import org.foo.example.facades.FooFacade;
    
    @Path("foo")
    @Consumes({"application/json", "application/xml"})
    @Produces({"application/json", "application/xml"})
    class FooResource {
    
        @EJB
        FooFacade facade;
    
        @GET
        public List<Foo> getAll() {
            return facade.findAll();
        }
    
        @POST
        public Foo create(Foo item) {
            facade.create(item);
            return item;
        }
    
        @GET
        @Path("{id}")
        public Foo getOne(@PathParam("id") Integer id) {
            return facade.find(id);
        }
    
        @PUT
        @Path("{id}")
        public Foo update(@PathParam("id") Integer id, Foo item) {
            item.setId(id);
            facade.update(item);
            return item;
        }
    
        @DELETE
        @Path("{id}")
        public void delete(@PathParam("id") Integer id) {
            facade.remove(facade.find(id));
        }    
    }


    UPD.
    Запилил демо-приложение, можешь взять посмотреть.

    Суть такая: ставим wildfly, добавляем пользователя. Запускаем сервер. Можно зайти в админку 127.0.0.1:9990
    Там на вкладке Configuration->Datasources будет 1 источник данных - ExampleDS. Это h2 - встроенная БД, которая крутится в данном случае в оперативке и при перезапуске сервера сбрасывается.

    В файле persistence.xml настраиваем ресурсы: указываем имя persistence-unit, и его тип - JTA. Таким образом, ничего локально настраивать не надо, приложение получает всё через ресурс, который настроен на самом сервере, по его имени (java:jboss/datasources/ExampleDS). Единственное, в конфиге указываем
    <property name="hibernate.hbm2ddl.auto" value="update" />
    чтобы таблицы в БД автоматом создавались (если их нет).

    В пакете entities лежат 2 сущности: User и Post. Обе аннотированны Entity, таким образом, JPA может с ними работать. Ещё в сущностях присутствуют аннотации для валидации сущностей (это всякие NotNull, Size, Min, Valid и т.д.). Так же, там есть простая двусторонняя связь. В сущности Post есть связь ManyToOne к сущности User, в сущности User есть связь OneToMany со списком постов пользователя. Последняя связь нужна, чтобы обеспечить каскадность на уровне JPA, но геттеров/сеттеров на неё нет, потому что я не хочу, чтобы этот список вылезал при получении пользователя. По-хорошему, надо её убрать, а в таблице post (которая связана с сущностью Post) надо самому прописать каскадность при удалении, потому что пользователь особо не должен знать, что у него в зависимых.

    Далее, в пакете dao находятся классы для доступа к сущностям.
    AbstractDao - это абстрактный generic-класс, в котором описаны операции для управления хранением сущностей. Все методы там тривиальны, за исключением получения.

    Вообще, есть несколько способов получить сущность. Можно использовать простые SQL-запросы, можно указывать именованные запросы (NamedQuery), которые можно описать либо через одноимённую аннотацию, либо в коде. Вторые имеют бонус в виде типобезопасности.
    И ещё один из вариантов - это динамические запросы через CriteriaBuilder. JPA генерирует метаклассы для классов, отмеченных аннотацией Entity. Запросы можно строить, в том числе, используя эти метаклассы. Большим плюсом является то, что такие запросы можно (и нужно) делать типобезопасными (CriteriaQuery, TypedQuery). И IDE не ругается на приведение типов, которое было бы в случае простых нетипизированных запросов. Вообще, по этой ссылке есть подробное описание таких типобезопасных запросов.

    Особое внимание к методам findWithLazy и findAllWithLazy. В сущности Post поле owner помечено как связь ManyToOne с ленивым типом получения (fetch = FetchType.LAZY). Просто так такое поле получить трудно: ленивая загрузка работает только в пределах сессии: создалась сессия, запросились данные, сессия закрылась. И ленивые поля по-умолчанию не добавляются к возвращаемому объекту. Есть несколько способов побороть это. Можно убрать ленивость (fetch = FetchType.EAGER). Можно вызвать метод size() для поля-коллекции. Можно вручную получать поля. У меня сделано именно так. В методы findWithLazy и findAllWithLazy передаётся список полей, необходимых для получения. Я создаю запрос на получение корневого элемента: Root<T> root = criteriaQuery.from(entityClass);, а затем в цикле получаю необходимые поля: for (String field : fields) { root.fetch(field); }. При выполнении запроса эти поля присоединятся к результату.

    В классах UserDao и PostDao я указываю аннотацию Stateless для CDI и реализую абстрактный метод getEntityManager() для получения экземпляра PersistenceManager. Сам экземпляр я внедряю через аннотацию PersistenceContext, где в качестве параметра unitName я указываю имя persistence-unit, которое обозначил в persistence.xml.

    Ну и наконец, использование классов DAO в приложении (пакет resources). Я создаю простой REST API с помощью JAX-RS. Для каждой сущности создаю по своему ресурсу, в который внедряю через аннотацию EJB нужный DAO-класс. Там, думаю, всё очевидно.

    В описании репозитория указано, как запустить и потестить всё это дело.

    Надеюсь, всё понятно.
    Ответ написан
    4 комментария
  • Java, куда именно податься?

    AlPsc
    @AlPsc
    Java/high load/big data
    Во-первых, не забудьте после Шилдта прочесть книгу Джошуа Блоха "Effective Java" (в одном из соседних вопросов упоминается её русский перевод, так что он, видимо, существует) – по моему скромному мнению, это обязательное чтиво для любого Java-программиста.
    Во-вторых, если уж выбирать между Android и чем-то ещё, то надо понимать плюсы и минусы обоих путей. Напишу то, что пришло мне в голову, на полноту и истину в последней инстанции не претендую.

    Android:
    Плюсы:
    • Работы много. Очень. В том числе и удалённой.
    • Получить начальные навыки довольно легко – сейчас есть огромное количество статей, пошаговых руководств и прочих материалов, которые как позволяют учиться новому, так и быстро решать типовые задачи/проблемы.

    Минусы:
    • С точки зрения изучения Java эта среда довольно специфическая. Во-первых, используется довольно старый диалект (Java 6). (В комментариях справедливо поправили, что сейчас на Android доступна Java 7.) Во-вторых, набор библиотечных классов несколько отличается от Java SE, и это значит, что при необходимости писать приложения на "настоящей" Java просто взять и переключиться по щелчку пальцев не получится, а какая-то часть "мобильных" навыков и практик окажется бесполезной.
    • Хорошо программировать на Java значит не только знать язык, но и уметь выбирать прочие инструменты (дополнительные библиотеки и т.п.), которыми, конечно, тоже надо уметь пользоваться. В этом смысле Android тоже довольно далёк от того, к чему привыкли разработчики Java SE/EE: всякие вещи типа JDBC/Hibernate/you-name-it на Android либо отсутствуют в принципе, либо не могут быть использованы из-за ограничений среды (тот же нестандартный набор библиотечных классов). Это опять же означает, что, научившись писать на Java под Android, вы не сможете просто взять и начать разрабатывать, скажем, enterprise/backend приложения, и конкуренцию в этой области с кандидатами, у которых есть соответствующий опыт, выдержать вряд ли сможете. (Я бы ничего этого не писал, но у вопроса есть метка "карьера", так что вы сами напросились :) )
    • Большая часть компаний, занятых мобильной разработкой – сервисные, со всеми вытекающими. Лично для меня это минус, т.к. мне продуктовые компании больше по вкусу.
    Java SE/EE
    Плюсы:
    • Работы много. Очень. В том числе и удалённой.
    • Более широкие возможности применения своих навыков

    Минусы:
    • Более высокий порог вхождения
    • Несколько сложнее наработать портфолио, потому что жизненный цикл "больших" приложений зачастую всё же гораздо длиннее, чем в случае мобильной разработки, где очень большую часть работы составляют проекты для сторонних заказчиков, где изначально задана конкретная конечная цель, сразу оговариваются сроки, и т.п. (Сужу по собственному опыту работы в компании, которая занималась как раз разработкой мобильных приложений для заморских заказчиков.) Конечно, в случае с удалённой работой/сервисными компаниями это может оказаться правдой для приложений любых типов.

    Итак, выбирая Android, вы раньше сможете начать зарабатывать деньги, но "чистая" Java, на мой взгляд, даёт больше перспектив. И по сути Android-программист и Java-программист – совсем не одно и то же. Так что выбор профессионального пути зависит от того, как вы вообще видите себя в ближайшем будущем в этой профессии: хотите ли вы быстро освоиться и получить способ зарабатывать деньги, не сильно задумываясь о смене деятельности в перспективе, либо же вам интересны разные области программирования, и вам хочется многое попробовать.
    Ответ написан
    4 комментария
  • Какой джентельменский набор для Java-разработчика?

    ostapbender
    @ostapbender
    Можно почитать An Opinionated Guide to Modern Java Development. Опус в трёх частях.
    Ответ написан
    Комментировать
  • Андроид: LayoutInflater - зачем он нужен?

    Collosteam
    @Collosteam
    Android кодирую
    Класс LayoutInflater содержит метод:
    public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
            if (DEBUG) System.out.println("INFLATING from resource: " + resource);
            XmlResourceParser parser = getContext().getResources().getLayout(resource);
            try {
                return inflate(parser, root, attachToRoot);
            } finally {
                parser.close();
            }
        }

    Соответственно парсер парсит xml и преобразует в объект класса View;

    Когда вы в Activity указываете setContentView(int id);
    Вы передаете ссылку на xml. Внутри Activity находится экземпляр класса Window, он парсит ваш xml и получает объект класса View. Дальше при вызове в Activity - findViewById(int id), вы обращаетесь к экземпляру класса Window, в свою очередь он делегирует (передает вызов) экземпляру класса View.
    А класс View содержит метод findViewById(int id), и уже он возвращает искомый объект, соответственно в обоих случаях механизм один и тот же, только LayoutInflater обычно используется для парсинга xml не являющихся layout Activity. Например при отрисовке Fragments и CustomView или при компоновке Activity из Java.

    Посмотрите исходники SDK, там очень подробно все описано.
    Ответ написан
    Комментировать