Статьи

Учебное пособие по GWT 2 Spring 3 JPA 2 Hibernate 3.5 — демонстрация Eclipse и Maven 2

Некоторое время назад мой друг и коллега напали на меня, сказав: «Только половина мира использует Maven ». Его заявление поразило меня, когда я понял, что наша самая популярная статья (до сих пор) GWT 2 Spring 3 JPA 2 Hibernate 3.5 Tutorial представляет подход к интеграции GWTSpring на основе плагина Google Web Toolkit ( GWT ) Eclipse , в котором отсутствуют все преимущества. что Maven предоставляет. Мой коллега был прав, Maven — это стандартный инструмент управления и понимания проектов. Maven может управлять сборкой проекта, составлением отчетов и документацией из центральной части информации, файла объектной модели проекта (POM).

В этом пошаговом руководстве будет показано, как разработать простое веб-приложение с использованием Google Web Toolkit ( GWT ) для расширенного клиента и Spring в качестве серверной инфраструктуры на стороне сервера. Пример веб-приложения предоставит функции для выполнения операций CRUD (Create Retrieve Update Delete) с базой данных. Для слоя доступа к данным мы будем использовать JPA поверх Hibernate, а для базы данных мы будем использовать Hypersonic. Конечно, вы можете изменить конфигурацию и использовать все что угодно. Мы развернем веб-приложение на экземпляре Apache — Tomcat.

Maven поставляется в двух вариантах: автономный инструмент с поддержкой командной строки и как плагин интеграции IDE ( Eclipse и Netbeans). Нашей предпочтительной средой разработки является Eclipse , поэтому в качестве предварительного условия у вас должна быть установлена Eclipse с поддержкой Maven . Установка плагина Maven для Eclipse выходит за рамки данного руководства и не будет обсуждаться. Тем не менее вам понадобятся следующие компоненты:

  1. Затмение отсюда
  2. Плагин Maven для Eclipse отсюда
  3. GWT — библиотека Spring «glue» spring4gwt отсюда

Мы будем использовать Eclipse Galileo и «m2eclipse» Maven Integration для Eclipse Plugin версии 0.10.0, GWT версии 2.0.3, Spring версии 3.0.1, Hibernate версии 3.5.1, Hypersonic версии 1.8.0.10, slf4j-log4j12 версии 1.5. 8, версия 0.9.1.2 пула соединений c3p0 и версия 0.0.1 spring4gwt для этого руководства.

Хватит разговоров, давайте запачкаем руки!

  1. Создать новый проект Maven , перейти к файлу? Проект? Мавен? Maven Project
  2. На странице мастера «Выберите имя проекта и местоположение» убедитесь, что опция «Создать простой проект (пропустить выбор архетипа») не отмечена, нажмите «Далее», чтобы продолжить со значениями по умолчанию.
  3. На странице мастера «Выбор архетипа» выберите «Индексатор Nexus» в раскрывающемся списке «Каталог» и после обновления области выбора архетипов выберите архетип «gwt-maven-plugin» из «org.codehaus». Моджо «использовать. Вы можете использовать текстовое поле «фильтр», чтобы сузить результаты поиска. Нажмите «Далее», чтобы продолжить
  4. На странице мастера «Введите идентификатор артефакта» вы можете указать название и основной пакет вашего проекта. Мы установим переменную «Group Id» в «com.javacodegeeks», а переменную «Artifact Id» в «gwtspring». Вышеупомянутые выборы составляют основной пакет проекта как «com.javacodegeeks.gwtspring» и имя проекта как «gwtspring». Нажмите «Готово», чтобы выйти из мастера и создать свой проект.

