Hibernate Show SQL
When you are developing Spring Boot applications with database interactions, you typically use Hibernate as the Object Relationship Mapping (ORM) tool.
Instead of directly coupling your code with Hibernate, often, you'd rather use Spring Data JPA, a Spring Framework project.
Spring Data JPA makes the implementation of the data access layer incredibly easy by abstracting most of the complexities involved in persisting data.
Often, when you are working with Hibernate and Spring Data JPA, you will need to see what is happening under the hood. It is very helpful to see the actual SQL statements being generated by Hibernate.
Due to the nature of the abstractions offered by Hibernate and Spring Data JPA, it's very easy to inadvertently create n+1 queries — which is very detrimental to the performance of your application.
In this post, I'll share a tip on how to configure Hibernate and Spring Data JPA to log executed SQL statements and used bind parameters.
The Application
For the purpose of this post, I've created a simple Spring Boot application. In this application, we can perform CRUD operations on a Product
entity.
Here is the Product
entity.
Product.java:
package guru.springframework.domain;
import javax.persistence.*;
import java.math.BigDecimal;
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
@Version
private Integer version;
private String productId;
private String description;
private String imageUrl;
private BigDecimal price;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getProductId() {
return productId;
}
public void setProductId(String productId) {
this.productId = productId;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
}
Below is a JUnit test class to save and retrieve products.
If you are new to JUnit, I'd suggest checking out my JUnit series of posts.
ProductRepositoryTest.java:
package guru.springframework.repositories;
import guru.springframework.configuration.RepositoryConfiguration;
import guru.springframework.domain.Product;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.math.BigDecimal;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = {RepositoryConfiguration.class})
public class ProductRepositoryTest {
private ProductRepository productRepository;
@Autowired
public void setProductRepository(ProductRepository productRepository) {
this.productRepository = productRepository;
}
@Test
public void testSaveProduct(){
//setup product
Product product = new Product();
product.setDescription("Spring Framework Guru Shirt");
product.setPrice(new BigDecimal("18.95"));
product.setProductId("1234");
//save product, verify has ID value after save
assertNull(product.getId()); //null before save
productRepository.save(product);
assertNotNull(product.getId()); //not null after save
//fetch from DB
Product fetchedProduct = productRepository.findOne(product.getId());
//should not be null
assertNotNull(fetchedProduct);
//should equal
assertEquals(product.getId(), fetchedProduct.getId());
assertEquals(product.getDescription(), fetchedProduct.getDescription());
//update description and save
fetchedProduct.setDescription("New Description");
productRepository.save(fetchedProduct);
//get from DB, should be updated
Product fetchedUpdatedProduct = productRepository
.findOne(fetchedProduct.getId());
assertEquals(fetchedProduct.getDescription(),
fetchedUpdatedProduct.getDescription());
//verify count of products in DB
long productCount = productRepository.count();
assertEquals(productCount, 1);
//get all products, list should only have one
Iterable<Product> products = productRepository.findAll();
int count = 0;
for(Product p : products){
count++;
}
assertEquals(count, 1);
}
}
Activating Logging in Hibernate
To activate the logging of the executed SQL statements with Spring Boot, set the log level of the org.hibernate.SQL
category to DEBUG
.
If you wish to see the bind values, you can set the log level of org.hibernate.type.descriptor.sql
to TRACE
.
If you are new to logging frameworks, refer my series on Log4J2.
Here is the logging configuration in the application.properties.
application.properties:
#show sql statement
logging.level.org.hibernate.SQL=debug
#show sql values
logging.level.org.hibernate.type.descriptor.sql=trace
Here is the log output showing the SQL statements generated by Hibernate:
org.hibernate.SQL=debug
2018-02-04 22:34:46.861 DEBUG 1065 --- [ main] org.hibernate.SQL :
select product0_.id as id1_0_0_, product0_.description as descript2_0_0_, product0_.image_url as image_ur3_0_0_, product0_.price as price4_0_0_, product0_.product_id as product_5_0_0_, product0_.version as version6_0_0_ from product product0_ where product0_.id=?
org.hibernate.type.descriptor.sql=trace
2018-02-04 22:34:46.861 DEBUG 1065 --- [ main] org.hibernate.SQL :
select product0_.id as id1_0_0_, product0_.description as descript2_0_0_, product0_.image_url as image_ur3_0_0_, product0_.price as price4_0_0_, product0_.product_id as product_5_0_0_, product0_.version as version6_0_0_ from product product0_ where product0_.id=?
2018-02-04 22:34:46.862 TRACE 1065 --- [ main] o.h.type.descriptor.sql.BasicBinder :
binding parameter [1] as [INTEGER] - [1]
2018-02-04 22:34:46.862 TRACE 1065 --- [ main] o.h.type.descriptor.sql.BasicExtractor :
extracted value ([descript2_0_0_] : [VARCHAR]) - [New Description]
2018-02-04 22:34:46.863 TRACE 1065 --- [ main] o.h.type.descriptor.sql.BasicExtractor :
extracted value ([image_ur3_0_0_] : [VARCHAR]) - [https://springframework.guru/wp-content/uploads/2015/04/spring_framework_guru_shirt-rf412049699c14ba5b68bb1c09182bfa2_8nax2_512.jpg]
2018-02-04 22:34:46.863 TRACE 1065 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([price4_0_0_] : [NUMERIC]) - [18.95]
2018-02-04 22:34:46.863 TRACE 1065 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([product_5_0_0_] : [VARCHAR]) - [1234]
2018-02-04 22:34:46.863 TRACE 1065 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([version6_0_0_] : [INTEGER]) - [1]
Activating Logging With Spring Data JPA
If you are using Spring Data JPA with Hibernate as the persistence provider, add the following two lines in application.properties:
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
Here is the log output:
Hibernate:
select
product0_.id as id1_0_0_,
product0_.description as descript2_0_0_,
product0_.image_url as image_ur3_0_0_,
product0_.price as price4_0_0_,
product0_.product_id as product_5_0_0_,
product0_.version as version6_0_0_
from
product product0_
where
product0_.id=?
Conclusion
As you can see, it's very easy to enable the logging of SQL statements with Spring Boot and Hibernate.
Being able to see what Hibernate is actually doing with the database is very important.
Often, when I'm working on a Spring Boot project, I will enable the SQL output just as a sanity check. I may believe everything is okay. But I have, in fact, found problems which I was unaware of by examining the SQL output.