일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- linux
- server
- Unity Editor
- --watch
- springboot
- Camera Zoom
- MySQL
- spread 연산자
- express
- Google Developer API
- unity
- Camera Movement
- Google Refund
- draganddrop
- css framework
- critical rendering path
- Digital Ocean
- Git
- react
- mongoDB
- Unity IAP
- nodejs
- java
- screencapture
- Spring Boot
- Packet Network
- OverTheWire
- docker
- rpg server
- SDK upgrade
- Today
- Total
우당탕탕 개발일지
[SpringBoot] 4. JPA 적용하기 ( Repository 계층) 본문
지금까지는 아래처럼, 문자열에 SQL 구문을 직접 적었다. 이런 방식은 여러가지 문제가 있다.
public boolean isUserNotExist(int uid){
String querySQL = "select * from user where id = ?";
return jdbcTemplate.query(querySQL, (res,rowNum)->0,uid).isEmpty();
}
- 실수할 가능성이 많고, 실수를 인지하기 힘들다 . 문자열에 오타가 날 경우, 컴파일 시점이 아니라 런타임 시점에 발견되기 때문이다.
- 특정 데이터베이스에 종속되게 된다. 상황에 따라 DB가 변경될 수 있는데, 만약 db가 변경될 경우, 모든 코드를 재작성해야한다.
- 반복작업이 많아진다. CRUD 쿼리는 자주 사용하게되는데, 사용할때마다 작성해야한다.
- 데이터베이스의 테이블과 객체는 패러다임이 다르다
JPA
이러한 불편한 점을 개선하기 위해 나온것이 JPA(Java Persistence API) 이다. 쉽게 말하면, Java의 객체와 SQL의 테이블을 매핑시켜서 유저가 직접 SQL문을 작성하지 않고 DB를 조작할 수 있게끔 도와주는 친구이다.
객체와 관계형 DB의 테이블을 짝지어 데이터를 영구적으로 저장할 수 있도록 도와주는 Java진영의 규칙이다. Java진영의 ORM이다. Hibernate이 JPA를 구현해놓았다. Hibernate은 내부적으로 JDBC를 사용한다.
User.java 를 생성하여 객체-테이블을 매핑할 수 있도록 정의해준다. @Id, @Column, @GeneratedValue 와 같은 어노테이션을 이용하여 DB의 테이블에 매핑시켜준다.
//User.java
package com.group.library_app.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity
public class User {
@Id //primary key 로 간주
@GeneratedValue(strategy= GenerationType.IDENTITY) //자동생성. identity == auto_increment
private Long id ;
@Column(nullable= false, length=25,name="name")
private String name;
@Column(nullable= true,name="age")
private Integer age;
protected User(){} //기본생성자가 필요함.
public User(String name, Integer age){
if(name == null || name.isBlank()){
throw new IllegalArgumentException("invalid name : "+name);
}
this.name = name;
this.age = age;
}
}
application.yaml 파일에서 JPA 설정을 해준다.
##application.yaml
spring:
datasource:
url: "jdbc:mysql://localhost/library" ##java database connector 를 사용해 java 와 db를 연결
username: "myname"
password: "mypassword"
driver-class-name: com.mysql.cj.jdbc.Driver #db접근시 사용할 프로그램
jpa:
hibernate:
ddl-auto: none
properties:
hibernate:
show_sql: true
format_sql: true
dialect: org.hibernate.dialect.MySQL8Dialect
각 옵션에 대한 설명은 간략히 한다. 나중에 필요할때 따로 찾아보도록하자.
ddl-auto 옵션 : spring이 시작할때 db와 객체의 매핑이 다를 경우 어떻게 처리할지.
##create : 기존테이블이 있을 경우 삭제 후 재생성
##create-drop : 스프링이 종료될때 매핑된 테이블을 모두 제거
##update: 객체와 테이블이 다른 부분만 변경
##validate : 객체와 테이블이 동일한지 확인
##none : 별다른 조치없음.
show_sql옵션 :jpa를 사용해 db에 sql을 날릴 때 sql을 보여줄 것인지
format_sql 옵션 : sql을 보여줄때 이쁘게 포맷팅 할 것인지
dialect 옵션 : 다양한 종류의 db를 자동으로 해당문법으로 수정해주는 역할. sql마다 문법이 다름. 현재는 mysql8 에 맞춰달라는 의미이다.
UserRepository와 UserService 수정하기
직접 구현하던 UserRepository 에서, JpaRepository 를 상속받은 클래스로 변경한다.
//UserRepository.java
package com.group.library_app.domain.user;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User,Long>{
//함수명에 정확한 필드이름이 들어가야함
public User findByName(String name);
//public Optional<User> findByName(String name);
public List<User> findAllByName(String name);
public boolean existByName(String name);
public int countByName(String name);
public List<User> findAllByNameAndAge(String name,int age);
public List<User> findAllByAgeBetween(int startAge, int endAge);
}
클래스가 굉장히 축약된 것을 알 수 있다. 기본으로 제공되는 save(), findById(), delete() 등 외에, 다양한 SQL구문을 확장시킬 수 있도록 함수를 제작할 수 있다. 더 많은 조합이 가능한데, Spring Data JPA 에서 정해놓은 네이밍 컨벤션
을 이용하여 적절한 SQL문을 작성할 수 있다.
UserService에서도 변화가 있다. userRepository 에서 제공하는 save(), delete() 등을 간편하게 가져오는것을 확인할 수 있다. 기본으로 제공되는 함수 + UserRepository 에서 확장한 함수 모두 사용가능하다.
//UserService.java
package com.group.library_app.service.user;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service;
import com.group.library_app.domain.user.User;
import com.group.library_app.domain.user.UserRepository;
import com.group.library_app.dto.user.request.UserCreateRequest;
import com.group.library_app.dto.user.request.UserUpdateRequest;
import com.group.library_app.dto.user.response.UserResponse;
//로직을 담당함.
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository _repository){
userRepository = _repository;
}
public void saveUser(UserCreateRequest request){
userRepository.save(new User(request.getName(), request.getAge()));
}
public List<UserResponse> getUsers()
{
List<User> users = userRepository.findAll();
return users.stream().map(user -> new UserResponse(user)).collect(Collectors.toList());
}
public void updateUser(UserUpdateRequest request){
User user = userRepository.findById(request.getId()).orElseThrow(()->new IllegalArgumentException("invalid id"));
user.UpdateName(request.getName());
userRepository.save(user);
}
public void deleteUser(String name){
User user = userRepository.findByName(name);
if(user == null) throw new IllegalArgumentException("invalid id");
userRepository.delete(user);
}
}
'Server' 카테고리의 다른 글
[SpringBoot] 6. 데이터베이스의 객체와 연관관계 (Repository 계층) (1) | 2025.01.08 |
---|---|
[SpringBoot] 5. 트랜잭션 (Service 계층) (0) | 2025.01.06 |
[SpringBoot] vscode 에서 import ... not exist 현상 (0) | 2025.01.06 |
[SpringBoot] 3. 리팩토링 & 스프링 컨테이너 (0) | 2025.01.05 |
[SpringBoot] 2. MySQL 연결하기 (0) | 2025.01.04 |