Description: This application is a sample of fetching all attributes of an entity (Author) as a Spring projection (DTO). Commonly, a DTO contains a subset of attributes, but, sometimes we need to fetch the whole entity as a DTO. In such cases, we have to pay attention to the chosen approach. Choosing wisely can spare us from performance penalties.

Key points:

  • fetching the result set as a List<Object[]> or List<AuthorDto> via a JPQL of type SELECT a FROM Author a WILL fetch the result set as entities in Persistent Context as well - avoid this approach
  • fetching the result set as a List<Object[]> or List<AuthorDto> via a JPQL of type SELECT a.id AS id, a.name AS name, ... FROM Author a will NOT fetch the result set in Persistent Context - this is efficient
  • fetching the result set as a List<Object[]> or List<AuthorDto> via a native SQL of type SELECT id, name, age, ... FROM author will NOT fetch the result set in Persistent Context - but, this approach is pretty slow
  • fetching the result set as a List<Object[]> via Spring Data query builder mechanism WILL fetch the result set in Persistent Context - avoid this approach
  • fetching the result set as a List<AuthorDto> via Spring Data query builder mechanism will NOT fetch the result set in Persistent Context
  • fetching the result set as read-only entitites (e.g., via the built-in findAll() method) should be considered after JPQL with explicit list of columns to be fetched and query builder mechanism.

Author.java

@Data
@Entity
public class Author implements Serializable {

private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;
private String genre;
private int age;
}

AuthorRepository.java

@Repository
public interface AuthorRepository extends JpaRepository<Author, Long> {

@Query("SELECT a FROM Author a")
List<Object[]> fetchAsArray();

@Query("SELECT a.id AS id, a.age AS age, a.name AS name, a.genre AS genre FROM Author a")
List<Object[]> fetchAsArrayColumns();

@Query(value = "SELECT id, age, name, genre FROM author", nativeQuery = true)
List<Object[]> fetchAsArrayNative();

List<Object[]> findFirstBy();

@Query("SELECT a FROM Author a")
List<AuthorDto> fetchAsDto();

@Query("SELECT a.id AS id, a.age AS age, a.name AS name, a.genre AS genre FROM Author a")
List<AuthorDto> fetchAsDtoColumns();

@Query(value = "SELECT id, age, name, genre FROM author", nativeQuery = true)
List<AuthorDto> fetchAsDtoNative();

List<AuthorDto> findBy();
}

BookStoreService.java

@RequiredArgsConstructor
@Service
public class BookstoreService {

private final AuthorRepository authorRepository;

@PersistenceContext
private final EntityManager entityManager;

@Transactional(readOnly = true)
public void fetchAuthorAsReadOnlyEntities() {
List<Author> authors = authorRepository.findAll();
authors.forEach(a -> System.out.println(a));

briefOverviewOfPersistentContextContent();
}

@Transactional(readOnly = true)
public void fetchAuthorAsArrayOfObject() {
List<Object[]> authors = authorRepository.fetchAsArray();
authors.forEach(a -> System.out.println(Arrays.toString(a)));

briefOverviewOfPersistentContextContent();
}

@Transactional(readOnly = true)
public void fetchAuthorAsArrayOfObjectColumns() {
List<Object[]> authors = authorRepository.fetchAsArrayColumns();
authors.forEach(a -> System.out.println(Arrays.toString(a)));

briefOverviewOfPersistentContextContent();
}

@Transactional(readOnly = true)
public void fetchAuthorAsArrayOfObjectNative() {
List<Object[]> authors = authorRepository.fetchAsArrayNative();
authors.forEach(a -> System.out.println(Arrays.toString(a)));

briefOverviewOfPersistentContextContent();
}

@Transactional(readOnly = true)
public void fetchAuthorAsArrayOfObjectQueryBuilderMechanism() {
List<Object[]> authors = authorRepository.findFirstBy();
authors.forEach(a -> System.out.println(Arrays.toString(a)));

briefOverviewOfPersistentContextContent();
}

@Transactional(readOnly = true)
public void fetchAuthorAsDtoClass() {
List<AuthorDto> authors = authorRepository.fetchAsDto();
authors.forEach(System.out::println);

briefOverviewOfPersistentContextContent();
}

@Transactional(readOnly = true)
public void fetchAuthorAsDtoClassColumns() {
List<AuthorDto> authors = authorRepository.fetchAsDtoColumns();
authors.forEach(a -> System.out.println("Author{id=" + a.getId()
+ ", name=" + a.getName() + ", genre=" + a.getGenre()
+ ", age=" + a.getAge() + "}"));

briefOverviewOfPersistentContextContent();
}

@Transactional(readOnly = true)
public void fetchAuthorAsDtoClassNative() {
List<AuthorDto> authors = authorRepository.fetchAsDtoNative();
authors.forEach(a -> System.out.println("Author{id=" + a.getId()
+ ", name=" + a.getName() + ", genre=" + a.getGenre()
+ ", age=" + a.getAge() + "}"));

briefOverviewOfPersistentContextContent();
}

@Transactional(readOnly = true)
public void fetchAuthorByGenreAsDtoClassQueryBuilderMechanism() {
List<AuthorDto> authors = authorRepository.findBy();
authors.forEach(a -> System.out.println("Author{id=" + a.getId()
+ ", name=" + a.getName() + ", genre=" + a.getGenre()
+ ", age=" + a.getAge() + "}"));

briefOverviewOfPersistentContextContent();
}

private void briefOverviewOfPersistentContextContent() {
org.hibernate.engine.spi.PersistenceContext persistenceContext = getPersistenceContext();

int managedEntities = persistenceContext.getNumberOfManagedEntities();

System.out.println("\n-----------------------------------");
System.out.println("Total number of managed entities: " + managedEntities);

// getEntitiesByKey() will be removed and probably replaced with #iterateEntities()
Map<EntityKey, Object> entitiesByKey = persistenceContext.getEntitiesByKey();
entitiesByKey.forEach((key, value) -> System.out.println(key + ": " + value));

for (Object entry : entitiesByKey.values()) {
EntityEntry ee = persistenceContext.getEntry(entry);
System.out.println(
"Entity name: " + ee.getEntityName()
+ " | Status: " + ee.getStatus()
+ " | State: " + Arrays.toString(ee.getLoadedState()));
}
;

System.out.println("\n-----------------------------------\n");
}

private org.hibernate.engine.spi.PersistenceContext getPersistenceContext() {

SharedSessionContractImplementor sharedSession = entityManager.unwrap(
SharedSessionContractImplementor.class
);

return sharedSession.getPersistenceContext();
}

}

