JavaRush /Java блог /Random UA /Знайомство з Maven, Spring, MySQL, Hibernate та перший CR...
Макс
41 рівень

Знайомство з Maven, Spring, MySQL, Hibernate та перший CRUD додаток (частина 4)

Стаття з групи Random UA
Добридень. У цій статті я хотів би поділитися своїм першим знайомством з такими речами як Maven, Spring, Hibernate, MySQL та Tomcat у процесі створення простого CRUD програми. Це остання частина. Стаття розрахована насамперед на тих, хто вже пройшов тут 30-40 рівнів, але за межі чистої джави поки не вибирався і тільки починає (або збирається починати) виходити у відкритий світ із усіма цими технологіями, фреймворками та іншими незнайомими словами. Це заключна четверта частина статті "Знайомство з Maven, Spring, MySQL, Hibernate та перший CRUD додаток". Попередні частини можна побачити, перейшовши за посиланнями:

Зміст:

Оформлення та веб-ресурси

Наш додаток працює, ось тільки без сліз на нього не подивишся, нудні написи, негарні посилання, порожнє біле тло. Потрібно це виправити та додати різних красивостей. Як це зробити. Ну, по-перше, можна просто погратися зі сторінками і наробити будь-якої всячини за допомогою можливостей HTML . Але якщо одним тільки HTML намагатиметься змінювати фони, кольори, розміри, розташування елементів і т.д. і т.п. то в результаті можна зі сторінки такий бардак зробити, що потім уже нічого там не розбереш. Та й до того ж HTML можливості для оформлення досить обмежені. Краще використовувати для цього CSS (Cascading Style Sheets – каскадні таблиці стилів). Тоді все, що стосується оформлення, можна зібрати в одному місці, а потім застосовувати це до потрібного елементу сторінки. CSS код можна писати прямо наjsp сторінці у спеціальному тегу, але набагато зручніше винести це в окремий файл, а потім просто застосувати до потрібних сторінок. Для розміщення файлів зі стилями та інших статичних веб-ресурсів створимо окрему директорію всередині webapp . Щоб не плутати веб-ресурси зі звичайними ресурсами (де у нас лежить db.properties ), назвемо цю директорію просто res і туди поміщатимемо всі CSS файли, картинки і т.п:
Знайомство з Maven, Spring, MySQL, Hibernate та перший CRUD додаток (частина 4) - 1
Щоб використовувати ці файли, нам потрібно вказати їх розташування в конфігурації. Ідемо в наш клас WebConfig. Раніше ми використовували анотацію @EnableWebMvc, щоб нічого не налаштовувати, а просто використовувати дефолтну конфігурацію. Але тепер виникла потреба дещо налаштувати. І тому використовуємо інтерфейс WebMvcConfigurer, який дозволяє перевизначати методи конфігурації. Т.о. ми можемо використовувати конфігурацію за замовчуванням, але деякі моменти налаштувати під себе. У разі нам потрібен спосіб addResourceHandlers, з допомогою якого вкажемо розташування статичних веб-ресурсів. Про всяк випадок весь клас цілком виглядає так:
package testgroup.filmography.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "testgroup.filmography")
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/res/**").addResourceLocations("/res/");
    }

    @Bean
    ViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/pages/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }
}
Тепер щоб використовувати наш CSS на сторінці потрібно зробити на нього посилання всередині тега head:
<head>
    <link href="<c:url value="/res/style.css"/>" rel="stylesheet" type="text/css"/>
</head>
Достатньо додати цей рядок і зробити, наприклад, ось такий простенький CSS файл:
table {
    border-spacing: 0 10px;
    font:  bold 100% Georgia, serif;
    margin: 40px auto;
    text-shadow: 5px 5px 5px #3F3F7F;
    background: #B1B9D9;
    text-align: center;
    vertical-align: middle;
    width: 50%;
    border: 10px solid blue;
}
І це вже повністю змінить нашу таблицю (дурне звичайно виглядати, але це так, для прикладу): Знайомство з Maven, Spring, MySQL, Hibernate та перший CRUD додаток (частина 4) - 2Ну думаю немає потреби докладно розповідати про CSS, тут все досить просто. На просторах інтернету можна знайти безліч вже готових варіантів оформлення таблиць та форм. Але краще зробити оформлення самостійно, тут не потрібно бути дизайнером, це ж все-таки не якийсь складний сайт. Навіть кілька годин після першого знайомства вистачить, щоб зробити досить гарне та акуратне оформлення такої простої сторінки. До того ж в інтернеті повно уроків, прикладів, є спеціальні сайти, де одночасно на одному екрані можна редагувати HTML, CSS і відразу бачити як це все виглядає. Небагато практики і можна творити справжнє мистецтво. Правда тут теж не варто дуже захоплюватися (якщо звичайно немає планів стати дизайнером), а то оформлення це така штука, тут можна застрягти дуже довго. Я якраз почав розбиратися з CSS щось безпосередньо завис. Все хотів спробувати, кожну властивість щось крутив, колупав, експериментував, брав сторінки яких-небудь сайтів і переробляв їх до невпізнання. Я, напевно, тиждень розважався з цією іграшкою, більше нічого не роблячи, поки мене не відпустило :) Ну а так взагалі звичайно не варто перестаратися і робити незрозуміле строкате диво-юдо, треба робити простенько і зі смаком. Єдине варто зазначити. Потрібно розуміти, що потрібно робити простенько та зі смаком. Єдине варто зазначити. Потрібно розуміти, що потрібно робити простенько та зі смаком. Єдине варто зазначити. Потрібно розуміти, щоCSS – це опис оформлення , а HTML – це розмітка . Не варто намагатися абсолютно все зробити через CSS, деякі моменти буде зробити дуже складно, а деякі просто неможливо, коли в HTML це робиться парою зайвих рядків, а то й зовсім якимось одним атрибутом у тезі. Потрібно їх комбінувати, будь-які кольори, фони, шрифти робити в CSS, а якщо, наприклад, потрібно об'єднати пару осередків таблиці, простіше використовувати засоби HTML. Ну ось наприклад що у мене вдалося зробити зі сторінками за пару-трійку годин колупання з CSS і HTML (не буду тут вже розписувати, яку кашу я навернув щоб цього досягти, в кінці буде посилання на гітхаб цього проекту там можна буде подивитися): Знайомство з Maven, Spring, MySQL, Hibernate та перший CRUD додаток (частина 4) - 3Не знаю звичайно, наскільки грамотно я там все зробив, але враховуючи, що я вперше побачив CSS за тиждень до цього, думаю цілком придатно вийшло.

Розбиття на сторінки

Зараз все працює, виглядає начебто непогано, але в таблиці поки що лише кілька записів. А якщо в ній буде сотня фільмів, або тисяча, не дуже зручно промотувати такий довжелезний список. Набагато зручніше, коли список виводиться сторінками, штук по 10 записів, наприклад. Тому зараз і спробуємо реалізувати розбиття списку на сторінки (іноді це ще називають "пейджинг" або "пагінація") .)). Зробити це можна по-різному. Наприклад, можна передавати повний список на jsp сторінку і там вже формувати табличку на потрібну кількість записів. Або можна, скажімо, у сервісі витягувати із загального списку потрібні записи, а потім цей міні-список відправляти на jsp сторінку для відображення. Але найкраще, звичайно, робити це на рівні бази даних. Ідея в тому, щоб не брати з бази повний список і потім розбивати його на шматки, а спочатку діставати з бази потрібний шматок, не торкаючись іншого. Адже навіщо нам витягувати з бази всі сотні чи тисячі записів одразу, якщо потрібна нам знаходиться в першій десятці, краще витягнемо лише одну цю десятку. Ідемо до DAO і додамо для методу allFilmsпараметрint page, який буде відповідати за номер сторінки (у сервісі, природно, робимо те саме). І трохи змінимо реалізацію цього методу, якщо раніше ми витягували весь список, то тепер витягуватимемо лише частину. З цим нам допоможуть методи setFirstResult(з якого рядка таблиці почати) і setMaxResults(скільки записів вивести):
@SuppressWarnings("unchecked")
public List<Film> allFilms(int page) {
    Session session = sessionFactory.getCurrentSession();
    return session.createQuery("from Film").setFirstResult(10 * (page - 1)).setMaxResults(10).list();
}
Тобто. якщо це 1-а сторінка, виводимо максимум 10 записів, починаючи з 0-ої, якщо це 5 сторінка, то 10 записів починаючи з 40-ї (не забуваємо, що нумерація в базі починається з 0). А ще додатково створимо метод, який повертатиме кількість записів у таблиці. Нам це знадобиться для того, щоб знати кількість всіх сторінок і сформувати для них посилання на нашій сторінці jsp:
public int filmsCount() {
     Session session = sessionFactory.getCurrentSession();
     return session.createQuery("select count(*) from Film", Number.class).getSingleResult().intValue();
 }
За допомогою такого запиту ми отримаємо кількість усіх записів у таблиці (у вигляді intзначення), не витягуючи при цьому самі записи. Тепер йдемо в контролер і попрацюємо над методом, який повертає головну сторінку зі списком фільмів:
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView allFilms(@RequestParam(defaultValue = "1") int page) {
    List&ltFilm> films = filmService.allFilms(page);
    int filmsCount = filmService.filmsCount();
    int pagesCount = (filmsCount + 9)/10;
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName("films");
    modelAndView.addObject("page", page);
    modelAndView.addObject("filmsList", films);
    modelAndView.addObject("filmsCount", filmsCount);
    modelAndView.addObject("pagesCount", pagesCount);
    return modelAndView;
}
Тут з'явилася нова анотація @RequestParam, яка вказує, що це значення ми отримуємо з параметра запиту. Тепер, якщо ми перейдемо на адресау з параметром, наприклад http://localhost:8080/ ?page=4 , то отримаємо, відповідно, 4 сторінку. Встановлюємо значення за промовчанням " 1 ", щоб під час запуску програми, коли ми переходимо на адресау без параметра http://localhost:8080/, ми отримували першу сторінку. У способі ми отримуємо кількість всіх записів, потім таким ось нехитрим методом обчислюємо кількість сторінок. Тобто. якщо у нас 10 записів, це 1 сторінка, а якщо 11, то це вже 2. Ну і передаємо в модель усю цю справу. Кількість сторінок потрібно знати, щоб сформувати для них усіх посилання в циклі, а кількість фільмів нехай буде про всяк випадок, якщо, наприклад, захочеться вивести цю інформацію десь на сторінці. Тепер залишилося тільки зайти в films.jsp і додати посилання на кожну сторінку за допомогою такої конструкції:
<c:forEach begin="1" end="${pagesCount}" step="1" varStatus="i">
    <c:url value="/" var="url">
        <c:param name="page" value="${i.index}"/>
    </c:url>
    <a href="${url}">${i.index}</a>
</c:forEach>
Створюємо посилання за лічильником 1, 2, 3, ... та встановлюємо параметр за значенням індексу.

До речі так само, як і з розбиттям на сторінки, можна додати й інші фішки, наприклад сортування, фільтрацію, пошук. Просто передаємо в метод DAO різні параметри і формуємо за допомогою відповідний запит до бд, щоб витягнути потрібні записи, у потрібному порядку.

Зробимо ще один маленький штрих. Повертаємось у контролер. У методах додавання, редагування та видалення після виконання операції ми робимо перенаправлення на головну сторінку "redirect:/" . Т.о. якщо ми будемо десь на 50-й сторінці і натиснемо редагувати запис, то після виконання ми перейдемо на адресау " / ", тобто. повернемося на 1 сторінку. Це не дуже зручно, хотілося б повернутися туди ж, звідки й прийшли. Вирішимо це дуже просто. Створимо в класі FilmControllerзмінну екземпляруint page
private int page;
Усередині методу allFilmsбудемо надавати цій змінній значення параметра page:
this.page = page;
Т.о. щоразу, коли виконується цей метод (тобто коли ми переміщаємося сторінками), змінну записуватиметься значення поточної сторінки. А потім ми використовуємо це значення в наших методах, щоб перенаправити назад на цю сторінку.
modelAndView.setViewName("redirect:/?page=" + this.page);

Висновок

На цьому, мабуть, і скінчимо. Вийшов повноцінний CRUD додаток. Воно, звичайно, далеко від ідеалу (дуже далеко), але його можна оптимізувати і доопрацювати, виправити косяки і додати функціональності. Можна використовувати вбудовані методи для crud операцій; прикрутити фільтрацію, пошук, сортування; додати інші пов'язані таблиці; реалізувати підтримку різних користувачів з авторизацією та автентифікацією тощо. і т.п. Простір для фантазії безмежний, так що дерзайте. Посилання на github цього проекту . Дякую за увагу. PS Це моя перша у житті спроба написати статтю, так що не судіть суворо :). Перепрошую, що не додав різних посилань на корисні ресурси, на жаль, все ніяк не можу виробити звичку зберігати посилання на джерела, звідки черпаю інформацію. Ну і, звичайно ж, вибачаюсь за багато літер, стислість не мій талант, сподіваюся хтось зможе це подужати. Посилання на всі частини
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