Статьи

Двунаправленные отношения с Джексоном в веб-сервисах REST в WildFly

Это пример двунаправленной связи между объектами Java в веб-службе REST с API Джексона .

  1. Предположим, у нас есть двунаправленные отношения между двумя сущностями: Родителем и Ребенком.

    bidirectional_relationship_tables

  2. Использование MySQL Workbench для генерации файла схемы SQL для этих двух таблиц.
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    DROP SCHEMA IF EXISTS `bidirectional_schema` ;
    CREATE SCHEMA IF NOT EXISTS `bidirectional_schema` DEFAULT CHARACTER SET utf8 ;
    USE `bidirectional_schema` ;
     
    -- -----------------------------------------------------
    -- Table `bidirectional_schema`.`PARENT`
    -- -----------------------------------------------------
    DROP TABLE IF EXISTS `bidirectional_schema`.`PARENT` ;
     
    CREATE  TABLE IF NOT EXISTS `bidirectional_schema`.`PARENT` (
      `PARENT_ID` INT NOT NULL ,
      `PARENT_CONTENT` VARCHAR(45) NULL ,
      PRIMARY KEY (`PARENT_ID`) )
    ENGINE = InnoDB;
     
    -- -----------------------------------------------------
    -- Table `bidirectional_schema`.`CHILD`
    -- -----------------------------------------------------
    DROP TABLE IF EXISTS `bidirectional_schema`.`CHILD` ;
     
    CREATE  TABLE IF NOT EXISTS `bidirectional_schema`.`CHILD` (
      `CHILD_ID` INT NOT NULL ,
      `CHILD_CONTENT` VARCHAR(45) NULL ,
      `PARENT_PARENT_ID` INT NOT NULL ,
      PRIMARY KEY (`CHILD_ID`) ,
      INDEX `fk_CHILD_PARENT_idx` (`PARENT_PARENT_ID` ASC) ,
      CONSTRAINT `fk_CHILD_PARENT`
        FOREIGN KEY (`PARENT_PARENT_ID` )
        REFERENCES `bidirectional_schema`.`PARENT` (`PARENT_ID` )
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB;
  3. Затем мы вставим некоторые данные в эти две таблицы с помощью этих операторов вставки:
    1
    2
    3
    INSERT INTO `bidirectional_schema`.`parent` (`PARENT_ID`, `PARENT_CONTENT`) VALUES ('1', 'First Parent');
    INSERT INTO `bidirectional_schema`.`child` (`CHILD_ID`, `CHILD_CONTENT`, `PARENT_PARENT_ID`) VALUES ('1', 'First Child', '1');
    INSERT INTO `bidirectional_schema`.`child` (`CHILD_ID`, `CHILD_CONTENT`, `PARENT_PARENT_ID`) VALUES ('2', 'Second Child', '1');
  4. Зависимости Maven для API Джексона, необходимые для этого примера.
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    <dependency>
     <groupid>com.fasterxml.jackson.core</groupid>
     <artifactid>jackson-annotations</artifactid>
     <version>2.5.2</version>
    </dependency>
    <dependency>
     <groupid>com.fasterxml.jackson.core</groupid>
     <artifactid>jackson-core</artifactid>
     <version>2.5.2</version>
    </dependency>
    <dependency>
     <groupid>com.fasterxml.jackson.core</groupid>
     <artifactid>jackson-databind</artifactid>
     <version>2.5.2</version>
    </dependency>

    Кроме того, вы можете подобрать требуемые зависимости Java EE по этой ссылке: https://wikis.oracle.com/display/GlassFish/Java+EE+7+Maven+Coordinates

  5. Использование Eclipse для генерации сущностей из приведенных выше таблиц. Файл -> Создать -> Другое, затем в мастере выберите JPA Entities из таблиц. Появится мастер для создания соединения с уже созданной схемой. Затем выберите две таблицы: дочернюю и родительскую. Наконец убедитесь, что «Список сгенерированных классов в persistence.xml».

    bidirectional_relationship_entities_generation_1

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

    bidirectional_relationship_entities_generation_2

  6. В файле persistence.xml нет ничего особенного. Но есть два момента, которые следует учитывать. Во-первых, я не указывал поставщика постоянства, поскольку я склонен использовать WildFly, поэтому приложение будет использовать поставщик постоянства по умолчанию, который называется Hibernate. Во-вторых, я создал источник данных в WildFly, чтобы его можно было использовать для подключения к нашей схеме. В-третьих, я использовал EclipseLink, поэтому я использую этот JPA-провайдер org.eclipse.persistence.jpa.PersistenceProvider. Проверьте этот пост, чтобы указать WildFly на EclipseLink.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <?xml version="1.0" encoding="UTF-8"?>
     <persistence-unit name="BidirectionalPU">
         <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
         <jta-data-source>java:jboss/datasources/Bidirectional_DataSource</jta-data-source>
         <class>com.ithinkisink.entity.Child</class>
         <class>com.ithinkisink.entity.Parent</class>
     </persistence-unit>
    </persistence>
  7. Первый класс, который мы реализуем, — это класс ApplicationConfiguration, который будет указывать на родительский путь наших будущих служб REST.
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    package com.ithinkisink;
     
    import javax.inject.Singleton;
    import javax.ws.rs.ApplicationPath;
    import javax.ws.rs.core.Application;
     
    /**
     *
     * @author Belal
     *
     */
    @Singleton
    @ApplicationPath("rest")
    public class ApplicationConfiguration extends Application {
    }
  8. Затем мы создадим класс MyService, который будет иметь два веб-сервиса REST. Первый извлечет родителя с выбранными детьми. Второй вариант противоположен этому и будет извлекать ребенка с его родителями.
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    package com.ithinkisink.service;
     
    package com.ithinkisink.service;
     
    import javax.ejb.EJB;
    import javax.ws.rs.GET;
    import javax.ws.rs.Path;
    import javax.ws.rs.Produces;
    import javax.ws.rs.core.MediaType;
     
    import com.ithinkisink.entity.Child;
    import com.ithinkisink.entity.Parent;
     
    /**
     *
     * @author Belal
     *
     */
    @Path("MyService")
    public class MyService {
     
     @EJB
     MyServiceDAO myServiceDAO;
      
     @GET
     @Path("/retrieveParentWithChildren")
     @Produces(MediaType.APPLICATION_JSON)
     public Parent retrieveParentWithChildren() {
      return myServiceDAO.retrieveParentWithChildren();
     }
      
     @GET
     @Path("/retrieveChildWithParent")
     @Produces(MediaType.APPLICATION_JSON)
     public Child retrieveChildWithParent() {
      return myServiceDAO.retrieveChildWithParent();
     }
    }
  9. Затем мы создадим ServiceDAO и заметим, что EntityManager внедряется с подключением к постоянному модулю, который мы уже определили в файле persistence.xml. Я передал название единицы персистентности, чтобы прояснить этот момент. Но вы можете просто использовать аннотацию @PersistenceContext без указания атрибута unitName, тогда он будет использовать наш модуль постоянства, поскольку в нашем приложении не определено никакого другого модуля постоянства.
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    package com.ithinkisink.service;
     
    import javax.ejb.Stateless;
    import javax.persistence.EntityManager;
    import javax.persistence.PersistenceContext;
    import javax.persistence.Query;
     
    import com.ithinkisink.entity.Child;
    import com.ithinkisink.entity.Parent;
     
    @Stateless
    public class MyServiceDAO {
      
     @PersistenceContext(unitName="BidirectionalPU")
     protected EntityManager entityManager;
      
     public Parent retrieveParentWithChildren() {
      int parentId = 1;
      Query q = entityManager.createQuery("SELECT p FROM " + "Parent " + "p JOIN FETCH p.childs WHERE p.parentId = :parentId ");
      q.setParameter("parentId", parentId);
      return (q.getResultList().size() > 0) ? (Parent) q.getResultList().get(0) : null;
     }
      
     public Child retrieveChildWithParent() {
      int childId = 1;
      Query q = entityManager.createQuery("SELECT c FROM " + "Child " + "c JOIN FETCH c.parent WHERE c.childId = :childId ");
      q.setParameter("childId", childId);
      return (q.getResultList().size() > 0) ? (Child) q.getResultList().get(0) : null;
     }
    }
  10. Теперь, когда наше приложение готово к тестированию, добавим его на сервер WildFly. Тогда запуск сервера должен быть успешным.

    bidorectional_relationship_wildfly

  11. Для простоты я использовал Postman для тестирования запущенных веб-сервисов REST. Итак, ниже приведены скриншоты из результатов на Почтальоне.

    bidirectional_rest_1

    bidirectional_rest_2