Статьи

GWT 2 Spring 3 JPA 2 Hibernate 3.5 Учебник

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

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

  1. Затмение отсюда
  2. Плагин GWT для Eclipse отсюда
  3. Распределение рамок Spring отсюда
  4. Распределение фреймворка Hibernate персистентности отсюда
  5. Гиперзвуковая база данных отсюда
  6. Библиотека Apache Commons-Logging отсюда
  7. AOP Alliance (стандарт Java / J2EE AOP) отсюда
  8. Библиотека SLF4J отсюда
  9. Библиотека Apache log4j отсюда
  10. И последнее, но не менее важное: скачайте библиотеку GWT — Spring «glue» spring4gwt отсюда

Мы будем использовать Eclipse Galileo, GWT версии 2.0.3, Spring версии 3.0.1, Hibernate версии 3.5.2, Hypersonic версии 1.8.1.2, Apache commons-logging version 1.1.1, AOP Alliance (Java / J2EE AOP Standard) версия 1.0, SLF4J версии 1.5.8, Apache log4j версии 1.2.16 и spring4gwt версии 0.0.1 для этого руководства.

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

  1. Создать новый проект GWT , перейти к файлу? Новый проект веб-приложения
  2. Мы назовем наш проект GWTSpring. Базовый пакет будет com.javacodegeeks.gwtspring, также используйте только Google Web Toolkit, поэтому снимите флажок «Использовать Google App Engine » в окне мастера.

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

  1. Папка / src содержит все исходные файлы приложения
  • {package_name} .client subpackage содержит все исходные файлы, доступные только для клиентской части приложения
  • Подпакет {имя_пакета} .server содержит все исходные файлы, доступные только для серверной части приложения.
  • Подпакет {имя_пакета} .shared содержит все исходные файлы, доступные как клиентской, так и серверной части приложения.
  • Папка / test содержит все исходные файлы для модульных тестов
  • Папка / war содержит необходимые файлы для создания корректного веб-приложения.
  • Чтобы правильно интегрировать Spring с GWT во время выполнения, мы должны предоставить все необходимые библиотеки для веб-приложения. Поэтому скопируйте файлы, перечисленные ниже в / war / WEB-INF / lib (скопируйте соответствующие файлы, если вы используете разные версии)

    Из весны распространения

    • /dist/org.springframework.expression-3.0.1.RELEASE-A.jar
    • /dist/org.springframework.beans-3.0.1.RELEASE-A.jar
    • /dist/org.springframework.oxm-3.0.1.RELEASE-A.jar
    • /dist/org.springframework.jms-3.0.1.RELEASE-A.jar
    • /dist/org.springframework.jdbc-3.0.1.RELEASE-A.jar
    • /dist/org.springframework.core-3.0.1.RELEASE-A.jar
    • /dist/org.springframework.context-3.0.1.RELEASE-A.jar
    • /dist/org.springframework.asm-3.0.1.RELEASE-A.jar
    • /dist/org.springframework.aspects-3.0.1.RELEASE-A.jar
    • /dist/org.springframework.transaction-3.0.1.RELEASE-A.jar
    • /dist/org.springframework.context.support-3.0.1.RELEASE-A.jar
    • /dist/org.springframework.aop-3.0.1.RELEASE-A.jar
    • /dist/org.springframework.orm-3.0.1.RELEASE-A.jar
    • /dist/org.springframework.instrument-3.0.1.RELEASE-A.jar
    • /dist/org.springframework.instrument.tomcat-3.0.1.RELEASE-A.jar
    • /dist/org.springframework.test-3.0.1.RELEASE-A.jar
    • /dist/org.springframework.web-3.0.1.RELEASE-A.jar
    • /dist/org.springframework.web.portlet-3.0.1.RELEASE-A.jar
    • /dist/org.springframework.web.servlet-3.0.1.RELEASE-A.jar
    • /dist/org.springframework.web.struts-3.0.1.RELEASE-A.jar

    Из дистрибутива Hibernate

    • hibernate3.jar
    • /lib/required/antlr-2.7.6.jar
    • /lib/required/commons-collections-3.1.jar
    • /lib/required/dom4j-1.6.1.jar
    • /lib/required/javassist-3.9.0.GA.jar
    • /lib/required/jta-1.1.jar
    • /lib/required/slf4j-api-1.5.8.jar
    • /lib/jpa/hibernate-jpa-2.0-api-1.0.0.Final.jar
    • /lib/optional/c3p0/c3p0-0.9.1.jar

    Из гиперзвукового распределения

    • /lib/hsqldb.jar

    Из дистрибутива регистрации Apache Commons

    • Обще-каротаж 1.1.1.jar

    Из дистрибутива AOP Alliance (стандарт Java / J2EE AOP)

    • aopalliance.jar

    Из дистрибутива SLF4J

    • SLF4J-log4j12-1.5.8.jar

    Из дистрибутива Apache log4j

    • log4j-1.2.16.jar

    Библиотека sping4gwt

    • spring4gwt-0.0.1.jar

    Теперь нам нужно позаботиться о зависимостях для нашего проекта Eclipse . Следующие jar-файлы должны быть включены в путь сборки Java проекта:

    • спящий режим JPA-2,0-апи-1.0.0.Final.jar-
    • org.springframework.beans-3.0.1.RELEASE-a.jar
    • org.springframework.context-3.0.1.RELEASE-a.jar
    • org.springframework.core-3.0.1.RELEASE-a.jar
    • org.springframework.orm-3.0.1.RELEASE-a.jar
    • org.springframework.transaction-3.0.1.RELEASE-a.jar

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

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

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

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

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

    1
    2
    3
    4
    <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>/gwtspring/springGwtServices/*</url-pattern>
    </servlet-mapping>

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

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

    Для продолжения мы должны создать файл persistence.xml, чтобы описать соединение с базой данных с использованием JPA . Файл pesistence.xml должен находиться в каталоге META-INF, который, в свою очередь, должен быть доступен веб-приложению во время выполнения (в пути к классам). Чтобы выполнить вышеупомянутые требования, мы должны создать папку META-INF в папке проекта / war / WEB-INF / classes. Для этого мы создаем новую исходную папку с именем, например, «resources», и создаем внутри нее папку META-INF. Наконец, создайте файл persistence.xml в папке / 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
        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>

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

    1. Если вы намереваетесь развернуть веб-приложение на сервере приложений J2EE, который поддерживает транзакции JTA, например, JBoss, или использовать другие базы данных, например Oracle , MySQL и т. Д., Ознакомьтесь с нашим «Учебником по JBoss Spring JPA Hibernate» здесь , чтобы найти альтернативные конфигурации.

    Теперь давайте создадим файл applicationContext.xml, который будет управлять контейнером Spring . Создайте файл в каталоге / war / 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
    27
    28
    29
    30
    31
    32
        xsi:schemaLocation="
     
        <context:component-scan base-package="com.javacodegeeks.gwtspring"/>
     
        <task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
     
        <task:executor id="myExecutor" pool-size="5"/>
     
        <task:scheduler id="myScheduler" pool-size="10"/>
     
        <tx:annotation-driven/>
     
        <bean class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean" id="entityManagerFactory">
            <property name="persistenceUnitName" value="MyPersistenceUnit"/>
        </bean>
     
        <bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
            <property name="entityManagerFactory" ref="entityManagerFactory"/>
        </bean>
     
    </beans>

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

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

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

    DTO — это объект, который может использоваться как клиентом, так и сервером, поэтому вы должны создать подпакет «dto» в пакете «shared» и поместить туда 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;
        }
     
    }

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

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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. Асинхронный аналог указанного интерфейса также должен быть предоставлен для включения асинхронной связи (см. Ниже)
    2. Мы аннотируем интерфейс, чтобы определить URL, по которому будет доступен сервис. Поскольку сервис является сервисом Spring, мы хотим, чтобы spring4gwt перехватывает вызовы RPC и выполняет вызов сервиса Spring . Для этого мы определяем относительный путь, который будет обрабатываться «springGwtRemoteServiceServlet», объявленным в нашем файле web.xml, как показано выше.
    3. Имя службы, объявленное в аннотации «RemoteServiceRelativePath», в данном случае «employeeService», должно соответствовать имени bean-компонента службы Spring . Мы определим имя бина службы Spring в классе реализации службы (см. Ниже)

    Асинхронный счетчик части сервисного интерфейса следует

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    package com.javacodegeeks.gwtspring.shared.services;
     
    import com.google.gwt.user.client.rpc.AsyncCallback;
    import com.javacodegeeks.gwtspring.shared.dto.EmployeeDTO;
     
    public interface EmployeeServiceAsync {
     
        void deleteEmployee(long employeeId, AsyncCallback<Void> callback);
     
        void findEmployee(long employeeId, AsyncCallback<EmployeeDTO> callback);
     
        void saveEmployee(long employeeId, String name, String surname,
                String jobDescription, AsyncCallback<Void> callback);
     
        void saveOrUpdateEmployee(long employeeId, String name, String surname,
                String jobDescription, AsyncCallback<Void> callback);
     
        void updateEmployee(long employeeId, String name, String surname,
                String jobDescription, AsyncCallback<Void> callback);
     
    }

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

    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. Транзакционное поведение применяется только при обращениях клиентов к сервису. Транзакционное поведение не применяется к внутриоперационным вызовам. Например, если клиент вызывает операцию, которая не аннотирована как транзакционная, и реализация последней вводит вызов другой операции того же сервиса, которая аннотирована транзакционной, тогда для объединенных операций транзакционное поведение не будет применено.

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

    Найдите точку входа вашего приложения GWT . Файл должен называться как {имя_проекта} .java, в нашем случае GWTSpring.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
    package com.javacodegeeks.gwtspring.client;
     
    import com.google.gwt.core.client.EntryPoint;
    import com.google.gwt.core.client.GWT;
    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.EmployeeService;
    import com.javacodegeeks.gwtspring.shared.services.EmployeeServiceAsync;
     
    /**
     * Entry point classes define <code>onModuleLoad()</code>.
     */
    public class GWTSpring 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 = GWT
                .create(EmployeeService.class);
     
        /**
         * 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("
    <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 Id : " + employeeDTO.getEmployeeId() + " Name : " + employeeDTO.getEmployeeName() + " Surname : " + employeeDTO.getEmployeeSurname() + " 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 , прозрачно для клиента.

    Наконец найдите главную веб-страницу для вашего проекта. Файл должен называться как {имя_проекта} .html, в нашем случае GWTSpring.html, и находиться в папке / war нашего проекта. Измените главную веб-страницу, как показано ниже

    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
    <!doctype html>
    <!-- The DOCTYPE declaration above will set the    -->
    <!-- browser's rendering engine into               -->
    <!-- "Standards Mode". Replacing this declaration  -->
    <!-- with a "Quirks Mode" doctype may lead to some -->
    <!-- differences in layout.                        -->
     
    <html>
      <head>
        <meta http-equiv="content-type" content="text/html; charset=UTF-8">
     
        <!--                                                               -->
        <!-- Consider inlining CSS to reduce the number of requested files -->
        <!--                                                               -->
        <link type="text/css" rel="stylesheet" href="GWTSpring.css">
     
        <!--                                           -->
        <!-- Any title is fine                         -->
        <!--                                           -->
        <title>Spring GWT Web Application Starter Project</title>
         
        <!--                                           -->
        <!-- This script loads your compiled module.   -->
        <!-- If you add any GWT meta tags, they must   -->
        <!-- be added before this line.                -->
        <!--                                           -->
        <script type="text/javascript" language="javascript" src="gwtspring/gwtspring.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>Spring GWT 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>

    Чтобы скомпилировать приложение, щелкните правой кнопкой мыши имя проекта и выберите «Запуск от имени»? Скомпилировать приложение GWT

    Для развертывания веб-приложения просто скопируйте папку / war в папку «webapps» Apache — Tomcat . Вы можете изменить имя папки war на любое другое, желательно переименовать его после имени проекта, например, GWTSpring.

    Чтобы запустить приложение, укажите ваш браузер по следующему адресу

    HTTP: // локальный: 8080 / GWTSpring /

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

    Мало, это был большой урок!

    Вы можете скачать проект отсюда (необходимые сторонние библиотеки, как описано в начале, не включены)

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

    Джастин

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