Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Spring GraphQL with Blaze Persistence

An example project demonstrating how to integrate Blaze Persistence Entity Views with Spring GraphQL to achieve optimal query performance by fetching only the fields requested in the GraphQL query.

Overview

When building GraphQL APIs with JPA, a common problem is over-fetching data from the database. Even if the client requests only specific fields, traditional approaches often load entire entities with all their relationships.

This project solves that problem by combining:

  • Spring GraphQL for the GraphQL layer
  • Blaze Persistence Entity Views for projections that map directly to GraphQL selections
  • Custom Repository Infrastructure that automatically applies GraphQL field selections to database queries

The result: if a client requests only id and name fields, the generated SQL will select only those columns.

Tech Stack

Technology Version
Java 25
Spring Boot 4.0.0
Blaze Persistence 1.6.17
MySQL 8.x

Key Features

  • Selective Field Fetching: Only requested GraphQL fields are fetched from the database
  • Entity Views as GraphQL Types: Blaze Persistence Entity Views map directly to GraphQL types
  • Pagination Support: Built-in offset-based pagination with sorting
  • Criteria Filtering: Type-safe filtering using Blaze Persistence CriteriaBuilder
  • ThreadLocal DataFetchingEnvironment: Access GraphQL context at repository level without passing it through service layers

Project Structure

src/main/java/com/lprevidente/spring_graphql_blaze/
├── author/                    # Author domain
│   ├── Author.java            # JPA Entity
│   ├── AuthorView.java        # Blaze Entity View (GraphQL type)
│   ├── AuthorGraphQLController.java
│   ├── AuthorService.java
│   └── AuthorRepository.java
├── book/                      # Book domain
│   ├── Book.java              # JPA Entity
│   ├── BookView.java          # Blaze Entity View (GraphQL type)
│   ├── BookCriteria.java      # Search criteria
│   ├── BookGraphQLController.java
│   ├── BookService.java
│   └── BookRepository.java
├── common/                    # Shared components
│   ├── Criteria.java          # Generic criteria interface
│   └── MyPageRequest.java     # Pagination DTO
├── graphql/                   # GraphQL infrastructure
│   ├── DataFetchingEnvironmentHolder.java
│   └── DataFetchingEnvironmentInstrumentation.java
└── persistence/               # Custom repository infrastructure
    ├── EntityViewGraphQLRepository.java
    ├── EntityViewGraphQLRepositoryImpl.java
    ├── EntityViewGraphQLRepositoryFactoryBean.java
    ├── EntityViewRepositoryFactory.java
    └── PersistenceConfig.java

How It Works

1. Entity Views as GraphQL Types

Entity Views define projections that mirror GraphQL types:

@EntityView(Book.class)
public interface BookView {

    @IdMapping
    Long getId();

    String getName();

    int getPageCount();

    @Mapping("authors")
    Set<AuthorView> getAuthors();
}

2. GraphQL Schema

type BookView {
    id: ID!
    name: String!
    pageCount: Int!
    authors: [AuthorView!]!
}

extend type Query {
    books: [BookView!]!
    bookById(id: ID!): BookView
    booksPaginated(pageRequest: PageRequest!, criteria: BookCriteria): BookPage!
}

3. ThreadLocal DataFetchingEnvironment

The DataFetchingEnvironmentInstrumentation captures the GraphQL context and stores it in a ThreadLocal, making it accessible at the repository level:

@Component
public class DataFetchingEnvironmentInstrumentation extends SimplePerformantInstrumentation {

    @Override
    public DataFetcher<?> instrumentDataFetcher(
            DataFetcher<?> dataFetcher,
            InstrumentationFieldFetchParameters parameters,
            InstrumentationState state) {

        return environment -> {
            DataFetchingEnvironmentHolder.set(environment);
            return dataFetcher.get(environment);
        };
    }

