Skip to content

JPA Persistence Fundamentals (Entity Lifecycle, Queries, Multiple Databases)

Spring Boot connects using:

  1. JDBC Driver
  2. DataSource configuration
  3. Hibernate/JPA ORM
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
spring.datasource.url=jdbc:mysql://localhost:3306/testdb
spring.datasource.username=root
spring.datasource.password=1234
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
@Entity
public class Employee {
@Id
private Long id;
private String name;
}
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
}

Spring Boot automatically configures the DataSource, Hibernate, and transaction management.

Entities move through four lifecycle states.

State Description
Transient Object exists only in memory
Persistent Managed by the persistence context
Detached No longer managed by the persistence context
Removed Marked for deletion
Employee emp = new Employee(); // Transient
entityManager.persist(emp); // Persistent
entityManager.detach(emp); // Detached
entityManager.remove(emp); // Removed
stateDiagram-v2
    [*] --> Transient
    Transient --> Persistent: persist()
    Persistent --> Detached: detach()/session close
    Persistent --> Removed: remove()
    Detached --> Persistent: merge()
    Removed --> [*]
Feature save() saveAndFlush()
Database write During flush/commit Immediately
Performance Better Slightly slower
Use case Normal persistence Immediate synchronization
repository.save(employee);

The entity is saved to the persistence context and written during flush or transaction commit.

repository.saveAndFlush(employee);

The entity is immediately flushed to the database.

Feature JPQL Native SQL
Operates on Entities Tables
Database independent Yes No
Syntax Object-oriented SQL
Performance Good Sometimes faster
@Query("SELECT e FROM Employee e WHERE e.name = :name")
List<Employee> findByName(String name);
@Query(value = "SELECT * FROM employee WHERE name = ?1", nativeQuery = true)
List<Employee> findByName(String name);

Spring Data JPA generates queries automatically from repository method names.

List<Employee> findByName(String name);
List<Employee> findBySalaryGreaterThan(double salary);
List<Employee> findByNameAndDepartment(String name, String department);

Generated SQL equivalent:

SELECT * FROM employee WHERE name = ?;
Keyword Example
And findByNameAndAge
Or findByNameOrDept
Between findBySalaryBetween
Like findByNameLike
OrderBy findByAgeOrderByName

Common use cases:

  • Multi-tenant applications
  • Separate read/write databases
  • Microservices with multiple data sources
spring.datasource.primary.url=jdbc:mysql://localhost:3306/db1
spring.datasource.secondary.url=jdbc:mysql://localhost:3306/db2
@Configuration
@EnableJpaRepositories(
basePackages = "com.app.repo1",
entityManagerFactoryRef = "entityManagerFactory1",
transactionManagerRef = "transactionManager1"
)
public class DB1Config {
}

Create a similar configuration for the second database.

repo1 -> DB1
repo2 -> DB2
  • Entities move through four lifecycle states — Transient, Persistent, Detached, Removed — and understanding these explains a lot of confusing persistence-context behavior (e.g. why changes to a detached entity don’t get saved).
  • Use saveAndFlush() only when you need the write visible immediately (e.g. before a native query in the same transaction); save() is more efficient for normal use.
  • JPQL operates on entities and is database-independent; native SQL operates on tables and can be faster but ties you to a specific database.
  • Derived query methods cover most simple queries without any JPQL; reach for @Query once the method name would get unwieldy.
  • Multiple datasources require separate EntityManagerFactory/TransactionManager beans per database, with repositories split by base package.