По следам Spring Pet Clinic. Maven/ Spring Context/ Spring Test/ Spring ORM/ Spring Data JPA
Здравствуйте! Spring MVC, согласно обзору инструментов и технологий Java за 2014 г. от RevbelLabs, является самым популярным веб фреймворком. Далее тот же обзор называет лидера ORM — Hibernate и лидера веб-контейнеров — Apache Tomcat. Добавим сюда самую используемую java script библиотеку jQuery, самый популярный css фреймворк Bootstrap, до сих пор самую популярную (несморя на наступление Gradle) инструмент сборки Maven, абсолютный лидер среди тестовый фреймворков JUnit и получим пример приложения на Spring от его создателей: Spring Pet Clinic (демо приложение). Кроме перечисленного, в этот достаточно несложный по функциональности проект влючены также Spring-Jdbc, Spring-ORM, Spring Data JPA, Hibernate Validator, SLF4J, Json Jackson, JSP, JSTL, WebJars, Dandelion DataTables, HSQLDB, Hamcrest, Mockito и десятки других зависимостей.Прогресс в разработке ПО подразумевает сокращение объема собственного кода приложения, в идеале, только до бизнес логики приложения. Однако это дается не бесплатно — количество зависимостей даже для простого проекта перевалило за полсотни (в PetClinic в WEB-INF\lib находится 61 jar). Конечно не объязательно знать их все, некоторые jar подтягиваются в фоне, и мы даже не подозреваем о них, пока не посмотрим на готовый war или невыполним mvn project-info-reports: dependencies (в IDEA: Show Dependencies… на проекте Maven). Но с основными приходится работать. И на борьбу с некоторыми их особенностями иногда тратятся часы, а то и дни. А еще приходится сталкиваться с багами самих фреймворков…Недавно, вдохновленный Pet Clinic, при создании вебинара по этим технологиям я сделал приложение «Todo Management List»: управление списоком дел с авторизацией и регистрацией пользователей. К зависимостям Pet Clinic добавились еще Spring Security/ совсем свежий Spring Security Test и плагины к jQuery Jeditable и jQuery notification. Объем статьи не позволяет описать шаги создания приложения (вебинар по созданию приложения занимает 30 часов), поэтому здесь делюсь ресурсами, некоторыми мыслями и решениями, пришедшими в процессе его создания.На PaaS Heroku можно найти демо приложения (первый раз при запуске возможна долгая загрузка и ошибка сервера, повторить).
На просторах интернета немало приложений, построенных на Spring/ JPA/ MVC/ Security. Можно скачать сорсы и выбрать наиболее подходящее вам решение.
В конфигурировании Spring есть тенденция прятать детали реализации под свои пространства имен. Конфигурация становится меньше и понятнее, однако процесс кастомизации или дебага становится не совсем тривиальный: сначала нужно найти бины, где это реализовано. Сравните например инициализацию базы:
В тестах Spring принято использовать транзакционность: после выполнения каждого теста происходит rollback базы в исходное состояние. Однако сам @Transactional сильно влияет на поведение тестов: например, вы забыли в сервисе/репозитории @Transactional, тест прошел, а приложение упало. Еще хуже, когда в тесте достаются для сравнения сущности из базы: они попадают в тот же транзакционный контекст и поведение тестируемых методов становится несколько другим (спасает только evict или detach). Состояние базы при дебаге теста также не отображается, пока не закончилась транзакция теста. Более честно использовать инициализатор базы перед каждым тестом:
@Autowired private DataSource dataSource;
public DbPopulator (String scriptLocation) { super (RESOURCE_LOADER.getResource (scriptLocation)); }
public void execute () { DatabasePopulatorUtils.execute (this, dataSource); } } @ContextConfiguration («classpath: spring/spring-app.xml») @RunWith (SpringJUnit4ClassRunner.class) @ActiveProfiles ({«postgres», «jpa»}) public class TodoItemServiceTest {
@Autowired private DbPopulator dbPopulator;
@Before public void setUp () throws Exception { dbPopulator.execute (); }
Привыкнув в Spring 3.0 к багам о необъявленной в persistence.xml сущности был удивлен, что все работает без этого! После некоторого копания в коде увидел, что весь target/classes сканируется на entity анотации. Также порадовала возможность конфигурировать JPA без persistence.xml. Можно задавать конкретные пакеты для сканирования модели, конфигурировать специфичные для провайдера и общие JPA параметры. Причем их можно вынести в общий db.properties файл:
Традиционный выбор реализации DataSource Commons DBCP похоже сдает свои позиции. По данным StackOverflow для реализации нужно брать BoneCP, используемый в playframework (если вы его уже используете или собираетесь, учтите, что требуются некоторые услилия, чтобы избежать утечек памяти, озвученных в докладе от разработчика Plumbr). А в PetClinic используется tomcat-jdbc. Если приложение деплоится в Tomcat, можно не включать его в war (scope=provided), но при этом в $TOMCAT_HOME/libнеобходимо положить драйвер базы, т.к из родного tomcat-jdbc библиотеки вашего war недоступены.Ну и конечно при деплое в Tomcat не стоит забывать про возможность брать пул коннектов из ресурсов context.xml конфигурации Tomcat:
Привыкнув в каждом проекте создавать собственный AbstractDAO, параметризированный сущностью и ключем с имплементацией основных CRUD на основе EntityManager был обрадован, что наконец-то он вошел в Spring, правда, в проект Spring Data JPA: JpaRepository
Риторический вопрос читателям: можно ли java считать динамическим языком:)? Если JpaRepository и сгенерированных методов недостаточно, можно писать собственную имплементацию методов или запросы Query. В @Query можно писать JPQL запросы (которые генерятся в @NamedQuery), а можно ссылаться на уже объявленные @NamedQuery в сущностях (почему то в PetClinic @NamedQuery игнорируются, хотя такие запросы строятся и проверяются на этапе деплоя).Например, метод
@Modifying @Transactional @Query (name = User.DELETE) int delete (@Param («id») int id); ссылается на объявленный в User @NamedQuery @NamedQueries ({ … @NamedQuery (name = User.DELETE, query = «DELETE FROM User u WHERE u.id=: id») })
public class User extends NamedEntity {
public static final String DELETE = «User.delete»;
В отличии от void CrudRepository.delete (ID id) он возвратит количество модифицированных записей.Однако есть проблема: наследование бизнес интерфейса доступа к даным от JpaRepository означает, что уровень сервиса становится зависимым от реализации. Кроме того, например, в методе List
public interface ProxyUserRepository extends JpaRepository
@Modifying @Query («DELETE FROM User u WHERE u.id=?1») @Transactional int delete (int id);
@Override @Transactional User save (User user);
@Override User findOne (Integer id);
@Override
List
@Repository public class DataJpaUserRepository implements UserRepository { private static final Sort SORT_NAME_EMAIL = new Sort («name», «email»);
@Autowired private ProxyUserRepository proxy;
@Override public boolean delete (int id) { return proxy.delete (id) != 0; }
@Override public User save (User user) { return proxy.save (user); }
@Override public User get (int id) { return proxy.findOne (id); }
@Override
public List
Spring Maven Логгирование Персистентность Если будет статья понравится, буду готовить часть 2 с неуместившимися Spring MVC, Spring Security, Jackson и пр.Спасибо за внимание, будет интересно услышать ваше мнение по затронутым темам.