[ 10주차 - 1020 ]
금일 커리큘럼
├ 09:00 ~ 12:00 backend 프로그래밍 (Spring Data JDBC, 기존 DAO 패턴 활용)
└ 13:00 ~ 18:00 backend 프로그래밍 (repository 패턴 활용)
1. Spring Data JDBC
스프링에서 JDBC를 더 쉽게 사용할 수 있도록 도와주는 모듈
- 객체-관계 매핑(ORM)과는 다르게, 단순한 CRUD(Create, Read, Update, Delete) 작업에 초점을 맞추고 있음.
- SQL 쿼리를 직접 작성하지 않고도 데이터베이스 작업을 수행할 수 있도록 도와줌.
- 복잡한 매핑이나 관계 설정이 필요하지 않은 간단한 애플리케이션에 적합함.
스프링 데이터 JDBC의 주요 기능
- 리포지토리 지원: CRUD 작업을 위한 기본적인 메서드를 제공하는 리포지토리 인터페이스를 자동으로 생성.
- 장점 : 쿼리문 작성 없이도 데이터베이스 작업 가능
- 관련 어노테이션:
CrudRepository,PagingAndSortingRepository
- 트랜잭션 관리: 스프링의 트랜잭션 관리 기능과 통합되어 데이터 일관성을 보장.
- 매핑: 도메인 객체와 데이터베이스 테이블 간의 매핑을 지원.
스프링 데이터 JDBC vs 기존 JDBC
| 구분 | 기존 JDBC | 스프링 데이터 JDBC |
|---|---|---|
| 코드 복잡도 | 연결, 쿼리, 반복문 등 복잡한 코드 필요 | 간결한 코드 작성 가능 |
| 트랜잭션 관리 | 수동 관리 | 자동 관리 |
| 객체 매핑 | 수동 매핑 | 자동 매핑 지원 |
| 예외처리 | 수동 처리 | 스프링 예외 변환 지원 |
코드량 차이점
- 기존 JDBC는 커넥션, 쿼리 작성, 결과 처리 등 많은 보일러플레이트 코드가 필요.
- 스프링 데이터 JDBC는
JdbcTemplate과 같은 유틸리티를 사용하여 코드량을 크게 줄일 수 있음.
// 기존 JDBC 코드 예시
public UserDAOImpl Implements UserDAO {
private final DataSource dataSource;
public UserDAOImpl(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public List<User> findAll() {
List<User> users = new ArrayList<>();
String sql = "SELECT * FROM users";
try (Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
User user = new User();
user.setId(rs.getLong("id"));
user.setName(rs.getString("name"));
users.add(user);
}
} catch (SQLException e) {
e.printStackTrace();
}
return users;
}
}
// -------------------------------------------------
// 스프링 데이터 JDBC 코드 예시
@Repository
@RequiredArgsConstructor // final 붙은 필드를 생성자 자동 DI 주입 생성
public class UserDAOImpl Implements UserDAO {
private final JdbcTemplate jdbcTemplate;
@Override
public List<User> findAll(User user) {
String sql = "SELECT * FROM users";
// BeanPropertyRowMapper 로 컬럼명 <-> 필드 자동 매핑
return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));
}
}
2. 스프링 데이터 JDBC 시작
프로젝트 생성
- 필수 플러그인
- Spring Web
- Spring Data JDBC
- MySQL Driver
- Lombok
의존성 추가
- build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-jdbc' // 해당
implementation 'com.mysql:mysql-connector-j' // 해당
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
데이터베이스 설정 (yml)
spring:
application:
name: library-app
datasource:
url: jdbc:mysql://localhost:3306/librarydb
username: root
password: your_password
driver-class-name: com.mysql.cj.jdbc.Driver
# SQL 로그 출력 (학습용)
logging:
level:
org.springframework.jdbc: DEBUG
3. 스프링 데이터 JDBC - 기존 DAO 방식 확인
주요 클래스 및 어노테이션
- JdbcTemplate : 스프링에서 제공하는 JDBC 작업을 간소화하는 유틸리티 클래스.
- @BeanPropertyRowMapper : JDBC 결과 집합의 각 행을 도메인 객체에 매핑하는 데 사용되는 클래스.
- 필드 <-> 컬럼 자동 매핑 지원
- 컬럼에 (_) 있는 경우도 자동 대문자 매핑 지원 (예: user_name -> userName)
- @RequiredArgsConstructor : Lombok 어노테이션으로, final 필드에 대한 생성자를 자동으로 생성.
UserDAO 쿼리 예시
@Repository
@RequiredArgsConstructor // final 붙은 필드를 생성자 자동 DI 주입 생성 (Lombok)
public class UserDAOImpl Implements UserDAO {
private final JdbcTemplate jdbcTmpl;
@Override
// select All
public List<User> findAll() {
String sql = "SELECT * FROM users";
// BeanPropertyRowMapper 로 컬럼명 <-> 필드 자동 매핑
// return jdbcTmpl.query(sql, new BeanPropertyRowMapper<>(User.class));
/* 만약 필드(userName) 컬럼(name) 매핑이 다를 경우
* 수동 매핑 처리 방식
*/
return jdbcTmpl.query(sql, (rs, rowNum) -> {
User user = new User();
user.setId(rs.getLong("id"));
user.setName(rs.getString("name")); // 컬럼명이 다를 경우 수동 매핑
user.setEmail(rs.getString("email"));
return user;
});
}
@Override
// select By Id
public User findById(Long id) {
String sql = "SELECT * FROM users WHERE id = ?";
return jdbcTmpl.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), id);
}
@Override
// insert
public int save(User user) {
String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
return jdbcTmpl.update(sql, user.getName(), user.getEmail());
}
@Override
// update
public int update(User user) {
String sql = "UPDATE users SET name = ?, email = ? WHERE id = ?";
return jdbcTmpl.update(sql, user.getName(), user.getEmail(), user.getId());
}
@Override
// delete
public int deleteById(Long id) {
String sql = "DELETE FROM users WHERE id = ?";
return jdbcTmpl.update(sql, id);
}
}
4. 스프링 데이터 JDBC - 레파지토리 패턴
스프링 데이터 JDBC에서 제공하는 레파지토리 인터페이스를 사용하여 DAO 구현을 간소화할 수 있음.
- JDBC 흐름에서 데이터 JDBC 가 담당하는 부분
Controller (웹 요청 처리)
↓
Service (비즈니스 로직 처리)
↓
Repository (데이터 액세스 처리) # <- 여기서 스프링 데이터 JDBC 제공
↓
Database (데이터베이스)
CrudRepository 사용
CRUD 작업을 위한 메서드를 포함된 인터페이스
1) 기존 객체 클래스 로직 수정 필요
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Table("users") // 테이블명 매핑
public class User {
@Id // PK 매핑 <- 필수
private Long id;
private String name;
private String email;
@CreatedDate // 감사(Auditing)로 자동 세팅 (insert 시)
@Column("created_at") // 컬럼명 매핑
private LocalDateTime createdAt;
public User(String name, String email) {
this.name = name;
this.email = email;
}
}
2) UserRepository 생성
@Repository
public interface UserRepository extends CrudRepository<User, Long> {
// 메소드 구현 없이 선언만 하면 자동으로 쿼리 생성됨
// 별도 쿼리메서드 추가 가능
Optional<User> findByName(String name);
Optional<User> findByEmail(String email);
}
| 메서드 이름 패턴 | 생성되는 SQL | 예시 |
|---|---|---|
findBy{필드명} |
WHERE 절 추가 | findByName |
findBy{필드1}And{필드2} |
AND 조건 | findByNameAndEmail |
findBy{필드1}Or{필드2} |
OR 조건 | findByNameOrEmail |
findBy{필드}Like |
LIKE 검색 | findByNameLike |
findBy{필드}Containing |
LIKE '%keyword%' | findByNameContaining |
findBy{필드}StartingWith |
LIKE 'keyword%' | findByNameStartingWith |
findBy{필드}EndingWith |
LIKE '%keyword' | findByNameEndingWith |
3) 메인에서 테스트
- CrudRepository 주요 메서드
save(S entity): 엔티티 저장 (insert / update)findById(ID id): ID로 엔티티 조회findAll(): 모든 엔티티 조회deleteById(ID id): ID로 엔티티 삭제count(): 엔티티 개수 조회existsById(ID id): ID 존재 여부 확인
@SpringBootApplication
@EnableJdbcAuditing // 감사 활성화
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
@Bean
public CommandLineRunner commandLineRunner(UserRepository repo) {
return args -> {
final String email = "John@John.com";
// 1) insert
// User addUser = repo.save(new User("John", "John@John.com"));
User addUser = repo.findByEmail(email)
.orElseGet(() -> repo.save(new User("John", email)));
System.out.println("추가됨 id=" + addUser.getId());
// 2) update
addUser.setName("John22");
addUser = repo.save(addUser);
System.out.println("업데이트(name) -> " + addUser);
addUser.setEmail("John22@exam.com");
addUser = repo.save(addUser);
System.out.println("업데이트(email) -> " + addUser);
// 3) findAll
// repository.findAll().forEach(System.out::println);
Iterable<User> iterable = repo.findAll();
List<User> userLists = new ArrayList<>();
iterable.forEach(userLists::add);
System.out.println(userLists);
// 4) findById
User userById = repo.findById(addUser.getId()).orElse(null);
System.out.println(userById);
// delete
repo.deleteById(addUser.getId());
};
}
}
5. JDBC 레파지토리 실습 (friend)
DB 테이블 생성
CREATE TABLE friend (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255),
email VARCHAR(255)
);
디렉토리 구조
src/main/java
└── org/example/springjdbc
└── friendapp
├── controller
│ └── FriendController.java
│
├── domain
│ └── Friend.java
│
├── repository
│ └── FriendRepository.java
│
├── service
│ └── FriendService.java
│
└── FriendApplication.java
src/main/resources
└── templates
└── friends
├── addForm.html
├── detail.html
├── editForm.html
└── list.html
friendapp 패키지
domain
- Friend.java
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.springframework.data.annotation.Id;
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Friend {
@Id
private Long id;
private String name;
private String email;
}
repository
- FriendRepository.java
import org.example.springjdbc.friendapp.domain.Friend;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface FriendRepository extends CrudRepository<Friend, Long> { }
service
- FriendService.java
import lombok.RequiredArgsConstructor;
import org.example.springjdbc.friendapp.domain.Friend;
import org.example.springjdbc.friendapp.repository.FriendRepository;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class FriendService {
private final FriendRepository friendRepository;
/** 친구 추가 */
public Friend addFriend(Friend friend) {
return friendRepository.save(friend);
}
/** 친구 목록 */
public Iterable<Friend> getFriends() {
return friendRepository.findAll();
}
/** 친구 id 조회 */
public Friend getFriendById(Long id) {
return friendRepository.findById(id).orElseThrow();
}
/** 친구 정보 업데이트 */
public Friend updateFriend(Friend friend) {
return friendRepository.save(friend);
}
/** 친구 삭제 */
public void deleteFriend(Long id) {
friendRepository.deleteById(id);
}
}
controller
- FriendController.java
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.example.springjdbc.friendapp.domain.Friend;
import org.example.springjdbc.friendapp.service.FriendService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequiredArgsConstructor // final 생성자
@RequestMapping("/friends")
public class FriendController {
private final FriendService friendService;
/** 친구 추가 (C) */
// form page
@GetMapping("/add")
public String addForm(Model model) {
return "friends/addForm";
}
// post get
@PostMapping("/add")
public String addFriend(@ModelAttribute Friend friend) {
Friend saveFirend = friendService.addFriend(friend);
System.out.println("[Controller] add :: " + saveFirend);
return "redirect:/friends/add";
}
/** 친구 정보 (R) */
// list page
@GetMapping("/list")
public String getFriends(Model model) {
Iterable<Friend> friends = friendService.getFriends();
model.addAttribute("friends", friends);
System.out.println("[Controller] Iterable :: " + friends);
return "friends/list";
}
// detail
@GetMapping("/detail/{id}")
public String detailFriend(Model model, @PathVariable("id") Long id) {
Friend detailFriend = friendService.getFriendById(id);
model.addAttribute("friend", detailFriend);
System.out.println("[Controller] detailFriend :: " + detailFriend);
return "friends/detail";
}
/** 친구 수정 (U) */
@GetMapping("/edit/{id}")
public String editForm(Model model, @PathVariable("id") Long id) {
Friend editFriend = friendService.getFriendById(id);
model.addAttribute("friend", editFriend);
return "friends/editForm";
}
@PostMapping("/edit")
public String editFriend(@ModelAttribute Friend friend) {
friendService.updateFriend(friend);
return "redirect:/friends/list";
}
/** 친구 삭제 (D) */
@GetMapping("/delete/{id}")
public String deleteFriend(Model model, @PathVariable("id") Long id) {
friendService.deleteFriend(id);
return "redirect:/friends/list";
}
}
FriendApplication.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class FriendApplication {
public static void main(String[] args) {
SpringApplication.run(FriendApplication.class, args);
}
}
template 폴더
friends
- addForm.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>친구 추가 입력</title>
<link rel="stylesheet" th:href="@{/css/app.css}">
</head>
<body>
<header><h1>친구 추가</h1></header>
<main>
<section class="section _form">
<form method="post" th:action="@{/friends/add}">
<div class="form_item">
<div class="field">
<label for="name">이름 : </label>
<input type="text" id="name" name="name">
</div>
</div>
<div class="form_item">
<div class="field">
<label for="email">이메일 : </label>
<input type="text" id="email" name="email">
</div>
</div>
<div class="btn_group">
<button type="submit" class="btn _action">친구등록</button>
</div>
</form>
</section>
</main>
<footer></footer>
</body>
</html>
- detail.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>친구 상세정보</title>
<link rel="stylesheet" th:href="@{/css/app.css}">
</head>
<body>
<header><h1>친구 정보</h1></header>
<main>
<section class="section _detail">
<div class="detail_area">
<div class="inner_head">
<i class="icon">
<img src="http://via.placeholder.com/128x128" alt="">
</i>
<h2>[[${friend.name}]]님</h2>
</div>
<div class="inner_body">
<ul class="info_list">
<li>
<div class="dt">
<span>서버 아이디</span>
</div>
<div class="dd">
<span>[[${friend.id}]]</span>
</div>
</li>
<li>
<div class="dt">
<span>친구 이름</span>
</div>
<div class="dd">
<span>[[${friend.name}]]</span>
</div>
</li>
<li>
<div class="dt">
<span>친구 이메일</span>
</div>
<div class="dd">
<span>[[${friend.email}]]</span>
</div>
</li>
</ul>
</div>
</div>
<div class="btn_group">
<a class="btn _primary" th:href="@{/friend/list}">목록으로</a>
<a class="btn _action" th:href="@{/friends/edit/{id}(id=${friend.id})}">친구 수정</a>
<a class="btn _secondary" th:href="@{/friends/delete/{id}(id=${friend.id})}">친구 삭제</a>
</div>
</section>
</main>
<footer></footer>
</body>
</html>
- editForm.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>친구 정보 수정</title>
<link rel="stylesheet" th:href="@{/css/app.css}">
</head>
<body>
<header><h1>친구 정보 수정</h1></header>
<main>
<section class="section _form">
<form method="post" th:action="@{/friends/edit}">
<input type="hidden" name="id" th:value="${friend.id}">
<div class="form_item">
<div class="field">
<label for="name">이름 : </label>
<input type="text" id="name" name="name" th:value="${friend.name}">
</div>
</div>
<div class="form_item">
<div class="field">
<label for="email">이메일 : </label>
<input type="text" id="email" name="email" th:value="${friend.email}">
</div>
</div>
<div class="btn_group">
<button type="submit" class="btn _action">정보 수정</button>
</div>
</form>
</section>
</main>
<footer></footer>
</body>
</html>
- list.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>친구 목록</title>
<link rel="stylesheet" th:href="@{/css/app.css}">
</head>
<body>
<header><h1>친구 목록</h1></header>
<main>
<section class="section _list">
<div class="table_area" th:if="${!friends.empty}">
<table>
<colgroup>
<col style="width: calc(100%/3)">
<col style="width: calc(100%/3)">
<col style="width: calc(100%/3)">
</colgroup>
<thead>
<tr>
<th>아이디</th>
<th>이름</th>
<th>이메일</th>
</tr>
</thead>
<tbody>
<tr th:each="friend : ${friends}">
<td><span>[[${friend.id}]]</span></td>
<td>
<a
class="underline_btn"
th:href="@{/friends/detail/{id}(id=${friend.id})}"
th:title="|${friend.name}님 정보 보기|">
[[${friend.name}]]
</a>
</td>
<td><span>[[${friend.email}]]</span></td>
</tr>
</tbody>
</table>
</div>
<div class="nodata" th:if="${friends.empty}">
<p>친구 목록이 없습니다.<br>추가해주세요.</p>
</div>
<div class="btn_group">
<a class="btn _primary" th:href="@{/friends/add}">친구 등록</a>
</div>
</section>
</main>
<footer></footer>
</body>
</html>
- resource/static/css/app.css
/* 파일하나로 처리 */
* { margin: 0; padding: 0; box-sizing: border-box; }
ul li, ol li { list-style: none; }
a { text-decoration: none; }
html { font-size: 62.5%; width: 100%; min-height: 100%; }
body { display: flex; flex-direction: column; font-size: 1.4rem; font-family: "Pretendard", "Noto Sans KR", sans-serif; line-height: 1.5; color: #333; min-height: 100dvh; background: #f4f4f4; }
/* 헤더 */
header { position: sticky; top: 0; margin: 0; padding: 20px 0; width: 100%; background: #005386; color: #fff; text-align: center; box-shadow: 0 2px 8px rgba(0,0,0,0.05); }
header h1 { font-size: 1.8rem; color: #fff; text-align: center; }
/* 메인 섹션 */
main { width: 100%; padding: 20px; flex: 1; }
section {
width: 100%; margin: 20px 0;
&._form { margin-inline: auto; max-width: 400px; background: #fff; padding: 30px 25px; border-radius: 12px; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.08); }
}
/* 각 폼 항목 */
.form_item { margin-bottom: 20px; }
.field { display: flex; flex-direction: column; }
label { margin-bottom: 6px; font-weight: 600; color: #555; }
/* 입력창 스타일 */
input[type="text"] { padding: 10px 12px; border: 1px solid #ccc; border-radius: 6px; font-size: 1.4rem; transition: border-color 0.2s, box-shadow 0.2s; }
input[type="text"]:focus { border-color: #4caf50; outline: none; box-shadow: 0 0 0 3px rgba(76, 175, 80, 0.2); }
/* 버튼 */
.btn_group { display: flex; align-items: center; justify-content: center; gap: 8px; margin-top: 20px; }
.btn { display: inline-flex; width: auto; color: #fff; font-size: 1.4rem; font-weight: 600; padding: 10px 24px; border: none; border-radius: 6px; cursor: pointer; transition: background-color 0.2s, transform 0.1s; }
.btn {
&._primary { background-color: #4650e0; }
&._action { background-color: #4caf50; }
&._secondary { background-color: #555555; }
}
.btn:hover { /*background-color:; */}
.btn:active { transform: scale(0.98); }
/* 테이블 */
.table_area { width: 100%; padding: 20px; }
.table_area { }
.table_area table { position:relative; width: 100%; border-collapse: collapse; border-top: 1px solid transparent; }
.table_area table::before { content:''; position:absolute; top:-1px; width:100%; height:1px; background-color: #121315; }
.table_area table tr:first-child th,
.table_area table tr:first-child td { border-top: 0; }
.table_area table tr th:first-child,
.table_area table tr td:first-child { border-left: 0; }
.table_area table tr th:last-child,
.table_area table tr td:last-child { border-right: 0; }
.table_area table th { color: #121315; background-color: #F3F6FB; }
.table_area table td { color: #576072; background-color: #fff; }
.table_area table td > strong { font-weight: 600; }
.table_area table td .align_right { display:block; text-align:right; }
.table_area table th,
.table_area table td { padding: 8px; border: 1px solid #E7EDF6; font-size: 1.4rem; }
.table_area table tbody th,
.table_area table tbody td { text-align: left; }
.table_area table thead th,
.table_area table thead + tbody th,
.table_area table thead + tbody td { text-align: center; }
.table_area table thead tr th[rowspan],
.table_area table thead tr:last-child:not(:first-child) th { border-bottom-color: #E7EDF6; }
.table_area table + table { margin-top: 24px !important; }
etc
자주사용되는 CRUD 쿼리 메서드
| 메서드 이름 패턴 | 생성되는 SQL | 예시 | 설명 |
|---|---|---|---|
findBy{필드명} |
WHERE 필드 = ? |
findByName |
정확히 일치하는 값 조회 |
findBy{필드1}And{필드2} |
WHERE 필드1 = ? AND 필드2 = ? |
findByNameAndEmail |
여러 조건 AND 연산 |
findBy{필드1}Or{필드2} |
WHERE 필드1 = ? OR 필드2 = ? |
findByNameOrEmail |
여러 조건 OR 연산 |
findBy{필드}Like |
WHERE 필드 LIKE ? |
findByNameLike |
LIKE 검색 (직접 패턴 지정) |
findBy{필드}Containing |
WHERE 필드 LIKE '%?%' |
findByNameContaining |
부분 문자열 포함 검색 |
findBy{필드}StartingWith |
WHERE 필드 LIKE '?%' |
findByNameStartingWith |
특정 문자로 시작하는 검색 |
findBy{필드}EndingWith |
WHERE 필드 LIKE '%?' |
findByNameEndingWith |
특정 문자로 끝나는 검색 |
findBy{필드}IgnoreCase |
WHERE UPPER(필드) = UPPER(?) |
findByNameIgnoreCase |
대소문자 무시 검색 |
findBy{필드}GreaterThan |
WHERE 필드 > ? |
findByAgeGreaterThan |
값보다 큰 조건 |
findBy{필드}LessThan |
WHERE 필드 < ? |
findByAgeLessThan |
값보다 작은 조건 |
findBy{필드}Between |
WHERE 필드 BETWEEN ? AND ? |
findByAgeBetween |
범위 검색 |
findBy{필드}IsNull |
WHERE 필드 IS NULL |
findByEmailIsNull |
NULL 값 검색 |
findBy{필드}IsNotNull |
WHERE 필드 IS NOT NULL |
findByEmailIsNotNull |
NOT NULL 값 검색 |
findBy{필드}In |
WHERE 필드 IN (?) |
findByNameIn |
여러 값 중 하나와 일치 |
findBy{필드}NotIn |
WHERE 필드 NOT IN (?) |
findByNameNotIn |
여러 값과 일치하지 않음 |