WEB개발/Spring
하이버네이트(Hibernate) & JPA(Java Persistence API)
wooyeon06
2024. 5. 23. 16:44
JPA (Java Persistence API)는 Java에서 관계형 데이터베이스(RDBMS)와의 상호작용을 관리하기 위한 표준 API입니다. JPA는 Java 객체를 데이터베이스의 테이블에 매핑하고, 객체-관계 매핑(ORM)을 통해 객체지향적인 방식으로 데이터베이스 작업을 수행할 수 있도록 도와줍니다.
하이버네이트(Hibernate)는 객체 관계 매핑(ORM)을 지원하는 자바 오픈 소스 프레임워크입니다. 여러 가지 메소드와 기능을 제공합니다.
=> Hibernate는 JPA 규격을 구현한 구체적인 ORM
일반적으로 다음과 같은 메소드들을 사용할 수 있습니다.
session.save(Object obj) | 객체를 데이터베이스에 저장합니다. |
session.update(Object obj) | 데이터베이스에 있는 객체를 업데이트합니다. |
session.delete(Object obj) | 데이터베이스에서 객체를 삭제합니다. |
session.get(Class clazz, Serializable id) | 주어진 클래스와 식별자에 해당하는 객체를 검색합니다. |
session.createQuery(String hql) | HQL(Hibernate Query Language)을 사용하여 쿼리를 생성합니다. |
session.createCriteria(Class clazz): | Criteria API를 사용하여 쿼리를 생성합니다. |
session.beginTransaction() | 트랜잭션을 시작합니다. |
session.getTransaction().commit(): | 트랜잭션을 커밋합니다. |
session.getTransaction().rollback() | 트랜잭션을 롤백합니다. |
session.close() | 세션을 닫습니다. |
Criteria API는 객체 지향적으로 데이터베이스 쿼리를 생성하는 방법입니다
import org.hibernate.Session; import javax.persistence.EntityManager; public class YourService { @PersistenceContext private EntityManager entityManager; public void someMethod() { // EntityManager를 사용하여 Hibernate Session을 얻음 Session session = entityManager.unwrap(Session.class); // Criteria API 사용 예시 CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder(); CriteriaQuery<YourEntity> criteriaQuery = criteriaBuilder.createQuery(YourEntity.class); Root<YourEntity> root = criteriaQuery.from(YourEntity.class); criteriaQuery.select(root).where(criteriaBuilder.equal(root.get("property"), "value")); List<YourEntity> results = session.createQuery(criteriaQuery).getResultList(); } }
위의 Criteria API 코드에 해당하는 SQL 쿼리는 다음과 같습니다
SELECT * FROM your_entity_table WHERE property = 'value';
import org.hibernate.Session;
import org.hibernate.Transaction;
public class Main {
public static void main(String[] args) {
// Hibernate 세션 가져오기
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction transaction = null;
try {
transaction = session.beginTransaction();
// 데이터베이스에서 사용자 객체 가져오기 (예를 들어, ID가 1인 사용자)
User user = session.get(User.class, 1L);
// 사용자 객체의 속성 업데이트
user.setName("Updated Name");
user.setEmail("updated@email.com");
// 업데이트된 사용자 객체를 데이터베이스에 반영
session.update(user);
transaction.commit();
System.out.println("사용자 업데이트가 성공적으로 완료되었습니다.");
} catch (Exception e) {
if (transaction != null) {
transaction.rollback();
}
e.printStackTrace();
} finally {
session.close();
}
}
}
쿼리 네이밍 규칙
Spring Data JPA는 메서드 이름을 분석하여 자동으로 쿼리를 생성하는 쿼리 네이밍 규칙을 제공합니다.
기본적인 규칙
Spring Data JPA에서 메서드 이름을 기반으로 쿼리를 생성하는 방식은 메서드 이름 규칙을 따릅니다. 이 규칙을 따르면 Spring Data JPA가 자동으로 JPQL(Java Persistence Query Language) 쿼리를 생성합니다.
메서드 이름 규칙
- findBy: 조건에 맞는 엔티티를 검색하는 메서드.
- 예시: findByName, findByEmail
- countBy: 조건에 맞는 엔티티의 개수를 반환하는 메서드.
- 예시: countByName, countByAgeGreaterThan
- existsBy: 조건에 맞는 엔티티가 존재하는지 확인하는 메서드.
- 예시: existsByEmail
- deleteBy: 조건에 맞는 엔티티를 삭제하는 메서드.
- 예시: deleteByName, deleteByAgeLessThan
- 기본 조회 (findBy)
- 메서드 이름 앞에 findBy를 붙이면 해당 속성에 대해 조회 쿼리를 자동으로 생성합니다.
- findBy<속성명> 형식으로 사용합니다.
List<User> findByName(String name); // name 속성이 일치하는 User 목록 조회
List<User> findByEmail(String email); // email 속성이 일치하는 User 목록 조회
- 복합 조건 조회 (And, Or, LessThan, GreaterThan 등)
- findBy 뒤에 속성명을 이어서 조건을 결합할 수 있습니다.
- And: 여러 조건을 AND로 결합합니다.
- Or: 여러 조건을 OR로 결합합니다.
- LessThan, GreaterThan, LessThanEqual, GreaterThanEqual: 비교 연산자.
- Between: 범위 조건.
- Like: 부분 일치를 사용한 조회.
- IsNull, IsNotNull: null 값을 조건으로 사용.
List<User> findByNameAndAgeGreaterThan(String name, int age); // name과 age가 조건에 맞는 User 목록 조회
List<User> findByAgeBetween(int startAge, int endAge); // age가 startAge와 endAge 사이인 User 목록 조회
List<User> findByEmailOrName(String email, String name); // email 또는 name이 조건에 맞는 User 목록 조회
- 정렬 및 페이지 처리 (OrderBy, Pageable)
- OrderBy를 사용하여 결과를 정렬할 수 있습니다.
List<User> findByAgeGreaterThan(int age); // age가 지정된 값보다 큰 User 목록 조회
List<User> findByEmailLike(String emailPattern); // email이 특정 패턴과 일치하는 User 목록 조회
List<User> findByEmailIsNotNull(); // email이 null이 아닌 User 목록 조회
- 집계 함수 (count, exists, delete)
- countBy, existsBy, deleteBy 등으로 집계 및 삭제 작업을 처리할 수 있습니다.
long countByName(String name); // name이 일치하는 User 수 반환
boolean existsByEmail(String email); // email이 일치하는 User가 존재하는지 확인
void deleteByName(String name); // name이 일치하는 User 삭제
- Is와 Equals
- Is와 Equals를 사용할 때는 속성 값이 null인지 여부를 체크할 수 있습니다.
List<User> findByNameIs(String name); // name이 일치하는 User 목록 조회
List<User> findByAgeIsNotNull(); // age가 null이 아닌 User 목록 조회
스프링부트 + JPA
build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
implementation("org.springframework.boot:spring-boot-starter-log4j2")
// Spring Boot Data JPA
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// MySQL Driver
runtimeOnly 'com.mysql:mysql-connector-j'
}
application.properties
spring.application.name=demo
logging.config=classpath:log4j2.xml
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/TEST?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true
spring.datasource.username=wooyeon
spring.datasource.password=wooyeon
#application.properties에서 SQL 쿼리를 로깅하도록 설정합니다.
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
java class
@ServletComponentScan
@SpringBootApplication
@EntityScan(basePackages = "com.example.demo.db") // 엔티티가 위치한 패키지를 지정
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping
public List<UserEntity> getAllUsers() {
return userService.getAllUsers();
}
}
@Service
public class UserService {
private final UserRepository userRepository;
@PersistenceContext
private EntityManager entityManager;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public List<UserEntity> getAllUsers() {
return userRepository.findAll();
}
}
@Repository
public interface UserRepository extends JpaRepository<UserEntity, Integer> {
List<UserEntity> findByUsername(String name);
// 1. Native Query 작성
@Query(value = "SELECT * FROM user WHERE name = :name", nativeQuery = true)
List<UserEntity> findUsersByNameNative(@Param("name") String name);
}
@Entity
@Table(name="USER")
@Setter
@Getter
public class UserEntity {
@Id
@Column(nullable = false, unique = true)
private int id;
@Column(nullable = false)
private String password;
@Column
private String username;
@Column
private String email;
@Column
private String role;
@Column(nullable = false)
private LocalDateTime createdAt;
}
MYSQL
CREATE TABLE USER (
id INTEGER primary KEY AUTO_INCREMENT,
password VARCHAR(255) NOT NULL,
username VARCHAR(50),
email VARCHAR(100),
role VARCHAR(50) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);