0. SQL Database
Spring Framework는 광범위한 SQL Database 와의 작업을 지원합니다.
- JdbcTemplate : JDBC 기술을 사용하여 기존의 코드를 단순화화고, 오류 처리 및 리소스 관리를 개선한 헬퍼 클래스
- Hibernate : JPA 기반 ORM 기술
Spring Data JPA는 Repository 인터페이스를 제공합/니다.
- Repository 인터페이스 구현을 통해 Entity 관련 Table과 직접적으로 통신할 수 있습니다.
- Repository 인터페이스가 제공하는 메서드 이름의 컨벤션으로 메서드를 생성하면 자동으로 관련 쿼리를 생성하여 통신합니다.
1. DataSource
항목 | 설명 |
인터페이스 |
javax.sql.DataSource
|
기능 |
Database Connection 생성 및 관리
|
연결 유형 |
일반 연결, 풀링된 연결, 분산 연결 등
|
사용 정보 |
URL, Credentials (username/password)
|
DataSource Configuration
항목 | 설명 |
설정 방식 |
spring.datasource.* 외부 설정 프로퍼티 사용
|
Driver 지정 |
spring.datasource.driver-class-name에 벤더별 FQCN 지정
|
설정) DataSource (application.yml)
더보기
spring:
datasource:
url: jdbc:mysql://localhost:3306/springboot_document
username: root
password: password
driver-class-name: com.mysql.jdbc.Driver
Supported Connection Pools
HikariCP
항목 | 설명 |
고성능 |
타 커넥션 풀보다 빠르고 효율적 (Tomcat, DBCP 등)
|
경량화 |
내부 코드가 심플
최소한의 기능으로 구성됨 |
빠른 응답 속도 |
짧은 커넥션 생성 시간
낮은 오버헤드 |
자동 튜닝 |
대부분의 설정이 자동 최적화됨
|
Spring Boot 통합 |
spring-boot-starter-jdbc 또는 data-jpa 의존성 추가 시, 자동 설정됨
|
설정) HikariCP (application.yml)
더보기
spring:
datasource:
url: jdbc:mysql://localhost:3306/springboot_document
username: root
password: password
driver-class-name: com.mysql.jdbc.Driver
type: com.zaxxer.hikari.HikariDataSource
hikari:
minimum-idle: 5
maximum-pool-size: 20
idle-timeout: 30000
pool-name: MyHikariCP
max-lifetime: 1800000
connection-timeout: 30000
auto-commit: true
설정 항목 | 한 줄 요약 | 상세 설명 |
minimum-idle | 최소 유지 커넥션 수 |
커넥션 풀이 유휴 상태일 때 유지할 최소 커넥션 수입니다.
성능을 위해 일정 수를 유지함. 기본값: 10 |
maximum-pool-size | 최대 커넥션 수 |
동시에 사용할 수 있는 최대 커넥션 수입니다.
서버 트래픽/성능 요구에 따라 조정. 기본값: 10 |
idle-timeout | 유휴 커넥션 종료 시간 |
커넥션이 유휴 상태로 유지될 수 있는 최대 시간(ms).
이후 커넥션은 종료됩니다. 기본값: 600000 (10분) |
pool-name | 커넥션 풀 이름 |
모니터링/디버깅용 커넥션 풀 이름입니다.
명확한 식별이 가능하게 합니다. |
max-lifetime | 커넥션의 최대 생존 시간 |
커넥션이 생성된 이후 유지될 수 있는 최대 시간(ms).
이 시간이 지나면 커넥션은 종료되고 새로 생성됩니다. 기본값: 1800000 (30분) |
connection-timeout | 커넥션 획득 대기 시간 |
커넥션 풀이 가득 찼을 때, 커넥션을 얻기 위해 기다릴 수 있는 최대 시간(ms).
초과 시 예외 발생. 기본값: 30000 (30초) |
auto-commit | 자동 커밋 여부 |
쿼리 실행 후 자동으로 커밋할지 여부.
일반적으로 true이며, 트랜잭션 제어 시 false로 설정합니다. |
Embedded Database Support
항목 | 설명 |
지원 DB |
H2, HSQL, Derby (in-memory)
|
자동 설정 조건 |
classpath에 관련 의존성 존재 시
|
Connection URL | 자동 설정됨 |
선택 옵션 |
spring.datasource.embedded-database-connection 프로퍼티로 지정 가능
|
2. Using JdbcTemplate
- Spring Boot는 JdbcTemplate와 NamedParameterJdbcTemplate 클래스를 자동설정합니다.
- 빈을 주입받아 사용할 수 있습니다.
3. JPA and Spring Data JPA
- JPA는 Java Persistence API의 약자로 Object를 RDB의 테이블과 매핑을 도와주는 자바 핵심 명세입니다.
spring-boot-starter-data-jpa
- spring boot에서 JPA 기반 ORM을 사용하는데 필요한 의존성을 제공하는 모듈
구성요소 | 설명 |
Hibernate | JPA 표준 명세를 구현하는 라이브러리 (가장 유명함) |
Spring ORM |
Spring에서 ORM 지원을 위한 모듈
|
Spring Data JPA |
JPA 기반 Repository 구현을 자동화해주는 모듈
|
Entity Scanning
항목 | 설명 |
Entity 정의 |
@Entity, @Embeddable, @MappedSuperclass
|
Entity 스캔 방식 |
@SpringBootApplication 아래의 패키지를 자동 스캔
|
기존 방식 |
persistence.xml 사용 (Spring Boot에서는 필요 없음)
|
예제) @Entity
더보기
create table user (
id int auto_increment primary key,
name varchar(10)
);
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // AUTO_INCREMENT에 해당
private int id;
private String name;
public User() {}
public User(String name) { this.name = name; }
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
JPA 초기화 도구
항목 | SmartInitializingSingleton | ObjectProvider |
설명 | 모든 빈이 초기화된 후 실행되는 콜백 인터페이스 |
지연 의존성 주입 도구
|
목적 | 모든 빈 초기화 후 후처리 |
특정 빈의 지연 주입
조건적 사용 |
실행 시점 | 모든 싱글턴 빈 초기화 후 1회 실행 |
필요 시점에 사용자가 직접 호출
|
예외 상황 | 모든 빈이 있어야 안전 |
빈이 없어도 안전하게 처리 가능
|
대표 사용처 | 초기 데이터 세팅, JPA 초기화 등 |
선택적 기능 로딩, 순환 참조 회피 등
|
예제) SmartInitializingSingleton
더보기
@Component
public class DataInitializer implements SmartInitializingSingleton {
private final UserRepository userRepository;
public DataInitializer(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public void afterSingletonsInstantiated() {
// 모든 빈이 초기화된 후 실행
if (userRepository.count() == 0) {
userRepository.save(new User("admin", "admin@example.com"));
System.out.println("초기 admin 계정을 생성했습니다.");
}
}
}
예제) ObjectProvider
더보기
@Component
public class ReportService {
private final ObjectProvider<EmailService> emailServiceProvider;
public ReportService(ObjectProvider<EmailService> emailServiceProvider) {
this.emailServiceProvider = emailServiceProvider;
}
public void sendReport() {
EmailService emailService = emailServiceProvider.getIfAvailable();
if (emailService != null) {
emailService.send("admin@example.com", "Daily Report");
} else {
System.out.println("EmailService가 없어 보고서를 전송하지 않습니다.");
}
}
}
Bootstrapping mode
- Spring Data JPA Repository를 초기화하는 시점을 설정하는 옵션
- spring.data.jpa.repositories.bootstrap-mode에서 설정합니다.
모드 | 설명 | 초기화 시점 | 비동기 여부 | 예시 |
default | 기본 동기 초기화 | 애플리케이션 컨텍스트 초기화 시 바로 Repository 생성됨 | ❌ | 대부분의 경우 사용되는 기본값 |
deferred | 비동기 초기화 | ApplicationContext 초기화 완료 이후에 백그라운드에서 초기화 | ✅ |
부트 시간이 긴 Repository가 많은 경우, 빠른 기동을 원할 때 사용
예: Admin 기능용 Repository만 30개 있어도 사용자 기능과 무관한 경우 |
lazy | 지연 초기화 | 최초 요청 시점에 초기화 | ✅ |
정말 필요한 시점까지 객체 생성/연결 지연
예: 특수 API에서만 쓰이는 Repository를 매번 안 띄우고 싶을 때 |
- 비동기 초기화 사용 시 AsyncTaskExecutor가 필요함 (기본 이름은 applicationTaskExecutor)
예제) deferred
더보기
spring:
data:
jpa:
repositories:
bootstrap-mode: deferred
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
}
public HelloController(ObjectProvider<UserRepository> userRepositoryObjectProvider) {
this.userRepositoryObjectProvider = userRepositoryObjectProvider;
System.out.println("✅ HelloController 생성자 호출됨");
}
@Component
public class RepositoryLogger implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (beanName.contains("userRepository")) System.out.println("🟡 Repository 초기화됨: " + beanName + " (" + bean.getClass().getName() + ")");
return bean;
}
}
✅ HelloController 생성자 호출됨
🟡 Repository 초기화됨: userRepository (org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean)
예제) lazy
더보기
spring:
data:
jpa:
repositories:
bootstrap-mode: lazy
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
}
public HelloController(ObjectProvider<UserRepository> userRepositoryObjectProvider) {
this.userRepositoryObjectProvider = userRepositoryObjectProvider;
System.out.println("✅ HelloController 생성자 호출됨");
}
@GetMapping("/bootstrap-mode")
public ResponseEntity<Long> bootstrapMode() {
return ResponseEntity.ok().body(userRepositoryObjectProvider.getObject().findAll().stream().count());
}
@Component
public class RepositoryLogger implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (beanName.contains("userRepository")) System.out.println("🟡 Repository 초기화됨: " + beanName + " (" + bean.getClass().getName() + ")");
return bean;
}
}
✅ HelloController 생성자 호출됨
------------------------------------------------------------------------
(/bootstrap-mode 호출 시)
🟡 Repository 초기화됨: userRepository (org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean)
Creating and Dropping JPA Databases
설정 | 설명 |
spring.jpa.hibernate.ddl-auto |
Hibernate의 hbm2ddl.auto 속성
(ex. none, validate, update, create, create-drop) |
spring.jpa.generate-ddl |
자동 DDL 생성 여부
- Embedded DB일 경우만 자동으로 생성됨 (기본값: 설정 X) - ApplicationContext 초기화 완료 시 DDL이 실행됨 |
Open EntityManager in View (OEMIV)
- HTTP 요청-응답 수명 주기 전체에서 EntityManager를 열어두는 패턴
- 일반적으로 서비스 계층에서 트랜잭션이 종료되고 데이터베이스 연결이 반환된 후에 EntityManager가 닫히게 됩니다.
- 이 시점에 지연 로딩을 시도하면 LazyInitializationException이 발생합니다.
항목 | 설명 |
목적 |
View 렌더링 단계까지 EntityManager의 Lazy 로딩을 가능하게 함
|
장점 | HTTP 응답을 생성하는 동안 지연 로딩(Lazy Loading)을 사용할 수 있게 됨 |
단점 |
- 필요 없는 쿼리 실행 가능성 있음 → 성능 저하
- 트랜잭션이 닫힌 후 데이터 로딩 → 일관성 문제 발생 가능 |
기본 동작 |
OpenEntityManagerInViewInterceptor 자동 등록됨
|
설정 |
spring.jpa.open-in-view=true
|
예제) LazyInitializationException 발생
더보기
spring:
jpa:
open-in-view: false
@Controller
@RequiredArgsConstructor
public class ViewController {
private final TeamRepository teamRepository;
@GetMapping("/teams/{id}")
public String getTeam(@PathVariable Integer id, Model model) {
model.addAttribute("team", teamRepository.findById(id).get());
return "team-view";
}
}
<!-- src/main/resources/templates/team-view.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Hello Thymeleaf</title>
</head>
<body>
<ul>
<li th:each="member : ${team.members}">
<span th:text="${member.name}">회원명</span>
</li>
</ul>
</body>
</html>
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: org.example.jpa.Team.members: could not initialize proxy - no Session
참고
'Spring > Spring Boot' 카테고리의 다른 글
[Spring Boot][Test] 6. Test (0) | 2025.04.21 |
---|---|
[Spring Boot] 5. Executable Jars (0) | 2023.10.13 |
[Spring Boot] 3. Web: Servlet Web Application (0) | 2023.10.12 |
[Spring Boot] 2-10. Core Features: Auto-Configuration (0) | 2023.10.11 |
[Spring Boot] 2-6. Core Features: JSON (0) | 2023.10.09 |