moved Rest Pagination article code from spring-rest-full to spring-boot-rest
This commit is contained in:
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.baeldung.web;
|
||||
package com.baeldung;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.baeldung.persistence;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
public interface IOperations<T extends Serializable> {
|
||||
|
||||
// read - all
|
||||
|
||||
Page<T> findPaginated(int page, int size);
|
||||
|
||||
// write
|
||||
|
||||
T create(final T entity);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.baeldung.persistence.dao;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import com.baeldung.persistence.model.Foo;
|
||||
|
||||
public interface IFooDao extends JpaRepository<Foo, Long> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.baeldung.persistence.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
|
||||
@Entity
|
||||
public class Foo implements Serializable {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private long id;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String name;
|
||||
|
||||
public Foo() {
|
||||
super();
|
||||
}
|
||||
|
||||
public Foo(final String name) {
|
||||
super();
|
||||
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
// API
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(final long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
final Foo other = (Foo) obj;
|
||||
if (name == null) {
|
||||
if (other.name != null)
|
||||
return false;
|
||||
} else if (!name.equals(other.name))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append("Foo [name=").append(name).append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.baeldung.persistence.service;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
import com.baeldung.persistence.IOperations;
|
||||
import com.baeldung.persistence.model.Foo;
|
||||
|
||||
public interface IFooService extends IOperations<Foo> {
|
||||
|
||||
Page<Foo> findPaginated(Pageable pageable);
|
||||
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package com.baeldung.persistence.service.common;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.baeldung.persistence.IOperations;
|
||||
|
||||
@Transactional
|
||||
public abstract class AbstractService<T extends Serializable> implements IOperations<T> {
|
||||
|
||||
// read - all
|
||||
|
||||
@Override
|
||||
public Page<T> findPaginated(final int page, final int size) {
|
||||
return getDao().findAll(PageRequest.of(page, size));
|
||||
}
|
||||
|
||||
// write
|
||||
|
||||
@Override
|
||||
public T create(final T entity) {
|
||||
return getDao().save(entity);
|
||||
}
|
||||
|
||||
protected abstract PagingAndSortingRepository<T, Long> getDao();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.baeldung.persistence.service.impl;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.baeldung.persistence.dao.IFooDao;
|
||||
import com.baeldung.persistence.model.Foo;
|
||||
import com.baeldung.persistence.service.IFooService;
|
||||
import com.baeldung.persistence.service.common.AbstractService;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
public class FooService extends AbstractService<Foo> implements IFooService {
|
||||
|
||||
@Autowired
|
||||
private IFooDao dao;
|
||||
|
||||
public FooService() {
|
||||
super();
|
||||
}
|
||||
|
||||
// API
|
||||
|
||||
@Override
|
||||
protected PagingAndSortingRepository<Foo, Long> getDao() {
|
||||
return dao;
|
||||
}
|
||||
|
||||
// custom methods
|
||||
|
||||
@Override
|
||||
public Page<Foo> findPaginated(Pageable pageable) {
|
||||
return dao.findAll(pageable);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.baeldung.spring;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.jdbc.datasource.DriverManagerDataSource;
|
||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
@Configuration
|
||||
@EnableTransactionManagement
|
||||
@PropertySource({ "classpath:persistence-${envTarget:h2}.properties" })
|
||||
@ComponentScan({ "com.baeldung.persistence" })
|
||||
// @ImportResource("classpath*:springDataPersistenceConfig.xml")
|
||||
@EnableJpaRepositories(basePackages = "com.baeldung.persistence.dao")
|
||||
public class PersistenceConfig {
|
||||
|
||||
@Autowired
|
||||
private Environment env;
|
||||
|
||||
public PersistenceConfig() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||
final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
|
||||
em.setDataSource(dataSource());
|
||||
em.setPackagesToScan(new String[] { "com.baeldung.persistence.model" });
|
||||
|
||||
final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
|
||||
// vendorAdapter.set
|
||||
em.setJpaVendorAdapter(vendorAdapter);
|
||||
em.setJpaProperties(additionalProperties());
|
||||
|
||||
return em;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
final DriverManagerDataSource dataSource = new DriverManagerDataSource();
|
||||
dataSource.setDriverClassName(Preconditions.checkNotNull(env.getProperty("jdbc.driverClassName")));
|
||||
dataSource.setUrl(Preconditions.checkNotNull(env.getProperty("jdbc.url")));
|
||||
dataSource.setUsername(Preconditions.checkNotNull(env.getProperty("jdbc.user")));
|
||||
dataSource.setPassword(Preconditions.checkNotNull(env.getProperty("jdbc.pass")));
|
||||
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PlatformTransactionManager transactionManager() {
|
||||
final JpaTransactionManager transactionManager = new JpaTransactionManager();
|
||||
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
|
||||
|
||||
return transactionManager;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
|
||||
return new PersistenceExceptionTranslationPostProcessor();
|
||||
}
|
||||
|
||||
final Properties additionalProperties() {
|
||||
final Properties hibernateProperties = new Properties();
|
||||
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
|
||||
hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
|
||||
// hibernateProperties.setProperty("hibernate.globally_quoted_identifiers", "true");
|
||||
return hibernateProperties;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.baeldung.spring;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.servlet.ViewResolver;
|
||||
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import org.springframework.web.servlet.view.InternalResourceViewResolver;
|
||||
|
||||
@Configuration
|
||||
@ComponentScan("com.baeldung.web")
|
||||
@EnableWebMvc
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
|
||||
public WebConfig() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ViewResolver viewResolver() {
|
||||
final InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
|
||||
viewResolver.setPrefix("/WEB-INF/view/");
|
||||
viewResolver.setSuffix(".jsp");
|
||||
return viewResolver;
|
||||
}
|
||||
|
||||
// API
|
||||
@Override
|
||||
public void addViewControllers(final ViewControllerRegistry registry) {
|
||||
registry.addViewController("/graph.html");
|
||||
registry.addViewController("/homepage.html");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
|
||||
configurer.defaultContentType(MediaType.APPLICATION_JSON);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,5 +27,4 @@ public class MyErrorController extends BasicErrorController {
|
||||
HttpStatus status = getStatus(request);
|
||||
return new ResponseEntity<>(body, status);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package com.baeldung.web.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class WebConfig {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.baeldung.web.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import com.baeldung.persistence.model.Foo;
|
||||
import com.baeldung.persistence.service.IFooService;
|
||||
import com.baeldung.web.exception.MyResourceNotFoundException;
|
||||
import com.baeldung.web.hateoas.event.PaginatedResultsRetrievedEvent;
|
||||
import com.baeldung.web.hateoas.event.ResourceCreatedEvent;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(value = "/auth/foos")
|
||||
public class FooController {
|
||||
|
||||
@Autowired
|
||||
private ApplicationEventPublisher eventPublisher;
|
||||
|
||||
@Autowired
|
||||
private IFooService service;
|
||||
|
||||
public FooController() {
|
||||
super();
|
||||
}
|
||||
|
||||
// API
|
||||
|
||||
// read - all
|
||||
|
||||
@RequestMapping(params = { "page", "size" }, method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public List<Foo> findPaginated(@RequestParam("page") final int page, @RequestParam("size") final int size,
|
||||
final UriComponentsBuilder uriBuilder, final HttpServletResponse response) {
|
||||
final Page<Foo> resultPage = service.findPaginated(page, size);
|
||||
if (page > resultPage.getTotalPages()) {
|
||||
throw new MyResourceNotFoundException();
|
||||
}
|
||||
eventPublisher.publishEvent(new PaginatedResultsRetrievedEvent<Foo>(Foo.class, uriBuilder, response, page,
|
||||
resultPage.getTotalPages(), size));
|
||||
|
||||
return resultPage.getContent();
|
||||
}
|
||||
|
||||
@GetMapping("/pageable")
|
||||
@ResponseBody
|
||||
public List<Foo> findPaginatedWithPageable(Pageable pageable, final UriComponentsBuilder uriBuilder,
|
||||
final HttpServletResponse response) {
|
||||
final Page<Foo> resultPage = service.findPaginated(pageable);
|
||||
if (pageable.getPageNumber() > resultPage.getTotalPages()) {
|
||||
throw new MyResourceNotFoundException();
|
||||
}
|
||||
eventPublisher.publishEvent(new PaginatedResultsRetrievedEvent<Foo>(Foo.class, uriBuilder, response,
|
||||
pageable.getPageNumber(), resultPage.getTotalPages(), pageable.getPageSize()));
|
||||
|
||||
return resultPage.getContent();
|
||||
}
|
||||
|
||||
// write
|
||||
|
||||
@RequestMapping(method = RequestMethod.POST)
|
||||
@ResponseStatus(HttpStatus.CREATED)
|
||||
@ResponseBody
|
||||
public Foo create(@RequestBody final Foo resource, final HttpServletResponse response) {
|
||||
Preconditions.checkNotNull(resource);
|
||||
final Foo foo = service.create(resource);
|
||||
final Long idOfCreatedResource = foo.getId();
|
||||
|
||||
eventPublisher.publishEvent(new ResourceCreatedEvent(this, response, idOfCreatedResource));
|
||||
|
||||
return foo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.baeldung.web.controller;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.util.UriTemplate;
|
||||
|
||||
import com.baeldung.web.util.LinkUtil;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(value = "/auth/")
|
||||
public class RootController {
|
||||
|
||||
public RootController() {
|
||||
super();
|
||||
}
|
||||
|
||||
// API
|
||||
|
||||
// discover
|
||||
|
||||
@RequestMapping(value = "admin", method = RequestMethod.GET)
|
||||
@ResponseStatus(value = HttpStatus.NO_CONTENT)
|
||||
public void adminRoot(final HttpServletRequest request, final HttpServletResponse response) {
|
||||
final String rootUri = request.getRequestURL()
|
||||
.toString();
|
||||
|
||||
final URI fooUri = new UriTemplate("{rootUri}/{resource}").expand(rootUri, "foo");
|
||||
final String linkToFoo = LinkUtil.createLinkHeader(fooUri.toASCIIString(), "collection");
|
||||
response.addHeader("Link", linkToFoo);
|
||||
}
|
||||
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
package com.baeldung.web.hateoas.event;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* Event that is fired when a paginated search is performed.
|
||||
* <p/>
|
||||
* This event object contains all the information needed to create the URL for the paginated results
|
||||
*
|
||||
* @param <T>
|
||||
* Type of the result that is being handled (commonly Entities).
|
||||
*/
|
||||
public final class PaginatedResultsRetrievedEvent<T extends Serializable> extends ApplicationEvent {
|
||||
private final UriComponentsBuilder uriBuilder;
|
||||
private final HttpServletResponse response;
|
||||
private final int page;
|
||||
private final int totalPages;
|
||||
private final int pageSize;
|
||||
|
||||
public PaginatedResultsRetrievedEvent(final Class<T> clazz, final UriComponentsBuilder uriBuilderToSet, final HttpServletResponse responseToSet, final int pageToSet, final int totalPagesToSet, final int pageSizeToSet) {
|
||||
super(clazz);
|
||||
|
||||
uriBuilder = uriBuilderToSet;
|
||||
response = responseToSet;
|
||||
page = pageToSet;
|
||||
totalPages = totalPagesToSet;
|
||||
pageSize = pageSizeToSet;
|
||||
}
|
||||
|
||||
// API
|
||||
|
||||
public final UriComponentsBuilder getUriBuilder() {
|
||||
return uriBuilder;
|
||||
}
|
||||
|
||||
public final HttpServletResponse getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public final int getPage() {
|
||||
return page;
|
||||
}
|
||||
|
||||
public final int getTotalPages() {
|
||||
return totalPages;
|
||||
}
|
||||
|
||||
public final int getPageSize() {
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* The object on which the Event initially occurred.
|
||||
*
|
||||
* @return The object on which the Event initially occurred.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final Class<T> getClazz() {
|
||||
return (Class<T>) getSource();
|
||||
}
|
||||
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.baeldung.web.hateoas.event;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
public class ResourceCreatedEvent extends ApplicationEvent {
|
||||
private final HttpServletResponse response;
|
||||
private final long idOfNewResource;
|
||||
|
||||
public ResourceCreatedEvent(final Object source, final HttpServletResponse response, final long idOfNewResource) {
|
||||
super(source);
|
||||
|
||||
this.response = response;
|
||||
this.idOfNewResource = idOfNewResource;
|
||||
}
|
||||
|
||||
// API
|
||||
|
||||
public HttpServletResponse getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public long getIdOfNewResource() {
|
||||
return idOfNewResource;
|
||||
}
|
||||
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
package com.baeldung.web.hateoas.listener;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import com.baeldung.web.hateoas.event.PaginatedResultsRetrievedEvent;
|
||||
import com.baeldung.web.util.LinkUtil;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.net.HttpHeaders;
|
||||
|
||||
@SuppressWarnings({ "rawtypes" })
|
||||
@Component
|
||||
class PaginatedResultsRetrievedDiscoverabilityListener implements ApplicationListener<PaginatedResultsRetrievedEvent> {
|
||||
|
||||
private static final String PAGE = "page";
|
||||
|
||||
public PaginatedResultsRetrievedDiscoverabilityListener() {
|
||||
super();
|
||||
}
|
||||
|
||||
// API
|
||||
|
||||
@Override
|
||||
public final void onApplicationEvent(final PaginatedResultsRetrievedEvent ev) {
|
||||
Preconditions.checkNotNull(ev);
|
||||
|
||||
addLinkHeaderOnPagedResourceRetrieval(ev.getUriBuilder(), ev.getResponse(), ev.getClazz(), ev.getPage(), ev.getTotalPages(), ev.getPageSize());
|
||||
}
|
||||
|
||||
// - note: at this point, the URI is transformed into plural (added `s`) in a hardcoded way - this will change in the future
|
||||
final void addLinkHeaderOnPagedResourceRetrieval(final UriComponentsBuilder uriBuilder, final HttpServletResponse response, final Class clazz, final int page, final int totalPages, final int pageSize) {
|
||||
plural(uriBuilder, clazz);
|
||||
|
||||
final StringBuilder linkHeader = new StringBuilder();
|
||||
if (hasNextPage(page, totalPages)) {
|
||||
final String uriForNextPage = constructNextPageUri(uriBuilder, page, pageSize);
|
||||
linkHeader.append(LinkUtil.createLinkHeader(uriForNextPage, LinkUtil.REL_NEXT));
|
||||
}
|
||||
if (hasPreviousPage(page)) {
|
||||
final String uriForPrevPage = constructPrevPageUri(uriBuilder, page, pageSize);
|
||||
appendCommaIfNecessary(linkHeader);
|
||||
linkHeader.append(LinkUtil.createLinkHeader(uriForPrevPage, LinkUtil.REL_PREV));
|
||||
}
|
||||
if (hasFirstPage(page)) {
|
||||
final String uriForFirstPage = constructFirstPageUri(uriBuilder, pageSize);
|
||||
appendCommaIfNecessary(linkHeader);
|
||||
linkHeader.append(LinkUtil.createLinkHeader(uriForFirstPage, LinkUtil.REL_FIRST));
|
||||
}
|
||||
if (hasLastPage(page, totalPages)) {
|
||||
final String uriForLastPage = constructLastPageUri(uriBuilder, totalPages, pageSize);
|
||||
appendCommaIfNecessary(linkHeader);
|
||||
linkHeader.append(LinkUtil.createLinkHeader(uriForLastPage, LinkUtil.REL_LAST));
|
||||
}
|
||||
|
||||
if (linkHeader.length() > 0) {
|
||||
response.addHeader(HttpHeaders.LINK, linkHeader.toString());
|
||||
}
|
||||
}
|
||||
|
||||
final String constructNextPageUri(final UriComponentsBuilder uriBuilder, final int page, final int size) {
|
||||
return uriBuilder.replaceQueryParam(PAGE, page + 1).replaceQueryParam("size", size).build().encode().toUriString();
|
||||
}
|
||||
|
||||
final String constructPrevPageUri(final UriComponentsBuilder uriBuilder, final int page, final int size) {
|
||||
return uriBuilder.replaceQueryParam(PAGE, page - 1).replaceQueryParam("size", size).build().encode().toUriString();
|
||||
}
|
||||
|
||||
final String constructFirstPageUri(final UriComponentsBuilder uriBuilder, final int size) {
|
||||
return uriBuilder.replaceQueryParam(PAGE, 0).replaceQueryParam("size", size).build().encode().toUriString();
|
||||
}
|
||||
|
||||
final String constructLastPageUri(final UriComponentsBuilder uriBuilder, final int totalPages, final int size) {
|
||||
return uriBuilder.replaceQueryParam(PAGE, totalPages).replaceQueryParam("size", size).build().encode().toUriString();
|
||||
}
|
||||
|
||||
final boolean hasNextPage(final int page, final int totalPages) {
|
||||
return page < (totalPages - 1);
|
||||
}
|
||||
|
||||
final boolean hasPreviousPage(final int page) {
|
||||
return page > 0;
|
||||
}
|
||||
|
||||
final boolean hasFirstPage(final int page) {
|
||||
return hasPreviousPage(page);
|
||||
}
|
||||
|
||||
final boolean hasLastPage(final int page, final int totalPages) {
|
||||
return (totalPages > 1) && hasNextPage(page, totalPages);
|
||||
}
|
||||
|
||||
final void appendCommaIfNecessary(final StringBuilder linkHeader) {
|
||||
if (linkHeader.length() > 0) {
|
||||
linkHeader.append(", ");
|
||||
}
|
||||
}
|
||||
|
||||
// template
|
||||
|
||||
protected void plural(final UriComponentsBuilder uriBuilder, final Class clazz) {
|
||||
final String resourceName = clazz.getSimpleName().toLowerCase() + "s";
|
||||
uriBuilder.path("/auth/" + resourceName);
|
||||
}
|
||||
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.baeldung.web.hateoas.listener;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.http.HttpHeaders;
|
||||
import com.baeldung.web.hateoas.event.ResourceCreatedEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
@Component
|
||||
class ResourceCreatedDiscoverabilityListener implements ApplicationListener<ResourceCreatedEvent> {
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(final ResourceCreatedEvent resourceCreatedEvent) {
|
||||
Preconditions.checkNotNull(resourceCreatedEvent);
|
||||
|
||||
final HttpServletResponse response = resourceCreatedEvent.getResponse();
|
||||
final long idOfNewResource = resourceCreatedEvent.getIdOfNewResource();
|
||||
|
||||
addLinkHeaderOnResourceCreation(response, idOfNewResource);
|
||||
}
|
||||
|
||||
void addLinkHeaderOnResourceCreation(final HttpServletResponse response, final long idOfNewResource) {
|
||||
// final String requestUrl = request.getRequestURL().toString();
|
||||
// final URI uri = new UriTemplate("{requestUrl}/{idOfNewResource}").expand(requestUrl, idOfNewResource);
|
||||
|
||||
final URI uri = ServletUriComponentsBuilder.fromCurrentRequestUri().path("/{idOfNewResource}").buildAndExpand(idOfNewResource).toUri();
|
||||
response.setHeader(HttpHeaders.LOCATION, uri.toASCIIString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.baeldung.web.util;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Provides some constants and utility methods to build a Link Header to be stored in the {@link HttpServletResponse} object
|
||||
*/
|
||||
public final class LinkUtil {
|
||||
|
||||
public static final String REL_COLLECTION = "collection";
|
||||
public static final String REL_NEXT = "next";
|
||||
public static final String REL_PREV = "prev";
|
||||
public static final String REL_FIRST = "first";
|
||||
public static final String REL_LAST = "last";
|
||||
|
||||
private LinkUtil() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
/**
|
||||
* Creates a Link Header to be stored in the {@link HttpServletResponse} to provide Discoverability features to the user
|
||||
*
|
||||
* @param uri
|
||||
* the base uri
|
||||
* @param rel
|
||||
* the relative path
|
||||
*
|
||||
* @return the complete url
|
||||
*/
|
||||
public static String createLinkHeader(final String uri, final String rel) {
|
||||
return "<" + uri + ">; rel=\"" + rel + "\"";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.baeldung.web.util;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import com.baeldung.web.exception.MyResourceNotFoundException;
|
||||
|
||||
/**
|
||||
* Simple static methods to be called at the start of your own methods to verify correct arguments and state. If the Precondition fails, an {@link HttpStatus} code is thrown
|
||||
*/
|
||||
public final class RestPreconditions {
|
||||
|
||||
private RestPreconditions() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
// API
|
||||
|
||||
/**
|
||||
* Check if some value was found, otherwise throw exception.
|
||||
*
|
||||
* @param expression
|
||||
* has value true if found, otherwise false
|
||||
* @throws MyResourceNotFoundException
|
||||
* if expression is false, means value not found.
|
||||
*/
|
||||
public static void checkFound(final boolean expression) {
|
||||
if (!expression) {
|
||||
throw new MyResourceNotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if some value was found, otherwise throw exception.
|
||||
*
|
||||
* @param expression
|
||||
* has value true if found, otherwise false
|
||||
* @throws MyResourceNotFoundException
|
||||
* if expression is false, means value not found.
|
||||
*/
|
||||
public static <T> T checkFound(final T resource) {
|
||||
if (resource == null) {
|
||||
throw new MyResourceNotFoundException();
|
||||
}
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
server.port=8082
|
||||
server.servlet.context-path=/spring-boot-rest
|
||||
|
||||
### Spring Boot default error handling configurations
|
||||
#server.error.whitelabel.enabled=false
|
||||
#server.error.include-stacktrace=always
|
||||
@@ -0,0 +1,22 @@
|
||||
## jdbc.X
|
||||
#jdbc.driverClassName=com.mysql.jdbc.Driver
|
||||
#jdbc.url=jdbc:mysql://localhost:3306/spring_hibernate4_01?createDatabaseIfNotExist=true
|
||||
#jdbc.user=tutorialuser
|
||||
#jdbc.pass=tutorialmy5ql
|
||||
#
|
||||
## hibernate.X
|
||||
#hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
|
||||
#hibernate.show_sql=false
|
||||
#hibernate.hbm2ddl.auto=create-drop
|
||||
|
||||
|
||||
# jdbc.X
|
||||
jdbc.driverClassName=org.h2.Driver
|
||||
jdbc.url=jdbc:h2:mem:security_permission;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
|
||||
jdbc.user=sa
|
||||
jdbc.pass=
|
||||
|
||||
# hibernate.X
|
||||
hibernate.dialect=org.hibernate.dialect.H2Dialect
|
||||
hibernate.show_sql=false
|
||||
hibernate.hbm2ddl.auto=create-drop
|
||||
@@ -0,0 +1,10 @@
|
||||
# jdbc.X
|
||||
jdbc.driverClassName=com.mysql.jdbc.Driver
|
||||
jdbc.url=jdbc:mysql://localhost:3306/spring_hibernate4_01?createDatabaseIfNotExist=true
|
||||
jdbc.user=tutorialuser
|
||||
jdbc.pass=tutorialmy5ql
|
||||
|
||||
# hibernate.X
|
||||
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
|
||||
hibernate.show_sql=false
|
||||
hibernate.hbm2ddl.auto=create-drop
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jpa="http://www.springframework.org/schema/data/jpa"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/data/jpa
|
||||
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"
|
||||
>
|
||||
|
||||
<jpa:repositories base-package="com.baeldung.persistence.dao"/>
|
||||
|
||||
</beans>
|
||||
Reference in New Issue
Block a user