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) 쿼리를 생성합니다.

 

메서드 이름 규칙

  1. findBy: 조건에 맞는 엔티티를 검색하는 메서드.
    • 예시: findByName, findByEmail
  2. countBy: 조건에 맞는 엔티티의 개수를 반환하는 메서드.
    • 예시: countByName, countByAgeGreaterThan
  3. existsBy: 조건에 맞는 엔티티가 존재하는지 확인하는 메서드.
    • 예시: existsByEmail
  4. 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
);