Skip to content

Commit

Permalink
Add lazy load bytecode enhancement unit test coverage for books model
Browse files Browse the repository at this point in the history
  • Loading branch information
igdianov committed Nov 22, 2023
1 parent 465eab3 commit df56453
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 12 deletions.
12 changes: 12 additions & 0 deletions build/pom.xml
Expand Up @@ -15,6 +15,13 @@

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-platform</artifactId>
<version>${hibernate.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
Expand Down Expand Up @@ -75,6 +82,11 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.hibernate.orm.tooling</groupId>
<artifactId>hibernate-enhance-maven-plugin</artifactId>
<version>${hibernate.version}</version>
</plugin>
</plugins>
</pluginManagement>
</build>
Expand Down
1 change: 1 addition & 0 deletions dependencies/pom.xml
Expand Up @@ -20,6 +20,7 @@
<joda-time.version>2.12.5</joda-time.version>
<graphql-java-extended-scalars.version>20.2</graphql-java-extended-scalars.version>
<jakarta.persistence-api.version>3.1.0</jakarta.persistence-api.version>
<hibernate.version>6.2.3.Final</hibernate.version>
</properties>

<dependencyManagement>
Expand Down
Expand Up @@ -139,6 +139,7 @@ public class JavaScalars {
newScalarType("SqlTimestamp", "SQL Timestamp type", new GraphQLSqlTimestampCoercing())
);
scalarsRegistry.put(Byte[].class, newScalarType("ByteArray", "ByteArray type", new GraphQLLOBCoercing()));
scalarsRegistry.put(byte[].class, newScalarType("ByteArray", "ByteArray type", new GraphQLLOBCoercing()));
scalarsRegistry.put(Instant.class, newScalarType("Instant", "Instant type", new GraphQLInstantCoercing()));
scalarsRegistry.put(
ZonedDateTime.class,
Expand All @@ -158,6 +159,10 @@ public static Collection<GraphQLScalarType> scalars() {
return Collections.unmodifiableCollection(scalarsRegistry.values());
}

public static boolean contains(Class<?> key) {
return scalarsRegistry.containsKey(key);
}

public static GraphQLScalarType of(Class<?> key) {
return scalarsRegistry.computeIfAbsent(key, JavaScalars::computeGraphQLScalarType);
}
Expand Down Expand Up @@ -669,7 +674,7 @@ public static class GraphQLLOBCoercing implements Coercing<Object, Object> {
@Override
public Object serialize(Object input) {
if (input.getClass() == byte[].class) {
return input;
return new String((byte[]) input, StandardCharsets.UTF_8);
}
return null;
}
Expand Down
Expand Up @@ -1412,7 +1412,7 @@ private GraphQLOutputType getGraphQLTypeFromJavaType(Class<?> clazz) {
classCache.putIfAbsent(clazz, enumType);

return enumType;
} else if (clazz.isArray()) {
} else if (clazz.isArray() && !JavaScalars.contains(clazz)) {
return GraphQLList.list(JavaScalars.of(clazz.getComponentType()));
}

Expand Down
Expand Up @@ -792,6 +792,58 @@ public void queryBooksAuthorWithExplictOptionalTrue() {
assertThat(result.toString()).isEqualTo(expected);
}

@Test
public void queryAuthorsWithLazyLoadProfilePicture() {
//given
String query = """
query {
Authors {
select {
id
name
profilePicture
}
}
}
""";

String expected = """
{Authors={select=[{id=1, name=Leo Tolstoy, profilePicture=iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAlElEQVR4nO2UQQrEIAxFe/8jSRUrKtZFEasIQs7yh3RRKEOnixmYjYsQ0OT5E2ImIsIvbRpAjB7iq7GptcIYg947rLUopbwl5JzPGPattXtgSgkhBAghsG3b4fncOQfv/RnDIL7jB+d5/qxQKYV1XS9ArTWWZbkoZHsExhgPICfdlcxKpZTY9/25ZBp/mcb6on9s7Bc+TJAvSO7XjwAAAABJRU5ErkJggg==}, {id=4, name=Anton Chekhov, profilePicture=iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAlElEQVR4nO2UQQrEIAxFe/8jSRUrKtZFEasIQs7yh3RRKEOnixmYjYsQ0OT5E2ImIsIvbRpAjB7iq7GptcIYg947rLUopbwl5JzPGPattXtgSgkhBAghsG3b4fncOQfv/RnDIL7jB+d5/qxQKYV1XS9ArTWWZbkoZHsExhgPICfdlcxKpZTY9/25ZBp/mcb6on9s7Bc+TJAvSO7XjwAAAABJRU5ErkJggg==}, {id=8, name=Igor Dianov, profilePicture=iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAlElEQVR4nO2UQQrEIAxFe/8jSRUrKtZFEasIQs7yh3RRKEOnixmYjYsQ0OT5E2ImIsIvbRpAjB7iq7GptcIYg947rLUopbwl5JzPGPattXtgSgkhBAghsG3b4fncOQfv/RnDIL7jB+d5/qxQKYV1XS9ArTWWZbkoZHsExhgPICfdlcxKpZTY9/25ZBp/mcb6on9s7Bc+TJAvSO7XjwAAAABJRU5ErkJggg==}]}}
""";

//when
Object result = executor.execute(query).getData();


// then
assertThat(result.toString()).isEqualTo(expected.strip());
}

@Test
public void queryAuthorsWithNoProfilePicture() {
//given
String query = """
query {
Authors {
select {
id
name
}
}
}
""";

String expected = "{Authors={select=[{id=1, name=Leo Tolstoy}, {id=4, name=Anton Chekhov}, {id=8, name=Igor Dianov}]}}";

//when
Object result = executor.execute(query).getData();


// then
assertThat(result.toString()).isEqualTo(expected.strip());
}


// https://github.com/introproventures/graphql-jpa-query/issues/30
@Test
public void queryForEntityWithMappedSuperclass() {
Expand Down
8 changes: 4 additions & 4 deletions schema/src/test/resources/data.sql
Expand Up @@ -112,19 +112,19 @@ insert into thing (id, type) values
('2D1EBC5B7D2741979CF0E84451C5BBB1', 'Thing1');

-- Books
insert into author (id, name, genre) values (1, 'Leo Tolstoy', 'NOVEL');
insert into author (id, name, genre, profile_picture) values (1, 'Leo Tolstoy', 'NOVEL', 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAlElEQVR4nO2UQQrEIAxFe/8jSRUrKtZFEasIQs7yh3RRKEOnixmYjYsQ0OT5E2ImIsIvbRpAjB7iq7GptcIYg947rLUopbwl5JzPGPattXtgSgkhBAghsG3b4fncOQfv/RnDIL7jB+d5/qxQKYV1XS9ArTWWZbkoZHsExhgPICfdlcxKpZTY9/25ZBp/mcb6on9s7Bc+TJAvSO7XjwAAAABJRU5ErkJggg==');
insert into book (id, title, author_id, genre, publication_date, description)
values (2, 'War and Peace', 1, 'NOVEL', '1869-01-01', 'The novel chronicles the history of the French invasion of Russia and the impact of the Napoleonic era on Tsarist society through the stories of five Russian aristocratic families.');
insert into book (id, title, author_id, genre, publication_date, description)
values (3, 'Anna Karenina', 1, 'NOVEL', '1877-04-01', 'A complex novel in eight parts, with more than a dozen major characters, it is spread over more than 800 pages (depending on the translation), typically contained in two volumes.');
insert into author (id, name, genre) values (4, 'Anton Chekhov', 'PLAY');
insert into author (id, name, genre, profile_picture) values (4, 'Anton Chekhov', 'PLAY', 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAlElEQVR4nO2UQQrEIAxFe/8jSRUrKtZFEasIQs7yh3RRKEOnixmYjYsQ0OT5E2ImIsIvbRpAjB7iq7GptcIYg947rLUopbwl5JzPGPattXtgSgkhBAghsG3b4fncOQfv/RnDIL7jB+d5/qxQKYV1XS9ArTWWZbkoZHsExhgPICfdlcxKpZTY9/25ZBp/mcb6on9s7Bc+TJAvSO7XjwAAAABJRU5ErkJggg==');
insert into book (id, title, author_id, genre, publication_date, description)
values (5, 'The Cherry Orchard', 4, 'PLAY', '1904-01-17', 'The play concerns an aristocratic Russian landowner who returns to her family estate (which includes a large and well-known cherry orchard) just before it is auctioned to pay the mortgage.');
insert into book (id, title, author_id, genre, publication_date, description)
values (6, 'The Seagull', 4, 'PLAY', '1896-10-17', 'It dramatises the romantic and artistic conflicts between four characters');
insert into book (id, title, author_id, genre, publication_date, description)
values (7, 'Three Sisters', 4, 'PLAY', '1900-01-01', 'The play is sometimes included on the short list of Chekhov''s outstanding plays, along with The Cherry Orchard, The Seagull and Uncle Vanya.[1]');
insert into author (id, name, genre) values (8, 'Igor Dianov', 'JAVA');
insert into author (id, name, genre, profile_picture) values (8, 'Igor Dianov', 'JAVA', 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAlElEQVR4nO2UQQrEIAxFe/8jSRUrKtZFEasIQs7yh3RRKEOnixmYjYsQ0OT5E2ImIsIvbRpAjB7iq7GptcIYg947rLUopbwl5JzPGPattXtgSgkhBAghsG3b4fncOQfv/RnDIL7jB+d5/qxQKYV1XS9ArTWWZbkoZHsExhgPICfdlcxKpZTY9/25ZBp/mcb6on9s7Bc+TJAvSO7XjwAAAABJRU5ErkJggg==');

insert into book_tags (book_id, tags) values (2, 'war'), (2, 'piece');
insert into book_tags (book_id, tags) values (3, 'anna'), (3, 'karenina');
Expand Down Expand Up @@ -163,4 +163,4 @@ insert into calculated_entity (id, title, info) values
-- FloatingThing
insert into floating_thing (id, float_value, double_value, big_decimal_value) values
(1, 4.55, 4.55, 4.55),
(2, -0.44, -0.44, -0.44)
(2, -0.44, -0.44, -0.44)
20 changes: 20 additions & 0 deletions tests/models/books/pom.xml
Expand Up @@ -24,4 +24,24 @@
<optional>true</optional>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.hibernate.orm.tooling</groupId>
<artifactId>hibernate-enhance-maven-plugin</artifactId>
<executions>
<execution>
<configuration>
<failOnError>true</failOnError>
<enableLazyInitialization>true</enableLazyInitialization>
</configuration>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Expand Up @@ -16,6 +16,7 @@

package com.introproventures.graphql.jpa.query.schema.model.book;

import jakarta.persistence.Basic;
import jakarta.persistence.CollectionTable;
import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
Expand All @@ -25,6 +26,7 @@
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.Lob;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OrderBy;
import java.util.HashSet;
Expand All @@ -38,7 +40,7 @@
@Getter
@Setter
@ToString
@EqualsAndHashCode(exclude = { "books", "phoneNumbers" }) // Fixes NPE in Hibernate when initializing loaded collections #1
@EqualsAndHashCode // Fixes NPE in Hibernate when initializing loaded collections #1
public class Author {

@Id
Expand All @@ -48,13 +50,23 @@ public class Author {

@OneToMany(mappedBy = "author", fetch = FetchType.LAZY)
@OrderBy("id ASC")
@ToString.Exclude
@EqualsAndHashCode.Exclude
Set<Book> books;

@ElementCollection(fetch = FetchType.LAZY)
@CollectionTable(name = "author_phone_numbers", joinColumns = @JoinColumn(name = "author_id"))
@Column(name = "phone_number")
@ToString.Exclude
@EqualsAndHashCode.Exclude
private Set<String> phoneNumbers = new HashSet<>();

@Enumerated(EnumType.STRING)
Genre genre;

@Lob
@Basic(fetch = FetchType.LAZY)
@ToString.Exclude
@EqualsAndHashCode.Exclude
private byte[] profilePicture;
}
Expand Up @@ -33,10 +33,12 @@
import java.util.Set;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;

@Data
@Entity
@EqualsAndHashCode(exclude = { "author", "tags", "publishers" })
@EqualsAndHashCode
@ToString
public class Book {

@Id
Expand All @@ -53,9 +55,13 @@ public class Book {

@ElementCollection(fetch = FetchType.LAZY)
@GraphQLDescription("A set of user-defined tags")
@ToString.Exclude
@EqualsAndHashCode.Exclude
private Set<String> tags = new LinkedHashSet<>();

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@ToString.Exclude
@EqualsAndHashCode.Exclude
Author author;

@Enumerated(EnumType.STRING)
Expand All @@ -64,6 +70,8 @@ public class Book {
Date publicationDate;

@ElementCollection(fetch = FetchType.LAZY)
@ToString.Exclude
@EqualsAndHashCode.Exclude
Set<Publisher> publishers;

@Transient
Expand Down
8 changes: 4 additions & 4 deletions tests/models/books/src/main/resources/books.sql

Large diffs are not rendered by default.

0 comments on commit df56453

Please sign in to comment.