Java Spring Boot (Part 5)
Neil Haddley • October 30, 2023
Spring for GraphQL
Build GraphQL applications with Spring for GraphQL and GraphQL Java.

I created a new GitHub repository

I used spring initializr to create a new project
The finished project
I used http://localhost:8080/graphiql to demonstrate the finished code (in line with my node GraphQL implementation)

books

author(id: 6)

addAuthor

addBook

SELECT * FROM AUTHOR

SELECT * FROM BOOK

SELECT * FROM BOOK_AUTHORS
Implementation
Here are the Java implementation details

application.properties

SeedDataConfig.java

Author.js

Book.java

AuthorRepository.java

BookRepository.java

schema.graphqls

AuthorController.java

BookController.java

pom.xml
application.properties
TEXT
1server.port=8080 2 3logging.level.web=DEBUG 4logging.level.sql=DEBUG 5logging.level.com.example=DEBUG 6 7# generate the hibernate entities 8spring.jpa.generate-ddl=true 9 10spring.graphql.graphiql.enabled=true 11 12spring.datasource.url=jdbc:h2:mem:testdb 13spring.datasource.driverClassName=org.h2.Driver 14spring.datasource.username=sa 15spring.datasource.password=password 16spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
SeedDataConfig.java
TEXT
1package com.haddley.haddleyspringgraphql.config; 2 3import com.haddley.haddleyspringgraphql.models.Book; 4import com.haddley.haddleyspringgraphql.models.Author; 5import com.haddley.haddleyspringgraphql.repositories.BookRepository; 6import com.haddley.haddleyspringgraphql.repositories.AuthorRepository; 7import lombok.RequiredArgsConstructor; 8import org.springframework.boot.CommandLineRunner; 9import org.springframework.stereotype.Component; 10 11import java.util.ArrayList; 12import java.util.Arrays; 13 14@Component 15@RequiredArgsConstructor 16public class SeedDataConfig implements CommandLineRunner { 17 18 private final AuthorRepository authorRepository; 19 private final BookRepository BookRepository; 20 21 @Override 22 public void run(String... args) throws Exception { 23 24 25 if (authorRepository.count() == 0) { 26 27 28 /* 29 const authors = [ 30 { id: 1, name: 'Samer Buna' }, 31 { id: 2, name: 'Robin Wieruch' }, 32 { id: 3, name: 'Nader Dabit' }, 33 { id: 4, name: 'Sebastian Grebe' }, 34 { id: 5, name: 'Eve Porcello' }, 35 { id: 6, name: 'Alex Banks' } 36 ] 37 */ 38 39 40 Author author1 = Author.builder() 41 .name("Samer Buna") 42 .build(); 43 44 author1 = authorRepository.save(author1); 45 46 Author author2 = Author.builder() 47 .name("Robin Wieruch") 48 .build(); 49 50 author2 = authorRepository.save(author2); 51 52 Author author3 = Author.builder() 53 .name("Nader Dabit") 54 .build(); 55 56 author3 = authorRepository.save(author3); 57 58 Author author4 = Author.builder() 59 .name("Sebastian Grebe") 60 .build(); 61 62 author4 = authorRepository.save(author4); 63 64 Author author5 = Author.builder() 65 .name("Eve Porcello") 66 .build(); 67 68 author5 = authorRepository.save(author5); 69 70 Author author6 = Author.builder() 71 .name("Alex Banks") 72 .build(); 73 74 author6 = authorRepository.save(author6); 75 76 /* 77 const books = [ 78 { id: 1, isbn10: '161729568X', name: 'GraphQL in Action', authorIds: [1] }, 79 { id: 2, isbn10: '1730853935', name: 'The Road to GraphQL: Your journey to master pragmatic GraphQL in JavaScript with React.js and Node.js', authorIds: [2] }, 80 { id: 3, isbn10: '1492059897', name: 'Full Stack Serverless: Modern Application Development with React, AWS, and GraphQL', authorIds: [3] }, 81 { id: 4, isbn10: '1789134528', name: 'Hands-On Full-Stack Web Development with GraphQL and React', authorIds: [4] }, 82 { id: 5, isbn10: '1492030716', name: 'Learning GraphQL: Declarative Data Fetching for Modern Web Apps', authorIds: [5, 6] }, 83 { id: 6, isbn10: '1492051721', name: 'Learning React: Modern Patterns for Developing React Apps', authorIds: [5, 6] }, 84 ] 85 */ 86 87 Book book1 = Book.builder() 88 .isbn10("161729568X") 89 .name("GraphQL in Action") 90 .authors(new ArrayList<>(Arrays.asList(author1))) 91 .build(); 92 93 Book book2 = Book.builder() 94 .isbn10("1730853935") 95 .name("The Road to GraphQL: Your journey to master pragmatic GraphQL in JavaScript with React.js and Node.js") 96 .authors(new ArrayList<>(Arrays.asList(author2))) 97 .build(); 98 99 Book book3 = Book.builder() 100 .isbn10("1492059897") 101 .name("Full Stack Serverless: Modern Application Development with React, AWS, and GraphQL") 102 .authors(new ArrayList<>(Arrays.asList(author3))) 103 .build(); 104 105 Book book4 = Book.builder() 106 .isbn10("1789134528") 107 .name("Hands-On Full-Stack Web Development with GraphQL and React") 108 .authors(new ArrayList<>(Arrays.asList(author4))) 109 .build(); 110 111 Book book5 = Book.builder() 112 .isbn10("1492030716") 113 .name("Learning GraphQL: Declarative Data Fetching for Modern Web Apps") 114 .authors(new ArrayList<>(Arrays.asList(author5,author6))) 115 .build(); 116 117 Book book6 = Book.builder() 118 .isbn10("1492051721") 119 .name("Learning React: Modern Patterns for Developing React Apps") 120 .authors(new ArrayList<>(Arrays.asList(author5,author6))) 121 .build(); 122 123 BookRepository.save(book1); 124 BookRepository.save(book2); 125 BookRepository.save(book3); 126 BookRepository.save(book4); 127 BookRepository.save(book5); 128 BookRepository.save(book6); 129 130 } 131 } 132 133}
Author.js
TEXT
1package com.haddley.haddleyspringgraphql.models; 2 3import jakarta.persistence.*; 4import lombok.*; 5 6import java.util.ArrayList; 7import java.util.List; 8 9@Entity 10@Data 11@ToString 12@Builder 13@AllArgsConstructor 14@EqualsAndHashCode(exclude = "books") 15public class Author { 16 17 @Id 18 @GeneratedValue 19 Long id; 20 21 String name; 22 23 @ManyToMany(mappedBy = "authors", cascade = CascadeType.ALL) 24 List<Book> books; 25 26 public Author() { 27 books = new ArrayList<>(); 28 } 29}
Book.java
TEXT
1package com.haddley.haddleyspringgraphql.models; 2 3import jakarta.persistence.*; 4import lombok.*; 5 6import java.util.ArrayList; 7import java.util.List; 8 9@Entity 10@Data 11@ToString 12@Builder 13@AllArgsConstructor 14@EqualsAndHashCode(exclude = "authors") 15public class Book { 16 17 @Id 18 @GeneratedValue 19 Long id; 20 21 String name; 22 23 String isbn10; 24 25 @ManyToMany(fetch = FetchType.LAZY) 26 List<Author> authors; 27 28 public Book() { 29 authors = new ArrayList<>(); 30 } 31}
AuthorRepository.java
TEXT
1package com.haddley.haddleyspringgraphql.repositories; 2 3import com.haddley.haddleyspringgraphql.models.Author; 4import org.springframework.data.jpa.repository.JpaRepository; 5 6public interface AuthorRepository extends JpaRepository<Author, Long> { 7}
BookRepository.java
TEXT
1package com.haddley.haddleyspringgraphql.repositories; 2 3import com.haddley.haddleyspringgraphql.models.Book; 4import org.springframework.data.jpa.repository.JpaRepository; 5 6public interface BookRepository extends JpaRepository<Book, Long> { 7}
schema.graphqls
TEXT
1type Query { 2 books: [Book] 3 book(id: ID!): Book 4 authors: [Author] 5 author(id: ID!): Author 6} 7 8type Mutation { 9 deleteBook(id: ID!): Book 10 addBook( isbn10: String!, name: String!, authorIds: [ID]!): Book 11 addAuthor( name: String!): Author 12} 13 14type Author { 15 id: ID! 16 name: String! 17 books: [Book] 18} 19 20type Book { 21 id: ID! 22 isbn10: String! 23 name: String! 24 authors: [Author] 25}
AuthorController.java
TEXT
1package com.haddley.haddleyspringgraphql.controllers; 2 3import com.haddley.haddleyspringgraphql.models.Author; 4import com.haddley.haddleyspringgraphql.repositories.AuthorRepository; 5import lombok.NonNull; 6import lombok.RequiredArgsConstructor; 7import org.springframework.graphql.data.method.annotation.Argument; 8import org.springframework.graphql.data.method.annotation.MutationMapping; 9import org.springframework.graphql.data.method.annotation.QueryMapping; 10import org.springframework.web.bind.annotation.RequestMapping; 11import org.springframework.web.bind.annotation.RestController; 12 13import java.util.Optional; 14 15@RestController 16@RequestMapping("/api/authors") 17@RequiredArgsConstructor 18public class AuthorController { 19 20 private final AuthorRepository authorRepository; 21 22 @QueryMapping 23 public Iterable<Author> authors() { 24 return authorRepository.findAll(); 25 } 26 27 @QueryMapping 28 public Optional<Author> author(@Argument Long id) { 29 return authorRepository.findById(id); 30 } 31 32 @MutationMapping 33 public Author addAuthor(@NonNull @Argument String name) { 34 Author author = Author.builder() 35 .name(name) 36 .build(); 37 38 authorRepository.save(author); 39 40 return author; 41 } 42}
BookController.java
TEXT
1package com.haddley.haddleyspringgraphql.controllers; 2 3import com.haddley.haddleyspringgraphql.models.Book; 4import com.haddley.haddleyspringgraphql.models.Author; 5import com.haddley.haddleyspringgraphql.repositories.BookRepository; 6import com.haddley.haddleyspringgraphql.repositories.AuthorRepository; 7import lombok.NonNull; 8import lombok.RequiredArgsConstructor; 9import org.springframework.graphql.data.method.annotation.Argument; 10import org.springframework.graphql.data.method.annotation.MutationMapping; 11import org.springframework.graphql.data.method.annotation.QueryMapping; 12import org.springframework.web.bind.annotation.RequestMapping; 13import org.springframework.web.bind.annotation.RestController; 14 15import java.util.List; 16import java.util.stream.Collectors; 17import java.util.Optional; 18 19@RestController 20@RequestMapping("/api/books") 21@RequiredArgsConstructor 22public class BookController { 23 24 private final AuthorRepository authorRepository; 25 private final BookRepository bookRepository; 26 27 @QueryMapping 28 public Iterable<Book> books() { 29 return bookRepository.findAll(); 30 } 31 32 @QueryMapping 33 public Optional<Book> book(@Argument Long id) { 34 return bookRepository.findById(id); 35 } 36 37 @MutationMapping 38 public Optional<Book> deleteBook(@Argument Long id) { 39 Optional<Book> book = bookRepository.findById(id); 40 book.ifPresent(b -> bookRepository.deleteById(id)); 41 return book; 42 } 43 44 @MutationMapping 45 public Book addBook(@NonNull @Argument String isbn10, @NonNull @Argument String name, @NonNull @Argument List<Long> authorIds) { 46 List<Author> authors = authorIds.stream() 47 .map(authorRepository::findById) 48 .flatMap(Optional::stream) 49 .collect(Collectors.toList()); 50 51 Book book = Book.builder() 52 .isbn10(isbn10) 53 .name(name) 54 .authors(authors) 55 .build(); 56 57 bookRepository.save(book); 58 59 return book; 60 } 61}
pom.xml
TEXT
1<?xml version="1.0" encoding="UTF-8"?> 2<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4 <modelVersion>4.0.0</modelVersion> 5 <parent> 6 <groupId>org.springframework.boot</groupId> 7 <artifactId>spring-boot-starter-parent</artifactId> 8 <version>3.1.5</version> 9 <relativePath/> <!-- lookup parent from repository --> 10 </parent> 11 <groupId>com.haddley</groupId> 12 <artifactId>haddley-spring-graphql</artifactId> 13 <version>0.0.1-SNAPSHOT</version> 14 <name>haddley-spring-graphql</name> 15 <description>Demo project for Spring Boot</description> 16 <properties> 17 <java.version>17</java.version> 18 </properties> 19 <dependencies> 20 <dependency> 21 <groupId>org.springframework.boot</groupId> 22 <artifactId>spring-boot-starter-data-jpa</artifactId> 23 </dependency> 24 <dependency> 25 <groupId>org.springframework.boot</groupId> 26 <artifactId>spring-boot-starter-web</artifactId> 27 </dependency> 28 <dependency> 29 <groupId>org.springframework.boot</groupId> 30 <artifactId>spring-boot-starter-graphql</artifactId> 31 </dependency> 32 33 <dependency> 34 <groupId>org.springframework.boot</groupId> 35 <artifactId>spring-boot-devtools</artifactId> 36 <scope>runtime</scope> 37 <optional>true</optional> 38 </dependency> 39 <dependency> 40 <groupId>com.h2database</groupId> 41 <artifactId>h2</artifactId> 42 <scope>runtime</scope> 43 </dependency> 44 <dependency> 45 <groupId>org.projectlombok</groupId> 46 <artifactId>lombok</artifactId> 47 <optional>true</optional> 48 </dependency> 49 <dependency> 50 <groupId>org.springframework.boot</groupId> 51 <artifactId>spring-boot-starter-test</artifactId> 52 <scope>test</scope> 53 </dependency> 54 <dependency> 55 <groupId>org.springframework</groupId> 56 <artifactId>spring-webflux</artifactId> 57 <scope>test</scope> 58 </dependency> 59 <dependency> 60 <groupId>org.springframework.graphql</groupId> 61 <artifactId>spring-graphql-test</artifactId> 62 <scope>test</scope> 63 </dependency> 64 </dependencies> 65 66 <build> 67 <plugins> 68 <plugin> 69 <groupId>org.springframework.boot</groupId> 70 <artifactId>spring-boot-maven-plugin</artifactId> 71 <configuration> 72 <excludes> 73 <exclude> 74 <groupId>org.projectlombok</groupId> 75 <artifactId>lombok</artifactId> 76 </exclude> 77 </excludes> 78 </configuration> 79 </plugin> 80 </plugins> 81 </build> 82 83</project>