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:
 
...
<build>
	<plugins>
	...
		<plugin>
			<groupId>org.apache.maven.plugins</groupId>
			<artifactId>maven-compiler-plugin</artifactId>
			<executions>
				<execution>
					<phase>compile</phase>
					<goals>
						<goal>compile</goal>
					</goals>
				</execution>
			</executions>
			<configuration>
				<_source>1.8</_source>
				<target>1.8</target>
			</configuration>
		</plugin>
...
	</plugins>
</build>
...

 
Додаємо потрібні залежності на Spring (4-ої версії, який підтримує зміни в новою версією JDK, а так само тягне бібліотеку, яка вміє працювати з байткод, згенерованої 8-ий Java'ой) / Hibernate і SSP. Все інше за смаком. Версії винесені в «dependency management» секцію в parent pom'e.
 
<dependencies>
...
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-context</artifactId>
	</dependency>

	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-context-support</artifactId>
	</dependency>

	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-orm</artifactId>
	</dependency>

	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-webmvc</artifactId>
	</dependency>
	
	<dependency>
		<groupId>org.hibernate</groupId>
		<artifactId>hibernate-core</artifactId>
	</dependency>

	<dependency>
		<groupId>org.hibernate</groupId>
		<artifactId>hibernate-entitymanager</artifactId>
	</dependency>

	<dependency>
		<groupId>org.hibernate.javax.persistence</groupId>
		<artifactId>hibernate-jpa-2.0-api</artifactId>
	</dependency>
	
	<!-- Scalate (SSP) support-->
	<dependency>
		<groupId>org.fusesource.scalate</groupId>
		<artifactId>scalate-core_2.10</artifactId>
	</dependency>

	<dependency>
		<groupId>org.fusesource.scalate</groupId>
		<artifactId>scalate-spring-mvc_2.10</artifactId>
	</dependency>
...
</dependencies>

 
Перша проблема, на яку натрапив — несумісність Scala компілятора, який йде залежністю з бібліотекою «Scalate» (саме завдяки їй ми маємо підтримку SSP) з байт-кодом Java 8.
Довелося явно прописати залежність в проект на Scala компілятор, що б все злетіло:
 
 
<dependency>
	<groupId>org.scala-lang</groupId>
	<artifactId>scala-compiler</artifactId>
	<version>2.10.4</version>
</dependency>

  
/ / Спочатку хотів проапгрейдить залежність до 2.11, але «кишки» сильно змінилися і в останній доступній версії Scalate (1.6.1) це поки що ще не підтримав.
 
Так само ми хочемо, що б наші SSP були прекомпілірованние і ми дізналися про проблему при компіляції, а ні на продакшині.
Тому додаємо плагін для цього:
 
 
<build>
	<plugins>
		...
		<plugin>
			<groupId>org.fusesource.scalate</groupId>
			<artifactId>maven-scalate-plugin_2.10</artifactId>
			<version>1.6.1</version>
			<!--Support jdk 8-->
			<dependencies>
				<dependency>
					<groupId>org.scala-lang</groupId>
					<artifactId>scala-compiler</artifactId>
					<version>2.10.4</version>
				</dependency>
			</dependencies>
			<executions>
				<execution>
					<goals>
						<goal>precompile</goal>
					</goals>
				</execution>
			</executions>
		</plugin>
		...
	</plugins>
</build>

/ / Як помітно, доданий той же хачек зі Scala компілятором
 
 

Трохи коду

 
Ну з конфігурацією майже всі
 
Тепер можна почати бавитися з кодом і радіти плюшками JDK 8:
 
Мій базовий DAO:
 
 
public interface BaseDAO<T extends Model<ID>, ID extends Serializable> extends EntityManagerAware {
    Class<T> 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<T> findByQuery(String jpqlQueryString) {
        return findByQueryWithParams(jpqlQueryString, Collections.emptyMap());
    }

    default List<T> findByQueryWithParams(String jpqlQueryString, Map<String, Object> params) {
        TypedQuery<T> query = getEntityManager().createQuery(jpqlQueryString, getEntityClass());
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            query.setParameter(entry.getKey(), entry.getValue());
        }
        return query.getResultList();
    }
}

 
На жаль, без дополнптіельной прошарку у вигляді абстрактного класу обійтися не вдалося:
 
public abstract class AbstractBaseDAO<T extends Model<ID>, ID extends Serializable> implements BaseDAO<T, ID> {
    @PersistenceContext
    EntityManager entityManager;

    @Override
    public EntityManager getEntityManager() {
        return entityManager;
    }
}

 
Конкретний інтерфейс DAO:
 
public interface PersonDAO extends BaseDAO<Person, UUID> {
    @Override
    default Class<Person> getEntityClass() {
        return Person.class;
    }

    List<Person> findAll();
}

 
Ну і відповідно імплементація:
 
 
@Repository
public class PersonDAOImpl extends AbstractBaseDAO<Person, UUID> implements PersonDAO {

    @Override
    public List<Person> findAll() {
        return findByQuery("select p from Person p");
    }
}

 
У результаті ми отримуємо CRUD для сховища та, на мій погляд, очищаємо імплементацію від побаченого шуму.
/ / Звичайно можна було використовувати Spring Data JPA і тоді ручками CRUD взагалі не довелося б писати, але деякі речі звідти мені не подобаються: У разі вручну генеруються / привласнених ID він буде завжди робити merge замість persist. Та й таким чином досить простіше контролювати поведінку системи.
 
Так само позбавляємося від потреби використовувати сторонні бібліотеки типу Guava, який дозволяють писати в більш функціональному стилі і отримуємо все з коробки:
 
 
List<PersonForm> all = personService.findAll().stream().map(PersonForm::from).collect(Collectors.<PersonForm>toList());

 
View для відображення списку:
 
 
<%@ val people: java.util.List[name.dargiri.web.controller.PeopleController.PersonForm]%>
<div class="page-header">
<h1>People</h1>
</div>
<table class="table table-striped">
<thead>
    <tr>
        <th>#</th>
        <th>Username</th>
        <th>Action</th>
    </tr>
</thead>
<tbody>
    <% for(person <- people ) { %>
    <tr>
        <td>
            <%=person.id%>
        </td>
        <td>
            <%=person.username%>
        </td>
        <td>
            <a href="<%=uri("/people/edit/" + person.id)%>">Edit</a> |
            <a href="<%=uri("/people/delete/" + person.id)%>">Delete</a>
        </td>
    </tr>
    <% } %>
</tbody>
</table>


Збірка проекту проста. У випадку якщо ви викачали мій проект і хочете його подеплоіть в сервлет контейнер, скажімо в 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, бо подобається він мені.
 
 Скріншоти Створення користувача:
 
 
Список користувачів:
 
 
Редагування користувача:
 
 
 
 
Код прикладу:
 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.php
 
 P.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.html
Spring — projects.spring.io / spring-framework /

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.