Skip to content
  • not suitable driver found error
  • authentication failed error
  • Class.forName("driver) - is not not required any more
  • Entity -> which we map to table
  • DTO (Data Transfer Object) -> object which goes in response
  • , schema is same as Entity but they are not linked to DB / Tables,
  • FATAL: database "springdemo" does not exist => if database doesn't exist
  • dialect
  • spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect => # Hibernate has clreated dialect classes for database that wraps drivers
  • Is it necessary?
    • 👉 In modern Spring Boot (2.x and 3.x): usually NO
    • Spring Boot can auto-detect the dialect from your datasource: spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
    • When you SHOULD specify it
    • You may want to set it explicitly if:
    • You face dialect-related errors
    • You’re using a custom or older database version

Create-drop means create the databse schema when app starts and delete on shutdown

spring.jpa.hibernate.ddl-auto=create-drop

Hibernate has clreated dialect classes for database that wraps drivers

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

defer Spring's data.sql execution until after JPA/Hibernate has created the schema

spring.jpa.defer-datasource-initialization=true

add below if you use other file name than import.sql

where to find the data file (optional if you name it data.sql in resources)

spring.sql.init.data-locations=classpath:/data.sql

show queries generated by hibernate in console

spring.jpa.show-sql=true

ensure Spring runs SQL initialization even for non-embedded DBs

spring.sql.init.mode=always

Format the sql query on console

spring.jpa.properties.hibernate.format_sql=true

hibernate

  • create configuratoin -> hibernate.cfg.xml
  • build sessionFactory
  • open session from sessionFactory
  • sessionFactoryo.beginTransaction() [this is not required for read/fetching data , its required only for create, update and delete]
  • session.persist(obj)
  • transation.commit()
  • session.close()
  • sf.close()
  • Error
  • not able to locate persiter

Annotations

  • @Entity // Map this class as a table in the database
  • entity should have public or protected no-arg constructor
  • private construtor is not accessible to hibernate
  • it should have => fields, no arg constructor, constructor that accepts all fields, getter and setter and toString method
  • we injects Entity manager in Dao/service using auto wiring /injection
  • @Entity Manager => use when you require low level control and flexibility
  • @JPARepository => use when you require high level abstraction
  • @Repository
  • Marks a class as a DAO/Data Access Layer component.
  • Used for classes that interact with the database.
  • Enables automatic exception translation (converts DB exceptions into Spring exceptions
  • Makes the class eligible for component scanning and dependency injection using @Autowired
  • @Transactional
  • Manages database transactions automatically.
  • Commits all changes if execution is successful.
  • Rolls back changes if an exception/error occurs.
  • Ensures data consistency and integrity during multiple DB operations.

  • JPA entity manager requires data source
  • Data source defines database connection info
  • Data source and JPA Entity Manager are automatically created by spring boot at startup

# HikariCP Quick Notes

## Default Values

```properties
maximumPoolSize = 10
minimumIdle = 10

So by default, Hikari usually creates 10 DB connections at startup.


Key Concepts

maximumPoolSize

spring.datasource.hikari.maximum-pool-size=10
  • Max total DB connections allowed

  • Shared across entire application


minimumIdle

Controls how many idle connections stay ready.

Example:

spring.datasource.hikari.minimum-idle=1

Then:

  • Startup creates 1 connection

  • More connections are created dynamically when load increases

  • Pool can grow until maximumPoolSize


Dynamic Growth Example

minimumIdle=1
maximumPoolSize=10

Flow:

  1. Startup → 1 connection

  2. More concurrent requests → new connections created

  3. Stops at 10 connections

  4. Extra requests wait for free connection


Important

connection.close();

does NOT close DB connection.

It returns connection back to the pool.


Debug Logs

logging.level.com.zaxxer.hikari=DEBUG

Shows actual connection creation and pool activity.

Q. Why must I use Integer instead of int in JpaRepository?

public class PokemonRepository extends JpaRepository { here why Integer why not int

In Java Generics, you cannot use primitive data types like int. Generics only accept objects, which is why you must use the wrapper class Integer.

Here is a breakdown of exactly why JpaRepository enforces this rule:

1. Primitive vs. Object Rule

Java primitives (like int, long, boolean) are designed for raw performance and memory efficiency. They do not inherit from the base Object class.

Because Java Generics (the <> brackets) are checked at compile-time and converted to Object references under the hood, they require objects. Integer is simply the object wrapper for a primitive int.

2. The Null Value Requirement

Database primary keys can sometimes be empty or null (for example, before a new entity is saved and its ID is auto-generated by PostgreSQL).

  • An int cannot be null. Its default value is always 0. If Spring Data JPA tries to process a new Pokemon without an ID yet, an int would cause errors.
  • An Integer can be null, which perfectly represents a missing or unassigned database ID.

* * * * *

Here is a structured, interview-ready Question & Answer guide based on the troubleshooting journey we just completed. These are highly common, real-world questions asked during Spring Boot and Hibernate/JPA technical interviews.

Q1: What does the exception ObjectOptimisticLockingFailureException (or StaleObjectStateException) mean in Spring Data JPA?

A: This exception indicates that a data concurrency conflict occurred. Hibernate expected to update or delete a specific row in the database, but when it executed the SQL statement, zero rows were modified. Hibernate interprets this as a sign that the target database row was either deleted or modified by another concurrent transaction out from under the current session.

Q2: We encountered this exception even though our entity did not have a @Version field. How can a missing @Version annotation still trigger an optimistic locking failure?

A: If an entity does not have a @Version property, Hibernate throws this exception when you attempt to update a detached entity reference that does not exist in the database or matches incorrectly. When you pass an entity with a pre-set ID (e.g., id = 1) to repository.save(), Hibernate executes a SQL UPDATE statement. If that record cannot be found in the target table (due to manual tracking issues, bad IDs, or a primitive mismatch), the database reports that zero rows were updated. Hibernate detects this zero-row change and immediately throws the locking failure exception.


Q3: Why is it bad practice to use a primitive int for an ID field in a JPA entity, and how does switching to wrapper Integer prevent database issues?

A:

  • Primitive int: It cannot hold a null value; its default state is always 0. If you try to save a brand new entity, Hibernate sees id = 0, assumes it is an existing record, and attempts a SQL UPDATE instead of an INSERT. This fails or overwrites incorrect data.
  • Wrapper Integer: It defaults to null. When Hibernate detects a null ID, it knows with 100% certainty that the entity is new and cleanly executes a SQL INSERT statement, allowing PostgreSQL to auto-generate the identity sequence correctly.

Q4: How do you properly implement an update operation in a Spring Data JPA service layer to completely avoid detached entity state exceptions?

A: You should follow the Fetch-and-Update pattern within a managed transaction instead of passing a raw request payload directly into the save method. The exact interview-ready code sample:

@Transactionalpublic Pokemon updatePokemon(Integer id, Pokemon incomingData) { // 1. Fetch the managed entity from the DB to attach it to the current Hibernate context Pokemon existingPokemon = pokemonRepository.findById(id) .orElseThrow(() -> new ResourceNotFoundException("Pokemon not found"));

// 2. Map the updated fields onto the attached entity
existingPokemon.setName(incomingData.getName());
existingPokemon.setType(incomingData.getType());

// 3. Save the managed entity back safely
return pokemonRepository.save(existingPokemon);

}


Q5: If a client explicitly passes an "id": 1 in the JSON body of a POST request meant for creating data, how does Hibernate react, and how do you protect against it?

A: Hibernate will misinterpret the creation request as a database update because an explicit ID is present. It will attempt to update a row with ID 1 instead of creating a new row. To protect the system, you must explicitly nullify the incoming ID in your service layer logic right before saving:

public Pokemon createPokemon(Pokemon newPokemon) { newPokemon.setId(null); // Overrides any malicious or accidental client input return pokemonRepository.save(newPokemon); // Guarantees a SQL INSERT statement }


Are there any other Spring Boot architecture or Hibernate transaction topics you would like to prepare for your interview?


In Spring Boot, .build() is used because ResponseEntity.noContent() returns a Builder object, not a finished ResponseEntity. You must call .build() to finalise the configuration and generate the actual ResponseEntity object that Spring Boot can send back to the client.


The Mechanics: Why can't we just return ResponseEntity.noContent()?

When you return data (like a 200 OK with a Pokemon object), you pass the data directly into the body method like this:

return ResponseEntity.ok(myPokemonObject); // Returns a complete ResponseEntity

However, a 204 No Content response cannot have a body by HTTP definition. Since there is no data to pass into a body method to close the chain, Spring uses the Builder Pattern.

  • ResponseEntity.noContent() sets the status code to 204 and returns a HeadersBuilder.
  • .build() closes the builder chain and creates the immutable ResponseEntity instance with an empty body.

Interview-Ready Q&A Format

Q: What is the purpose of the .build() method when returning a ResponseEntity in a Spring Controller? A: The .build() method is an implementation of the Builder Design Pattern. Methods like noContent(), badRequest(), or notFound() return intermediate builder interfaces (like HeadersBuilder or BodyBuilder) instead of a completed response object. Calling .build() is mandatory to execute the final assembly step, converting that builder configuration into a concrete, immutable ResponseEntity instance ready to be transmitted over HTTP.


Would you like to review how to use this pattern to cleanly return a 404 Not Found status when a database lookup fails?

------

------

------

------

------

------

------