Configuration, JPA data access and profiles
Layered properties, environment-specific profiles, and a repository layer with the traps called out.
Configuration and profiles
# src/main/resources/application.properties
spring.application.name=demo
server.port=8080
spring.jpa.open-in-view=false
app.greeting=Hello# src/main/resources/application.yml
spring:
datasource:
url: jdbc:postgresql://localhost:5432/demo
username: demo
password: ${DB_PASSWORD} # read from the environment, never committed
jpa:
hibernate:
ddl-auto: validate@Component
@ConfigurationProperties(prefix = "app")
class AppProperties {
private String greeting = "";
// getters and setters
}
@Configuration
@Profile("prod")
class ProdCacheConfig {
// only active when the prod profile is on
}- Resolution order, later wins:
application.properties, then the profile-specific file, then environment variables, then command-line arguments. - Placeholders read other properties or environment variables, which keeps secrets out of the repository. Inject real credentials at deploy time.
- Profiles select a set of configuration; they do not change code by themselves, so keep the differences declarative.
- Activate with
--spring.profiles.active=prod, theSPRING_PROFILES_ACTIVEenvironment variable, orspring.profiles.activein the base file.
Data access with Spring Data JPA
@Entity
@Table(name = "users")
class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String email;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Order> orders = new ArrayList<>();
// getters and setters omitted
}
interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmailIgnoreCase(String email);
@Query("select u from User u join fetch u.orders where u.id = :id")
Optional<User> findWithOrders(long id);
}| Concern | What to do |
|---|---|
| Connection pool | HikariCP is the default; size it to the database, not to request volume |
| Schema changes | Flyway or Liquibase in production; never rely on ddl-auto=update |
| N+1 selects | Use a fetch join or @EntityGraph for collections you actually render |
| Lazy loading | Fetch inside the service, or project to a DTO, rather than touching relations in a template |
| Transactions | Annotate the service method, and mark read-only queries as such |
| Pagination | Return Page<T> from the repository instead of loading every row |
⚠️
spring.jpa.hibernate.ddl-auto=update is convenient in development and dangerous in production: it never drops columns, and there is no review or rollback. Use a migration tool and set validate in production.FAQ
Do I need a database running to start the app?
No. Add H2 with runtime scope and Spring Boot auto-configures an in-memory datasource. The same repository code then works against PostgreSQL once you supply its driver and URL.
What does open-in-view do?
It keeps the Hibernate session open for the whole request so lazy loading works during serialisation. That convenience hides N+1 queries, so most services disable it and fetch explicitly.
Related
REST controllers and dependency injection Project setup and your first controller
Last refreshed 2026-09-18.