    @Override
    public InstrumentationContext<ExecutionResult> beginExecution(
            InstrumentationExecutionParameters parameters, InstrumentationState state) {
        return SimpleInstrumentationContext.whenCompleted((_, _) -> DataFetchingEnvironmentHolder.clear());
    }
}

4. Custom Repository with Selective Fetching

The repository uses Blaze Persistence's GraphQLEntityViewSupport to create settings based on requested fields:

@Override
public <V> List<V> findAll(Class<V> viewClass) {
    final var environment = DataFetchingEnvironmentHolder.get();
    final var setting = graphQLEntityViewSupport.createSetting(viewClass, environment);
    final var criteriaBuilder = criteriaBuilderFactory.create(entityManager, getDomainClass());

    return entityViewManager.applySetting(setting, criteriaBuilder).getResultList();
}

5. Example: Field Selection in Action

GraphQL Query:

query {
    books {
        id
        name
    }
}

Generated SQL:

SELECT b.id, b.name FROM books b

GraphQL Query with relations:

query {
    books {
        id
        name
        authors {
            firstName
        }
    }
}

Generated SQL:

SELECT b.id, b.name, a.first_name 
FROM books b 
LEFT JOIN books_authors ba ON b.id = ba.book_id 
LEFT JOIN authors a ON ba.author_id = a.id

API Examples

Queries

Get all books:

query {
    books {
        id
        name
        pageCount
        authors {
            id
            firstName
            lastName
        }
    }
}

Get book by ID:

query {
    bookById(id: 1) {
        id
        name
        authors {
            firstName
        }
    }
}

Paginated books with filtering:

query {
    booksPaginated(
        pageRequest: { page: 0, pageSize: 10, orders: [{ property: "name", order: ASC }] }
        criteria: { minPageCount: 100, authorId: 1 }
    ) {
        totalElements
        totalPages
        hasNext
        content {
            id
            name
            pageCount
        }
    }
}

Mutations

Create an author:

mutation {
    createAuthor(input: { firstName: "John", lastName: "Doe" }) {
        id
        firstName
        lastName
    }
}

Create a book:

mutation {
    createBook(input: { name: "My Book", pageCount: 350, authorIds: [1, 2] }) {
        id
        name
        authors {
            firstName
        }
    }
}

Configuration

Database Configuration

Configure your MySQL connection in application.yaml:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/graphql_blaze
    username: root
    password: your_password
  jpa:
    hibernate:
      ddl-auto: update

GraphQL Configuration

GraphQL endpoint is available at /graphql by default. GraphiQL UI can be enabled:

spring:
  graphql:
    graphiql:
      enabled: true

Running the Project

  1. Ensure you have Java 25 installed
  2. Start a MySQL database
  3. Configure database connection in application.yaml
  4. Run the application:
./mvnw spring-boot:run
  1. Access GraphiQL at http://localhost:8080/graphiql

Key Dependencies

<!-- Blaze Persistence Core -->
<dependency>
    <groupId>com.blazebit</groupId>
    <artifactId>blaze-persistence-core-api-jakarta</artifactId>
</dependency>

<!-- Blaze Persistence Entity Views -->
<dependency>
    <groupId>com.blazebit</groupId>
    <artifactId>blaze-persistence-entity-view-api-jakarta</artifactId>
</dependency>

<!-- Blaze Persistence GraphQL Integration -->
<dependency>
    <groupId>com.blazebit</groupId>
    <artifactId>blaze-persistence-integration-graphql-jakarta</artifactId>
</dependency>

<!-- Spring GraphQL -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-graphql</artifactId>
</dependency>

Benefits

  1. Performance: No over-fetching, only selected fields are queried
  2. Type Safety: Entity Views provide compile-time safety
  3. Clean Architecture: GraphQL context doesn't pollute service layer signatures
  4. Pagination: Built-in support with total count and sorting
  5. Filtering: Type-safe criteria using Blaze Persistence CriteriaBuilder

License

This project is provided as an example for educational purposes.

About

Small example project on how to integrate Spring JPA with Blaze and GraphQL

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages