Каталог

Разработка веб-приложения

При изучении технологий Ext JS и Java, написал web-приложение «Каталог автомобилей». Хочу поделиться с Вами этим опытом.

Вид и функциональность приложения

  • Добавление;
  • Удаление;
  • Редактирование;
  • Поиск;
  • Валидация данных;



Инструменты

  1. IntelliJ IDEA 13 скачать
  2. Ext JS 5.0.1 скачать
  3. Apache Tomcat 8.0.12 скачать
  4. MySQL 5.6.20 скачать
  5. Apache Maven 3.0.5 скачать
  6. Java 1.8.0_20 скачать
  7. Java doc читать
  8. Ext JS Guides читать
  9. Hibernate читать
  10. JPA читать
  11. Spring читать
  12. SQL читать
  13. MVC читать
  14. DAO читать
  15. Layer Service читать
  16. Дескриптор развертывания читать


Укажите путь к Java в Project SDK:


Укажите путь к своей Maven home directory:


«Maven projects need to be imported» кликаем Enable Auto-Import

Добавим Tomcat Server:


В Application server укажите путь до Tomcat сервера:



Ok -> Apply -> Ok

Проверим, что всё работает:


Клиент (ExtJS)

Добавим файлы фрэймворка Ext JS:

Модель Ext JS MVC:

Создадим файл app.js:

