JAVA-11535 Move spring-data related modules to persistence-modules (#12126)
This commit is contained in:
@@ -88,6 +88,9 @@
|
||||
<module>spring-data-mongodb-reactive</module>
|
||||
<module>spring-data-neo4j</module>
|
||||
<module>spring-data-redis</module>
|
||||
<module>spring-data-rest</module>
|
||||
<module>spring-data-rest-2</module>
|
||||
<module>spring-data-rest-querydsl</module>
|
||||
<module>spring-data-solr</module>
|
||||
<module>spring-hibernate-3</module>
|
||||
<module>spring-hibernate-5</module> <!-- long running -->
|
||||
|
||||
20
persistence-modules/spring-data-rest-2/README.md
Normal file
20
persistence-modules/spring-data-rest-2/README.md
Normal file
@@ -0,0 +1,20 @@
|
||||
## Spring Data REST
|
||||
|
||||
This module contains articles about Spring Data REST
|
||||
|
||||
### Relevant Articles:
|
||||
- [Guide to Spring Data REST Validators](https://www.baeldung.com/spring-data-rest-validators)
|
||||
- [Spring Data Web Support](https://www.baeldung.com/spring-data-web-support)
|
||||
- [Spring REST and HAL Browser](https://www.baeldung.com/spring-rest-hal)
|
||||
|
||||
### The Course
|
||||
The "REST With Spring" Classes: http://bit.ly/restwithspring
|
||||
|
||||
# Running the project
|
||||
The application uses [Spring Boot](http://projects.spring.io/spring-boot/), so it is easy to run. You can start it any of a few ways:
|
||||
* Run the `main` method from `SpringDataRestApplication`
|
||||
* Use the Maven Spring Boot plugin: `mvn spring-boot:run`
|
||||
* Package the application as a JAR and run it using `java -jar intro-spring-data-rest-2.jar`
|
||||
|
||||
# Viewing the running application
|
||||
To view the running application, visit [http://localhost:8080](http://localhost:8080) in your browser
|
||||
100
persistence-modules/spring-data-rest-2/pom.xml
Normal file
100
persistence-modules/spring-data-rest-2/pom.xml
Normal file
@@ -0,0 +1,100 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>spring-data-rest-2</artifactId>
|
||||
<version>1.0</version>
|
||||
<name>spring-data-rest-2</name>
|
||||
<packaging>jar</packaging>
|
||||
<description>Intro to Spring Data REST</description>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>parent-boot-2</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<relativePath>../../parent-boot-2</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-rest</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-rest-hal-explorer</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.querydsl</groupId>
|
||||
<artifactId>querydsl-apt</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.querydsl</groupId>
|
||||
<artifactId>querydsl-jpa</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.mysema.maven</groupId>
|
||||
<artifactId>maven-apt-plugin</artifactId>
|
||||
<version>${maven.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>process</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>target/generated-sources</outputDirectory>
|
||||
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<properties>
|
||||
<start-class>com.baeldung.books.SpringDataRestApplication</start-class>
|
||||
<maven.version>1.0</maven.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.baeldung.books;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SpringDataRestApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SpringDataRestApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.baeldung.books.config;
|
||||
|
||||
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.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.jdbc.datasource.DriverManagerDataSource;
|
||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
|
||||
|
||||
@Configuration
|
||||
@EnableJpaRepositories(basePackages = "com.baeldung.books.repositories")
|
||||
public class DbConfig {
|
||||
|
||||
@Autowired
|
||||
private Environment env;
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
final DriverManagerDataSource dataSource = new DriverManagerDataSource();
|
||||
dataSource.setDriverClassName(env.getProperty("driverClassName"));
|
||||
dataSource.setUrl(env.getProperty("url"));
|
||||
dataSource.setUsername(env.getProperty("user"));
|
||||
dataSource.setPassword(env.getProperty("password"));
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||
final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
|
||||
em.setDataSource(dataSource());
|
||||
em.setPackagesToScan(new String[] { "com.baeldung.books.models" });
|
||||
em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
|
||||
em.setJpaProperties(additionalProperties());
|
||||
return em;
|
||||
}
|
||||
|
||||
final Properties additionalProperties() {
|
||||
final Properties hibernateProperties = new Properties();
|
||||
if (env.getProperty("hibernate.hbm2ddl.auto") != null) {
|
||||
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
|
||||
}
|
||||
if (env.getProperty("hibernate.dialect") != null) {
|
||||
hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
|
||||
}
|
||||
if (env.getProperty("hibernate.show_sql") != null) {
|
||||
hibernateProperties.setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
|
||||
}
|
||||
return hibernateProperties;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Profile("h2")
|
||||
@PropertySource("classpath:persistence-h2.properties")
|
||||
class H2Config {
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.baeldung.books.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.core.mapping.ExposureConfiguration;
|
||||
import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
|
||||
import com.baeldung.books.models.WebsiteUser;
|
||||
|
||||
@Configuration
|
||||
public class RestConfig implements RepositoryRestConfigurer {
|
||||
|
||||
@Override
|
||||
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration repositoryRestConfiguration,
|
||||
CorsRegistry cors) {
|
||||
ExposureConfiguration config = repositoryRestConfiguration.getExposureConfiguration();
|
||||
config.forDomainType(WebsiteUser.class).withItemExposure((metadata, httpMethods) -> httpMethods.disable(HttpMethod.PATCH));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.baeldung.books.config;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.rest.core.event.ValidatingRepositoryEventListener;
|
||||
import org.springframework.validation.Validator;
|
||||
|
||||
@Configuration
|
||||
public class ValidatorEventRegister implements InitializingBean {
|
||||
|
||||
@Autowired
|
||||
ValidatingRepositoryEventListener validatingRepositoryEventListener;
|
||||
|
||||
@Autowired
|
||||
private Map<String, Validator> validators;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
List<String> events = Arrays.asList("beforeCreate", "afterCreate", "beforeSave", "afterSave", "beforeLinkSave", "afterLinkSave", "beforeDelete", "afterDelete");
|
||||
|
||||
for (Map.Entry<String, Validator> entry : validators.entrySet()) {
|
||||
events.stream().filter(p -> entry.getKey().startsWith(p)).findFirst().ifPresent(p -> validatingRepositoryEventListener.addValidator(p, entry.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.baeldung.books.exception.handlers;
|
||||
|
||||
import org.springframework.data.rest.core.RepositoryConstraintViolationException;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.validation.ObjectError;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ControllerAdvice
|
||||
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
|
||||
|
||||
@ExceptionHandler({ RepositoryConstraintViolationException.class })
|
||||
public ResponseEntity<Object> handleAccessDeniedException(Exception ex, WebRequest request) {
|
||||
RepositoryConstraintViolationException nevEx = (RepositoryConstraintViolationException) ex;
|
||||
|
||||
String errors = nevEx.getErrors().getAllErrors().stream().map(ObjectError::toString).collect(Collectors.joining("\n"));
|
||||
return new ResponseEntity<>(errors, new HttpHeaders(), HttpStatus.NOT_ACCEPTABLE);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.baeldung.books.models;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
|
||||
@Entity
|
||||
public class WebsiteUser {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private long id;
|
||||
|
||||
private String name;
|
||||
private String email;
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.baeldung.books.repositories;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
|
||||
import org.springframework.data.rest.core.annotation.RestResource;
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
|
||||
import com.baeldung.books.models.WebsiteUser;
|
||||
|
||||
@CrossOrigin
|
||||
@RepositoryRestResource(collectionResourceRel = "users", path = "users")
|
||||
public interface UserRepository extends CrudRepository<WebsiteUser, Long> {
|
||||
|
||||
@Override
|
||||
@RestResource(exported = false)
|
||||
void delete(WebsiteUser entity);
|
||||
|
||||
@Override
|
||||
@RestResource(exported = false)
|
||||
void deleteAll();
|
||||
|
||||
@Override
|
||||
@RestResource(exported = false)
|
||||
void deleteAll(Iterable<? extends WebsiteUser> entities);
|
||||
|
||||
@Override
|
||||
@RestResource(exported = false)
|
||||
void deleteById(Long aLong);
|
||||
|
||||
@RestResource(path = "byEmail", rel = "customFindMethod")
|
||||
WebsiteUser findByEmail(@Param("email") String email);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.baeldung.books.validators;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.Validator;
|
||||
|
||||
import com.baeldung.books.models.WebsiteUser;
|
||||
|
||||
@Component("beforeCreateWebsiteUserValidator")
|
||||
public class WebsiteUserValidator implements Validator {
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return WebsiteUser.class.equals(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(Object obj, Errors errors) {
|
||||
|
||||
WebsiteUser user = (WebsiteUser) obj;
|
||||
if (checkInputString(user.getName())) {
|
||||
errors.rejectValue("name", "name.empty");
|
||||
}
|
||||
|
||||
if (checkInputString(user.getEmail())) {
|
||||
errors.rejectValue("email", "email.empty");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkInputString(String input) {
|
||||
return (input == null || input.trim().length() == 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.baeldung.halbrowser;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class App {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(App.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.baeldung.halbrowser.config;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.baeldung.halbrowser.data.BookRepository;
|
||||
import com.baeldung.halbrowser.model.Book;
|
||||
|
||||
@Component
|
||||
public class DBLoader implements ApplicationRunner {
|
||||
|
||||
private final BookRepository bookRepository;
|
||||
|
||||
@Autowired
|
||||
DBLoader(BookRepository bookRepository){
|
||||
this.bookRepository = bookRepository;
|
||||
}
|
||||
|
||||
|
||||
public void run(ApplicationArguments applicationArguments) throws Exception {
|
||||
|
||||
String[] templates = {
|
||||
"Up and running with %s",
|
||||
"%s Basics",
|
||||
"%s for Beginners",
|
||||
"%s for Neckbeards",
|
||||
"Under the hood: %s",
|
||||
"Discovering %s",
|
||||
"A short guide to %s",
|
||||
"%s with Baeldung"
|
||||
};
|
||||
|
||||
String[] buzzWords = {
|
||||
"Spring REST Data",
|
||||
"Java 9",
|
||||
"Scala",
|
||||
"Groovy",
|
||||
"Hibernate",
|
||||
"Spring HATEOS",
|
||||
"The HAL Browser",
|
||||
"Spring webflux",
|
||||
};
|
||||
|
||||
String[] authorFirstName = {
|
||||
"John %s",
|
||||
"Steve %s",
|
||||
"Samantha %s",
|
||||
"Gale %s",
|
||||
"Tom %s"
|
||||
};
|
||||
|
||||
String[] authorLastName = {
|
||||
"Giles",
|
||||
"Gill",
|
||||
"Smith",
|
||||
"Armstrong"
|
||||
};
|
||||
|
||||
String[] blurbs = {
|
||||
"It was getting dark when the %s %s" ,
|
||||
"Scott was nearly there when he heard that a %s %s",
|
||||
"Diana was a lovable Java coder until the %s %s",
|
||||
"The gripping story of a small %s and the day it %s"
|
||||
};
|
||||
|
||||
String[] blublMiddles = {
|
||||
"distaster",
|
||||
"dog",
|
||||
"cat",
|
||||
"turtle",
|
||||
"hurricane"
|
||||
};
|
||||
|
||||
String[] end = {
|
||||
"hit the school",
|
||||
"memorised pi to 100 decimal places!",
|
||||
"became a world champion armwrestler",
|
||||
"became a Java REST master!!"
|
||||
};
|
||||
|
||||
Random random = new Random();
|
||||
|
||||
IntStream.range(0, 100)
|
||||
.forEach(i -> {
|
||||
String template = templates[i % templates.length];
|
||||
String buzzword = buzzWords[i % buzzWords.length];
|
||||
String blurbStart = blurbs[i % blurbs.length];
|
||||
String middle = blublMiddles[i % blublMiddles.length];
|
||||
String ending = end[i % end.length];
|
||||
String blurb = String.format(blurbStart, middle, ending);
|
||||
String firstName = authorFirstName[i % authorFirstName.length];
|
||||
String lastname = authorLastName[i % authorLastName.length];
|
||||
Book book = new Book(String.format(template, buzzword), String.format(firstName, lastname), blurb, random.nextInt(1000-200) + 200);
|
||||
|
||||
bookRepository.save(book);
|
||||
|
||||
System.out.println(book);
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.baeldung.halbrowser.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.data.rest.core.event.ValidatingRepositoryEventListener;
|
||||
import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer;
|
||||
import org.springframework.validation.Validator;
|
||||
|
||||
@Configuration
|
||||
public class RestConfig implements RepositoryRestConfigurer {
|
||||
|
||||
//access to global validator
|
||||
@Autowired
|
||||
@Lazy
|
||||
private Validator validator;
|
||||
|
||||
@Override
|
||||
public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
|
||||
validatingListener.addValidator("beforeCreate", validator );
|
||||
validatingListener.addValidator("beforeSave", validator);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.baeldung.halbrowser.data;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.data.rest.core.annotation.RestResource;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.baeldung.halbrowser.model.Book;
|
||||
|
||||
@Repository
|
||||
public interface BookRepository extends PagingAndSortingRepository<Book, Long> {
|
||||
|
||||
@RestResource(rel = "title-contains", path="title-contains")
|
||||
Page<Book> findByTitleContaining(@Param("query") String query, Pageable page);
|
||||
|
||||
@RestResource(rel = "author-contains", path="author-contains", exported = false)
|
||||
Page<Book> findByAuthorContaining(@Param("query") String query, Pageable page);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.baeldung.halbrowser.model;
|
||||
|
||||
import javax.persistence.*;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Entity
|
||||
public class Book {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
|
||||
@NotNull
|
||||
@Column(columnDefinition = "VARCHAR", length = 100)
|
||||
private String title;
|
||||
|
||||
@NotNull
|
||||
@Column(columnDefinition = "VARCHAR", length = 100)
|
||||
private String author;
|
||||
|
||||
@Column(columnDefinition = "VARCHAR", length = 1000)
|
||||
private String blurb;
|
||||
|
||||
private int pages;
|
||||
|
||||
public Book() {
|
||||
}
|
||||
|
||||
public Book(@NotNull String title, @NotNull String author, String blurb, int pages) {
|
||||
this.title = title;
|
||||
this.author = author;
|
||||
this.blurb = blurb;
|
||||
this.pages = pages;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public void setAuthor(String author) {
|
||||
this.author = author;
|
||||
}
|
||||
|
||||
public String getBlurb() {
|
||||
return blurb;
|
||||
}
|
||||
|
||||
public void setBlurb(String blurb) {
|
||||
this.blurb = blurb;
|
||||
}
|
||||
|
||||
public int getPages() {
|
||||
return pages;
|
||||
}
|
||||
|
||||
public void setPages(int pages) {
|
||||
this.pages = pages;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Book{" +
|
||||
"id=" + id +
|
||||
", title='" + title + '\'' +
|
||||
", author='" + author + '\'' +
|
||||
", blurb='" + blurb + '\'' +
|
||||
", pages=" + pages +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.baeldung.springdatawebsupport.application;
|
||||
|
||||
import com.baeldung.springdatawebsupport.application.entities.User;
|
||||
import com.baeldung.springdatawebsupport.application.repositories.UserRepository;
|
||||
import java.util.stream.Stream;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
@SpringBootApplication
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
CommandLineRunner initialize(UserRepository userRepository) {
|
||||
return args -> {
|
||||
Stream.of("John", "Robert", "Nataly", "Helen", "Mary").forEach(name -> {
|
||||
User user = new User(name);
|
||||
userRepository.save(user);
|
||||
});
|
||||
userRepository.findAll().forEach(System.out::println);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.baeldung.springdatawebsupport.application.controllers;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.querydsl.binding.QuerydslPredicate;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.baeldung.springdatawebsupport.application.entities.User;
|
||||
import com.baeldung.springdatawebsupport.application.repositories.UserRepository;
|
||||
import com.querydsl.core.types.Predicate;
|
||||
|
||||
@RestController
|
||||
public class UserController {
|
||||
|
||||
private final UserRepository userRepository;
|
||||
|
||||
@Autowired
|
||||
public UserController(UserRepository userRepository) {
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
@GetMapping("/users/{id}")
|
||||
public User findUserById(@PathVariable("id") User user) {
|
||||
return user;
|
||||
}
|
||||
|
||||
@GetMapping("/users")
|
||||
public Page<User> findAllUsers(Pageable pageable) {
|
||||
return userRepository.findAll(pageable);
|
||||
}
|
||||
|
||||
@GetMapping("/sortedusers")
|
||||
public Page<User> findAllUsersSortedByName() {
|
||||
Pageable pageable = PageRequest.of(0, 5, Sort.by("name"));
|
||||
return userRepository.findAll(pageable);
|
||||
}
|
||||
|
||||
@GetMapping("/filteredusers")
|
||||
public Iterable<User> getUsersByQuerydslPredicate(@QuerydslPredicate(root = User.class) Predicate predicate) {
|
||||
return userRepository.findAll(predicate);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.baeldung.springdatawebsupport.application.entities;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "users")
|
||||
public class User {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private long id;
|
||||
private final String name;
|
||||
|
||||
public User() {
|
||||
this.name = "";
|
||||
}
|
||||
|
||||
public User(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "User{" + "id=" + id + ", name=" + name + '}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.baeldung.springdatawebsupport.application.repositories;
|
||||
|
||||
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.baeldung.springdatawebsupport.application.entities.User;
|
||||
|
||||
@Repository
|
||||
public interface UserRepository extends PagingAndSortingRepository<User, Long>, QuerydslPredicateExecutor<User> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
spring.profiles.default=h2
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
</configuration>
|
||||
@@ -0,0 +1,8 @@
|
||||
driverClassName=org.h2.Driver
|
||||
url=jdbc:h2:mem:myDb;DB_CLOSE_DELAY=-1
|
||||
username=sa
|
||||
password=
|
||||
|
||||
hibernate.dialect=org.hibernate.dialect.H2Dialect
|
||||
hibernate.show_sql=true
|
||||
hibernate.hbm2ddl.auto=create-drop
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.baeldung;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import com.baeldung.books.SpringDataRestApplication;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = SpringDataRestApplication.class)
|
||||
public class SpringContextTest {
|
||||
|
||||
@Test
|
||||
public void whenSpringContextIsBootstrapped_thenNoExceptions() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.baeldung.books.validator;
|
||||
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
import com.baeldung.books.SpringDataRestApplication;
|
||||
import com.baeldung.books.models.WebsiteUser;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = SpringDataRestApplication.class, webEnvironment = SpringBootTest.WebEnvironment.MOCK)
|
||||
@AutoConfigureMockMvc
|
||||
public class SpringDataRestValidatorIntegrationTest {
|
||||
public static final String URL = "http://localhost";
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Autowired
|
||||
protected WebApplicationContext wac;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
mockMvc = webAppContextSetup(wac).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenStartingApplication_thenCorrectStatusCode() throws Exception {
|
||||
mockMvc.perform(get("/users")).andExpect(status().is2xxSuccessful());
|
||||
};
|
||||
|
||||
@Test
|
||||
@DirtiesContext(methodMode = DirtiesContext.MethodMode.BEFORE_METHOD)
|
||||
public void whenAddingNewCorrectUser_thenCorrectStatusCodeAndResponse() throws Exception {
|
||||
WebsiteUser user = new WebsiteUser();
|
||||
user.setEmail("john.doe@john.com");
|
||||
user.setName("John Doe");
|
||||
|
||||
mockMvc.perform(post("/users", user).contentType(MediaType.APPLICATION_JSON).content(new ObjectMapper().writeValueAsString(user))).andExpect(status().is2xxSuccessful()).andExpect(redirectedUrl("http://localhost/users/1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenAddingNewUserWithoutName_thenErrorStatusCodeAndResponse() throws Exception {
|
||||
WebsiteUser user = new WebsiteUser();
|
||||
user.setEmail("john.doe@john.com");
|
||||
|
||||
mockMvc.perform(post("/users", user).contentType(MediaType.APPLICATION_JSON).content(new ObjectMapper().writeValueAsString(user))).andExpect(status().isNotAcceptable()).andExpect(redirectedUrl(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenAddingNewUserWithEmptyName_thenErrorStatusCodeAndResponse() throws Exception {
|
||||
WebsiteUser user = new WebsiteUser();
|
||||
user.setEmail("john.doe@john.com");
|
||||
user.setName("");
|
||||
mockMvc.perform(post("/users", user).contentType(MediaType.APPLICATION_JSON).content(new ObjectMapper().writeValueAsString(user))).andExpect(status().isNotAcceptable()).andExpect(redirectedUrl(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenAddingNewUserWithoutEmail_thenErrorStatusCodeAndResponse() throws Exception {
|
||||
WebsiteUser user = new WebsiteUser();
|
||||
user.setName("John Doe");
|
||||
|
||||
mockMvc.perform(post("/users", user).contentType(MediaType.APPLICATION_JSON).content(new ObjectMapper().writeValueAsString(user))).andExpect(status().isNotAcceptable()).andExpect(redirectedUrl(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenAddingNewUserWithEmptyEmail_thenErrorStatusCodeAndResponse() throws Exception {
|
||||
WebsiteUser user = new WebsiteUser();
|
||||
user.setName("John Doe");
|
||||
user.setEmail("");
|
||||
mockMvc.perform(post("/users", user).contentType(MediaType.APPLICATION_JSON).content(new ObjectMapper().writeValueAsString(user))).andExpect(status().isNotAcceptable()).andExpect(redirectedUrl(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DirtiesContext(methodMode = DirtiesContext.MethodMode.BEFORE_METHOD)
|
||||
public void whenDeletingCorrectUser_thenCorrectStatusCodeAndResponse() throws Exception {
|
||||
WebsiteUser user = new WebsiteUser();
|
||||
user.setEmail("john.doe@john.com");
|
||||
user.setName("John Doe");
|
||||
mockMvc.perform(post("/users", user).contentType(MediaType.APPLICATION_JSON).content(new ObjectMapper().writeValueAsString(user))).andExpect(status().is2xxSuccessful()).andExpect(redirectedUrl("http://localhost/users/1"));
|
||||
mockMvc.perform(delete("/users/1").contentType(MediaType.APPLICATION_JSON).content(new ObjectMapper().writeValueAsString(user))).andExpect(status().isMethodNotAllowed());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DirtiesContext(methodMode = DirtiesContext.MethodMode.BEFORE_METHOD)
|
||||
public void whenSearchingByEmail_thenCorrectStatusCodeAndResponse() throws Exception {
|
||||
WebsiteUser user = new WebsiteUser();
|
||||
user.setEmail("john.doe@john.com");
|
||||
user.setName("John Doe");
|
||||
mockMvc.perform(post("/users", user).contentType(MediaType.APPLICATION_JSON).content(new ObjectMapper().writeValueAsString(user))).andExpect(status().is2xxSuccessful()).andExpect(redirectedUrl("http://localhost/users/1"));
|
||||
mockMvc.perform(get("/users/search/byEmail").param("email", user.getEmail()).contentType(MediaType.APPLICATION_JSON)).andExpect(status().is2xxSuccessful());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenSearchingByEmailWithOriginalMethodName_thenErrorStatusCodeAndResponse() throws Exception {
|
||||
WebsiteUser user = new WebsiteUser();
|
||||
user.setEmail("john.doe@john.com");
|
||||
user.setName("John Doe");
|
||||
mockMvc.perform(post("/users", user).contentType(MediaType.APPLICATION_JSON).content(new ObjectMapper().writeValueAsString(user))).andExpect(status().is2xxSuccessful()).andExpect(redirectedUrl("http://localhost/users/1"));
|
||||
mockMvc.perform(get("/users/search/findByEmail").param("email", user.getEmail()).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isNotFound());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.baeldung.springdatawebsupport.application.test;
|
||||
|
||||
import com.baeldung.springdatawebsupport.application.controllers.UserController;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
public class UserControllerIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private UserController userController;
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
public void whenUserControllerInjected_thenNotNull() throws Exception {
|
||||
assertThat(userController).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetRequestToUsersEndPointWithIdPathVariable_thenCorrectResponse() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.get("/users/{id}", "1").contentType(MediaType.APPLICATION_JSON_UTF8)).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$.id").value("1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetRequestToUsersEndPoint_thenCorrectResponse() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.get("/users").contentType(MediaType.APPLICATION_JSON_UTF8)).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$['pageable']['paged']").value("true"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetRequestToUserEndPointWithNameRequestParameter_thenCorrectResponse() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.get("/users").param("name", "John").contentType(MediaType.APPLICATION_JSON_UTF8)).andExpect(MockMvcResultMatchers.status().isOk())
|
||||
.andExpect(MockMvcResultMatchers.jsonPath("$['content'][0].['name']").value("John"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetRequestToSorteredUsersEndPoint_thenCorrectResponse() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.get("/sortedusers").contentType(MediaType.APPLICATION_JSON_UTF8)).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$['sort']['sorted']").value("true"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetRequestToFilteredUsersEndPoint_thenCorrectResponse() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.get("/filteredusers").param("name", "John").contentType(MediaType.APPLICATION_JSON_UTF8)).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$[0].name").value("John"));
|
||||
}
|
||||
}
|
||||
6
persistence-modules/spring-data-rest-querydsl/README.md
Normal file
6
persistence-modules/spring-data-rest-querydsl/README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
## Spring Data REST Querydsl
|
||||
|
||||
This module contains articles about Querydsl with Spring Data REST
|
||||
|
||||
### Relevant Articles:
|
||||
- [REST Query Language Over Multiple Tables with Querydsl Web Support](https://www.baeldung.com/rest-querydsl-multiple-tables)
|
||||
109
persistence-modules/spring-data-rest-querydsl/pom.xml
Normal file
109
persistence-modules/spring-data-rest-querydsl/pom.xml
Normal file
@@ -0,0 +1,109 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>spring-data-rest-querydsl</artifactId>
|
||||
<version>1.0</version>
|
||||
<name>spring-data-rest-querydsl</name>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>parent-boot-2</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<relativePath>../../parent-boot-2</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-rest</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.querydsl</groupId>
|
||||
<artifactId>querydsl-apt</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.querydsl</groupId>
|
||||
<artifactId>querydsl-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<mainClass>${start-class}</mainClass>
|
||||
<layout>ZIP</layout>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>${maven.source}</source>
|
||||
<target>${maven.target}</target>
|
||||
<compilerArgs>
|
||||
<arg>-verbose</arg>
|
||||
<arg>-parameters</arg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.mysema.maven</groupId>
|
||||
<artifactId>apt-maven-plugin</artifactId>
|
||||
<version>${mysema.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>process</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.directory}/generated-sources</outputDirectory>
|
||||
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<properties>
|
||||
<maven.source>1.8</maven.source>
|
||||
<maven.target>1.8</maven.target>
|
||||
<mysema.version>1.1.3</mysema.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.baeldung;
|
||||
|
||||
import com.baeldung.controller.repository.AddressRepository;
|
||||
import com.baeldung.controller.repository.UserRepository;
|
||||
import com.baeldung.entity.Address;
|
||||
import com.baeldung.entity.User;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
/**
|
||||
* Note: In the IDE, remember to generate query type classes before running the Integration Test (e.g. in Eclipse right-click on the project > Run As > Maven generate sources)
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EntityScan("com.baeldung.entity")
|
||||
@EnableJpaRepositories("com.baeldung.controller.repository")
|
||||
@EnableAutoConfiguration
|
||||
public class Application {
|
||||
|
||||
@Autowired
|
||||
private UserRepository personRepository;
|
||||
@Autowired
|
||||
private AddressRepository addressRepository;
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
private void initializeData() {
|
||||
// Create John
|
||||
final User john = new User("John");
|
||||
personRepository.save(john);
|
||||
final Address addressOne = new Address("Fake Street 1", "Spain", john);
|
||||
addressRepository.save(addressOne);
|
||||
// Create Lisa
|
||||
final User lisa = new User("Lisa");
|
||||
personRepository.save(lisa);
|
||||
final Address addressTwo = new Address("Real Street 1", "Germany", lisa);
|
||||
addressRepository.save(addressTwo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.baeldung.controller;
|
||||
|
||||
import com.baeldung.controller.repository.AddressRepository;
|
||||
import com.baeldung.controller.repository.UserRepository;
|
||||
import com.baeldung.entity.Address;
|
||||
import com.baeldung.entity.User;
|
||||
import com.querydsl.core.BooleanBuilder;
|
||||
import com.querydsl.core.types.Predicate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.querydsl.binding.QuerydslPredicate;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class QueryController {
|
||||
|
||||
@Autowired
|
||||
private UserRepository personRepository;
|
||||
@Autowired
|
||||
private AddressRepository addressRepository;
|
||||
|
||||
@GetMapping(value = "/users", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public Iterable<User> queryOverUser(@QuerydslPredicate(root = User.class) Predicate predicate) {
|
||||
final BooleanBuilder builder = new BooleanBuilder();
|
||||
return personRepository.findAll(builder.and(predicate));
|
||||
}
|
||||
|
||||
@GetMapping(value = "/addresses", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public Iterable<Address> queryOverAddress(@QuerydslPredicate(root = Address.class) Predicate predicate) {
|
||||
final BooleanBuilder builder = new BooleanBuilder();
|
||||
return addressRepository.findAll(builder.and(predicate));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.baeldung.controller.repository;
|
||||
|
||||
import com.baeldung.entity.Address;
|
||||
import com.baeldung.entity.QAddress;
|
||||
import com.querydsl.core.types.dsl.StringExpression;
|
||||
import com.querydsl.core.types.dsl.StringPath;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
|
||||
import org.springframework.data.querydsl.binding.QuerydslBinderCustomizer;
|
||||
import org.springframework.data.querydsl.binding.QuerydslBindings;
|
||||
import org.springframework.data.querydsl.binding.SingleValueBinding;
|
||||
|
||||
public interface AddressRepository
|
||||
extends JpaRepository<Address, Long>, QuerydslPredicateExecutor<Address>, QuerydslBinderCustomizer<QAddress> {
|
||||
@Override
|
||||
default void customize(final QuerydslBindings bindings, final QAddress root) {
|
||||
bindings.bind(String.class).first((SingleValueBinding<StringPath, String>) StringExpression::eq);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.baeldung.controller.repository;
|
||||
|
||||
import com.baeldung.entity.QUser;
|
||||
import com.baeldung.entity.User;
|
||||
import com.querydsl.core.types.dsl.StringExpression;
|
||||
import com.querydsl.core.types.dsl.StringPath;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
|
||||
import org.springframework.data.querydsl.binding.QuerydslBinderCustomizer;
|
||||
import org.springframework.data.querydsl.binding.QuerydslBindings;
|
||||
import org.springframework.data.querydsl.binding.SingleValueBinding;
|
||||
|
||||
public interface UserRepository
|
||||
extends JpaRepository<User, Long>, QuerydslPredicateExecutor<User>, QuerydslBinderCustomizer<QUser> {
|
||||
@Override
|
||||
default void customize(final QuerydslBindings bindings, final QUser root) {
|
||||
bindings.bind(String.class).first((SingleValueBinding<StringPath, String>) StringExpression::eq);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.baeldung.entity;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
@Entity
|
||||
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
|
||||
public class Address {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id", unique = true, nullable = false)
|
||||
private Long id;
|
||||
@Column
|
||||
private String address;
|
||||
@Column
|
||||
private String country;
|
||||
@OneToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "user_id")
|
||||
private User user;
|
||||
|
||||
public Address() {
|
||||
}
|
||||
|
||||
public Address(String address, String country, User user) {
|
||||
this.id = id;
|
||||
this.address = address;
|
||||
this.country = country;
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public String getCountry() {
|
||||
return country;
|
||||
}
|
||||
|
||||
public void setCountry(String country) {
|
||||
this.country = country;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.baeldung.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
|
||||
@Entity
|
||||
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
|
||||
public class User {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id", unique = true, nullable = false)
|
||||
private Long id;
|
||||
@Column
|
||||
private String name;
|
||||
@OneToOne(fetch = FetchType.LAZY, mappedBy = "user")
|
||||
private Address address;
|
||||
|
||||
public User() {
|
||||
}
|
||||
|
||||
public User(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Address getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(Address address) {
|
||||
this.address = address;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
spring:
|
||||
datasource:
|
||||
driver-class-name: com.mysql.jdbc.Driver
|
||||
hikari:
|
||||
pool-name: hikari-pool
|
||||
url: jdbc:mysql://localhost:3306/baeldung?verifyServerCertificate=false&useSSL=false&requireSSL=false
|
||||
username: root
|
||||
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: create
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
</configuration>
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.baeldung;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import com.baeldung.Application;
|
||||
|
||||
/**
|
||||
* Note: In the IDE, remember to generate query type classes before running the Integration Test (e.g. in Eclipse right-click on the project > Run As > Maven generate sources)
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = Application.class)
|
||||
public class SpringContextTest {
|
||||
|
||||
@Test
|
||||
public void whenSpringContextIsBootstrapped_thenNoExceptions() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.baeldung.springdatarestquerydsl;
|
||||
|
||||
import com.baeldung.Application;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = Application.class) @WebAppConfiguration
|
||||
public class IntegrationTest {
|
||||
|
||||
final MediaType contentType =
|
||||
new MediaType(MediaType.APPLICATION_JSON.getType(), MediaType.APPLICATION_JSON.getSubtype());
|
||||
|
||||
@Autowired private WebApplicationContext webApplicationContext;
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Before public void setupMockMvc() {
|
||||
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
|
||||
}
|
||||
|
||||
@Test public void givenRequestHasBeenMade_whenQueryOverNameAttribute_thenGetJohn() throws Exception {
|
||||
// Get John
|
||||
mockMvc.perform(get("/users?name=John")).andExpect(status().isOk()).andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$", hasSize(1))).andExpect(jsonPath("$[0].name", is("John")))
|
||||
.andExpect(jsonPath("$[0].address.address", is("Fake Street 1")))
|
||||
.andExpect(jsonPath("$[0].address.country", is("Spain")));
|
||||
}
|
||||
|
||||
@Test public void givenRequestHasBeenMade_whenQueryOverNameAttribute_thenGetLisa() throws Exception {
|
||||
// Get Lisa
|
||||
mockMvc.perform(get("/users?name=Lisa")).andExpect(status().isOk()).andExpect(content().contentType(contentType))
|
||||
.andExpect(jsonPath("$", hasSize(1))).andExpect(jsonPath("$[0].name", is("Lisa")))
|
||||
.andExpect(jsonPath("$[0].address.address", is("Real Street 1")))
|
||||
.andExpect(jsonPath("$[0].address.country", is("Germany")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.baeldung.springdatarestquerydsl;
|
||||
|
||||
import com.baeldung.Application;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = Application.class)
|
||||
@WebAppConfiguration
|
||||
public class QuerydslIntegrationTest {
|
||||
|
||||
final MediaType contentType =
|
||||
new MediaType(MediaType.APPLICATION_JSON.getType(), MediaType.APPLICATION_JSON.getSubtype());
|
||||
|
||||
@Autowired
|
||||
private WebApplicationContext webApplicationContext;
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Before
|
||||
public void setupMockMvc() {
|
||||
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenRequest_whenQueryUserFilteringByCountrySpain_thenGetJohn() throws Exception {
|
||||
mockMvc.perform(get("/users?address.country=Spain")).andExpect(status().isOk()).andExpect(content()
|
||||
.contentType
|
||||
(contentType))
|
||||
.andExpect(jsonPath("$", hasSize(1)))
|
||||
.andExpect(jsonPath("$[0].name", is("John")))
|
||||
.andExpect(jsonPath("$[0].address.address", is("Fake Street 1")))
|
||||
.andExpect(jsonPath("$[0].address.country", is("Spain")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenRequest_whenQueryUserWithoutFilter_thenGetJohnAndLisa() throws Exception {
|
||||
mockMvc.perform(get("/users")).andExpect(status().isOk()).andExpect(content()
|
||||
.contentType
|
||||
(contentType))
|
||||
.andExpect(jsonPath("$", hasSize(2)))
|
||||
.andExpect(jsonPath("$[0].name", is("John")))
|
||||
.andExpect(jsonPath("$[0].address.address", is("Fake Street 1")))
|
||||
.andExpect(jsonPath("$[0].address.country", is("Spain")))
|
||||
.andExpect(jsonPath("$[1].name", is("Lisa")))
|
||||
.andExpect(jsonPath("$[1].address.address", is("Real Street 1")))
|
||||
.andExpect(jsonPath("$[1].address.country", is("Germany")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:h2:mem:springcloud
|
||||
username: sa
|
||||
password:
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: create-drop
|
||||
properties:
|
||||
hibernate:
|
||||
dialect: org.hibernate.dialect.H2Dialect
|
||||
24
persistence-modules/spring-data-rest/README.md
Normal file
24
persistence-modules/spring-data-rest/README.md
Normal file
@@ -0,0 +1,24 @@
|
||||
## Spring Data REST
|
||||
|
||||
This module contains articles about Spring Data REST
|
||||
|
||||
### Relevant Articles:
|
||||
- [Introduction to Spring Data REST](https://www.baeldung.com/spring-data-rest-intro)
|
||||
- [Working with Relationships in Spring Data REST](https://www.baeldung.com/spring-data-rest-relationships)
|
||||
- [AngularJS CRUD Application with Spring Data REST](https://www.baeldung.com/angularjs-crud-with-spring-data-rest)
|
||||
- [Projections and Excerpts in Spring Data REST](https://www.baeldung.com/spring-data-rest-projections-excerpts)
|
||||
- [Spring Data REST Events with @RepositoryEventHandler](https://www.baeldung.com/spring-data-rest-events)
|
||||
- [Customizing HTTP Endpoints in Spring Data REST](https://www.baeldung.com/spring-data-rest-customize-http-endpoints)
|
||||
- [Spring Boot with SQLite](https://www.baeldung.com/spring-boot-sqlite)
|
||||
|
||||
### The Course
|
||||
The "REST With Spring" Classes: http://bit.ly/restwithspring
|
||||
|
||||
# Running the project
|
||||
The application uses [Spring Boot](http://projects.spring.io/spring-boot/), so it is easy to run. You can start it any of a few ways:
|
||||
* Run the `main` method from `SpringDataRestApplication`
|
||||
* Use the Maven Spring Boot plugin: `mvn spring-boot:run`
|
||||
* Package the application as a JAR and run it using `java -jar intro-spring-data-rest.jar`
|
||||
|
||||
# Viewing the running application
|
||||
To view the running application, visit [http://localhost:8080](http://localhost:8080) in your browser
|
||||
96
persistence-modules/spring-data-rest/pom.xml
Normal file
96
persistence-modules/spring-data-rest/pom.xml
Normal file
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>spring-data-rest</artifactId>
|
||||
<version>1.0</version>
|
||||
<name>spring-data-rest</name>
|
||||
<packaging>jar</packaging>
|
||||
<description>Intro to Spring Data REST</description>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>parent-boot-2</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<relativePath>../../parent-boot-2</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-rest</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.querydsl</groupId>
|
||||
<artifactId>querydsl-apt</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.querydsl</groupId>
|
||||
<artifactId>querydsl-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.mysema.maven</groupId>
|
||||
<artifactId>maven-apt-plugin</artifactId>
|
||||
<version>${maven.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>process</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>target/generated-sources</outputDirectory>
|
||||
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<properties>
|
||||
<start-class>com.baeldung.books.SpringDataRestApplication</start-class>
|
||||
<maven.version>1.0</maven.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.baeldung.books;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SpringDataRestApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SpringDataRestApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.baeldung.books.config;
|
||||
|
||||
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.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.jdbc.datasource.DriverManagerDataSource;
|
||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
|
||||
|
||||
@Configuration
|
||||
@EnableJpaRepositories(basePackages = "com.baeldung.books.repositories")
|
||||
// @PropertySource("persistence-h2.properties")
|
||||
// @PropertySource("persistence-hsqldb.properties")
|
||||
// @PropertySource("persistence-derby.properties")
|
||||
// @PropertySource("persistence-sqlite.properties")
|
||||
public class DbConfig {
|
||||
|
||||
@Autowired
|
||||
private Environment env;
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
final DriverManagerDataSource dataSource = new DriverManagerDataSource();
|
||||
dataSource.setDriverClassName(env.getProperty("driverClassName"));
|
||||
dataSource.setUrl(env.getProperty("url"));
|
||||
dataSource.setUsername(env.getProperty("user"));
|
||||
dataSource.setPassword(env.getProperty("password"));
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||
final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
|
||||
em.setDataSource(dataSource());
|
||||
em.setPackagesToScan(new String[] { "com.baeldung.books.models" });
|
||||
em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
|
||||
em.setJpaProperties(additionalProperties());
|
||||
return em;
|
||||
}
|
||||
|
||||
final Properties additionalProperties() {
|
||||
final Properties hibernateProperties = new Properties();
|
||||
if (env.getProperty("hibernate.hbm2ddl.auto") != null) {
|
||||
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
|
||||
}
|
||||
if (env.getProperty("hibernate.dialect") != null) {
|
||||
hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
|
||||
}
|
||||
if (env.getProperty("hibernate.show_sql") != null) {
|
||||
hibernateProperties.setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
|
||||
}
|
||||
return hibernateProperties;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Profile("h2")
|
||||
@PropertySource("classpath:persistence-h2.properties")
|
||||
class H2Config {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Profile("hsqldb")
|
||||
@PropertySource("classpath:persistence-hsqldb.properties")
|
||||
class HsqldbConfig {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Profile("derby")
|
||||
@PropertySource("classpath:persistence-derby.properties")
|
||||
class DerbyConfig {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Profile("sqlite")
|
||||
@PropertySource("classpath:persistence-sqlite.properties")
|
||||
class SqliteConfig {
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.baeldung.books.config;
|
||||
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import com.baeldung.books.events.AuthorEventHandler;
|
||||
import com.baeldung.books.events.BookEventHandler;
|
||||
|
||||
@Configuration
|
||||
@EnableWebMvc
|
||||
public class MvcConfig implements WebMvcConfigurer {
|
||||
|
||||
public MvcConfig() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
|
||||
configurer.enable();
|
||||
}
|
||||
|
||||
@Bean
|
||||
WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> enableDefaultServlet() {
|
||||
return (factory) -> factory.setRegisterDefaultServlet(true);
|
||||
}
|
||||
|
||||
@Bean
|
||||
AuthorEventHandler authorEventHandler() {
|
||||
return new AuthorEventHandler();
|
||||
}
|
||||
|
||||
@Bean
|
||||
BookEventHandler bookEventHandler() {
|
||||
return new BookEventHandler();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.baeldung.books.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
|
||||
import com.baeldung.books.projections.CustomBook;
|
||||
|
||||
@Configuration
|
||||
public class RestConfig implements RepositoryRestConfigurer {
|
||||
|
||||
@Override
|
||||
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration repositoryRestConfiguration,
|
||||
CorsRegistry cors) {
|
||||
repositoryRestConfiguration.getProjectionConfiguration().addProjection(CustomBook.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.baeldung.books.dialect;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
||||
|
||||
import java.sql.Types;
|
||||
|
||||
public class SQLiteDialect extends Dialect {
|
||||
|
||||
public SQLiteDialect() {
|
||||
registerColumnType(Types.BIT, "integer");
|
||||
registerColumnType(Types.TINYINT, "tinyint");
|
||||
registerColumnType(Types.SMALLINT, "smallint");
|
||||
registerColumnType(Types.INTEGER, "integer");
|
||||
registerColumnType(Types.BIGINT, "bigint");
|
||||
registerColumnType(Types.FLOAT, "float");
|
||||
registerColumnType(Types.REAL, "real");
|
||||
registerColumnType(Types.DOUBLE, "double");
|
||||
registerColumnType(Types.NUMERIC, "numeric");
|
||||
registerColumnType(Types.DECIMAL, "decimal");
|
||||
registerColumnType(Types.CHAR, "char");
|
||||
registerColumnType(Types.VARCHAR, "varchar");
|
||||
registerColumnType(Types.LONGVARCHAR, "longvarchar");
|
||||
registerColumnType(Types.DATE, "date");
|
||||
registerColumnType(Types.TIME, "time");
|
||||
registerColumnType(Types.TIMESTAMP, "timestamp");
|
||||
registerColumnType(Types.BINARY, "blob");
|
||||
registerColumnType(Types.VARBINARY, "blob");
|
||||
registerColumnType(Types.LONGVARBINARY, "blob");
|
||||
registerColumnType(Types.BLOB, "blob");
|
||||
registerColumnType(Types.CLOB, "clob");
|
||||
registerColumnType(Types.BOOLEAN, "integer");
|
||||
}
|
||||
|
||||
public IdentityColumnSupport getIdentityColumnSupport() {
|
||||
return new SQLiteIdentityColumnSupport();
|
||||
}
|
||||
|
||||
public boolean hasAlterTable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean dropConstraints() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getDropForeignKeyString() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getAddForeignKeyConstraintString(String constraintName, String[] foreignKey, String referencedTable, String[] primaryKey, boolean referencesPrimaryKey) {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getAddPrimaryKeyConstraintString(String constraintName) {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getForUpdateString() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getAddColumnString() {
|
||||
return "add column";
|
||||
}
|
||||
|
||||
public boolean supportsOuterJoinForUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean supportsIfExistsBeforeTableName() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean supportsCascadeDelete() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.baeldung.books.dialect;
|
||||
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.dialect.identity.IdentityColumnSupportImpl;
|
||||
|
||||
public class SQLiteIdentityColumnSupport extends IdentityColumnSupportImpl {
|
||||
|
||||
@Override
|
||||
public boolean supportsIdentityColumns() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentitySelectString(String table, String column, int type) throws MappingException {
|
||||
return "select last_insert_rowid()";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentityColumnString(int type) throws MappingException {
|
||||
return "integer";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.baeldung.books.events;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.springframework.data.rest.core.annotation.HandleAfterCreate;
|
||||
import org.springframework.data.rest.core.annotation.HandleAfterDelete;
|
||||
import org.springframework.data.rest.core.annotation.HandleBeforeCreate;
|
||||
import org.springframework.data.rest.core.annotation.HandleBeforeDelete;
|
||||
import org.springframework.data.rest.core.annotation.RepositoryEventHandler;
|
||||
|
||||
import com.baeldung.books.models.Author;
|
||||
|
||||
@RepositoryEventHandler
|
||||
public class AuthorEventHandler {
|
||||
Logger logger = Logger.getLogger("Class AuthorEventHandler");
|
||||
|
||||
public AuthorEventHandler() {
|
||||
super();
|
||||
}
|
||||
|
||||
@HandleBeforeCreate
|
||||
public void handleAuthorBeforeCreate(Author author) {
|
||||
logger.info("Inside Author Before Create....");
|
||||
String name = author.getName();
|
||||
}
|
||||
|
||||
@HandleAfterCreate
|
||||
public void handleAuthorAfterCreate(Author author) {
|
||||
logger.info("Inside Author After Create ....");
|
||||
String name = author.getName();
|
||||
}
|
||||
|
||||
@HandleBeforeDelete
|
||||
public void handleAuthorBeforeDelete(Author author) {
|
||||
logger.info("Inside Author Before Delete ....");
|
||||
String name = author.getName();
|
||||
}
|
||||
|
||||
@HandleAfterDelete
|
||||
public void handleAuthorAfterDelete(Author author) {
|
||||
logger.info("Inside Author After Delete ....");
|
||||
String name = author.getName();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.baeldung.books.events;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.springframework.data.rest.core.annotation.HandleBeforeCreate;
|
||||
import org.springframework.data.rest.core.annotation.RepositoryEventHandler;
|
||||
|
||||
import com.baeldung.books.models.Author;
|
||||
import com.baeldung.books.models.Book;
|
||||
|
||||
@RepositoryEventHandler
|
||||
public class BookEventHandler {
|
||||
Logger logger = Logger.getLogger("Class BookEventHandler");
|
||||
|
||||
@HandleBeforeCreate
|
||||
public void handleBookBeforeCreate(Book book) {
|
||||
|
||||
logger.info("Inside Book Before Create ....");
|
||||
book.getAuthors();
|
||||
}
|
||||
|
||||
@HandleBeforeCreate
|
||||
public void handleAuthorBeforeCreate(Author author) {
|
||||
logger.info("Inside Author Before Create ....");
|
||||
author.getBooks();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.baeldung.books.models;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToOne;
|
||||
|
||||
@Entity
|
||||
public class Address {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String location;
|
||||
|
||||
@OneToOne(mappedBy = "address")
|
||||
private Library library;
|
||||
|
||||
public Address() {
|
||||
}
|
||||
|
||||
public Address(String location) {
|
||||
super();
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public void setLocation(String location) {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public Library getLibrary() {
|
||||
return library;
|
||||
}
|
||||
|
||||
public void setLibrary(Library library) {
|
||||
this.library = library;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.baeldung.books.models;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToMany;
|
||||
|
||||
@Entity
|
||||
public class Author {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String name;
|
||||
|
||||
@ManyToMany(cascade = CascadeType.ALL)
|
||||
@JoinTable(name = "book_author", joinColumns = @JoinColumn(name = "book_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "author_id", referencedColumnName = "id"))
|
||||
private List<Book> books;
|
||||
|
||||
public Author() {
|
||||
}
|
||||
|
||||
public Author(String name) {
|
||||
super();
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public List<Book> getBooks() {
|
||||
return books;
|
||||
}
|
||||
|
||||
public void setBooks(List<Book> books) {
|
||||
this.books = books;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.baeldung.books.models;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
@Entity
|
||||
public class Book {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String title;
|
||||
|
||||
private String isbn;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "library_id")
|
||||
private Library library;
|
||||
|
||||
@ManyToMany(mappedBy = "books", fetch = FetchType.EAGER)
|
||||
private List<Author> authors;
|
||||
|
||||
public Book() {
|
||||
}
|
||||
|
||||
public Book(String title) {
|
||||
super();
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getIsbn() {
|
||||
return isbn;
|
||||
}
|
||||
|
||||
public void setIsbn(String isbn) {
|
||||
this.isbn = isbn;
|
||||
}
|
||||
|
||||
public Library getLibrary() {
|
||||
return library;
|
||||
}
|
||||
|
||||
public void setLibrary(Library library) {
|
||||
this.library = library;
|
||||
}
|
||||
|
||||
public List<Author> getAuthors() {
|
||||
return authors;
|
||||
}
|
||||
|
||||
public void setAuthors(List<Author> authors) {
|
||||
this.authors = authors;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.baeldung.books.models;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OneToOne;
|
||||
|
||||
import org.springframework.data.rest.core.annotation.RestResource;
|
||||
|
||||
@Entity
|
||||
public class Library {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
|
||||
@Column
|
||||
private String name;
|
||||
|
||||
@OneToOne
|
||||
@JoinColumn(name = "address_id")
|
||||
@RestResource(path = "libraryAddress")
|
||||
private Address address;
|
||||
|
||||
@OneToMany(mappedBy = "library")
|
||||
private List<Book> books;
|
||||
|
||||
public Library() {
|
||||
}
|
||||
|
||||
public Library(String name) {
|
||||
super();
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Address getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(Address address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public List<Book> getBooks() {
|
||||
return books;
|
||||
}
|
||||
|
||||
public void setBooks(List<Book> books) {
|
||||
this.books = books;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.baeldung.books.models;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
|
||||
@Entity
|
||||
public class Subject {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String name;
|
||||
|
||||
public Subject() {
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.baeldung.books.projections;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.rest.core.config.Projection;
|
||||
|
||||
import com.baeldung.books.models.Author;
|
||||
import com.baeldung.books.models.Book;
|
||||
|
||||
@Projection(name = "customBook", types = { Book.class })
|
||||
public interface CustomBook {
|
||||
@Value("#{target.id}")
|
||||
long getId();
|
||||
|
||||
String getTitle();
|
||||
|
||||
List<Author> getAuthors();
|
||||
|
||||
@Value("#{target.getAuthors().size()}")
|
||||
int getAuthorCount();
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.baeldung.books.repositories;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
import com.baeldung.books.models.Address;
|
||||
|
||||
public interface AddressRepository extends CrudRepository<Address, Long> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.baeldung.books.repositories;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
import com.baeldung.books.models.Author;
|
||||
|
||||
public interface AuthorRepository extends CrudRepository<Author, Long> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.baeldung.books.repositories;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
|
||||
|
||||
import com.baeldung.books.models.Book;
|
||||
import com.baeldung.books.projections.CustomBook;
|
||||
|
||||
@RepositoryRestResource(excerptProjection = CustomBook.class)
|
||||
public interface BookRepository extends CrudRepository<Book, Long> {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.baeldung.books.repositories;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
import com.baeldung.books.models.Library;
|
||||
|
||||
public interface LibraryRepository extends CrudRepository<Library, Long> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.baeldung.books.repositories;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.data.rest.core.annotation.RestResource;
|
||||
|
||||
import com.baeldung.books.models.Subject;
|
||||
|
||||
public interface SubjectRepository extends PagingAndSortingRepository<Subject, Long> {
|
||||
|
||||
@RestResource(path = "nameContains")
|
||||
public Page<Subject> findByNameContaining(@Param("name") String name, Pageable p);
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
spring.profiles.default=h2
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
</configuration>
|
||||
@@ -0,0 +1,8 @@
|
||||
driverClassName=org.apache.derby.jdbc.EmbeddedDriver
|
||||
url=jdbc:derby:memory:myD;create=true
|
||||
username=sa
|
||||
password=
|
||||
|
||||
hibernate.dialect=org.hibernate.dialect.DerbyDialect
|
||||
hibernate.show_sql=true
|
||||
hibernate.hbm2ddl.auto=create-drop
|
||||
@@ -0,0 +1,8 @@
|
||||
driverClassName=org.h2.Driver
|
||||
url=jdbc:h2:mem:myDb;DB_CLOSE_DELAY=-1
|
||||
username=sa
|
||||
password=
|
||||
|
||||
hibernate.dialect=org.hibernate.dialect.H2Dialect
|
||||
hibernate.show_sql=true
|
||||
hibernate.hbm2ddl.auto=create-drop
|
||||
@@ -0,0 +1,8 @@
|
||||
driverClassName=org.hsqldb.jdbc.JDBCDriver
|
||||
url=jdbc:hsqldb:mem:myDb
|
||||
username=sa
|
||||
password=
|
||||
|
||||
hibernate.dialect=org.hibernate.dialect.HSQLDialect
|
||||
hibernate.show_sql=true
|
||||
hibernate.hbm2ddl.auto=create-drop
|
||||
@@ -0,0 +1,7 @@
|
||||
driverClassName=org.sqlite.JDBC
|
||||
url=jdbc:sqlite:memory:myDb?cache=shared
|
||||
username=sa
|
||||
password=sa
|
||||
hibernate.dialect=com.baeldung.dialect.SQLiteDialect
|
||||
hibernate.hbm2ddl.auto=create-drop
|
||||
hibernate.show_sql=true
|
||||
@@ -0,0 +1,51 @@
|
||||
<!DOCTYPE html>
|
||||
<html ng-app="app">
|
||||
<head>
|
||||
<meta charset="ISO-8859-1">
|
||||
<title>User CRUD</title>
|
||||
<script
|
||||
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
|
||||
<script src="view/app.js"></script>
|
||||
<style>
|
||||
a {
|
||||
cursor: pointer;
|
||||
background-color: lightblue;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div ng-controller="UserCRUDCtrl">
|
||||
<table>
|
||||
<tr>
|
||||
<td width="100">ID:</td>
|
||||
<td><input type="text" id="id" ng-model="user.id" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="100">Name:</td>
|
||||
<td><input type="text" id="name" ng-model="user.name" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="100">Email:</td>
|
||||
<td><input type="text" id="email" ng-model="user.email" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
<br /> <br />
|
||||
<a ng-click="getUser(user.id)">Get User</a>
|
||||
<a ng-click="updateUser(user.id,user.name,user.email)">Update User</a>
|
||||
<a ng-click="addUser(user.name,user.email)">Add User</a>
|
||||
<a ng-click="deleteUser(user.id)">Delete User</a>
|
||||
|
||||
<br /> <br />
|
||||
<p style="color: green">{{message}}</p>
|
||||
<p style="color: red">{{errorMessage}}</p>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
<a ng-click="getAllUsers()">Get all Users</a><br />
|
||||
<br /> <br />
|
||||
<div ng-repeat="usr in users">
|
||||
{{usr.name}} {{usr.email}}
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
122
persistence-modules/spring-data-rest/src/main/webapp/view/app.js
Normal file
122
persistence-modules/spring-data-rest/src/main/webapp/view/app.js
Normal file
@@ -0,0 +1,122 @@
|
||||
var app = angular.module('app',[]);
|
||||
|
||||
app.controller('UserCRUDCtrl', ['$scope','UserCRUDService', function ($scope,UserCRUDService) {
|
||||
|
||||
$scope.updateUser = function () {
|
||||
UserCRUDService.updateUser($scope.user.id,$scope.user.name,$scope.user.email)
|
||||
.then(function success(response){
|
||||
$scope.message = 'User data updated!';
|
||||
$scope.errorMessage = '';
|
||||
},
|
||||
function error(response){
|
||||
$scope.errorMessage = 'Error updating user!';
|
||||
$scope.message = '';
|
||||
});
|
||||
}
|
||||
|
||||
$scope.getUser = function () {
|
||||
var id = $scope.user.id;
|
||||
UserCRUDService.getUser($scope.user.id)
|
||||
.then(function success(response){
|
||||
$scope.user = response.data;
|
||||
$scope.user.id = id;
|
||||
$scope.message='';
|
||||
$scope.errorMessage = '';
|
||||
},
|
||||
function error (response ){
|
||||
$scope.message = '';
|
||||
if (response.status === 404){
|
||||
$scope.errorMessage = 'User not found!';
|
||||
}
|
||||
else {
|
||||
$scope.errorMessage = "Error getting user!";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$scope.addUser = function () {
|
||||
if ($scope.user != null && $scope.user.name) {
|
||||
UserCRUDService.addUser($scope.user.name, $scope.user.email)
|
||||
.then (function success(response){
|
||||
$scope.message = 'User added!';
|
||||
$scope.errorMessage = '';
|
||||
},
|
||||
function error(response){
|
||||
$scope.errorMessage = 'Error adding user!';
|
||||
$scope.message = '';
|
||||
});
|
||||
}
|
||||
else {
|
||||
$scope.errorMessage = 'Please enter a name!';
|
||||
$scope.message = '';
|
||||
}
|
||||
}
|
||||
|
||||
$scope.deleteUser = function () {
|
||||
UserCRUDService.deleteUser($scope.user.id)
|
||||
.then (function success(response){
|
||||
$scope.message = 'User deleted!';
|
||||
$scope.user = null;
|
||||
$scope.errorMessage='';
|
||||
},
|
||||
function error(response){
|
||||
$scope.errorMessage = 'Error deleting user!';
|
||||
$scope.message='';
|
||||
})
|
||||
}
|
||||
|
||||
$scope.getAllUsers = function () {
|
||||
UserCRUDService.getAllUsers()
|
||||
.then(function success(response){
|
||||
$scope.users = response.data._embedded.users;
|
||||
$scope.message='';
|
||||
$scope.errorMessage = '';
|
||||
},
|
||||
function error (response ){
|
||||
$scope.message='';
|
||||
$scope.errorMessage = 'Error getting users!';
|
||||
});
|
||||
}
|
||||
|
||||
}]);
|
||||
|
||||
app.service('UserCRUDService',['$http', function ($http) {
|
||||
|
||||
this.getUser = function getUser(userId){
|
||||
return $http({
|
||||
method: 'GET',
|
||||
url: 'users/'+userId
|
||||
});
|
||||
}
|
||||
|
||||
this.addUser = function addUser(name, email){
|
||||
return $http({
|
||||
method: 'POST',
|
||||
url: 'users',
|
||||
data: {name:name, email:email}
|
||||
});
|
||||
}
|
||||
|
||||
this.deleteUser = function deleteUser(id){
|
||||
return $http({
|
||||
method: 'DELETE',
|
||||
url: 'users/'+id
|
||||
})
|
||||
}
|
||||
|
||||
this.updateUser = function updateUser(id,name,email){
|
||||
return $http({
|
||||
method: 'PATCH',
|
||||
url: 'users/'+id,
|
||||
data: {name:name, email:email}
|
||||
})
|
||||
}
|
||||
|
||||
this.getAllUsers = function getAllUsers(){
|
||||
return $http({
|
||||
method: 'GET',
|
||||
url: 'users'
|
||||
});
|
||||
}
|
||||
|
||||
}]);
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.baeldung;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import com.baeldung.books.SpringDataRestApplication;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = SpringDataRestApplication.class)
|
||||
public class SpringContextTest {
|
||||
|
||||
@Test
|
||||
public void whenSpringContextIsBootstrapped_thenNoExceptions() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.baeldung.books.events;
|
||||
|
||||
import com.baeldung.books.events.AuthorEventHandler;
|
||||
import com.baeldung.books.models.Author;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.data.rest.core.annotation.RepositoryEventHandler;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class AuthorEventHandlerUnitTest {
|
||||
|
||||
@Test
|
||||
public void whenCreateAuthorThenSuccess() {
|
||||
Author author = mock(Author.class);
|
||||
AuthorEventHandler authorEventHandler = new AuthorEventHandler();
|
||||
authorEventHandler.handleAuthorBeforeCreate(author);
|
||||
Mockito.verify(author, Mockito.times(1)).getName();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenDeleteAuthorThenSuccess() {
|
||||
Author author = mock(Author.class);
|
||||
AuthorEventHandler authorEventHandler = new AuthorEventHandler();
|
||||
authorEventHandler.handleAuthorAfterDelete(author);
|
||||
Mockito.verify(author, Mockito.times(1)).getName();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.baeldung.books.events;
|
||||
|
||||
import com.baeldung.books.events.BookEventHandler;
|
||||
import com.baeldung.books.models.Author;
|
||||
import com.baeldung.books.models.Book;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class BookEventHandlerUnitTest {
|
||||
@Test
|
||||
public void whenCreateBookThenSuccess() {
|
||||
Book book = mock(Book.class);
|
||||
BookEventHandler bookEventHandler = new BookEventHandler();
|
||||
bookEventHandler.handleBookBeforeCreate(book);
|
||||
Mockito.verify(book, Mockito.times(1)).getAuthors();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCreateAuthorThenSuccess() {
|
||||
Author author = mock(Author.class);
|
||||
BookEventHandler bookEventHandler = new BookEventHandler();
|
||||
bookEventHandler.handleAuthorBeforeCreate(author);
|
||||
Mockito.verify(author, Mockito.times(1)).getBooks();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.baeldung.books.projections;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.response.Response;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import com.baeldung.books.SpringDataRestApplication;
|
||||
import com.baeldung.books.models.Author;
|
||||
import com.baeldung.books.models.Book;
|
||||
import com.baeldung.books.repositories.AuthorRepository;
|
||||
import com.baeldung.books.repositories.BookRepository;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = SpringDataRestApplication.class, webEnvironment = WebEnvironment.DEFINED_PORT)
|
||||
|
||||
public class SpringDataProjectionLiveTest {
|
||||
private static final String BOOK_ENDPOINT = "http://localhost:8080/books";
|
||||
private static final String AUTHOR_ENDPOINT = "http://localhost:8080/authors";
|
||||
|
||||
@Autowired
|
||||
private BookRepository bookRepo;
|
||||
|
||||
@Autowired
|
||||
private AuthorRepository authorRepo;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
if (!bookRepo.findById(1L).isPresent()) {
|
||||
Book book = new Book("Animal Farm");
|
||||
book.setIsbn("978-1943138425");
|
||||
book = bookRepo.save(book);
|
||||
Author author = new Author("George Orwell");
|
||||
author = authorRepo.save(author);
|
||||
author.setBooks(Arrays.asList(book));
|
||||
author = authorRepo.save(author);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetBook_thenOK() {
|
||||
final Response response = RestAssured.get(BOOK_ENDPOINT + "/1");
|
||||
|
||||
assertEquals(200, response.getStatusCode());
|
||||
assertTrue(response.asString().contains("isbn"));
|
||||
assertFalse(response.asString().contains("authorCount"));
|
||||
// System.out.println(response.asString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetBookProjection_thenOK() {
|
||||
final Response response = RestAssured.get(BOOK_ENDPOINT + "/1?projection=customBook");
|
||||
|
||||
assertEquals(200, response.getStatusCode());
|
||||
assertFalse(response.asString().contains("isbn"));
|
||||
assertTrue(response.asString().contains("authorCount"));
|
||||
// System.out.println(response.asString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetAllBooks_thenOK() {
|
||||
final Response response = RestAssured.get(BOOK_ENDPOINT);
|
||||
|
||||
assertEquals(200, response.getStatusCode());
|
||||
assertFalse(response.asString().contains("isbn"));
|
||||
assertTrue(response.asString().contains("authorCount"));
|
||||
// System.out.println(response.asString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetAuthorBooks_thenOK() {
|
||||
final Response response = RestAssured.get(AUTHOR_ENDPOINT + "/1/books");
|
||||
|
||||
assertEquals(200, response.getStatusCode());
|
||||
assertFalse(response.asString().contains("isbn"));
|
||||
assertTrue(response.asString().contains("authorCount"));
|
||||
System.out.println(response.asString());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user