application.properties

spring.datasource.url=jdbc:mysql://localhost:3306/bookstoredb?createDatabaseIfNotExist=true
spring.datasource.username=root
spring.datasource.password=root

spring.jpa.hibernate.ddl-auto=create
spring.jpa.show-sql=true

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect

spring.jpa.open-in-view=false

spring.datasource.initialization-mode=always
spring.datasource.platform=mysql

data-mysql.sql

insert into author (age, name, genre, id) values (23, "Mark Janel", "Anthology", 1);
insert into author (age, name, genre, id) values (43, "Olivia Goy", "Horror", 2);
insert into author (age, name, genre, id) values (51, "Quartis Young", "Anthology", 3);
insert into author (age, name, genre, id) values (34, "Joana Nimar", "History", 4);
insert into author (age, name, genre, id) values (33, "Marin Kyrab", "History", 5);

MainApp.java

@SpringBootApplication
public class MainApplication {

@Autowired
private BookstoreService bookstoreService;

public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}

@Bean
public ApplicationRunner init() {
return args -> {
System.out.println("\n\n Fetch authors read-only entities:");
System.out.println("-----------------------------------------------------------------------------");
bookstoreService.fetchAuthorAsReadOnlyEntities();

System.out.println("\n\n Fetch authors as array of objects");
System.out.println("-----------------------------------------------------------------------------");
bookstoreService.fetchAuthorAsArrayOfObject();

System.out.println("\n\n Fetch authors as array of objects by specifying columns");
System.out.println("-----------------------------------------------------------------------------");
bookstoreService.fetchAuthorAsArrayOfObjectColumns();

System.out.println("\n\n Fetch authors as array of objects via native query");
System.out.println("-----------------------------------------------------------------------------");
bookstoreService.fetchAuthorAsArrayOfObjectNative();

System.out.println("\n\n Fetch authors as array of objects via query builder mechanism");
System.out.println("-----------------------------------------------------------------------------");
bookstoreService.fetchAuthorAsArrayOfObjectQueryBuilderMechanism();

System.out.println("\n\n Fetch authors as Spring projection (DTO):");
System.out.println("-----------------------------------------------------------------------------");
bookstoreService.fetchAuthorAsDtoClass();

System.out.println("\n\n Fetch authors as Spring projection (DTO) by specifying columns:");
System.out.println("-----------------------------------------------------------------------------");
bookstoreService.fetchAuthorAsDtoClassColumns();

System.out.println("\n\n Fetch authors as Spring projection (DTO) and native query:");
System.out.println("-----------------------------------------------------------------------------");
bookstoreService.fetchAuthorAsDtoClassNative();

System.out.println("\n\n Fetch authors as Spring projection (DTO) via query builder mechanism:");
System.out.println("-----------------------------------------------------------------------------");
bookstoreService.fetchAuthorByGenreAsDtoClassQueryBuilderMechanism();
};
}
}

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store