[Из песочницы] Java 8, Spring, Hibernate, SSP — начинаем играться

Совсем недавно вышла Java 8. И у меня возникло желание написать что-то с использованием новых плюшечек, которые дает 8-ерка.Конкретно Лямбы, новый collection api, позволяющий работать с коллекциями в более функциональном стиле и default-методы в интерфейсах.Статья является кратким обзором моего опыта по интегрированию Java 8, Spring MVC, Hibernate и SSP.

Кому интересно, прошу под кат.

Предисловие Я долгое время (и все еще) продолжаю восхищаться языком Scala, но к сожалению, мне все еще мой пропитанный ынтырпрайзом мозг мешает перейти на чистую Scala.В первую очередь из-за привязки ко внешним библиотекам (Hibernate, Spring, Spring MVC), к которым я до сих пор питаю слабость.Я пытался их и использовать в Scala-проектах, но остается постоянное впечатление, что занимаешься постоянной расстановкой костылей и подпорок и не удается писать в Scala-стиле, скорее пишешь на Java, но со Scala синтаксисом + костыли и подпорки в виде неявных преобразований Java-коллекций в Scala-коллекции и обратно.

Поэтому я решил пойти немного более «мягким» путем и использовать знакомый стек. Единственное изменение, к которому я пришел — использовать SSP (Scala Server Pages) вместо JSP (Java Server Pages), что бы получить статическую поддержку на стороне View и не иметь сильной головной боли с тем, что что-то ломается при рефакторинге и ты узнаешь это уже после деплоймента (когда какой-то блок тупо перестает отображаться либо что еще хуже подпортит данные в БД)

Начало Итак, начнем.Будем использовать мой любимый Maven.

Дадим Maven’у нампек, что проект у нас будет использовать Java 8:

org.apache.maven.plugins maven-compiler-plugin compile compile <_source>1.8 1.8 … Добавляем нужные зависимости на Spring/Hibernate и SSP. Все остальное по вкусу.

org.springframework spring-context

org.springframework spring-context-support

org.springframework spring-orm

org.springframework spring-webmvc org.hibernate hibernate-core

org.hibernate hibernate-entitymanager

org.hibernate.javax.persistence hibernate-jpa-2.0-api org.fusesource.scalate scalate-core_2.10

org.fusesource.scalate scalate-spring-mvc_2.10 … Первая проблема, на которую натолкнулся — несовместимость Scala компилятора, который идет зависимостью с библиотекой «Scalate»(именно благодаря ей мы имеем поддержку SSP) с байт-кодом Java 8.Пришлось явно прописать зависимость в проект на Scala компилятор, что бы все взлетело:

org.scala-lang scala-compiler 2.10.4 //изначально хотел проапгрейдить зависимость до 2.11, но «кишки» сильно поменялись и в последней доступной версии Scalate (1.6.1) это пока что еще не поддержано.

Так же мы хотим, что бы наши SSP были прекомпилированы и мы узнали о проблеме при компиляции, а ни на продакшне.Поэтому добавляем плагин для этого:

org.fusesource.scalate maven-scalate-plugin_2.10 1.6.1 org.scala-lang scala-compiler 2.10.4 precompile //как заметно, добавлен тот же хачек со Scala компиляторомНемного кода Ну с конфигурацией почти все

Теперь можно начать баловаться с кодом и радоваться плюшкам JDK 8:

Мой базовый DAO:

public interface BaseDAO, ID extends Serializable> extends EntityManagerAware { Class getEntityClass ();

default void persist (T entity) { if (entity.isNew ()) { entity.assignId (); }

getEntityManager ().persist (entity); getEntityManager ().flush (); }

default T find (ID id) { return getEntityManager ().find (getEntityClass (), id); }

default void delete (T entity) { getEntityManager ().remove (entity); }

default List findByQuery (String jpqlQueryString) { return findByQueryWithParams (jpqlQueryString, Collections.emptyMap ()); }

default List findByQueryWithParams (String jpqlQueryString, Map params) { TypedQuery query = getEntityManager ().createQuery (jpqlQueryString, getEntityClass ()); for (Map.Entry entry: params.entrySet ()) { query.setParameter (entry.getKey (), entry.getValue ()); } return query.getResultList (); } } К сожалению, без дополнптиельной прослойки в виде абстрактного класса обойтись не удалось:

