🌈Spring Boot JPA + Lombok 엔티티 매핑 실습
(자동 테이블 생성, CRUD)
🌈1. DB 설정 + JPA 설정(application.properties)
application.properties 에 작성
// DB연결 설정
spring.datasource.url=jdbc:oracle:thin:@localhost:1521:orcl
spring.datasource.username=sqlid
spring.datasource.password=sqlpw
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
🔽 실행시 테이블 새로 자동생성
// JPA 테이블 생성
spring.jpa.hibernate.ddl-auto=create
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
🔽 테이블을 시작할때마다 재생성을 안하고 업데이트 하기위함
➡️ 재생성을 안할시 create를 지우고 update로 수정
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
JPA 설정
- ddl-auto=create → 실행 시 테이블을 새로 자동 생성 (데이터 초기화됨)
- show_sql=true → 실행되는 SQL 문장을 콘솔에서 확인 가능
- format_sql=true → SQL 문장을 보기 좋게 줄바꿈/들여쓰기 출력
🌈2. 파일 경로 및 설정

🌈3. 클래스 설정
Lombok 어노테이션 |
|
| @NoArgsConstructor | 매개변수가 없는 기본 생성자 자동 생성 |
| @AllArgsConstructor | 모든 필드를 매개변수로 받는 전체 생성자 자동 생성 |
JPA 어노테이션 |
|
| @Entity | 클래스 이름을 보고 테이블 자동생성 |
| @Id | PK(Primary Key) 지정. @Entity가 붙은 클래스에는 반드시 하나 필요 |
| @Table(name = 'e_test' ) | 테이블 이름 새로 지정 |
| @Column(name = "irum", nullable = false) | 매핑될 컬럼 이름 및 제약조건 지정 (컬럼 이름: irum, not null) |
🗂️entity
➡️📁EntityTest.java
package com.example.Ex02.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
@NoArgsConstructor // 매개변수가 없는 생성자
@AllArgsConstructor // 매개변수가 모두 있는 생성자
@Table(name = "eTest") // 테이블 이름 새로 지정
@Entity // 클래스 이름을 보고 테이블 자동생성
public class EntityTest {
@Id // PK(Primary Key) 지정 @Entity가 붙은 클래스에는 반드시 하나 필요
private int num;
@Column(name = "irum", nullable = false)
private String name;
private String addr;
private int age;
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
🌈4. 인터페이스 설정
package com.example.Ex02.repository;
import com.example.Ex02.entity.EntityTest;
import org.springframework.data.jpa.repository.JpaRepository;
// JpaRepository<엔티티 클래스, 프라이머리키(PK) 타입>
// EntityTest 엔티티를 관리하고, PK 타입은 int 이므로 Integer 사용
public interface EntityRepository extends JpaRepository<EntityTest, Integer> {
}
✅ 특징
- JpaRepository<EntityTest, Integer>
- 첫 번째 제네릭: 엔티티 클래스 (EntityTest)
- 두 번째 제네릭: PK 타입 (int → Integer)
- JpaRepository 를 상속했기 때문에 CRUD 기능이 자동으로 제공
- JpaRepository<T, ID> 는 CrudRepository<T, ID> 와 PagingAndSortingRepository<T, ID> 를 상속
- 그래서 save, findById, findAll, deleteById 같은 CRUD 메서드가 기본 내장
🌈5. EntityTest 엔티티를 저장(insert) - Save()
@SpringBootTest가 import가 안될때
더보기
더보기
spring-boot-starter-test 에서 scope주석 처리
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<!--<scope>test</scope>-->
</dependency>
@SpringBootTest
public class EntityRepositoryTest {
@Autowired
EntityRepository entityRepository;
@Test
@DisplayName("entity 테스트")
public void entitySave(){
EntityTest etest = new EntityTest();
etest.setNum(100);
etest.setName("ljr");
etest.setAddr("경기");
etest.setAge(20);
entityRepository.save(etest);
}
}
🔽실행방법 (메서드만 실행)
1. 왼쪽 메서드 실행 버튼

2. 마우스 우클릭 후 Run 'entitySave()'

➡️JpaRepository 의 save() 메서드는 반드시 엔티티(Entity) 객체만 받을 수 있습니다.

🌈6. EntityTest 엔티티를 조회(select) - findAll() / findByName()
6-1) 전체 조회 findAll()
- toString으로 출력시 dto에서 toString 오버라이딩
@Test
@DisplayName("10개 레코드 삽입")
public void e_testSave(){
String[] irum = {"윤아", "아이유", "웬디", "민호"};
String[] addr = {"서울", "제주", "부산"};
int[] age = {10, 20, 30, 40};
for(int i=1; i<=10; i++){
EntityTest etest = new EntityTest();
etest.setNum(i);
etest.setName(irum[i % irum.length]); // 1 2 3 0 1 2 나눠서 나머지 결과값으로 반복작업
etest.setAddr(addr[i % addr.length]);
etest.setAge(age[i % age.length]);
entityRepository.save(etest);
}
}
@Test
@DisplayName("전체 레코드 조회")
public void findAllTest(){
List<EntityTest> elists = entityRepository.findAll(); //전체 레코드 조회 : List 리턴
for(EntityTest entity :elists){
/* System.out.println(entity.getNum());
System.out.println(entity.getName());
System.out.println(entity.getAddr());
System.out.println(entity.getAge());*/
System.out.println(entity.toString()); // 메서드를 오버라이딩 하지않을시 => 주소출력, dto에서 오버라이딩
System.out.println("================");
}
}
6-2) 특정 레코드 조회 - findByName()
@Test
@DisplayName("이름이 '윤아'인 레코드 조회")
public void findName() {
List<EntityTest> elists = entityRepository.findByName("윤아");
for(EntityTest entity : elists){
System.out.println(entity.toString());
}
}
@Test
@DisplayName("이름에 '수'가 포함된 이름 조회")
public void findNameFind(){
List<CompanyEntity> lists = companyRepository.findByNameContaining("수");
for(CompanyEntity cEntity : lists) {
System.out.println(cEntity.toString());
}
}
@Test
@DisplayName("이름에 '수'가 포함된 이름 조회 & 회사명 내림차순 조회")
public void findNameAndCompanyFind(){
List<CompanyEntity> lists = companyRepository.findByNameContainingOrderByCompanyDesc("수");
for(CompanyEntity cEntity : lists) {
System.out.println(cEntity.toString());
}
}
@Test
@DisplayName("나이가 30인 레코드 조회")
public void findAge() {
List<EntityTest> elists = entityRepository.findByAge(30);
for(EntityTest entity : elists){
System.out.println(entity.toString());
}
}
@Test
@DisplayName("나이가 30 초과인 레코드 조회")
public void findAgeOver() {
List<EntityTest> elists = entityRepository.findByAgeGreaterThan(30);
for(EntityTest entity : elists){
System.out.println(entity.toString());
}
}
@Test
@DisplayName("나이가 30 이상인 레코드 조회")
public void findAgeEqual() {
List<EntityTest> elists = entityRepository.findByAgeGreaterThanEqual(30);
for(EntityTest entity : elists){
System.out.println(entity.toString());
}
}
인터페이스
// JpaRepository<EntityTest, Integer>
// → 첫 번째 제네릭(EntityTest): 엔티티 클래스
// → 두 번째 제네릭(Integer): 프라이머리키(기본키) 타입
public interface EntityRepository extends JpaRepository<EntityTest, Integer> {
// 1. name 컬럼이 특정 값과 일치하는 레코드 조회
List<EntityTest> findByName(String name);
// 2. age 컬럼이 특정 값과 일치하는 레코드 조회
List<EntityTest> findByAge(int i);
// 3. age 컬럼이 특정 값보다 큰 레코드 조회 (>)
List<EntityTest> findByAgeGreaterThan(int i);
// 4. age 컬럼이 특정 값 이상인 레코드 조회 (>=)
List<EntityTest> findByAgeGreaterThanEqual(int i);
List<CompanyEntity> findBySalaryGreaterThanEqual(int i);
List<CompanyEntity> findByNameContaining(String 수);
List<CompanyEntity> findByNameContainingOrderByCompanyDesc(String 수);
}
- 메서드 네이밍 규칙 (쿼리 메서드)
- findBy필드명 → 해당 필드 조건으로 조회
- findBy필드명And필드명 → 여러 조건 조회
- findBy필드명GreaterThan → > 조건
- findBy필드명GreaterThanEqual → >= 조건
- findBy필드명LessThan, Between, Like, In 등도 가능
- 자동으로 JPQL 생성됨
- 예) findByName("윤아")
→ 실행 SQL: select * from entity_test where name = '윤아'; - 예) findByAgeGreaterThan(20)
→ 실행 SQL: select * from entity_test where age > 20;
- 예) findByName("윤아")
6-3) id조회 (primarykey)
@Test
@DisplayName("id 조회")
// PK가 num인 경우 findById()와 findByNum()은 결과가 동일하다. 하지만 findById()는 PK 전용
public void getId(){
Optional<ItemEntity> o = itemRepository.findById(50L);
System.out.println("o" + o);
}
➡️findById()는 Optional을 리턴
➡️PK가 Long 타입 이므로 findById(50L) 메서드 안 숫자에 'L' 기입
6-4) 가격 조회(primarykey)
@Test
@DisplayName("6. price조회")
// price 300보다 작은 레코드를 가격기준 내림차순 정렬 조회 출력
// Query method
public void getPrice(){
List<ItemEntity>lists = itemRepository.findByPriceLessThanOrderByPriceDesc(300);
for (ItemEntity item:lists){
System.out.println(item);
}
}
@Test
@DisplayName("7. price조회")
// price 300큰 레코드를 가격기준 오름차순 정렬 조회 출력
// Query method
public void getPrice2(){
List<ItemEntity>lists = itemRepository.findByPriceGreaterThanOrderByPriceAsc(300);
for (ItemEntity item:lists){
System.out.println(item);
}
}
인터페이스
public interface ItemRepository extends JpaRepository<ItemEntity, Long> {
List<ItemEntity> findByItemNm(String ss);
List<ItemEntity> findByPriceLessThanOrderByPriceDesc(int i);
List<ItemEntity> findByPriceGreaterThanOrderByPriceAsc(int i);
}
🌈7. 쿼리 어노테이션 (JPQL)
//쿼리 어노테이션
@Test
@DisplayName("Query annotation으로 전체레코드 조회")
public void allSelect(){
List<CompanyEntity> lists = companyRepository.findAllQuery();
for(CompanyEntity cEntity : lists) {
System.out.println(cEntity.toString());
}
}
@Test
@DisplayName("전체 레코드 개수조회")
public void countCompany(){
int count = companyRepository.countCompany();
System.out.println("➡️count :" + count);
}
@Test
@DisplayName("이름이 '수지'인 레코드 조회")
public void findByNameSuji(){
List<CompanyEntity> lists = companyRepository.findByNameSuji("수지");
for(CompanyEntity cEntity : lists) {
System.out.println(cEntity.toString());
}
}
@Test
@DisplayName("이름에 '수'가 포함된 레코드 조회")
public void findByNameSujiContainQuery(){
List<CompanyEntity> lists = companyRepository.findNameContainSu("수");
for(CompanyEntity cEntity : lists) {
System.out.println(cEntity.toString());
}
}
@Test
@DisplayName("상세설명에 '어' 포함된 레코드 조회")
public void ItemDetailFind(){
List<ItemEntity>lists = itemRepository.findByItemDetail("어");
for (ItemEntity item:lists){
System.out.println(item);
}
}
@Test
@DisplayName("상세설명에 '어' 포함 & 가격이 300이상 레코드 조회")
public void ItemDetailPriceFind(){
List<ItemEntity>lists = itemRepository.findByItemDetailPrice("어", 300);
for (ItemEntity item:lists){
System.out.println(item);
}
}
@Test
@DisplayName("7. 레코드 삭제")
public void deleteId(){
itemRepository.deleteById(1L);
}
인터페이스
nativeQuery = true : 테이블명 작성
nativeQuery = false : 클래스명 작성
package com.example.Ex02.repository;
import com.example.Ex02.entity.CompanyEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
import java.util.Optional;
public interface CompanyRepository extends JpaRepository<CompanyEntity, Integer> {
// 1. 전체 조회
/*@Query(value = "select * from company_entity", nativeQuery = true)*/
@Query(value = "select i from CompanyEntity i", nativeQuery = false)
List<CompanyEntity> findAllQuery();
// 2. 레코드 개수 조회
@Query("select count(company) from CompanyEntity company")
int countCompany();
// 3. 이름이 '수지'인 사람 조회
@Query("SELECT i FROM CompanyEntity i WHERE i.name = :name")
List<CompanyEntity> findByNameSuji(@Param("name") String name);
// 4. 이름에 '수'가 포함된 사람조회 & 회사이름 내림차순
@Query("SELECT i FROM CompanyEntity i WHERE i.name LIKE %:name% order by company desc")
List<CompanyEntity> findNameContainSu(@Param("name") String name);
// 5. 상세설명에 '어'들어간 레코드 찾기
@Query(value = "select i from ItemEntity i where i.itemDetail like %:itemDetail%")
List<ItemEntity> findByItemDetail(@Param("itemDetail")String itemDetail);
// 6. 상세설명에 '어'포함 & price가 300이상인 레코드 조회
@Query(value = "select i from ItemEntity i where i.itemDetail like %:itemDetail% and i.price >= :price")
List<ItemEntity> findByItemDetailPrice(@Param("itemDetail, price") String itemDetail, int price);
//8. 특정 레코드 삭제
@Modifying
@Transactional
@Query(value = "delete from item_entity where item_name = :itemname", nativeQuery = true)
void deleteItemName(@Param("itemname") String itemName);
}
delete를 할때 두개의 어노테이션이 필요한 이유
1️⃣ @Modifying — “SELECT 아님!” 표시
2️⃣ @Transactional — : “commit / rollback” 보장
: 삭제가 되면 커밋, 안되면 롤백
데이터를 바꾸는(delete/update/insert) 모든 쿼리는 트랜잭션 안에서만 안전하게 실행
'기초 및 언어 > ▶ Spring' 카테고리의 다른 글
| 17. Spring_BookSite만들기-1 (1) | 2025.10.14 |
|---|---|
| 16. Spring_Thymeleaf 헤더(Fragment)로 로그인 상태 표시하기 (0) | 2025.10.10 |
| 14. Spring Number, 단위마다 , 붙이기 (0) | 2025.10.01 |
| 13. Spring Boot 쇼핑몰 실습: 로그인 후 상품 추가·수정·삭제, 장바구니 구현 (0) | 2025.10.01 |
| 12. Spring 파일 관리 주요 기능/메서드 정리 (0) | 2025.09.30 |