Что такое весна-дата-отдых?
spring-data-rest , недавнее дополнение к проекту spring-data , является платформой, которая помогает вам представить ваши сущности непосредственно как конечные точки веб-сервиса RESTful. В отличие от rails, grails или roo, он не генерирует никакого кода для достижения этой цели. Spring data-rest поддерживает проверку JPA, MongoDB, JSR-303, HAL и многие другие. Он действительно инновационный и позволяет настроить веб-сервис RESTful за считанные минуты. В этом примере я дам вам краткий обзор того, на что способен spring-data-rest.
Начальная конфигурация
Я собираюсь использовать новую веб-конфигурацию Java Servlet 3 вместо древнего web.xml. Здесь нет ничего особенного.
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 | publicclassWebAppInitializer extendsAbstractAnnotationConfigDispatcherServletInitializer {    @Override    protectedClass<?>[] getRootConfigClasses() {        returnnewClass<?>[]{AppConfiguration.class};    }    @Override    protectedClass<?>[] getServletConfigClasses() {        returnnewClass[]{WebConfiguration.class};    }    @Override    protectedString[] getServletMappings() {        returnnewString[]{"/"};    }} | 
WebAppInitializer.java на github
Я инициализирую hibernate как уровень абстракции базы данных в классе AppConfiguration . Я использую встроенную базу данных ( hsql ), так как хочу, чтобы эта витрина была простой, глупой. Тем не менее, здесь ничего особенного.
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | @Configuration@EnableJpaRepositories@EnableTransactionManagementpublicclassAppConfiguration {    @Bean    publicDataSource dataSource() {        EmbeddedDatabaseBuilder builder = newEmbeddedDatabaseBuilder();        returnbuilder.setType(EmbeddedDatabaseType.HSQL).build();    }    @Bean    publicLocalContainerEntityManagerFactoryBean entityManagerFactory() {        HibernateJpaVendorAdapter vendorAdapter = newHibernateJpaVendorAdapter();        vendorAdapter.setDatabase(Database.HSQL);        vendorAdapter.setGenerateDdl(true);        LocalContainerEntityManagerFactoryBean factory = newLocalContainerEntityManagerFactoryBean();        factory.setJpaVendorAdapter(vendorAdapter);        factory.setPackagesToScan(getClass().getPackage().getName());        factory.setDataSource(dataSource());        returnfactory;    }    @Bean    publicPlatformTransactionManager transactionManager() {        returnnewJpaTransactionManager();    }} | 
AppConfiguration.java на github
Теперь к настройке сервлета приложения: WebConfiguration
| 1 2 3 | @ConfigurationpublicclassWebConfiguration extendsRepositoryRestMvcConfiguration {} | 
WebConfiguration.java на github
О, ну это немного коротко, не так ли? Для полной настройки не требуется ни одной строки кода. Это действительно хорошее применение соглашения о конфигурации парадигмы. Теперь мы можем начать создавать репозитории spring-data-jpa, так как они будут автоматически представлены как ресурсы RESTful. И мы все еще можем добавить пользовательскую конфигурацию в класс WebConfiguration, если это необходимо.
Инициализация была действительно короткой и легкой. Нам не нужно было ничего особенного кодировать. Единственное, что мы сделали, — это установили соединение с базой данных и режим гибернации, что, очевидно, неизбежно. Теперь, когда мы настроили наш «REST Servlet» и постоянство, давайте перейдем к самому приложению, начиная с модели.
Модель
  Я сделаю это очень просто, создав только два связанных объекта. 
 
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 | @EntitypublicclassBook {    @Id    privateString isbn;    privateString title;    privateString language;    @ManyToMany    privateList<Author> authors;} | 
| 01 02 03 04 05 06 07 08 09 10 11 12 13 | @EntitypublicclassAuthor {    @Id    @GeneratedValue(strategy = GenerationType.SEQUENCE)    privateInteger id;    privateString name;    @ManyToMany(mappedBy = "authors")    privateList<Book> books;} | 
Чтобы наконец сделать сущности постоянными и выставленными как веб-сервис RESTful, нам нужны репозитории Spring-Data. Хранилище — это в основном DAO . Он предлагает функциональность CRUD для наших организаций. Spring-data отнимает большую часть ваших усилий по программированию, создавая такие репозитории. Нам просто нужно определить пустой интерфейс, Spring-Data делает все остальное из коробки. Тем не менее, он легко настраивается благодаря своему дизайну по соглашению над конфигурацией!
Актуальные репозитории
| 1 2 3 | @RestResource(path = "books", rel = "books")publicinterfaceBookRepository extendsPagingAndSortingRepository<Book, Long> {} | 
| 1 2 3 | @RestResource(path = "authors", rel = "authors")publicinterfaceAuthorRepository extendsPagingAndSortingRepository<Author, Integer> {} | 
AuthorRepository.java на github
Опять же, код почти не нужен. Даже аннотация @RestResource может быть опущена . Но если бы я это сделал, путь и rel были бы названы в честь сущности, которую я не хочу. Ресурс REST, содержащий несколько дочерних элементов, должен называться множественным.
Доступ к результату
Наш веб-сервис RESTful готов к развертыванию. После запуска он перечисляет все доступные ресурсы в корне, поэтому вы можете перемещаться оттуда.
ПОЛУЧИТЕ http: // localhost: 8080 /
| 01 02 03 04 05 06 07 08 09 10 | {  "links": [ {    "rel": "books",  }, {    "rel": "authors",  } ],  "content": [ ]} | 
Отлично! Теперь давайте создадим автора и книгу.
POST http: // localhost: 8080 / авторы
| 1 | {"name":"Uncle Bob"} | 
отклик
| 1 2 | 201CreatedLocation: http://localhost:8080/authors/1 | 
PUT http: // localhost: 8080 / books / 0132350882
| 1 2 3 4 5 6 7 8 9 | {  "title": "Clean Code",  "authors": [      {          "rel": "authors",      }  ]} | 
отклик
| 1 | 201Created | 
Заметили, как я использовал PUT для создания книги? Это потому, что его идентификатор является фактическим isbn. Я должен сказать серверу, какой isbn использовать, так как он не может догадаться. Я использовал POST для автора, поскольку его идентификатор — это просто инкрементное число, которое генерируется автоматически. Кроме того, я использовал ссылку, чтобы соединить и книгу ( / books / 0132350882 ) и автора ( / авторы / 1 ). Это в основном то, что гипермедиа все о: ссылки используются для навигации и отношений между сущностями.
Теперь давайте посмотрим, была ли книга создана соответствующим образом.
ПОЛУЧИТЕ http: // localhost: 8080 / books
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 | {  "links": [ ],  "content": [ {    "links": [ {      "rel": "books.Book.authors",    }, {      "rel": "self",    } ],    "title": "Clean Code"  } ],  "page": {    "size": 20,    "totalElements": 1,    "totalPages": 1,    "number": 1  }} | 
Отлично!
Вот тест интеграции, следуя этим шагам автоматически. Это также доступно в примере на github.
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | publicclassBookApiIT {    privatefinalRestTemplate restTemplate = newRestTemplate();    @Test    publicvoidtestCreateBookWithAuthor() throwsException {        finalURI authorUri = restTemplate.postForLocation(authorsUrl, sampleAuthor()); // create Author        finalURI bookUri = newURI(booksUrl + "/"+ sampleBookIsbn);        restTemplate.put(bookUri, sampleBook(authorUri.toString())); // create Book linked to Author        Resource<Book> book = getBook(bookUri);        assertNotNull(book);        finalURI authorsOfBookUri = newURI(book.getLink("books.Book.authors").getHref());        Resource<List<Resource<Author>>> authors = getAuthors(authorsOfBookUri);        assertNotNull(authors.getContent());        assertFalse(authors.getContent().isEmpty()); // check if /books/0132350882/authors contains an author    }    privateString sampleAuthor() {        return"{\"name\":\"Robert C. Martin\"}";    }    privatefinalString sampleBookIsbn = "0132350882";    privateString sampleBook(String authorUrl) {        return"{\"title\":\"Clean Code\",\"authors\":[{\"rel\":\"authors\",\"href\":\""+ authorUrl + "\"}]}";    }    privateResource<Book> getBook(URI uri) {        returnrestTemplate.exchange(uri, HttpMethod.GET, null, newParameterizedTypeReference<Resource<Book>>() {        }).getBody();    }    privateResource<List<Resource<Author>>> getAuthors(URI uri) {        returnrestTemplate.exchange(uri, HttpMethod.GET, null, newParameterizedTypeReference<Resource<List<Resource<Author>>>>() {        }).getBody();    }} | 
Вывод
Мы создали полный веб-сервис RESTful без особых усилий по написанию кода. Мы только что определили наши сущности и связь с базой данных. spring-data-rest заявил, что все остальное просто шаблон, и я согласен.
  Чтобы использовать веб-сервисы вручную, рассмотрите остальные оболочки .  Это командная оболочка, делающая навигацию в вашем веб-сервисе максимально простой и увлекательной.  Вот скриншот: 
 
  Полный пример доступен на моем github 
  https://github.com/gregorriegler/babdev-spring/tree/master/spring-data-rest 
  https://github.com/gregorriegler/babdev-spring