Статьи

Spring Data REST в действии

Что такое весна-дата-отдых?

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
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
 
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[]{AppConfiguration.class};
    }
 
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{WebConfiguration.class};
    }
 
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}

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
@EnableTransactionManagement
public class AppConfiguration {
 
    @Bean
    public DataSource dataSource() {
        EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
        return builder.setType(EmbeddedDatabaseType.HSQL).build();
    }
 
    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setDatabase(Database.HSQL);
        vendorAdapter.setGenerateDdl(true);
 
        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(vendorAdapter);
        factory.setPackagesToScan(getClass().getPackage().getName());
        factory.setDataSource(dataSource());
 
        return factory;
    }
 
    @Bean
    public PlatformTransactionManager transactionManager() {
        return new JpaTransactionManager();
    }
}

AppConfiguration.java на github

Теперь к настройке сервлета приложения: WebConfiguration

1
2
3
@Configuration
public class WebConfiguration extends RepositoryRestMvcConfiguration {
}

WebConfiguration.java на github

О, ну это немного коротко, не так ли? Для полной настройки не требуется ни одной строки кода. Это действительно хорошее применение соглашения о конфигурации парадигмы. Теперь мы можем начать создавать репозитории spring-data-jpa, так как они будут автоматически представлены как ресурсы RESTful. И мы все еще можем добавить пользовательскую конфигурацию в класс WebConfiguration, если это необходимо.

Инициализация была действительно короткой и легкой. Нам не нужно было ничего особенного кодировать. Единственное, что мы сделали, — это установили соединение с базой данных и режим гибернации, что, очевидно, неизбежно. Теперь, когда мы настроили наш «REST Servlet» и постоянство, давайте перейдем к самому приложению, начиная с модели.

Модель

Я сделаю это очень просто, создав только два связанных объекта.
bookapi_model

01
02
03
04
05
06
07
08
09
10
11
12
13
14
@Entity
public class Book {
 
    @Id
    private String isbn;
 
    private String title;
 
    private String language;
 
    @ManyToMany
    private List<Author> authors;
 
}

Book.java на GitHub

01
02
03
04
05
06
07
08
09
10
11
12
13
@Entity
public class Author {
 
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Integer id;
 
    private String name;
 
    @ManyToMany(mappedBy = "authors")
    private List<Book> books;
 
}

Автор.java на github

Чтобы наконец сделать сущности постоянными и выставленными как веб-сервис RESTful, нам нужны репозитории Spring-Data. Хранилище — это в основном DAO . Он предлагает функциональность CRUD для наших организаций. Spring-data отнимает большую часть ваших усилий по программированию, создавая такие репозитории. Нам просто нужно определить пустой интерфейс, Spring-Data делает все остальное из коробки. Тем не менее, он легко настраивается благодаря своему дизайну по соглашению над конфигурацией!

Актуальные репозитории

1
2
3
@RestResource(path = "books", rel = "books")
public interface BookRepository extends PagingAndSortingRepository<Book, Long> {
}

BookRepository.java на github

1
2
3
@RestResource(path = "authors", rel = "authors")
public interface AuthorRepository extends PagingAndSortingRepository<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",
    "href" : "http://localhost:8080/books"
  }, {
    "rel" : "authors",
  } ],
  "content" : [ ]
}

Отлично! Теперь давайте создадим автора и книгу.

POST http: // localhost: 8080 / авторы

1
{"name":"Uncle Bob"}

отклик

1
2
201 Created
Location: 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",
          "href": "http://localhost:8080/authors/1"
      }
  ]
}

отклик

1
201 Created

Заметили, как я использовал 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
public class BookApiIT {
 
    private final RestTemplate restTemplate = new RestTemplate();
 
    private final String authorsUrl = "http://localhost:8080/authors";
    private final String booksUrl = "http://localhost:8080/books";
 
    @Test
    public void testCreateBookWithAuthor() throws Exception {
        final URI authorUri = restTemplate.postForLocation(authorsUrl, sampleAuthor()); // create Author
 
        final URI bookUri = new URI(booksUrl + "/" + sampleBookIsbn);
        restTemplate.put(bookUri, sampleBook(authorUri.toString())); // create Book linked to Author
 
        Resource<Book> book = getBook(bookUri);
        assertNotNull(book);
 
        final URI authorsOfBookUri = new URI(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
    }
 
    private String sampleAuthor() {
        return "{\"name\":\"Robert C. Martin\"}";
    }
 
    private final String sampleBookIsbn = "0132350882";
 
    private String sampleBook(String authorUrl) {
        return "{\"title\":\"Clean Code\",\"authors\":[{\"rel\":\"authors\",\"href\":\"" + authorUrl + "\"}]}";
    }
 
    private Resource<Book> getBook(URI uri) {
        return restTemplate.exchange(uri, HttpMethod.GET, null, new ParameterizedTypeReference<Resource<Book>>() {
        }).getBody();
    }
 
    private Resource<List<Resource<Author>>> getAuthors(URI uri) {
        return restTemplate.exchange(uri, HttpMethod.GET, null, new ParameterizedTypeReference<Resource<List<Resource<Author>>>>() {
        }).getBody();
    }
}

BookApiIT.java на github

Вывод

Мы создали полный веб-сервис RESTful без особых усилий по написанию кода. Мы только что определили наши сущности и связь с базой данных. spring-data-rest заявил, что все остальное просто шаблон, и я согласен.

Чтобы использовать веб-сервисы вручную, рассмотрите остальные оболочки . Это командная оболочка, делающая навигацию в вашем веб-сервисе максимально простой и увлекательной. Вот скриншот:
rest_shell
Полный пример доступен на моем github
https://github.com/gregorriegler/babdev-spring/tree/master/spring-data-rest
https://github.com/gregorriegler/babdev-spring

Ссылка: Spring Data REST in Action от нашего партнера по JCG Грегора Риглера в блоге « Будь лучшим разработчиком» .