Java Spring Boot (Part 5)

Neil HaddleyOctober 30, 2023

Spring for GraphQL

Javaspring-bootjavagraphqlspring-for-graphql

I built a GraphQL application using 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

I reviewed the Java implementation details below.

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

JAVA
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

JAVASCRIPT
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

JAVA
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

JAVA
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

JAVA
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

SDL
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

JAVA
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

JAVA
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

XML
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>