Java Spring Boot (Part 5)

Neil HaddleyOctober 30, 2023

Spring for GraphQL

Build GraphQL applications with Spring for GraphQL and GraphQL Java.

I created a new GitHub repository

I created a new GitHub repository

I used spring initializr to create a new project

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

books

author(id: 6)

author(id: 6)

addAuthor

addAuthor

addBook

addBook

SELECT * FROM AUTHOR

SELECT * FROM AUTHOR

SELECT * FROM BOOK

SELECT * FROM BOOK

SELECT * FROM BOOK_AUTHORS

SELECT * FROM BOOK_AUTHORS

Implementation

Here are the Java implementation details

application.properties

application.properties

SeedDataConfig.java

SeedDataConfig.java

Author.js

Author.js

Book.java

Book.java

AuthorRepository.java

AuthorRepository.java

BookRepository.java

BookRepository.java

schema.graphqls

schema.graphqls

AuthorController.java

AuthorController.java

BookController.java

BookController.java

pom.xml

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>