public abstract class AbstractBaseDAO, ID extends Serializable> implements BaseDAO { @PersistenceContext EntityManager entityManager;

@Override public EntityManager getEntityManager () { return entityManager; } } Конкретный интерфейс DAO:

public interface PersonDAO extends BaseDAO { @Override default Class getEntityClass () { return Person.class; }

List findAll (); } Ну и соотвественно имплементация:

@Repository public class PersonDAOImpl extends AbstractBaseDAO implements PersonDAO {

@Override public List findAll () { return findByQuery («select p from Person p»); } } В результате мы получаем CRUD для репозитория и, на мой взгляд, очищаем имплементацию от побочного шума.//Конечно можно было использовать Spring Data JPA и тогда ручками CRUD вообще не пришлось бы писать, но некоторые вещи оттуда мне не нравятся: В случае вручную генерируемых/присвоенных ID он будет всегда делать merge вместо persist. Да и таким образом довольно проще контролировать поведение системы.

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

List all = personService.findAll ().stream ().map (PersonForm: from).collect (Collectors.toList ()); View для отображения списка:

<%@ val people: java.util.List[name.dargiri.web.controller.PeopleController.PersonForm]%>

<% for(person <- people ) { %> <% } %>
# Username Action
<%=person.id%> <%=person.username%> »>Edit | »>Delete

Сборка проекта проста. В случае если вы выкачали мой проект и хотите его подеплоить в сервлет контейнер, скажем в Tomcat, то запустим: mvn -P=build clean packageИ видим как пре-компилируются наши SSP’шечки:

… [INFO] --- maven-scalate-plugin_2.10:1.6.1: precompile (default) @ web --- [INFO] Precompiling Scalate Templates into Scala classes… [INFO] processing /Users/dionis/projects/spring-mvc-java8-web-app-template/web/src/main/webapp/WEB-INF/views/scalate/main/person.ssp [INFO] processing /Users/dionis/projects/spring-mvc-java8-web-app-template/web/src/main/webapp/WEB-INF/views/scalate/main/people.ssp [INFO] processing /Users/dionis/projects/spring-mvc-java8-web-app-template/web/src/main/webapp/WEB-INF/scalate/layouts/default.ssp … Так что если не дай бог что-то пошло не так и мы что-то поломали в них из того, что компилируется, то мы узнаем об этом сейчас, а не после деплоймента на dev/qa/staging/production environment.

В примере использовал Twitter Bootstrap, ибо нравится он мне.

Скриншоты Создание пользователя: 38782f602ea0918c8155bc665ec190c1.pngСписок пользователей: 432ed5d9eb3ab377bfa5dd03318b162c.png

Редактирование пользователя: e71dae7e9bd71f15eb60a38cd2bcee56.png

Код примера: github.com/dargiri/spring-mvc-java8-web-app-template

В качестве бесплатного бонуса:

Тоже самое на Java 7: github.com/dargiri/spring-mvc-java-web-app-template

Тоже самое на Scala: github.com/dargiri/spring-mvc-scala-web-app-template

И если вы все еще это читаете и вытащили себе код и хотите с ним поиграться. Я предпочитаю запускать из-под IDE, а не пользоваться плагинами для IDE.Поэтому в модуле web-app-launcher находим класс Launcher.Если вы пользуетесь Idea, то все запустится без проблем.Если вы пользуетесь Eclipse/Netbeans, то нужны некоторые манипуляции.Для Eclipse — достать изначально Eclipse с поддержкой JDK 8: www.eclipse.org/downloads/index-java8.phpP.P. S. Пишите код и да пребудет с вами сила.

Далее для проекта нужно выбрать maven-профайл build.И в классе Launcher значение переменной MULTI_MODULE_DEFAULT_PATH сменить с «web/src/main/webapp» на »…/web/src/main/webapp» либо на полный путь от корня вашей файловой системы.

Ссылки: Apache Maven — maven.apache.org/Scalate — scalate.fusesource.org/Scala — www.scala-lang.org/Apache Tomcat — tomcat.apache.org/Twitter Bootstrap — getbootstrap.com/Spring Data JPA — projects.spring.io/spring-data-jpa/Hibernate ORM — hibernate.org/orm/JDK 8 — www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.htmlSpring — projects.spring.io/spring-framework/

© Habrahabr.ru