Давайте вспомним несколько вещей о структуре проекта Maven GWT

  1. Папка / src / main / java содержит исходные файлы для динамического содержимого приложения
  • Подпакет {main_package} .client содержит исходные файлы, доступные только для клиентской части приложения
  • Подпакет {main_package} .server содержит исходные файлы, доступные только для серверной части приложения (этот подпакет не создается автоматически при создании проекта)
  • Подпакет {main_package} .shared содержит исходные файлы, доступные как клиентской, так и серверной части приложения (этот подпакет не создается автоматически при создании проекта)
  • / src / main / resources содержит исходные файлы для статического содержимого, например, статические html-страницы, и файлы ресурсов приложения, например, CSS-файлы.
  • Папка / src / test / java содержит все исходные файлы для модульных тестов
  • Папка / src / main / webapp содержит необходимые файлы для создания допустимого веб-приложения, например, «web.xml»
  • Папка / target содержит скомпилированные и упакованные результаты
  • Папка / war используется в процессе сборки и упаковки
  • «Pom.xml» — это файл объектной модели проекта (POM). Единый файл, который содержит все связанные с проектом конфигурации
  • Чтобы правильно интегрировать Spring с GWT во время выполнения, мы должны предоставить все необходимые библиотеки для веб-приложения. Откройте графический редактор вашего «pom.xml» и внесите следующие изменения:

    1. Найдите раздел «Свойства» на странице «Обзор» редактора POM и внесите следующие изменения:
    • Создайте новое свойство с именем org.springframework.version и значением 3.0.1.RELEASE
    • Создайте новое свойство с именем org.hibernate.version и значением 3.5.1-Final.
    • Измените значение свойства gwt.version на 2.0.3
    • Измените значения свойств maven.compiler.source и maven.compiler.target в соответствии с версией среды выполнения Java, мы будем использовать 1.6
  • Перейдите на страницу «Зависимости» редактора POM и создайте следующие зависимости (вы должны заполнить поля «GroupId», «Artifact Id» и «Version» в разделе «Dependency Details» на этой странице):
    • Идентификатор группы: org.springframework Идентификатор артефакта: spring-orm Версия: $ {org.springframework.version}
    • Идентификатор группы: org.springframework Идентификатор артефакта: spring-web Версия: $ {org.springframework.version}
    • Идентификатор группы: org.hibernate Идентификатор артефакта: hibernate-core Версия: $ {org.hibernate.version}
    • Идентификатор группы: org.hibernate Идентификатор артефакта: hibernate-annotations Версия: $ {org.hibernate.version}
    • Идентификатор группы: org.hibernate Идентификатор артефакта: hibernate-entitymanager Версия: $ {org.hibernate.version}
    • Идентификатор группы: org.hibernate.javax.persistence Идентификатор артефакта: hibernate-jpa-2.0-api Версия: 1.0.0.Final
    • Идентификатор группы: org.slf4j Идентификатор артефакта: slf4j-log4j12 Версия: 1.5.8
    • Идентификатор группы: org.hsqldb Идентификатор артефакта: hsqldb Версия: 1.8.0.10
    • Идентификатор группы: c3p0 Идентификатор артефакта: c3p0 Версия: 0.9.1.2
  • Найдите кнопку «Показать дополнительные вкладки» в правом верхнем углу вашего редактора POM и нажмите ее. Перейдите на страницу «Репозитории» и создайте следующий репозиторий (вы должны заполнить поля «Id» и «URL» в разделе «Репозиторий» на этой странице):
    • Идентификатор: JBoss URL: http://repository.jboss.org/maven2/

    Как видите, Maven декларативно управляет библиотечными зависимостями. Создается локальный репозиторий (по умолчанию в папке {user_home} /. M2), и все необходимые библиотеки загружаются и помещаются туда из общедоступных репозиториев. Кроме того, внутрибиблиотечные зависимости автоматически разрешаются и обрабатываются. Тем не менее, не все библиотеки доступны для публичных репозиториев. В этом случае мы должны вручную загрузить необходимую библиотеку и использовать ее в нашем проекте. Такой случай — библиотека «spring4gwt». Чтобы использовать его, мы должны создать папку «lib» в папке / src / main / webapp / WEB-INF и поместить туда «spring4gwt-0.0.1.jar». В следующей статье мы обсудим, как создать собственный общий репозиторий Maven и установить библиотеки, которые не являются общедоступными, так что следите за обновлениями!

    Следующим шагом является предоставление хуков для веб-приложения для загрузки контекста Spring при запуске и предоставления возможности «spring4gwt» перехватывать вызовы RPC между клиентом и сервером и преобразовывать их в вызовы службы Spring .

    Найдите файл «web.xml» в / src / main / webapp / WEB-INF и добавьте следующее:

    Для загрузки контекста Spring при запуске,

    1
    2
    3
    4
    5
    <listener>
     <listener-class>
      org.springframework.web.context.ContextLoaderListener
     </listener-class>
    </listener>

    В разделе сервлетов есть

    1
    2
    3
    4
    5
    <servlet>
     <servlet-name>springGwtRemoteServiceServlet</servlet-name>
     <servlet-class>org.spring4gwt.server.SpringGwtRemoteServiceServlet
     </servlet-class>
    </servlet>

    В разделе отображения сервлетов включите, чтобы spring4gwt перехватывал вызовы RPC.

    1
    2
    3
    4
    <servlet-mapping>
     <servlet-name>springGwtRemoteServiceServlet</servlet-name>
     <url-pattern>/com.javacodegeeks.gwtspring.Application/springGwtServices/*</url-pattern>
    </servlet-mapping>

    Что следует отметить здесь:

    1. Дочерний элемент url-pattern элемента servlet-mapping для сервлета springGwtRemoteServiceServlet должен быть изменен на любое имя вашего модуля GWT , например, {module_name} / springGwtServices / *, имя модуля по умолчанию {main_package} .Application
    2. Вы можете изменить имя сервлета spring4gwt (здесь SpringGwtRemoteServiceServlet) на любое другое

    Следующим шагом является создание файла «persistence.xml», чтобы описать соединение с базой данных с использованием JPA . Файл «pesistence.xml» должен находиться в каталоге META-INF, который, в свою очередь, должен быть доступен веб-приложению во время выполнения (в пути к классам). Чтобы выполнить вышеупомянутые требования, мы должны создать папку META-INF в папке проекта / src / main / resources. Наконец, создайте файл «persistence.xml» в папке / src / main / resources / META-INF. Пример persistence.xml представлен ниже

    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
     version="2.0">
     
     <persistence-unit name="MyPersistenceUnit" transaction-type="RESOURCE_LOCAL">
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
     
      <properties>
       <property name="hibernate.hbm2ddl.auto" value="update" />
       <property name="hibernate.show_sql" value="false" />
       <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />
       <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver" />
       <property name="hibernate.connection.url" value="jdbc:hsqldb:mem:javacodegeeks" />
       <property name="hibernate.connection.username" value="sa" />
       <property name="hibernate.connection.password" value="" />
     
       <property name="hibernate.c3p0.min_size" value="5" />
       <property name="hibernate.c3p0.max_size" value="20" />
       <property name="hibernate.c3p0.timeout" value="300" />
       <property name="hibernate.c3p0.max_statements" value="50" />
       <property name="hibernate.c3p0.idle_test_period" value="3000" />
     
      </properties>
     
     </persistence-unit>
     
    </persistence>

    Теперь давайте создадим файл applicationContext.xml, который будет управлять контейнером Spring . Создайте файл в каталоге / src / main / webapp / WEB-INF. Пример «applicationContext.xml» представлен ниже

    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
     xsi:schemaLocation="
     
     <context:component-scan base-package="com.javacodegeeks.gwtspring" />
     
     <tx:annotation-driven />
     
     <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
      <property name="persistenceUnitName" value="MyPersistenceUnit" />
     </bean>
     
     <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
      <property name="entityManagerFactory" ref="entityManagerFactory" />
     </bean>
     
    </beans>

    Что следует отметить здесь:

    1. Измените атрибут base-package элемента context: component-scan на тот, который является базовым пакетом вашего проекта, чтобы проверять его на наличие компонентов Spring (служб, DAO и т. Д.).
    2. Измените значение атрибута bean-компонента entityManagerFactory для свойства persistentUnitName на имя вашего постоянного модуля, как указано в файле «persistence.xml».

    В последней части этого руководства мы собираемся представить объект передачи данных (DTO) для передачи данных между клиентом и сервером, объект доступа к данным (DAO), который используется для доступа к базе данных, и сервис Spring для предоставления функциональности. к веб-клиенту GWT .

    DTO — это объект, который может использоваться как клиентом, так и сервером, поэтому вы должны создать подпакет «shared.dto» в вашем основном пакете (в нашем случае com.javacodegeeks.gwtspring) и поместить туда DTO. Мы собираемся создать EmployeeDTO, содержащий информацию для сотрудника, как показано ниже

    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
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    package com.javacodegeeks.gwtspring.shared.dto;
     
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.Id;
    import javax.persistence.Table;
     
    @Entity
    @Table(name = "EMPLOYEE")
    public class EmployeeDTO implements java.io.Serializable {
     
     private static final long serialVersionUID = 7440297955003302414L;
     
     @Id
     @Column(name="employee_id")
     private long employeeId;
     
     @Column(name="employee_name", nullable = false, length=30)
     private String employeeName;
     
     @Column(name="employee_surname", nullable = false, length=30)
     private String employeeSurname;
     
     @Column(name="job", length=50)
     private String job;
       
     public EmployeeDTO() {
     }
     
     public EmployeeDTO(int employeeId) {
      this.employeeId = employeeId; 
     }
     
     public EmployeeDTO(long employeeId, String employeeName, String employeeSurname,
       String job) {
      this.employeeId = employeeId;
      this.employeeName = employeeName;
      this.employeeSurname = employeeSurname;
      this.job = job;
     }
     
     public long getEmployeeId() {
      return employeeId;
     }
     
     public void setEmployeeId(long employeeId) {
      this.employeeId = employeeId;
     }
     
     public String getEmployeeName() {
      return employeeName;
     }
     
     public void setEmployeeName(String employeeName) {
      this.employeeName = employeeName;
     }
     
     public String getEmployeeSurname() {
      return employeeSurname;
     }
     
     public void setEmployeeSurname(String employeeSurname) {
      this.employeeSurname = employeeSurname;
     }
     
     public String getJob() {
      return job;
     }
     
     public void setJob(String job) {
      this.job = job;
     }
     
    }

    Чтобы объект DTO был доступен для клиента GWT, мы должны дать команду компилятору GWT проанализировать его. Для этого найдите основной файл модуля GWT , он должен называться «Application.gwt.xml» и находиться под вашим основным пакетом (в нашем случае com.javacodegeeks.gwtspring) и добавить следующие директивы:

    1
    2
    3
    <!-- Specify the paths for translatable code          -->
    <source path='client'/>
    <source path='shared'/>

    Объект DAO будет использоваться для доступа к базе данных и выполнения операций CRUD (Create Retrieve Update Delete). Это серверный компонент, поэтому его следует поместить в подпакет «server» нашего проекта. Создайте подпакет «server.dao» под основным пакетом проекта (в нашем случае com.javacodegeeks.gwtspring) и поместите туда DAO. Пример DAO представлен ниже

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    package com.javacodegeeks.gwtspring.server.dao;
     
    import javax.annotation.PostConstruct;
    import javax.persistence.EntityManagerFactory;
     
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Repository;
     
    import com.javacodegeeks.gwtspring.shared.dto.EmployeeDTO;
     
     
    @Repository("employeeDAO")
    public class EmployeeDAO extends JpaDAO<Long, EmployeeDTO> {
     
     @Autowired
     EntityManagerFactory entityManagerFactory;
     
     @PostConstruct
     public void init() {
      super.setEntityManagerFactory(entityManagerFactory);
     }
     
    }

    Как видите, класс EmployeeDAO расширяет базовый класс DAO (JpaDAO). Класс EmployeeDAO может содержать конкретные запросы, касающиеся объекта EmployeeDTO, но все операции CRUD могут обрабатываться из базового класса DAO (JpaDAO). Разместите класс JpaDAO на том же уровне, что и класс EmployeeDAO, в подпакете «dao». Ниже мы представляем класс JpaDAO

    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
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    package com.javacodegeeks.gwtspring.server.dao;
     
    import java.lang.reflect.ParameterizedType;
    import java.util.List;
     
    import javax.persistence.EntityManager;
    import javax.persistence.PersistenceException;
    import javax.persistence.Query;
     
    import org.springframework.orm.jpa.JpaCallback;
    import org.springframework.orm.jpa.support.JpaDaoSupport;
     
    public abstract class JpaDAO<K, E> extends JpaDaoSupport {
     protected Class<E> entityClass;
     
     @SuppressWarnings("unchecked")
     public JpaDAO() {
      ParameterizedType genericSuperclass = (ParameterizedType) getClass()
        .getGenericSuperclass();
      this.entityClass = (Class<E>) genericSuperclass
        .getActualTypeArguments()[1];
     }
     
     public void persist(E entity) {
      getJpaTemplate().persist(entity);
     }
     
     public void remove(E entity) {
      getJpaTemplate().remove(entity);
     }
       
     public E merge(E entity) {
      return getJpaTemplate().merge(entity);
     }
       
     public void refresh(E entity) {
      getJpaTemplate().refresh(entity);
     }
     
     public E findById(K id) {
      return getJpaTemplate().find(entityClass, id);
     }
       
     public E flush(E entity) {
      getJpaTemplate().flush();
      return entity;
     }
       
     @SuppressWarnings("unchecked")
     public List<E> findAll() {
      Object res = getJpaTemplate().execute(new JpaCallback() {
     
       public Object doInJpa(EntityManager em) throws PersistenceException {
        Query q = em.createQuery("SELECT h FROM " +
          entityClass.getName() + " h");
        return q.getResultList();
       }
         
      });
        
      return (List<E>) res;
     }
     
     @SuppressWarnings("unchecked")
     public Integer removeAll() {
      return (Integer) getJpaTemplate().execute(new JpaCallback() {
     
       public Object doInJpa(EntityManager em) throws PersistenceException {
        Query q = em.createQuery("DELETE FROM " +
          entityClass.getName() + " h");
        return q.executeUpdate();
       }
         
      });
     }
       
    }

    Наконец, мы собираемся создать интерфейс службы и классы реализации для доступа клиента GWT . Интерфейс сервиса должен быть доступен как клиенту, так и серверу, поэтому его следует поместить в «общий» подпакет нашего проекта. Создайте подпакет «services» и разместите там интерфейс сервиса. Ниже приведен пример класса интерфейса

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    package com.javacodegeeks.gwtspring.shared.services;
     
    import com.google.gwt.user.client.rpc.RemoteService;
    import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
     
    import com.javacodegeeks.gwtspring.shared.dto.EmployeeDTO;
     
    @RemoteServiceRelativePath("springGwtServices/employeeService")
    public interface EmployeeService extends RemoteService {
     
     public EmployeeDTO findEmployee(long employeeId);
     public void saveEmployee(long employeeId, String name, String surname, String jobDescription) throws Exception;
     public void updateEmployee(long employeeId, String name, String surname, String jobDescription) throws Exception;
     public void saveOrUpdateEmployee(long employeeId, String name, String surname, String jobDescription) throws Exception;
     public void deleteEmployee(long employeeId) throws Exception;
     
    }

    Что следует отметить здесь:

    1. Клиент GWT должен иметь возможность выполнять асинхронные удаленные вызовы процедур (RPC) для службы на стороне сервера. Таким образом, интерфейс службы должен расширять интерфейс RemoteService. Maven автоматически создает асинхронный аналог указанного интерфейса перед компиляцией проекта и упаковкой граней
    2. Мы аннотируем интерфейс, чтобы определить URL, по которому будет доступен сервис. Поскольку сервис является сервисом Spring, мы хотим, чтобы «spring4gwt» перехватывал вызовы RPC и выполнял вызов сервиса Spring . Для этого мы определяем относительный путь, который будет обрабатываться «springGwtRemoteServiceServlet», объявленным в нашем «web.xml», как показано выше.
    3. Имя службы, объявленное в аннотации «RemoteServiceRelativePath», в данном случае «employeeService», должно соответствовать имени bean-компонента службы Spring . Мы определим имя бина службы Spring в классе реализации службы (см. Ниже)

    Класс реализации сервиса является серверным компонентом, поэтому мы должны поместить его в подпакет «server» нашего проекта. Создайте подпакет «услуги» и разместите его там. Пример класса реализации сервиса представлен ниже

    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
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    package com.javacodegeeks.gwtspring.server.services;
     
    import javax.annotation.PostConstruct;
    import javax.annotation.PreDestroy;
     
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
     
    import com.javacodegeeks.gwtspring.server.dao.EmployeeDAO;
    import com.javacodegeeks.gwtspring.shared.dto.EmployeeDTO;
    import com.javacodegeeks.gwtspring.shared.services.EmployeeService;
     
    @Service("employeeService")
    public class EmployeeServiceImpl implements EmployeeService {
       
     @Autowired
     private EmployeeDAO employeeDAO;
     
     @PostConstruct
     public void init() throws Exception {
     }
       
     @PreDestroy
     public void destroy() {
     }
     
     public EmployeeDTO findEmployee(long employeeId) {
        
      return employeeDAO.findById(employeeId);
        
     }
       
     @Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
     public void saveEmployee(long employeeId, String name, String surname, String jobDescription) throws Exception {
         
      EmployeeDTO employeeDTO = employeeDAO.findById(employeeId);
        
      if(employeeDTO == null) {
       employeeDTO = new EmployeeDTO(employeeId, name,surname, jobDescription);
       employeeDAO.persist(employeeDTO);
      }
        
     }
       
     @Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
     public void updateEmployee(long employeeId, String name, String surname, String jobDescription) throws Exception {
        
      EmployeeDTO employeeDTO = employeeDAO.findById(employeeId);
        
      if(employeeDTO != null) {
       employeeDTO.setEmployeeName(name);
       employeeDTO.setEmployeeSurname(surname);
       employeeDTO.setJob(jobDescription);
      }
     
     }
       
     @Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
     public void deleteEmployee(long employeeId) throws Exception {
        
      EmployeeDTO employeeDTO = employeeDAO.findById(employeeId);
        
      if(employeeDTO != null)
       employeeDAO.remove(employeeDTO);
     
     }
       
     @Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
     public void saveOrUpdateEmployee(long employeeId, String name, String surname, String jobDescription) throws Exception {
        
      EmployeeDTO employeeDTO = new EmployeeDTO(employeeId, name,surname, jobDescription);
        
      employeeDAO.merge(employeeDTO);
        
     }
     
    }

    Что следует отметить здесь:

    1. Мы используем аннотацию стереотипа @Service («employeeService»), чтобы объявить, что этот класс представляет службу Spring с именем «exampleService». Контейнер Spring будет запускать все службы при запуске
    2. Мы используем аннотацию @Autowire, чтобы внедрить экземпляр класса DAO в employeeService. Для правильного создания экземпляра службы контейнер Spring должен сначала разрешить все потенциальные ссылки между службами, поэтому он создает экземпляр класса DAO и внедряет экземпляр в соответствующее поле employeeService — поле employeeDAO. В случае, если вам интересно, внедрение зависимости выполняется в соответствии с типом (Class) и, если не выполняется в соответствии с именем, это означает, что если мы определили несколько служб одного и того же типа (Class), внедренная будет одна с тем же именем как обозначенное поле
    3. Мы используем аннотации Java @PostConstruct и @PreDestroy, чтобы объявить методы, которые будут вызываться контейнером Spring после инициализации (все внедрения зависимостей сделаны) и предварительного уничтожения службы
    4. Мы используем аннотацию @Transactional для всех методов, которым необходимо выполнить операцию обновления базы данных (INSERT, UPDATE, DELETE).
    5. Мы НЕ используем аннотацию @Transactional для методов, которые выполняют операции извлечения (FIND) над базой данных (за исключением объектов, которые содержат лениво инициализированные ссылки — см. Ниже), и / или не выполняют никаких операций с базой данных. Это связано с тем, что каждый раз, когда вы вызываете метод, аннотированный как транзакционный, контейнер Spring включает в себя диспетчер сущностей вызова JPA и, как следствие, диспетчер транзакций платформы, чтобы определить поведение транзакции, которое будет применяться, вводя заметное снижение производительности, особенно для приложений с низкой задержкой / высокой пропускной способностью
    6. Для методов, которые выполняют операции извлечения (FIND) для объектов, которые содержат лениво инициализированные ссылки, вы должны использовать аннотацию @Transactional, обозначая тип распространения «NESTED», чтобы Spring мог поддерживать сеанс Hibernate открытым для всего вызова метода
    7. Транзакционное поведение применяется только при обращениях клиентов к сервису. Транзакционное поведение не применяется к внутриоперационным вызовам. Например, если клиент вызывает операцию, которая не аннотирована как транзакционная, и реализация последней вводит вызов другой операции того же сервиса, которая аннотирована транзакционной, тогда для объединенных операций транзакционное поведение не будет применяться. Это связано с тем, что Spring использует прокси-классы AOP для обеспечения поведения транзакций

    Мы почти закончили !, нам нужно разработать пользовательский интерфейс GWT для доступа к нашему сервису Spring .

    Найдите точку входа вашего приложения GWT . Файл должен называться «Application.java» и находиться в подпакете «client» или в нашем основном пакете. Измените класс точки входа, как показано ниже

    001
    002
    003
    004
    005
    006
    007
    008
    009
    010
    011
    012
    013
    014
    015
    016
    017
    018
    019
    020
    021
    022
    023
    024
    025
    026
    027
    028
    029
    030
    031
    032
    033
    034
    035
    036
    037
    038
    039
    040
    041
    042
    043
    044
    045
    046
    047
    048
    049
    050
    051
    052
    053
    054
    055
    056
    057
    058
    059
    060
    061
    062
    063
    064
    065
    066
    067
    068
    069
    070
    071
    072
    073
    074
    075
    076
    077
    078
    079
    080
    081
    082
    083
    084
    085
    086
    087
    088
    089
    090
    091
    092
    093
    094
    095
    096
    097
    098
    099
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    package com.javacodegeeks.gwtspring.client;
     
    import com.google.gwt.core.client.EntryPoint;
    import com.google.gwt.event.dom.client.ClickEvent;
    import com.google.gwt.event.dom.client.ClickHandler;
    import com.google.gwt.event.dom.client.KeyCodes;
    import com.google.gwt.event.dom.client.KeyUpEvent;
    import com.google.gwt.event.dom.client.KeyUpHandler;
    import com.google.gwt.user.client.rpc.AsyncCallback;
    import com.google.gwt.user.client.ui.Button;
    import com.google.gwt.user.client.ui.DialogBox;
    import com.google.gwt.user.client.ui.HTML;
    import com.google.gwt.user.client.ui.Label;
    import com.google.gwt.user.client.ui.RootPanel;
    import com.google.gwt.user.client.ui.TextBox;
    import com.google.gwt.user.client.ui.VerticalPanel;
    import com.javacodegeeks.gwtspring.shared.dto.EmployeeDTO;
    import com.javacodegeeks.gwtspring.shared.services.EmployeeServiceAsync;
     
     
    /**
     * Entry point classes define <code>onModuleLoad()</code>.
     */
    public class Application
        implements EntryPoint
    {
     
     /**
      * The message displayed to the user when the server cannot be reached or
      * returns an error.
      */
     private static final String SERVER_ERROR = "An error occurred while "
       + "attempting to contact the server. Please check your network "
       + "connection and try again. The error is : ";
     
     /**
      * Create a remote service proxy to talk to the server-side Employee service.
      */
     private final EmployeeServiceAsync employeeService = EmployeeServiceAsync.Util.getInstance();
     
     /**
      * This is the entry point method.
      */
     public void onModuleLoad() {
      final Button saveOrUpdateButton = new Button("SaveOrUpdate");
      final Button retrieveButton = new Button("Retrieve");
      final TextBox employeeInfoField = new TextBox();
      employeeInfoField.setText("Employee Info");
      final TextBox employeeIdField = new TextBox();
      final Label errorLabel = new Label();
     
      // We can add style names to widgets
      saveOrUpdateButton.addStyleName("sendButton");
      retrieveButton.addStyleName("sendButton");
     
      // Add the nameField and sendButton to the RootPanel
      // Use RootPanel.get() to get the entire body element
      RootPanel.get("employeeInfoFieldContainer").add(employeeInfoField);
      RootPanel.get("updateEmployeeButtonContainer").add(saveOrUpdateButton);
      RootPanel.get("employeeIdFieldContainer").add(employeeIdField);
      RootPanel.get("retrieveEmployeeButtonContainer").add(retrieveButton);
      RootPanel.get("errorLabelContainer").add(errorLabel);
     
      // Focus the cursor on the name field when the app loads
      employeeInfoField.setFocus(true);
      employeeInfoField.selectAll();
     
      // Create the popup dialog box
      final DialogBox dialogBox = new DialogBox();
      dialogBox.setText("Remote Procedure Call");
      dialogBox.setAnimationEnabled(true);
      final Button closeButton = new Button("Close");
      // We can set the id of a widget by accessing its Element
      closeButton.getElement().setId("closeButton");
      final Label textToServerLabel = new Label();
      final HTML serverResponseLabel = new HTML();
      VerticalPanel dialogVPanel = new VerticalPanel();
      dialogVPanel.addStyleName("dialogVPanel");
      dialogVPanel.add(new HTML("<b>Sending request to the server:</b>"));
      dialogVPanel.add(textToServerLabel);
      dialogVPanel.add(new HTML("<br><b>Server replies:</b>"));
      dialogVPanel.add(serverResponseLabel);
      dialogVPanel.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT);
      dialogVPanel.add(closeButton);
      dialogBox.setWidget(dialogVPanel);
     
      // Add a handler to close the DialogBox
      closeButton.addClickHandler(new ClickHandler() {
       public void onClick(ClickEvent event) {
        dialogBox.hide();
        saveOrUpdateButton.setEnabled(true);
        saveOrUpdateButton.setFocus(true);
        retrieveButton.setEnabled(true);
       }
      });
     
      // Create a handler for the saveOrUpdateButton and employeeInfoField
      class SaveOrUpdateEmployeeHandler implements ClickHandler, KeyUpHandler {
       /**
        * Fired when the user clicks on the saveOrUpdateButton.
        */
       public void onClick(ClickEvent event) {
        sendEmployeeInfoToServer();
       }
     
       /**
        * Fired when the user types in the employeeInfoField.
        */
       public void onKeyUp(KeyUpEvent event) {
        if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
         sendEmployeeInfoToServer();
        }
       }
     
       /**
        * Send the employee info from the employeeInfoField to the server and wait for a response.
        */
       private void sendEmployeeInfoToServer() {
        // First, we validate the input.
        errorLabel.setText("");
        String textToServer = employeeInfoField.getText();
     
        // Then, we send the input to the server.
        saveOrUpdateButton.setEnabled(false);
        textToServerLabel.setText(textToServer);
        serverResponseLabel.setText("");
     
        String[] employeeInfo = textToServer.split(" ");
         
        long employeeId = Long.parseLong(employeeInfo[0]);
        String employeeName = employeeInfo[1];
        String employeeSurname = employeeInfo[2];
        String employeeJobTitle = employeeInfo[3];
         
        employeeService.saveOrUpdateEmployee(employeeId, employeeName, employeeSurname, employeeJobTitle,
          new AsyncCallback<Void>() {
           public void onFailure(Throwable caught) {
            // Show the RPC error message to the user
            dialogBox
              .setText("Remote Procedure Call - Failure");
            serverResponseLabel
              .addStyleName("serverResponseLabelError");
            serverResponseLabel.setHTML(SERVER_ERROR + caught.toString());
            dialogBox.center();
            closeButton.setFocus(true);
           }
     
           public void onSuccess(Void noAnswer) {
            dialogBox.setText("Remote Procedure Call");
            serverResponseLabel
              .removeStyleName("serverResponseLabelError");
            serverResponseLabel.setHTML("OK");
            dialogBox.center();
            closeButton.setFocus(true);
           }
          });
       }
      }
       
      // Create a handler for the retrieveButton and employeeIdField
      class RetrieveEmployeeHandler implements ClickHandler, KeyUpHandler {
       /**
        * Fired when the user clicks on the retrieveButton.
        */
       public void onClick(ClickEvent event) {
        sendEmployeeIdToServer();
       }
     
       /**
        * Fired when the user types in the employeeIdField.
        */
       public void onKeyUp(KeyUpEvent event) {
        if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
         sendEmployeeIdToServer();
        }
       }
     
       /**
        * Send the id from the employeeIdField to the server and wait for a response.
        */
       private void sendEmployeeIdToServer() {
        // First, we validate the input.
        errorLabel.setText("");
        String textToServer = employeeIdField.getText();
     
        // Then, we send the input to the server.
        retrieveButton.setEnabled(false);
        textToServerLabel.setText(textToServer);
        serverResponseLabel.setText("");
     
        employeeService.findEmployee(Long.parseLong(textToServer), 
          new AsyncCallback<EmployeeDTO>() {
           public void onFailure(Throwable caught) {
            // Show the RPC error message to the user
            dialogBox
              .setText("Remote Procedure Call - Failure");
            serverResponseLabel
              .addStyleName("serverResponseLabelError");
            serverResponseLabel.setHTML(SERVER_ERROR + caught.toString());
            dialogBox.center();
            closeButton.setFocus(true);
           }
     
           public void onSuccess(EmployeeDTO employeeDTO) {
            dialogBox.setText("Remote Procedure Call");
            serverResponseLabel
              .removeStyleName("serverResponseLabelError");
            if(employeeDTO != null)
             serverResponseLabel.setHTML("Employee Information <br>Id : " + employeeDTO.getEmployeeId() + "<br>Name : " + employeeDTO.getEmployeeName() + "<br>Surname : " + employeeDTO.getEmployeeSurname() + "<br>Job Title : " + employeeDTO.getJob());
            else
             serverResponseLabel.setHTML("No employee with the specified id found");
            dialogBox.center();
            closeButton.setFocus(true);
           }
          });
       }
      }
     
      // Add a handler to send the employee info to the server
      SaveOrUpdateEmployeeHandler saveOrUpdateEmployeehandler = new SaveOrUpdateEmployeeHandler();
      saveOrUpdateButton.addClickHandler(saveOrUpdateEmployeehandler);
      employeeInfoField.addKeyUpHandler(saveOrUpdateEmployeehandler);
       
      // Add a handler to send the employee id to the server
      RetrieveEmployeeHandler retrieveEmployeehandler = new RetrieveEmployeeHandler();
      retrieveButton.addClickHandler(retrieveEmployeehandler);
      employeeIdField.addKeyUpHandler(retrieveEmployeehandler);
     }
     
    }

    Как видите, вызовы службы Spring выполняются так же, как и классические вызовы службы GWT , прозрачно для клиента.

    Наконец найдите главную веб-страницу для вашего проекта. Файл должен называться «Application.html» и находиться в / src / main / resources / {main_package} / публичной папке вашего проекта (в нашем случае / src / main / resources / com / javacodegeeks / gwtspring / public). Измените главную веб-страницу, как показано ниже

    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
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <!-- The HTML 4.01 Transitional DOCTYPE declaration-->
    <!-- above set at the top of the file will set     -->
    <!-- the browser's rendering engine into           -->
    <!-- "Quirks Mode". Replacing this declaration     -->
    <!-- with a "Standards Mode" doctype is supported, -->
    <!-- but may lead to some differences in layout.   -->
     
    <html>
      <head>
        <meta http-equiv="content-type" content="text/html; charset=UTF-8">
        <!--                                           -->
        <!-- Any title is fine                         -->
        <!--                                           -->
        <title><a href="http://www.springsource.org/">Spring</a> <a href="https://code.google.com/webtoolkit/">GWT</a> Web Application Starter Project</title>
         
        <!--                                           -->
        <!-- This script loads your compiled module.   -->
        <!-- If you add any <a href="https://code.google.com/webtoolkit/">GWT</a> meta tags, they must   -->
        <!-- be added before this line.                -->
        <!--                                           -->
        <script type="text/javascript" language="javascript" src="com.javacodegeeks.gwtspring.Application.nocache.js"></script>
      </head>
     
      <!--                                           -->
      <!-- The body can have arbitrary html, or      -->
      <!-- you can leave the body empty if you want  -->
      <!-- to create a completely dynamic UI.        -->
      <!--                                           -->
      <body>
     
        <!-- OPTIONAL: include this if you want history support -->
        <iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' style="position:absolute;width:0;height:0;border:0"></iframe>
     
        <!-- RECOMMENDED if your web app will not function without JavaScript enabled -->
        <noscript>
          <div style="width: 22em; position: absolute; left: 50%; margin-left: -11em; color: red; background-color: white; border: 1px solid red; padding: 4px; font-family: sans-serif">
            Your web browser must have JavaScript enabled
            in order for this application to display correctly.
          </div>
        </noscript>
     
        <h1 align="center"><a href="http://www.springsource.org/">Spring</a> <a href="https://code.google.com/webtoolkit/">GWT</a> Web Application Starter Project</h1>
     
        <table align="center">
          <tr>
            <td colspan="2" style="font-weight:bold;">Please enter employee info (id name surname job):</td>       
          </tr>
          <tr>
            <td id="employeeInfoFieldContainer"></td>
            <td id="updateEmployeeButtonContainer"></td>
          </tr>
          <tr>
          <tr>
            <td colspan="2" style="font-weight:bold;">Please enter employee id:</td>       
          </tr>
          <tr>
            <td id="employeeIdFieldContainer"></td>
            <td id="retrieveEmployeeButtonContainer"></td>
          </tr>
          <tr>
            <td colspan="2" style="color:red;" id="errorLabelContainer"></td>
          </tr>
        </table>
     
      </body>
    </html>

    Чтобы создать приложение, щелкните правой кнопкой мыши на вашем проекте? Беги как ? Maven пакет

    Для развертывания веб-приложения просто скопируйте файл «.war» из каталога «target» в папку Apache — Tomcat «webapps».

    Для запуска приложения укажите ваш браузер по следующему адресу

    HTTP: // локальный: 8080 / {application_name} /

    Если все прошло хорошо, вы должны увидеть свою главную веб-страницу. Должны отображаться два текстовых поля, за которыми следует кнопка. В первом текстовом поле вы можете сохранить или обновить сотрудника в базе данных. Введите в качестве входных данных идентификатор, имя, фамилию и описание задания, разделенные пробелом. При нажатии на кнопку «SaveOrUpdate» предоставленная информация будет сохранена в базе данных. Для существующих записей сотрудников (с тем же идентификатором) будет выполнено обновление. Второе текстовое поле используется для получения существующих записей сотрудников. Введите идентификатор сотрудника и нажмите кнопку «Получить». Если сотрудник существует, вы должны увидеть его идентификатор, имя, фамилию и описание работы.

    Вы можете скачать проект здесь

    Надеюсь, вам понравилось

    Джастин

    Статьи по Теме :
    Связанные фрагменты: