diff --git a/spring-boot-modules/spring-boot-3/pom.xml b/spring-boot-modules/spring-boot-3/pom.xml index cc8e7e1426..685df233ba 100644 --- a/spring-boot-modules/spring-boot-3/pom.xml +++ b/spring-boot-modules/spring-boot-3/pom.xml @@ -124,6 +124,8 @@ 1.5.2.Final 2.0.0 3.0.0-M7 + com.baeldung.sample.TodoApplication + \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/recordswithjpa/QueryService.java b/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/recordswithjpa/QueryService.java new file mode 100644 index 0000000000..4c1e73e530 --- /dev/null +++ b/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/recordswithjpa/QueryService.java @@ -0,0 +1,42 @@ +package com.baeldung.recordswithjpa; + +import com.baeldung.recordswithjpa.entity.Book; +import com.baeldung.recordswithjpa.records.BookRecord; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.Query; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Root; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class QueryService { + @PersistenceContext + private EntityManager entityManager; + + public List findAllBooks() { + CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(BookRecord.class); + Root root = query.from(Book.class); + query.select(cb + .construct(BookRecord.class, root.get("id"), root.get("title"), root.get("author"), root.get("isbn"))); + return entityManager.createQuery(query).getResultList(); + } + + public BookRecord findBookById(Long id) { + TypedQuery query = entityManager + .createQuery("SELECT new com.baeldung.recordswithjpa.records.BookRecord(b.id, b.title, b.author, b.isbn) " + + "FROM Book b WHERE b.id = :id", BookRecord.class); + query.setParameter("id", id); + return query.getSingleResult(); + } + + public List findAllBooksUsingMapping() { + Query query = entityManager.createNativeQuery("SELECT * FROM book", "BookRecordMapping"); + return query.getResultList(); + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/recordswithjpa/RecordsAsJpaApplication.java b/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/recordswithjpa/RecordsAsJpaApplication.java new file mode 100644 index 0000000000..d6eb1edec1 --- /dev/null +++ b/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/recordswithjpa/RecordsAsJpaApplication.java @@ -0,0 +1,13 @@ +package com.baeldung.recordswithjpa; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class RecordsAsJpaApplication { + + public static void main(String[] args) { + SpringApplication.run(RecordsAsJpaApplication.class, args); + } + +} diff --git a/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/recordswithjpa/entity/Book.java b/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/recordswithjpa/entity/Book.java new file mode 100644 index 0000000000..9ea982e323 --- /dev/null +++ b/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/recordswithjpa/entity/Book.java @@ -0,0 +1,70 @@ +package com.baeldung.recordswithjpa.entity; + +import com.baeldung.recordswithjpa.records.BookRecord; +import jakarta.persistence.*; + +@SqlResultSetMapping( + name = "BookRecordMapping", + classes = @ConstructorResult( + targetClass = BookRecord.class, + columns = { + @ColumnResult(name = "id", type = Long.class), + @ColumnResult(name = "title", type = String.class), + @ColumnResult(name = "author", type = String.class), + @ColumnResult(name = "isbn", type = String.class) + } + ) +) +@Entity +@Table(name = "book") +public class Book { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String title; + private String author; + private String isbn; + + public Book() { + } + + public Book(Long id, String title, String author, String isbn) { + this.id = id; + this.title = title; + this.author = author; + this.isbn = isbn; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getAuthor() { + return author; + } + + public void setAuthor(String author) { + this.author = author; + } + + public String getIsbn() { + return isbn; + } + + public void setIsbn(String isbn) { + this.isbn = isbn; + } + +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/recordswithjpa/records/BookRecord.java b/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/recordswithjpa/records/BookRecord.java new file mode 100644 index 0000000000..96968575d9 --- /dev/null +++ b/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/recordswithjpa/records/BookRecord.java @@ -0,0 +1,4 @@ +package com.baeldung.recordswithjpa.records; + +public record BookRecord(Long id, String title, String author, String isbn) { +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/recordswithjpa/records/CustomBookRecord.java b/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/recordswithjpa/records/CustomBookRecord.java new file mode 100644 index 0000000000..35e9be9513 --- /dev/null +++ b/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/recordswithjpa/records/CustomBookRecord.java @@ -0,0 +1,4 @@ +package com.baeldung.recordswithjpa.records; + +public record CustomBookRecord(Long id, String title) { +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/recordswithjpa/repository/BookRepository.java b/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/recordswithjpa/repository/BookRepository.java new file mode 100644 index 0000000000..4ef5f17296 --- /dev/null +++ b/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/recordswithjpa/repository/BookRepository.java @@ -0,0 +1,17 @@ +package com.baeldung.recordswithjpa.repository; + +import com.baeldung.recordswithjpa.entity.Book; +import com.baeldung.recordswithjpa.records.BookRecord; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; + +import java.util.List; + +public interface BookRepository extends CrudRepository { + List findBookByAuthor(String author); + + @Query("SELECT new com.baeldung.recordswithjpa.records.BookRecord(b.id, b.title, b.author, b.isbn) " + + "FROM Book b WHERE b.id = :id") + BookRecord findBookById(@Param("id") Long id); +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/recordswithjpa/repository/CustomBookRepository.java b/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/recordswithjpa/repository/CustomBookRepository.java new file mode 100644 index 0000000000..bb8002d70d --- /dev/null +++ b/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/recordswithjpa/repository/CustomBookRepository.java @@ -0,0 +1,9 @@ +package com.baeldung.recordswithjpa.repository; + +import com.baeldung.recordswithjpa.records.CustomBookRecord; + +import java.util.List; + +public interface CustomBookRepository { + List findAllBooks(); +} diff --git a/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/recordswithjpa/repository/CustomBookRepositoryImpl.java b/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/recordswithjpa/repository/CustomBookRepositoryImpl.java new file mode 100644 index 0000000000..8d420a9874 --- /dev/null +++ b/spring-boot-modules/spring-boot-3/src/main/java/com/baeldung/recordswithjpa/repository/CustomBookRepositoryImpl.java @@ -0,0 +1,20 @@ +package com.baeldung.recordswithjpa.repository; + +import com.baeldung.recordswithjpa.records.CustomBookRecord; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public class CustomBookRepositoryImpl implements CustomBookRepository { + private final JdbcTemplate jdbcTemplate; + + public CustomBookRepositoryImpl(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + public List findAllBooks() { + return jdbcTemplate.query("SELECT id, title FROM book", (rs, rowNum) -> new CustomBookRecord(rs.getLong("id"), rs.getString("title"))); + } +} diff --git a/spring-boot-modules/spring-boot-3/src/test/java/com/baeldung/recordswithjpa/QueryServiceIntegrationTest.java b/spring-boot-modules/spring-boot-3/src/test/java/com/baeldung/recordswithjpa/QueryServiceIntegrationTest.java new file mode 100644 index 0000000000..011895e7fa --- /dev/null +++ b/spring-boot-modules/spring-boot-3/src/test/java/com/baeldung/recordswithjpa/QueryServiceIntegrationTest.java @@ -0,0 +1,39 @@ +package com.baeldung.recordswithjpa; + +import com.baeldung.recordswithjpa.entity.Book; +import com.baeldung.recordswithjpa.records.BookRecord; +import com.baeldung.recordswithjpa.repository.BookRepository; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +public class QueryServiceIntegrationTest extends RecordsAsJpaIntegrationTest { + + @Autowired + private QueryService queryService; + + + @Test + void findAllBooks() { + List allBooks = queryService.findAllBooks(); + assertEquals(3, allBooks.size()); + } + + @Test + void findBookById() { + BookRecord bookById = queryService.findBookById(1L); + assertEquals("The Lord of the Rings", bookById.title()); + } + + @Test + void findAllBooksUsingMapping() { + List allBooksUsingMapping = queryService.findAllBooksUsingMapping(); + assertEquals(3, allBooksUsingMapping.size()); + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-3/src/test/java/com/baeldung/recordswithjpa/RecordsAsJpaIntegrationTest.java b/spring-boot-modules/spring-boot-3/src/test/java/com/baeldung/recordswithjpa/RecordsAsJpaIntegrationTest.java new file mode 100644 index 0000000000..f0869dad48 --- /dev/null +++ b/spring-boot-modules/spring-boot-3/src/test/java/com/baeldung/recordswithjpa/RecordsAsJpaIntegrationTest.java @@ -0,0 +1,30 @@ +package com.baeldung.recordswithjpa; + +import com.baeldung.recordswithjpa.entity.Book; +import com.baeldung.recordswithjpa.repository.BookRepository; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +public class RecordsAsJpaIntegrationTest { + @Autowired + protected BookRepository bookRepository; + + @BeforeEach + void setUp() { + Book book = new Book(1L,"The Lord of the Rings", "J.R.R. Tolkien", "978-0544003415"); + Book book2 = new Book(2L,"The Hobbit", "J.R.R. Tolkien", "978-0547928227"); + Book book3 = new Book(3L,"Harry Potter and the Philosopher's Stone", "J.K. Rowling", "978-0747532699"); + + bookRepository.save(book); + bookRepository.save(book2); + bookRepository.save(book3); + } + + @AfterEach + void tearDown() { + bookRepository.deleteAll(); + } +} diff --git a/spring-boot-modules/spring-boot-3/src/test/java/com/baeldung/recordswithjpa/repository/BookRepositoryIntegrationTest.java b/spring-boot-modules/spring-boot-3/src/test/java/com/baeldung/recordswithjpa/repository/BookRepositoryIntegrationTest.java new file mode 100644 index 0000000000..9173fea269 --- /dev/null +++ b/spring-boot-modules/spring-boot-3/src/test/java/com/baeldung/recordswithjpa/repository/BookRepositoryIntegrationTest.java @@ -0,0 +1,19 @@ +package com.baeldung.recordswithjpa.repository; + +import com.baeldung.recordswithjpa.RecordsAsJpaIntegrationTest; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class BookRepositoryIntegrationTest extends RecordsAsJpaIntegrationTest { + + @Test + void findBookByAuthor() { + assertEquals(2, bookRepository.findBookByAuthor("J.R.R. Tolkien").size()); + } + + @Test + void findBookById() { + assertEquals("The Lord of the Rings", bookRepository.findBookById(1L).title()); + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-3/src/test/java/com/baeldung/recordswithjpa/repository/CustomBookRepositoryIntegrationTest.java b/spring-boot-modules/spring-boot-3/src/test/java/com/baeldung/recordswithjpa/repository/CustomBookRepositoryIntegrationTest.java new file mode 100644 index 0000000000..866645429a --- /dev/null +++ b/spring-boot-modules/spring-boot-3/src/test/java/com/baeldung/recordswithjpa/repository/CustomBookRepositoryIntegrationTest.java @@ -0,0 +1,23 @@ +package com.baeldung.recordswithjpa.repository; + +import com.baeldung.recordswithjpa.RecordsAsJpaIntegrationTest; +import com.baeldung.recordswithjpa.records.CustomBookRecord; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class CustomBookRepositoryIntegrationTest extends RecordsAsJpaIntegrationTest { + + @Autowired + private CustomBookRepository customBookRepository; + + @Test + void findAllBooks() { + List allBooks = customBookRepository.findAllBooks(); + assertEquals(3, allBooks.size()); + } +} \ No newline at end of file