Stereotype Annotation
은 프로젝트 내에서 클래스의 역할 또는 목적을 나타내는 데 사용되며 스프링은 자동으로 이 Stereotype
의 클래스들을 탐지하고 Bean
으로 등록한다.
spring:
datasource:
url: jdbc:sqlite:db.sqlite
driver-class-name: org.sqlite.JDBC
jpa:
hibernate:
ddl-auto: create
show-sql: true
database-platform: org.hibernate.community.dialect.SQLiteDialect
package com.example.jpa.entities;
import jakarta.persistence.*;
import lombok.Data;
@Data
@Entity // 데이터베이스 테이블의 레코드를 나타냄
@Table(name = "students")
public class StudentEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// Column 어노테이션에 인자를 전달해서 테이블 Constraint 추가 가능
// @Column(name = "username", nullable = false, unique = true)
private String name;
private Integer age;
// @Column(nullable = false)
private String phone;
private String email;
}
데이터가 어떻게 테이블에 매핑되는지 명세하기 위해 존재하고 인터페이스와 어노테이션으로 구성된다.
자바 ORM(Object Relational Mapping) 기술의 표준 명세로, 객체와 관계형 데이터베이스를 매핑하기 위한 인터페이스를 제공하고, JPA를 이용하면 SQL 코드를 작성하지 않고도 객체를 통해 데이터베이스를 조작할 수 있다.
JPA 명세를 바탕으로 작동하는 ORM 프레임워크로 JPA로 표현된 객체를 실제 데이터베이스에 적용 및 사용한다.
Hibernate
를 사용하면 객체와 데이터베이스 테이블을 매핑하여 SQL 코드를 작성하는 과정을 줄일 수 있다.
package com.example.jpa;
import com.example.jpa.entities.StudentEntity;
import com.example.jpa.repos.StudentRepository;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class AppService {
private final StudentRepository studentRepository;
public AppService(AppRepository repository, StudentRepository studentRepository) {
// this.repository = repository;
this.studentRepository = studentRepository;
}
// CREATE
public void createStudent(
String name,
Integer age,
String phone,
String email
) {
// 새로운(new) Student(Entity)를 만들고 싶다
StudentEntity newEntity = new StudentEntity();
newEntity.setName(name);
newEntity.setAge(age);
newEntity.setPhone(phone);
newEntity.setEmail(email);
// repository.save()
this.studentRepository.save(newEntity);
}
// READ
public void readStudent(Long id) {
Optional<StudentEntity> optionalStudentEntity = this.studentRepository.findById(id);
// 1. 실제 데이터가 온 경우
if (optionalStudentEntity.isPresent()) {
System.out.println(optionalStudentEntity.get());
}
// 2. 결과가 null 이 되었을 경우
else {
System.out.println("no result");
}
}
// READ ALL
public void readStudentAll() {
System.out.println(this.studentRepository.findAll());
}
// UPDATE
public void updateStudent(
// 어떤 대상을 수정할 지 지정해야 한다.
Long id,
// 수정할 데이터
String name
) {
// 수정할 Entity 를 회수
Optional<StudentEntity> optionalEntity = this.studentRepository.findById(id);
// 수정할 Entity 를 찾은 경우
if (optionalEntity.isPresent()) {
// 실제 객체 회수
StudentEntity target = optionalEntity.get();
// 수정할 데이터 적용
target.setName(name);
// save
this.studentRepository.save(target);
} else {
// 없으면 없다고 알려준다.
System.out.println("could not find");
}
}
// DELETE
public void deleteStudent(Long id) {
Optional<StudentEntity> optionalEntity = this.studentRepository.findById(id);
// 삭제할 엔티티를 찾은 경우
if (optionalEntity.isPresent()) {
StudentEntity studentEntity = optionalEntity.get();
// 삭제
this.studentRepository.delete(studentEntity);
} else {
System.out.println("could not find");
}
}
// findAllBy
public void findAllByTest() {
System.out.println("findAllByOrderByName");
List<StudentEntity> studentEntities = this.studentRepository.findAllByOrderByName();
for (int i = 0; i < 5; i++) {
System.out.println(studentEntities.get(i));
}
System.out.println("...");
System.out.println("findAllByOrderByAgeDesc");
studentEntities = this.studentRepository.findAllByOrderByAgeDesc();
for (int i = 0; i < 5; i++) {
System.out.println(studentEntities.get(i));
}
System.out.println("...");
System.out.println("findAllByAgeLessThan");
studentEntities =
this.studentRepository.findAllByAgeLessThan(21);
for (int i = 0; i < 5; i++) {
System.out.println(studentEntities.get(i));
}
System.out.println("...");
System.out.println("findAllByPhoneStartingWith");
studentEntities =
this.studentRepository.findAllByPhoneStartingWith("010-");
for (int i = 0; i < 5; i++) {
System.out.println(studentEntities.get(i));
}
System.out.println("...");
}
// 주된 비즈니스 로직이 구현되는 공간
// Controller -> Service
// Service
// 1. 데이터베이스 조회
// 2. Component 사용
// 3. 모은 데이터를 가지고 응답 (의사결정)
// public List<Object> readStudentAll() {
// List<Object> queryResult = repository.selectStudentAll();
// // some business logic
// return queryResult;
// }
}
package com.example.jpa;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
//@Controller
@RestController
// 모든 메소드에 @ResponseBody 를 붙이는 용도
public class AppController {
// 사용자의 입력을 직접적으로 받는 요소
// MVC 에서 C를 담당
// RequestMapping과 같이 사용
private final AppService service;
public AppController(AppService service) {
this.service = service;
}
@GetMapping("create")
public @ResponseBody String create() {
this.service.createStudent(
"alex",
35,
"010-1234-5678",
"alex@gmail.com"
);
return "done";
}
@GetMapping("read")
public @ResponseBody String readOne() {
this.service.readStudent(1L);
return "done-read-one";
}
@GetMapping("read-all")
public @ResponseBody String readAll() {
this.service.readStudentAll();
return "done-read-all";
}
@GetMapping("update")
public @ResponseBody String update() {
this.service.updateStudent(1L, "alexander");
return "done-update";
}
@GetMapping("delete")
public @ResponseBody String delete() {
this.service.deleteStudent(1L);
return "done-delete";
}
@GetMapping("find")
public @ResponseBody String find() {
this.service.findAllByTest();
return "done-find";
}
// @RequestMapping("students")
// public void students() {
// List<Object> result = service.readStudentAll();
// }
//
// @GetMapping("home")
// public String home() {
// return "home";
// }
//
// @GetMapping("body")
// public @ResponseBody String body() {
// return "body";
// }
}
package com.example.jpa;
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
import java.util.List;
@Repository
public class AppRepository {
// 데이터베이스와의 소통을 담당
// @Transactional
public List<Object> selectStudentAll() {
return new ArrayList<>();
}
}
package com.example.jpa;
import com.google.gson.Gson;
import org.springframework.stereotype.Component;
@Component
public class AppComponent {
// Controller, Service, Repository 가 아닌
// 평범한 Bean 객체
// 외부 API 사용, 공유된 기능 개발, 그냥 IoC 등록하고 싶은 객체
// 필요로 하는 설정
private final AppConfigData configData;
private final Gson gson;
public AppComponent(AppConfigData configData, Gson gson) {
this.configData = configData;
this.gson = gson;
}
// 외부 API를 사용하는 메소드
public void useApi() {
// send request
}
}
package com.example.jpa;
public class AppConfigData {
private final String connectionUrl;
public AppConfigData(String connectionUrl){
this.connectionUrl = connectionUrl;
}
}
package com.example.jpa;
import com.google.gson.Gson;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfiguration {
// Spring을 활용하는데 필요한 다양한 설정을 담고 있는 용도
@Bean
public AppConfigData connectionUrl() {
// 이 메소드의 결과(반환 값)를 Bean 객체로 등록
if (true/* 현재 나의 상황에 따라서 다른 URl을 지급하는 코드 */)
return new AppConfigData("main-url");
else return new AppConfigData("backup-url");
}
@Bean
public Gson gson() {
return new Gson();
}
}
package com.example.jpa.repos;
import com.example.jpa.entities.StudentEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
// 데이터베이스와 소통하기 위한 창구
public interface StudentRepository
// JpaRepository<내가 사용할 Entity, 그 Entity의 ID 컬럼의 타입>
extends JpaRepository<StudentEntity, Long> {
// Spring Data JPA - Query Method
// 메소드 이름을 우리가 조회하고 싶은 조건을 붙여서 만든다.
// 하나 또는 많이
// (findBy || findAllBy) + [(Column + 조건) * n] + [OrderBy + Column]
// SELECT * FROM students ORDER BY name;
List<StudentEntity> findAllByOrderByName();
// SELECT * FROM students ORDER BY age;
List<StudentEntity> findAllByOrderByAgeDesc();
// SELECT * FROM students WHERE age < 30;
List<StudentEntity> findAllByAgeLessThan(Integer age);
// SELECT * FROM students WHERE phone LIKE '010-%';
List<StudentEntity> findAllByPhoneStartingWith(String phone);
}
spring:
datasource:
url: jdbc:sqlite:db.sqlite
driver-class-name: org.sqlite.JDBC
jpa:
hibernate:
ddl-auto: create
show-sql: true
database-platform: org.hibernate.community.dialect.SQLiteDialect
defer-datasource-initialization: true
sql:
init:
mode: always
https://school.programmers.co.kr/learn/courses/30/lessons/120810
class Solution {
public int solution(int num1, int num2) {
return num1 % num2;
}
}
very easy
Q. num2 = 0
이라면?
그렇다고 합니다. 꿀팁!
https://school.programmers.co.kr/learn/courses/30/lessons/120843
인덱스를 골라내는 법을 찾지 못하여 실패
힌트는 나머지 연산
다음에 재도전!
이번 주는 전부 힘들다 정말로. 이제 진짜 생존게임이 시작된 것 같다. 아침부터 오후까지 내내 코드만 줄줄이 치느라 이 수많은 로직들이 머리속에 입력되지 못하고 과부화가 와버렸다. 지금도 수업 마치고 조금 자고 일어나서 포스트를 작성하고 있는데 복습하는 겸 위키를 참고해서 읽어봐도 이 많은 걸 당장에 이해하기도 외우지도 못 할 것 같다. 내가 과연 이걸 이해해서 이용할 수 있을까. 다시 찾아온 걱정과 불안감. 역시 일단 내일만 살자.