Ext.application(< name: 'CarCatalog', launch: function () < Ext.create('Ext.container.Viewport', < layout: 'fit', items: < xtype: 'panel', html: '

Каталог автомобилей

' > >); > >);
  • Метод Ext.application инициализирует приложение Ext JS;
  • name: ‘CarCatalog’ указывает имя приложения, которое будет затем использоваться для создания полных имен классов приложения;
  • launch: function()<> тут происходит создание приложения;

Проверим, что всё работает:

View

Понадобится четыре вида — это вид поиска SearchCarView.js, вид таблицы CarGridView.js, вид формы добавления данных AddCarFormView.js и вид каркаса CarCatalogView.js, куда поместим все виды.

Ext.define('CarCatalog.view.SearchCarView', < extend: 'Ext.form.Panel', alias: 'widget.searchCarView', bodyPadding: 10, items: [ < xtype: 'textfield', name: 'search', fieldLabel: 'Введите название модели', maxLength: 200 >] >); 
Ext.define('CarCatalog.view.CarGridView', < extend: 'Ext.grid.Panel', alias: 'widget.carGridView', width: 400, height: 300, frame: true, iconCls: 'icon-user', viewConfig:< markDirty:false >, columns: [ < text: 'Модель', flex: 1, sortable: true, dataIndex: 'name', editor: < xtype:'textfield', allowBlank: false, blankText: 'Это поле должно быть заполнено' >>, < flex: 2, text: 'Цена', sortable: true, dataIndex: 'price', editor: < xtype:'textfield', regex: /^(4)*$/, regexText: 'Цена должна состоять из цифр', allowBlank: false, blankText: 'Это поле должно быть заполнено' > > ], plugins: [ Ext.create('Ext.grid.plugin.RowEditing', < clicksToEdit: 2, saveBtnText: 'Сохранить', cancelBtnText: 'Отменить' >) ], selType: 'rowmodel', dockedItems: [ < xtype: 'toolbar', items: [ < text: 'Добавить', action: 'add', iconCls: 'icon-add' >, '-', < action: 'delete', text: 'Удалить', iconCls: 'icon-delete', disabled: true >] > ] >); 
Ext.define('CarCatalog.view.AddCarFormView', < extend: 'Ext.window.Window', alias: 'widget.addCarFormView', autoShow: true, layout: 'fit', modal: true, items: [ < bodyPadding: 10, xtype: 'form', items: [ < xtype: 'textfield', name: 'name', fieldLabel: 'Название модели', allowBlank: false, blankText: 'Это поле должно быть заполнено' >, < xtype: 'textfield', name: 'price', fieldLabel: 'Цена', regex: /^(5)*$/, regexText: 'Цена должна состоять из цифр', allowBlank: false, blankText: 'Это поле должно быть заполнено' > ] > ], buttons: [ < text: 'Сохранить', action: 'save', disabled: true >, < text: 'Отменить', handler: function () < this.up('window').close(); >> ] >); 
Ext.define('CarCatalog.view.CarCatalogView', < extend: 'Ext.panel.Panel', width: 500, height: 360, padding: 10, alias: 'widget.carCatalogView', layout: 'border', items: [ < xtype: 'carGridView', region: 'center' >, < xtype: 'panel', html: '
Каталог автомобилей
', region: 'north', height: 80 >, < xtype: 'searchCarView', title: 'Поиск', region: 'west', width: 300, collapsible: true, collapsed: false >], renderTo: Ext.getBody() >);
  • Метод Ext.define(‘Имя’, ) создает класс-компонент, который может быть унаследован от какого-нибудь компонента. Например в CarGridView.js указали extend: ‘Ext.grid.Panel’, что будет представлять собой таблицу;

  • ‘carCatalogView’ алиас, который указали для вида CarCatalogView.js

Проверим, что всё работает:

Controller

Ext.define('CarCatalog.controller.CarCatalogController', < extend: 'Ext.app.Controller', init: function () < this.control(< >); > >); 
  • С помощью параметра init инициализируются обработчики для компонентов (кнопки, поля и т.д). Связать конпонент с обработчиком помогает функция control;
Модель и хранилище

Ext.define('CarCatalog.model.CarCatalogModel', < extend: 'Ext.data.Model', fields: ['name', 'price'], proxy: < type: 'rest', api: < create: 'car', read: 'car', destroy: 'car', update: 'car' >, reader: < type: 'json', root: 'data', successProperty: 'success' >, writer: < type: 'json', writeAllFields: true >> >); 
Ext.define('CarCatalog.store.CarCatalogStore', < extend: 'Ext.data.Store', requires : [ 'CarCatalog.model.CarCatalogModel' ], model: 'CarCatalog.model.CarCatalogModel', autoLoad: true, autoSync: true, proxy: < type: 'rest', api: < create: 'car', read: 'car', destroy: 'car', update: 'car' >, reader: < type: 'json', root: 'data', successProperty: 'success' >, writer: < type: 'json', writeAllFields: true >> >); 

  • ‘car’ имя, на которое будет замапен java-класс (контролер), который будет обрабатывать GET, POST, PUT, DELETE запросы с клиента;

Добавим в CarGridView.js параметр store: ‘CarCatalogStore’, для отображения данных в таблице:

Укажим контролер CarCatalogController.js и хранилище CarCatalogStore.js в app.js:

Проверим, что всё работает. 404 (Not Found) — это нормально, так как по адресу localhost:8080/car еще ничего нет:

Сервер (Java)

 UTF-8 1.7 3.2.2.RELEASE 3.1.4.RELEASE 1.5.6 1.2.17 4.2.2.Final 1.9.12 0.11.8 3.2.0 0.0.23-SNAPSHOT   mysql mysql-connector-java 5.1.6  org.springframework spring-orm $ org.springframework spring-tx $ org.hibernate hibernate-entitymanager $ org.springframework spring-test $ test  cglib cglib 2.2.2  commons-lang commons-lang 2.4 test  org.hibernate hibernate-validator 4.3.1.Final  org.hibernate hibernate-jpamodelgen 1.2.0.Final provided  com.mysema.querydsl querydsl-jpa $ com.mysema.querydsl querydsl-apt $ provided  org.codehaus.jackson jackson-core-asl $ org.codehaus.jackson jackson-mapper-asl $ org.springframework spring-webmvc $ org.springframework spring-aop $ commons-fileupload commons-fileupload 1.3  javax.servlet javax.servlet-api 3.0.1 provided  commons-logging commons-logging 1.1.3  joda-time joda-time 2.2  org.springframework.security spring-security-config $ org.springframework.security spring-security-ldap $ org.springframework.security spring-security-web $ org.springframework spring-aspects $ org.springframework.security spring-security-taglibs $ opensymphony quartz 1.6.3  org.aspectj aspectjrt 1.7.2 provided  org.aspectj aspectjweaver 1.7.2 provided  junit junit 4.11 test  commons-dbcp commons-dbcp 1.4  commons-pool commons-pool    org.slf4j slf4j-api $ org.slf4j slf4j-log4j12 $ log4j log4j $ com.jolbox bonecp 0.7.1.RELEASE   

Создадим папку java:

Создадим модель данных и слой доступа к данным (DAO):

@Entity @Table(name = "cars") public class Car implements Serializable < @Id @Column(name = "ID") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "name") private String name; @Column(name = "price") private Long price; public Car() < >public Long getId() < return id; >public void setId(Long id) < this.id = id; >public String getName() < return name; >public void setName(String name) < this.name = name; >public Long getPrice() < return price; >public void setPrice(Long price) < this.price = price; >> 
public interface CarDao < void add(Car car); void update(Car car); void delete(Car car); CollectiongetCars(String search); public List findByCar(String name, Long price); > 
public class CarDaoImpl implements CarDao < @PersistenceContext private EntityManager emf; @Override public void add(Car car) < emf.persist(car); >@Override public void update(Car car) < emf.merge(car); >@Override public void delete(Car car) < emf.remove(emf.getReference(Car.class, car.getId())); >@Override public Collection getCars(String search) < if (null == search || search.trim().isEmpty()) < return emf.createQuery( "select c from Car c") .getResultList(); >return emf.createQuery( "select c from Car c where c.name like :search") .setParameter("search", search.trim() + "%") .getResultList(); > public List findByCar(String name, Long price) < return emf.createQuery( "select c from Car c where c.name = :name and c.price = :price") .setParameter("name", name) .setParameter("price", price) .getResultList(); >> 

  • Метод getCars(String search) принимает значение поля поиска и если пусто — вернет все данные;
  • Метод findByCar(String name, Long price) используется для поиска дубликата при добавлении данных;

Создадим слой сервиса:

public interface CarService < Boolean add(Car car); void update(Car car); CollectiongetCars(String search); void delete(Car car); > 
public class CarServiceImpl implements CarService < private CarDao carDao; public CarDao getCarDao() < return carDao; >public void setCarDao(CarDao carDao) < this.carDao = carDao; >@Transactional @Override public Boolean add(Car car) < Listduplicate = carDao.findByCar(car.getName(), car.getPrice()); if (duplicate.isEmpty()) < carDao.add(car); return true; >return false; > @Transactional @Override public void update(Car car) < carDao.update(car); >@Transactional @Override public Collection getCars(String search) < return carDao.getCars(search); >@Transactional @Override public void delete(Car car) < carDao.delete(car); >> 

Создадим контролер, который будет замапен на адрес /car для обработки запросов с клиента:

@Controller @RequestMapping("/car") public class CarController < @Autowired private CarService carService; @RequestMapping(method = RequestMethod.GET) @ResponseBody public CollectiongetCars(String search) < return carService.getCars(search); >@RequestMapping(method = RequestMethod.POST) @ResponseBody public ExtResult setCar(@RequestBody Car car) < return new ExtResult(carService.add(car), car); >@RequestMapping(value = "", method = RequestMethod.DELETE) @ResponseBody public String deleteCar(@RequestBody Car car) < carService.delete(car); return "delete"; >@RequestMapping(value = "", method = RequestMethod.PUT) @ResponseBody public String updateCar(@RequestBody Car car) < carService.update(car); return "update"; >> 
  • Каждый метод замапен на соответствующий запрос с клиента. Внедряем зависимость с помощью spring аннотации Autowired и вызываем соответствующие методы у сервиса;
  • ExtResult — вспомогательный класс. Используется для ответа клиенту, что сущность, которую пытаемся записать в БД , дубликат или не дубликат;
public class ExtResult < private boolean success; private Car data; public ExtResult(boolean success, Car data) < this.success = success; this.data = data; >public ExtResult() < >public boolean isSuccess() < return success; >public void setSuccess(boolean success) < this.success = success; >public Car getData() < return data; >public void setData(Car data) < this.data = data; >> 

Проверьте, что всё работает. Соберите проект с помощью maven install и запустите приложение.

Создадим spring контекст my-context.xml c:

  • настройками подключения к БД;
  • бином EntityManager — объект, через который происходит взаимодействие с БД. Инжектится в CarDaoImpl.java;
  • инжектом объекта класса CarDaoImpl.java в объект класса CarServiceImpl.java;

         org.hibernate.dialect.MySQLDialect 1 thread true create org.hibernate.cfg.ImprovedNamingStrategy      

Создайте БД с названием CarCatalog и кодировкой utf_general_ci или измените название в контексте my-context.xml на своё. Также измените p:username и p:password, если не совпадает.

Создадим настройки для spring DispatcherServlet, который будет обрабатывать запросы с клиента:

  • context:component-scan поиск и регистрация компонентов в контейнере спринга;
  • mvc:view-controller домашняя страница;
  • mvc:resources автоматически обрабатывать запросы на получение статических данных;
   mvc-dispatcher org.springframework.web.servlet.DispatcherServlet 1  mvc-dispatcher /  CharacterEncodingFilter org.springframework.web.filter.CharacterEncodingFilter encoding UTF-8  forceEncoding true   CharacterEncodingFilter /*  contextConfigLocation classpath:/my-context.xml  org.springframework.web.context.ContextLoaderListener   

Добавим в контролер CarCatalogController.js параметр refs и обработчики для компонентов:

Ext.define('CarCatalog.controller.CarCatalogController', < extend: 'Ext.app.Controller', refs: [ , , , , , , , , ], init: function () < this.control(< 'carGridView button[action=add]': < click: this.onAddCar >, 'carGridView button[action=delete]': < click: this.onDelCar >, 'searchCarView textfield[name="search"]': < change: this.onChangeText >, 'carGridView': < cellclick: this.onLineGrid >, 'addCarFormView button[action=save]': < click: this.onSaveCar >, 'addCarFormView textfield[name=name]': < change: this.onValidation >, 'addCarFormView textfield[name=price]': < change: this.onValidation >>); >, onSaveCar: function (button) < var me = this; var carModel = Ext.create('CarCatalog.model.CarCatalogModel'); carModel.set(this.getAddCarFormView().down('form').getValues()); carModel.save(< success: function (operation, response) < var objAjax = operation.data; Ext.getStore('CarCatalogStore').add(objAjax); me.getAddCarFormView().close(); >, failure: function (dummy, result) < Ext.MessageBox.show(< title: 'Дубликат!', msg: 'Такая модель и цена уже существуют', buttons: Ext.Msg.OK, icon: Ext.Msg.ERROR >); > >); >, onAddCar: function () < Ext.widget('addCarFormView'); >, onDelCar: function () < var sm = this.getCarGridView().getSelectionModel(); var rs = sm.getSelection(); this.getCarGridView().store.remove(rs[0]); this.getCarGridView().store.commitChanges(); this.getCarGridDelete().disable(); >, onChangeText: function () < Ext.getStore('CarCatalogStore').load(< params: < search: this.getCarCatalogView().down('searchCarView').getValues() >>); >, onLineGrid: function () < this.getCarGridDelete().enable(); >, onValidation: function () < if (this.getAddCarFormName().validate() && this.getAddCarFormPrice().validate()) < this.getAddCarFormSave().enable(); >else < this.getAddCarFormSave().disable(); >> >); 

  • ref ссылка на что-то в selector’e;
  • selector указывает на компоненты, для быстро обращения к ним через ref;
  • onSaveCar создается модель данных и сохраняется;
  • onAddCar создает виджет формы добавления;
  • onDelCar удаляет запись;
  • onChangeText загружает данные в соответствии со значением в поле поиска;
  • onLineGrid при выделении строки кнопка «Удалить» становится активной;
  • onValidation валидация полей формы добавления;

И последнее — добавим иконки к кнопкам «Добавить» и «Удалить».

            

Источник

Читайте также:  Querying mysql with python
Оцените статью