From 002384b66831ee46cfe2c7b7c8f6fc67dd6b0600 Mon Sep 17 00:00:00 2001 From: Manfred <77407079+manfred106@users.noreply.github.com> Date: Sun, 28 Jan 2024 01:14:00 +0000 Subject: [PATCH] BAEL-6753: Storing PostgreSQL jsonb Using SpringBoot and JPA (#15632) * BAEL-6753: Storing PostgreSQL jsonb Using SpringBoot and JPA * BAEL-6753: Storing PostgreSQL jsonb Using SpringBoot and JPA --- .../spring-data-jpa-repo-4/pom.xml | 34 +++++++++ .../spring/data/persistence/json/Address.java | 15 ++++ .../json/AddressAttributeConverter.java | 36 +++++++++ .../json/JsonAttributeApplication.java | 13 ++++ .../data/persistence/json/StudentEntity.java | 31 ++++++++ .../persistence/json/StudentRepository.java | 16 ++++ .../persistence/json/StudentStrEntity.java | 27 +++++++ .../json/StudentStrRepository.java | 16 ++++ .../json/JsonAttributeLiveTest.java | 76 +++++++++++++++++++ 9 files changed, 264 insertions(+) create mode 100644 persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/Address.java create mode 100644 persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/AddressAttributeConverter.java create mode 100644 persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/JsonAttributeApplication.java create mode 100644 persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/StudentEntity.java create mode 100644 persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/StudentRepository.java create mode 100644 persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/StudentStrEntity.java create mode 100644 persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/StudentStrRepository.java create mode 100644 persistence-modules/spring-data-jpa-repo-4/src/test/java/com/baeldung/spring/data/persistence/json/JsonAttributeLiveTest.java diff --git a/persistence-modules/spring-data-jpa-repo-4/pom.xml b/persistence-modules/spring-data-jpa-repo-4/pom.xml index c823391d9f..9b03f7fdfb 100644 --- a/persistence-modules/spring-data-jpa-repo-4/pom.xml +++ b/persistence-modules/spring-data-jpa-repo-4/pom.xml @@ -30,6 +30,11 @@ org.springframework.boot spring-boot-starter-data-jpa + + io.hypersistence + hypersistence-utils-hibernate-55 + ${hypersistance-utils-hibernate-55.version} + com.h2database h2 @@ -47,6 +52,30 @@ guava ${guava.version} + + org.projectlombok + lombok + + + org.postgresql + postgresql + ${postgresql.version} + + + org.slf4j + slf4j-api + ${org.slf4j.version} + + + ch.qos.logback + logback-core + ${logback.version} + + + ch.qos.logback + logback-classic + ${logback.version} + @@ -98,4 +127,9 @@ + + 3.7.0 + 42.7.1 + + \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/Address.java b/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/Address.java new file mode 100644 index 0000000000..976a232082 --- /dev/null +++ b/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/Address.java @@ -0,0 +1,15 @@ +package com.baeldung.spring.data.persistence.json; + +import lombok.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode +public class Address { + + private String postCode; + + private String city; + +} diff --git a/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/AddressAttributeConverter.java b/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/AddressAttributeConverter.java new file mode 100644 index 0000000000..736f5e8ec2 --- /dev/null +++ b/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/AddressAttributeConverter.java @@ -0,0 +1,36 @@ +package com.baeldung.spring.data.persistence.json; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; + +@Converter +@Slf4j +public class AddressAttributeConverter implements AttributeConverter { + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public String convertToDatabaseColumn(Address address) { + try { + return objectMapper.writeValueAsString(address); + } catch (JsonProcessingException jpe) { + log.warn("Cannot convert Address into JSON"); + return null; + } + } + + @Override + public Address convertToEntityAttribute(String value) { + try { + return objectMapper.readValue(value, Address.class); + } catch (JsonProcessingException e) { + log.warn("Cannot convert JSON into Address"); + return null; + } + } + +} \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/JsonAttributeApplication.java b/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/JsonAttributeApplication.java new file mode 100644 index 0000000000..708b43a534 --- /dev/null +++ b/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/JsonAttributeApplication.java @@ -0,0 +1,13 @@ +package com.baeldung.spring.data.persistence.json; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class JsonAttributeApplication { + + public static void main(String[] args) { + SpringApplication.run(JsonAttributeApplication.class, args); + } + +} \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/StudentEntity.java b/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/StudentEntity.java new file mode 100644 index 0000000000..c5fd31ddd3 --- /dev/null +++ b/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/StudentEntity.java @@ -0,0 +1,31 @@ +package com.baeldung.spring.data.persistence.json; + +import javax.persistence.*; + +import io.hypersistence.utils.hibernate.type.json.JsonBinaryType; +import lombok.*; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; + +@Entity +@Table(name = "student") +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(of = {"id"}) +@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class) +public class StudentEntity { + + @Id + @Column(name = "student_id", length = 8) + private String id; + + @Column(name = "admit_year", length = 4) + private String admitYear; + + @Type(type = "jsonb") + @Column(name = "address", columnDefinition = "jsonb") + private Address address; + +} \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/StudentRepository.java b/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/StudentRepository.java new file mode 100644 index 0000000000..bae8ab6ad9 --- /dev/null +++ b/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/StudentRepository.java @@ -0,0 +1,16 @@ +package com.baeldung.spring.data.persistence.json; + +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface StudentRepository extends CrudRepository { + + @Query(value = "SELECT * FROM student WHERE address->>'postCode' = :postCode", nativeQuery = true) + List findByAddressPostCode(@Param("postCode") String postCode); + +} \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/StudentStrEntity.java b/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/StudentStrEntity.java new file mode 100644 index 0000000000..552cdeaed6 --- /dev/null +++ b/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/StudentStrEntity.java @@ -0,0 +1,27 @@ +package com.baeldung.spring.data.persistence.json; + +import javax.persistence.*; + +import lombok.*; + +@Entity +@Table(name = "student_str") +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(of = {"id"}) +public class StudentStrEntity { + + @Id + @Column(name = "student_id", length = 8) + private String id; + + @Column(name = "admit_year", length = 4) + private String admitYear; + + @Convert(converter = AddressAttributeConverter.class) + @Column(name = "address", length = 500) + private Address address; + +} \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/StudentStrRepository.java b/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/StudentStrRepository.java new file mode 100644 index 0000000000..ce40166b22 --- /dev/null +++ b/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/json/StudentStrRepository.java @@ -0,0 +1,16 @@ +package com.baeldung.spring.data.persistence.json; + +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface StudentStrRepository extends CrudRepository { + + @Query(value = "SELECT * FROM student WHERE address->>'postCode' = :postCode", nativeQuery = true) + List findByAddressPostCode(@Param("postCode") String postCode); + +} diff --git a/persistence-modules/spring-data-jpa-repo-4/src/test/java/com/baeldung/spring/data/persistence/json/JsonAttributeLiveTest.java b/persistence-modules/spring-data-jpa-repo-4/src/test/java/com/baeldung/spring/data/persistence/json/JsonAttributeLiveTest.java new file mode 100644 index 0000000000..1242b3f2f4 --- /dev/null +++ b/persistence-modules/spring-data-jpa-repo-4/src/test/java/com/baeldung/spring/data/persistence/json/JsonAttributeLiveTest.java @@ -0,0 +1,76 @@ +package com.baeldung.spring.data.persistence.json; + +import org.junit.jupiter.api.*; +import org.springframework.boot.test.context.SpringBootTest; + +import javax.inject.Inject; +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest(classes = JsonAttributeApplication.class) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +class JsonAttributeLiveTest { + + @Inject + private StudentStrRepository studentStrRepository; + + @Inject + private StudentRepository studentRepository; + + @Test + @Order(10) + void whenSaveAnStudentStrEntityAndFindById_thenTheRecordPresentsInDb() { + String studentId = "23876371"; + String postCode = "KT6 7BB"; + + Address address = new Address(postCode, "London"); + StudentStrEntity studentStrEntity = StudentStrEntity.builder() + .id(studentId) + .admitYear("2023") + .address(address) + .build(); + + StudentStrEntity savedStudentStrEntity = studentStrRepository.save(studentStrEntity); + + Optional studentEntityOptional = studentStrRepository.findById(studentId); + assertThat(studentEntityOptional.isPresent()).isTrue(); + + studentStrEntity = studentEntityOptional.get(); + assertThat(studentStrEntity.getId()).isEqualTo(studentId); + assertThat(studentStrEntity.getAddress().getPostCode()).isEqualTo(postCode); + } + + @Test + @Order(20) + void whenSaveAnStudentEntityAndFindById_thenTheRecordPresentsInDb() { + String studentId = "23876371"; + String postCode = "KT6 7BB"; + + Address address = new Address(postCode, "London"); + StudentEntity studentEntity = StudentEntity.builder() + .id(studentId) + .admitYear("2023") + .address(address) + .build(); + + StudentEntity savedStudentEntity = studentRepository.save(studentEntity); + + Optional studentEntityOptional = studentRepository.findById(studentId); + assertThat(studentEntityOptional.isPresent()).isTrue(); + + studentEntity = studentEntityOptional.get(); + assertThat(studentEntity.getId()).isEqualTo(studentId); + assertThat(studentEntity.getAddress().getPostCode()).isEqualTo(postCode); + } + + @Test + @Order(50) + void whenFindByAddressPostCode_thenReturnListIsNotEmpty() { + String postCode = "KT6 7BB"; + List studentStrEntityList = studentStrRepository.findByAddressPostCode(postCode); + assertThat(studentStrEntityList).isNotEmpty(); + } + +} \ No newline at end of file