Spring Boot 3 & Spring Framework 6 - Section 6 :

이정수·2024년 7월 5일

Udemy학습-Spring & React

목록 보기
16/20
  • @Configuration
    。외부 라이브러리 또는 Application의 class를 Spring Context에 의해 관리되는 Configuration class로 선언하는 Annotaton.
    ▶ 해당 Annotation이 선언된 Class 객체는 Spring Context에 등록 시 Spring Bean instance로서 생성되어 LifeCycle, Dependency가 관리되며 Spring Bean에 관한 설정 정보를 포함하는 Configuration class로 지정.

  • @ComponentScan("경로") :
    。Spring이 특정 package에 존재하는 @Component로 선언된 class 객체인 Spring Bean을 자동으로 찾기 위해 Spring Context를 정의한 class에 Spring Bean이 존재하는 경로를 Annotation으로 정의.
    。경로 설정을 안할 경우 , 현재 Spring Context가 선언된 Class 파일이 위치한 package 경로내에 존재하는 @Component가 선언된 Class 객체의 Spring Bean instance를 자동으로 찾아서 Auto-wiring을 수행한다.

  • @Autowired :
    Dependency Injection : Spirng에서 요구하는 변수 type에 해당하는 특정 Spring Context 내부의 Spring Bean instance를 식별 및 자동으로 주입.

  • @Repository :
    。DB와의 상호작용을 담당하는 Class객체의 경우 @Component 대신 선언.
    Spring Bean이 DB의 데이터를 참조 , 수정 시 사용.

    。주로 DB와 상호작용을 담당하는 DAO(Data Access Obejct : DB에 접근하는 역할을 수행하는 객체)를 구현하는 class에 선언.
    @Repository가 선언된 Class를 Spring의 Data Access 예외변환 기능과 함께 Spring Bean으로 등록하는 역할을 수행.

  • @GeneratedValue :
    。Primary Key를 자동할당.

  • Persistence Layer :
    。Data를 영구적으로 저장 및 관리함으로써 데이터의 지속성 보장.
    。DB와의 상호작용 & 통신 & CRUD & transaction 관리 등을 수행.

H2-Database ( In-Memory DB ) 기본 사용 설정하기.

  • H2 DataBase :
    。Java로 작성된 오픈소스 RDBMS
    。Spring Boot가 지원하는 In-Memory 형식의 RDBMS로서, Application 재기동시 초기화.
    ▶ 로컬 또는 테스트 환경에서 주로 사용됨.
    。개발용 Local DB로 사용이 용이

  • JPA와 Hibernate에 맞는 새로운 Spring Boot Project 생성
    。Spring Framework Dependencies :
    Spring Web / Spring Data JDBC / Spring Data JPA의 starter와 H2 DataBase의 framework를 dependency로서 설정.
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>
  • H2 console을 사용 설정 또는 H2 DB로 Access
    application.properties로 진입해서 다음 구문으로 property 설정하여 H2-DB를 사용설정하기.
spring.h2.console.enabled=true

이후 다음 URL로 h2-console 진입하기. http://localhost:8080/h2-console/
▶ Application 실행 시 console에서 지시하는 JDBC URL를 h2-console 사이트의 JDBC URL에 입력하여 h2 console로 진입한다.

。그러나 해당 URL은 동적 URL로서 Application 실행시마다 URL이 달라지므로 일일이 URL을 확인해야하므로 불편하다.
▶ 정적 JDBC URL로 변경. ( 뒷부분의 난수는 삭제한다. )

spring.datasource.url=jdbc:h2:mem:testdb

。정적 JDBC URL을 포함한 다음 property를 application.properties로 추가 후 재실행 시 난수를 포함한 동적 URL이 jdbc:h2:mem:testdb로 URL이 고정됨.

이후 다음처럼 URL 입력 후 h2-console로 connect.

Server Port 변경하기
application.properties에 다음 property를 정의.
server.port=8081
▶ 기존에 Application에 부여된 로컬가상주소로서 localhost:8080/ 로 진입이 아닌 localhost:8081/로 진입하게됨.

  • H2 DB 초기화 시 생성될 sql 파일을 이용하여 table 구현하기
    H2-DBIn-Memory DB이므로 Application 재시작할때마다 데이터 초기화되므로 Application 재실행마다 실행할 초기값 설절용 .sql파일을 생성.
    DB 사용 시 해당 .sql파일을 만들 필요는 없다.

    schema.sql :
    Application 초기화 시 초기 정의될 DB Schema를 정의하는 파일.
    Schema : DB 구조로서 Table, View, Index등의 객체를 정의.

    data.sql :
    Application 초기화 시 정의된 DB Table에 삽입할 초기 데이터를 정의하는 파일

    。실행 순서 : schema.sql -> data.sql

    Application 구동 및 초기화 시 구현될 table 정의
    sql파일을 src/main/resources/ 경로에 생성. ( 반드시 해당 경로에 존재해야함.)

         # schema.sql
    create table course(
        id bigint not null,
        name varchar(255) not null,
        author varchar(255) not null,
        primary key (id)
    );

    JDBC, JPA, Hibernate 등의 기법을 사용하도록 table을 생성
    spring-boot-starter-data-jpa를 활용 시 Application이 구동 및 초기화될때 자동으로 schema.sql을 참조하여 H2-DataBase에 table 생성.

Application을 DB와 상호작용할 수 있도록 하는 API

  • JDBC ( Java Database Connectivity ) :
    。자바(Java)에서 DB에 접속할 수 있도록 연결하는 API
    ▶ Java Application에서 DB에 접근하여 사용자가 작성한 SQL을 통해 SELECT , INSERT , UPDATE , DELETE 수행.

  • Spring JDBC
    。Spring Framework에서 제공하는 JDBC 추상화 library
    ▶ JDBC 기능으로 DB와 연결 시 JDBC 코드를 훨씬 더 적고 간결하게 작성하는 이점이 존재.

    (위 : JDBC / 아래 : Spring JDBC )
    • JdbcTemplate :
      Spring JDBC를 간소화하고 편리하게 활용하여 DB와 상호작용할 수 있도록 돕는 Class.
      DAO Class에서 instance를 생성하여 instance의 method에 SQL를 넣어서 활용하여 DB와 상호작용을 수행.

Persistence Layer의 Java Bean 객체.

  • DAO ( Data Access Object ) :
    DB의 데이터로 접근하는 Transaction Object로서, DB의 데이터를 조회하거나 조작하는 기능을 수행하는 객체.

    Spring에서는 Spring Bean으로서 @Repository Annotation으로 Class에 선언하여 Spring Bean instance으로 생성.
    ▶ 내부에는 Spring JDBC를 활용한 JdbcTemplate 같은 DB와 상호작용하는 method를 제공하는 Class를 instance로 생성하여 DB와의 CRUD를 수행하는 DAO로서 작용하도록 설정.

  • DTO ( Data Transfer Object )
    。계층간 데이터 교환에서 데이터를 전송하는 객체를 의미하는 Java Bean
    。 순전히 데이터를 저장 및 회수하는 기능을 제외하고 아무 기능을 가지고 있지 않음.
    Getter , Setter Method만 포함하고있는 순수한 Class.

  • VO ( Value Object )
    。간단한 Entity를 의미하는 작은 객체.
    DTO와 달리 Getter만 제공.

Spring JDBC를 활용해 SQL을 활용하여 DB에 정의된 Table과 상호작용

CommandLineRunner Interface를 상속한 Class를 정의 및 CommandLineRunner Interface의 Abstract Method run()를 구현하여 application 구동 시 DB와 상호작용하는 Business Logic를 구현한 Spring Bean instance의 method를 작동.
JdbcTemplateupdate(sql) method로 DB와 상호작용을 수행하는 method를 구현.

。JDBC와 Spring JDBC를 사용 시 SQL Query문을 많이 활용
。Query문 입력 시 text-block내에 SQL문을 입력.

  • Spring JDBC을 활용해 DB Table에 data insert
   // CourseJdbcRepository.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class CourseJdbcRepository {
    @Autowired // Field-base Dependency Injection 수행.
    private JdbcTemplate jdbcTemplate;
    private static String sqlinsert =
            """
                    insert into course values(2,'lee','머야');
            """;
    public void insert(){
        jdbcTemplate.update(sqlinsert);
    }
}

。해당 Class는 DAO객체로서 DB와 상호작용할 Spring Bean 용도로 사용하기위해 @Repository 선언.
JdbcTemplate Class 객체를 사용하여 Spring JDBC 사용.
JdbcTemplate객체.update(sql)을 통해 DB에 해당 SQL 구문을 적용.

    // CourseJdbcCommandRunner.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class CourseJdbcCommandRunner implements CommandLineRunner {
    @Autowired
    private CourseJdbcRepository cjcr;
    @Override
    public void run(String... args) throws Exception {
        cjcr.insert();
    }
}

。해당 Class는 Application 구동 시 특정 Spring Bean을 실행하는 CommandLineRunner interface를 상속.
@Component : Application 구동 시 @SpringBootApplication으로 선언한 Class가 Component Scan을 통해 해당 Class를 Spring Bean으로 식별하도록 함.
CourseJdbcRepository Class 객체를 @Autowired를 통해 Field-base 의존성 주입을 하여 Spring Bean instance 생성.
CommandLineRunner run() method 내부에 Spring Bean instance ( cjcr )에 구현된 DB와 상호작용하는 method( insert() )를 구동.

  • h2-console 진입하여 확인 시 JdbcTemplateupdate(sql) method를 통해 SQL query가 잘 작동했음을 확인 가능.
    • @SpringBootApplication :
      。Spring Boot Application의 진입점 Class에 사용되는 Annotation.
      ▶ Spring Boot에서 Application을 실행하는 역할을 수행.

      @SpringBootApplication을 통해 Spring Boot의 Auto-Configuration, Spring Bean의 생성 등이 자동으로 설정.
      ▶ Spring Application 실행 시 Spring BeanComponent Scan을 통해 자동으로 식별 및 생성되며 해당 Spring Bean@Component가 선언되어야한다.

      。해당 Annotation이 선언된 Class는 Spring Bean이자 Configuration Class로서 내부에 @Bean Method를 선언할 수 있다.
    @SpringBootApplication
    public class Practice2Application {
        public static void main(String[] args) {
            SpringApplication.run(Practice2Application.class, args);
        }
    }

    • CommandLineRunner :
      。Spring Application이 구동 시 실행해야하는 Spring Bean을 정의하기위해 사용하는 Interface.
      ▶ application 실행 시 동시에 정의된 query문을 실행하면서 초기화 하는 역할 수행.

      。해당 interface를 상속한 Class에서 구현해야하는 run() Method 제공.
      run() method는 String type의 가변 매개변수를 받는다.
      ▶ 해당 Method에 Application 구동 시 실행할 Spring Bean instance의 method 등을 서식.
    @Override
    public void run(String... args) throws Exception{ cjcr.insert() }

    @Override는 구현하지 않아도 되지만 다른 개발자가 파악하는 명시적 용도로 선언.



  • Course class를 생성하여 Spring JDBC를 통해 data의 삽입을 용이하게 하기.
    CourseJdbcRepository class에서 JdbcTemplateupdate(sql) method로 INSERT or DELETE or UPDATE를 수행하는 SQL문과 data를 포함하는 Course class의 instance를 통해 DB와 상호작용하는 method들을 생성

    CourseJdbcCommandRunner class에서 data를 생성자로 넣어서 Course instance들을 생성한 후 해당 Course instance들을 CourseJdbcRepository의 method의 매개변수로 넣어서 DB에 반영.
public class Course {
    private long id;
    private String name;
    private String description;
    public Course() {}
    // update 용도의 Course instance 생성자
    public Course( String name, long id) {
        this.id = id;
        this.name = name;
    }
    // insert 용도의 Course instance 생성자
    public Course(long id, String name, String description) {
        this.id = id;
        this.name = name;
        this.description = description;
    }
    public long getId() { return id; }
    public String getName() { return name; }
    public String getDescription() { return description; }
    // SELECT 기능 존재 시 Setter 설정
    public void setId(long id) { this.id = id; }
    public void setName(String name) { this.name = name; }
    public void setDescription(String description) { this.description = description; }
}

매개변수가 존재하는 생성자 구축 시 , 기본생성자도 같이 정의해야한다.
SELECT를 통해 Course data type으로서 결과값을 가져오게될 경우, Class에서 setter method를 정의.
。data를 받아 DB에 넣기 위한 instance로 작용할 Course class 정의.
update 용도의 Course instance의 생성자의 매개변수는 2개로 설정
update course set name=? where id = ?;

insert 용도의 Course instance의 생성자의 매개변수는 3개로 설정.
insert into course values(?,?,?);

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class CourseJdbcRepository {
    @Autowired // Field-base Dependency Injection 수행.
    private JdbcTemplate jdbcTemplate;
    private static String sqlinsert =
            """
                    insert into course values(?,?,?);
            """;
    private static String sqldelete =
            """
                    delete from course where id = ?;
            """;
    private static String sqlupdate =
            """
                    update course set name=? where id = ?;
            """;
    private static String sqlselect =
            """
                    select * from course where id = ?;    
            """;
    public void insert(Course cs){
        jdbcTemplate.update(sqlinsert,cs.getId(),cs.getName(),cs.getDescription());
    }
    public void delete(long id){
        jdbcTemplate.update(sqldelete,id);
    }
    public void update(Course cs){
        jdbcTemplate.update(sqlupdate,cs.getName(),cs.getId());
    }
    public Course select(long id){
        return jdbcTemplate.queryForObject(sqlselect,new BeanPropertyRowMapper<>(Course.class),id);
    }
}

。하드코딩을 통해 insert, update, delete , select를 수행하는 sql을 작성.
▶ 매개변수의 ?JdbcTemplateupdate(sql,매개변수) method의 매개변수에 값을 입력하여 전달.

。DB에 SELECT를 수행 시 return값이 존재하므로, select() method의 return type을 Spring Bean( Course 객체 )로 정의 및 jdbcTemplate객체.queryForObject()SELECT를 통해 반환된 data를 Course객체의 Spring Bean instance로 획득한다.
▶ 결과값을 가져오게될 경우, Course class에서 setter method를 정의.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class CourseJdbcCommandRunner implements CommandLineRunner {
    @Autowired
    private CourseJdbcRepository cjcr;
    @Override
    public void run(String... args) throws Exception {
        cjcr.insert(new Course(2,"Learn AWS","wjdtn747"));
        cjcr.insert(new Course(3,"Learn JPA","wjdtn747"));
        cjcr.delete(1);
        cjcr.update(new Course("Learn JDBC",3));
        System.out.println(cjcr.select(3));
    }
}

。 application이 실행되자마자 CommandLineRunnerrun() method를 통해 table에 반영할 data를 Course instance에 넣어서 CourseJdbcRepositoryinsert(), delete(), update(), select() method를 실행함.

cjcr.select(3) 의 경우 select를 통해 return한 data를 Spring Bean( Course 객체 )로 획득.

  • 하드코딩 :
    。코드 내부에 데이터를 직접 입력하는것을 의미.
    。매개변수에 들어가는 값을 직접 코드로 작성(상수로 값이 고정된 상태) 후 프로그램이 동작하게 하는것.

  • JdbcTemplate :
    Spring JDBC를 간소화하고 편리하게 활용하여 DB와 상호작용할 수 있도록 돕는 Class.
    DAO Class에서 instance를 생성하여 instance의 method에 SQL를 넣어서 활용하여 DB와 상호작용을 수행.
    DAO (Data Access Obejct) : DB에 접근하는 역할을 수행하는 객체

    。DB연결, Query 실행, 결과처리, 예외처리 등을 간편하게 처리
private JdbcTemplate springJdbcTemplate;

▶ JdbcTemplate class 객체 선언

JdbcTemplate Method

  • JdbcTemplate객체.update(SQL_query,매개변수값) :
    JdbcTemplate class의 update method를 통해 INSERT / UPDATE / DELETE의 SQL query를 활용하여 DB 수정.

    。sql의 매개변수가 다음처럼 ?로 표현된 경우, update method 매개변수에 sql 매개변수에 입력될 data를 정의.
String sql = "insert into course values(?,?,?)";
JdbcTemplate객체.update(sql, 1 , 2 , 3)

.

  • JdbcTemplate객체.queryForObject(SQL, new BeanPropertyRowMapper<>(Spring Bean) , 매개변수값 ) :
    단일행(Row) 결과를 반환하는 SELECT Query를 실행 시 사용.
    SELECT query를 통해 추출된 데이터가 Spring Bean instance로 변환되어 반환.
    ▶ 여러 행 결과 반환 시 jdbctemplate객체.query() 사용.

    데이터를 가져올 SQL의 Column명과 BeanPropertyRowMapper의 생성자에 정의된 Spring Bean의 Field명이 서로 동일해야 SQL 작동 시 해당 Spring Bean instance로 Mapping되어 input이 수행.

    。개발자는 SQL 작성 및 전달할 매개변수를 직접 정의하여 mapping을 수행.
    。transaction을 위한 connection 동기화 및 Spring 예외 변환기를 자동 실행함.
    결과값을 가져오게될 경우, SpringBean class에서 반드시 setter method를 정의.
private JdbcTemplate jdbcTemplate;
private static String sqlselect = " select * from course where id = ?;";   
jdbcTemplate.queryForObject(sqlselect,new BeanPropertyRowMapper<>(Course.class),id);
  • new BeanPropertyRowMapper<>( Spring Bean )
    RowMapper : 원하는 형태의 결과값으로 반환.
    queryForObject()의 return type은 data type만 가능하므로 , Select를 통해 도출된 Data를 사용자가 원하는 Format의 결과값을 반환하는 역할



  • JdbcTemplate객체.query(SQL, 매개변수포함Object배열 , RowMapper 객체)
    。DB에서 여러행(Row) 결과를 반환하는 SELECT Query를 실행 시 사용.
    RowMapper<Type>을 사용하여 결과를 Class Instance로 Mapping.
    • 활용예시
    public class PgGeoJSONTemplate {
        private String geojson;
        public PgGeoJSONTemplate(){}
        public String getGeojson() {
            return geojson;
        }
        public PgGeoJSONTemplate(String geojson) {
            this.geojson = geojson;
        }
        public void setGeojson(String geojson) {
            this.geojson = geojson;
        }
    }

    。 Return된 SQL 행 데이터를 포함할 Template 혹은 DTO 역할의 Class.
    queryForObject()에 의한 단일행데이터 반환 시 단일 Class instance 반환.
    query()에 의한 여러행데이터 반환 시 List<Class객체> 로 반환.

    import org.springframework.jdbc.core.RowMapper;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    public class GeoJSONRowMapper implements RowMapper<PgGeoJSONTemplate> {
        public PgGeoJSONTemplate mapRow(ResultSet rs, int rowNum) throws SQLException {
            PgGeoJSONTemplate template = new PgGeoJSONTemplate();
            template.setGeojson(rs.getString("geojson"));
            return template;
        }
    }
    public List<PgGeoJSONTemplate> createCourceGeoJSON(PgJdbcOptionTemplate option){
    	Object[] params = new Object[]{option.getFromnodeid(),option.getTonodeid(),option.getDistance()};
    	return jdbcTemplate.query(sqlpgrdijkstraGeoJSON,params,new GeoJSONRowMapper());
        }
    • RowMapper<ClassType>
      Spring JDBC에서 SQL Query의 결과를 Class 객체로 변환하는 Interface.
      JdbcTemplate객체.query()에서 사용되며 각 행을 원하는 Class객체로 Mapping.

      。해당 Interface를 Class로 구현 및 해당 Class 객체를 JdbcTemplate객체.query()의 매개변수로 설정.

      mapRow() 구현메소드를 구현하여 매개변수로 전달되는 ResultSet 객체를 이용하여 SQL의 각 ColumnTemplate, DTO 역할의 Class 객체의 Field로 Mapping.
    import org.springframework.jdbc.core.RowMapper;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    public class GeoJSONRowMapper implements RowMapper<PgGeoJSONTemplate> {
        public PgGeoJSONTemplate mapRow(ResultSet rs, int rowNum) throws SQLException {
            PgGeoJSONTemplate template = new PgGeoJSONTemplate();
          // SQL 데이터의 Column을 rs객체를 통해 가져와서
          // template 역할 Class의 Field로 Mapping.
            template.setGeojson(rs.getString("geojson")); 
            return template;
        }
    }
    • DTO( Data Transfer Object )
      。 계층 간 데이터 교환을 위해 사용하는 Class의 instance.
      ▶ 주로 Controller → Service → Repository 또는 API 요청/응답에서 데이터를 전달하는 용도로 사용됩니다.

JPA (Java Persistence API)

  • JAVA에서 ORM 기술표준으로 활용되는 interface의 집합.
    ▶ 호환성을 목적으로 DB mapping을 용도로 해당 interface를 구현한 Class를 생성하기 위해 활용되는 Framework.
  • JPA를 구현한 대표적인 오픈소스로 Hibernate가 존재.

    ORM(Object-Relational Mapping) :
    。Application의 DB Entity Class instance를 RDBMS의 DB Table에 자동으로 영속화 하는것을 의미.
    DB Entity instance와 RDBMS DB Table 간 자동 Mapping.

    SQL을 직접 사용하지 않고, DB Entity instance를 중심으로 DB의 Data 조작을 수행.
    RDBMS ( H2-DB, mySQL , postgreSQL ) 의 변경에도 유연한 적용이 가능.

    • 영속성 컨텍스트 :
      。Entity를 영구 저장하는 환경
      。데이터를 영구적으로 저장 및 관리함으로써 데이터의 영속성 보장.
      。DB와의 상호작용 & CRUD & transaction 관리를 수행.

    • 영속화 :
      。영속성 컨텍스트에 관리되는 상태로 만드는것을 의미.

JPA와 Hibernate의 차이점

  • JPA
    API를 정의 ( 객체를 DB의 table로 Mapping하는 방식 정의 )
    @Entity : Interface로서 Entity가 무엇인지 정의
    @Id , @Column : 변수를 DB Table의 Field로 mapping
    EntityManager에 존재하는 Method를 구현함으로써 JPA API 사용.

  • Hibernate
    JPA를 구현한 대표적인 오픈소스.
    Hibernate JAR을 class path에 추가해서 Hibernate를 JPA 구현체로 사용.
    ▶ (ex. @Entity의 경우. jakarta.persistence 대신 org.hibernate.annotations사용. )
    。코드에서 직접 Hibernate annotation을 사용하지 않는 이유는 다른 JPA 구현체(Toplink 등)이 존재하므로, Hibernate로만 한정해서 사용하지 않기 위해!

    ▶ 따라서. 코드는 JPA를 사용하여 작성하고, Hibernate는 코드가 아닌, 구현체로만 사용하는것이 좋다.
    • Dialect :
      JPA 또는 Hibernate에서 사용하는 특정 DB에 해당하는 SQL문법과 기능을 정의한 설정.
      ▶ 각 DB( MySQL , PostgreSQL , Oracle )등은 공통적으로 SQL 표준을 따르지만, 각각 조금씩 차이가 발생하는 SQL 문법을 가지므로 Hibernate는 해당 문법/기능적 차이를 Dialect를 통해 자동으로 조정.

JPA를 활용해 Spring Bean을 DB에 정의된 Table과 Mapping하여 상호작용

  • JPA의 ORM 특징을 활용하여 Course class의 Spring BeanDB Entity로서 DB table로 mapping
    。Course class에 Setter 메소드가 존재해야 SELECT를 통해 데이터를 가져올 수 있다.
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
@Entity(name="course")
public class Course2 {
    @Id
    private long id;
    @Column(name="name")
    private String name1;
    // DB table에서 동일 이름의 Column를 가지는 경우 따로 @Column을 정의 안해도 된다.
    private String author;
    public Course2(){};
    public Course2(long id, String name1, String author) {
        this.id = id;
        this.name1 = name1;
        this.author = author;
    }
    public long getId() { return id; }
    public void setId(long id) { this.id = id; }
    public String getName1() {  return name1; }
    public void setName1(String name1) { this.name1 = name1; }
    public String getAuthor() { return author; }
    public void setAuthor(String author) { this.author = author; }
}
  • @Entity : Class와 DB table 간을 Mapping하는 DB Entity를 생성
    ▶ 이후 DB Table의 Column역할을 수행할 DB Entity의 Field에 @Id, @Column을 선언.

    @Id : 선언된 Field를 DB의 Table의 primary key로 정의하는 annotation.
    @Column(name="DB_table_Column이름") : DB table에서 "name"라는 이름을 가지는 Column과 해당 Field를 연결.
  • JPA를 활용해 Data를 insert, delete, select를 수행하는 Repository를 구현.
    。사전에 DB Table과 Mapping된 DB Entity ( Course2 )이 마련되어있어야한다.
    。Spring JDBC에서는 JdbcTemplate를 통해 DB와 상호작용을 수행한다면, JPA에서는 EntityManager를 통해 DB와 상호작용을 수행
    。JPA로 Query를 수행시, @Repository와 함께 @transactional을 선언!
 // CourseJpaRepository
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.transaction.Transactional;
import org.springframework.stereotype.Repository;
@Transactional
@Repository
public class CourseJpaRepository {
    @PersistenceContext
    private EntityManager em;
    public void insert(Course2 course){
        em.merge(course);
    }
    public Course2 select(long id){
        return em.find(Course2.class, id);
    }
    public void delete(long id){
        Course2 c1 = em.find(Course2.class, id);
        em.remove(c1);
    }
}

@Transactional , @Repository : JPA를 활용한( EntityManager ) DB작업 수행 시 해당 Annotation을 Class에 선언.
@PersistenceContext : EntityManager Class의 instance를 해당 Field에 의존성주입하는데 사용하는 Annotation.
em.merge(course) : @Entity를 선언한 DB Entity instance를 전달받아서 해당 instance의 data를 DB Table로 insert.
em.find(Course2.class, id) : DB Table에서 해당 id를 가진 DB Entity instance를 반환.
em.remove(c1) : em.find(Course2.class, id)를 통해 얻은 DB Entity instance를 입력하여 DB Table의 해당 instance를 삭제.

          // CourseCommandRunner
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class CourseCommandRunner implements CommandLineRunner {
    @Autowired
    private CourseJpaRepository cjcr;
    public void run(String... args) throws Exception {
        cjcr.insert(new Course2(1,"Learn AWS jpa","wjdtn747"));
        cjcr.insert(new Course2(2,"Learn JDBC jpa","wjdtn747"));
        cjcr.insert(new Course2(3,"Learn JPA jpa","wjdtn747"));
        cjcr.delete(2);
        System.out.println(cjcr.select(1));
        System.out.println(cjcr.select(3));
    }
}

@Component : @SpringBootApplication으로 선언한 진입점 Class를 통해 Application 구동 시 Component Scan을 통해 해당 Class를 Spring Bean으로 식별하도록 함.
@Autowired : CourseJpaRepository class의 instance를 Field에 의존성 주입. ( Field-based Injection )

  • 이때 , JPA를 사용해서 생성된 SQL을 확인하려면 다음 구문을 application.properties에 추가.
spring.jpa.show-sql=true


다음처럼 SQL Query문이 도출됨.
CodeLineRunnerrun()으로 DB작업을 실행 시 data의 Primary Key가 겹치지 않는 한 JDBC 방식과 JPA방식으로 동시에 data insert가 가능.

  • @Repository :
    。DB와의 상호작용을 담당하는 Class객체의 경우 @Component 대신 선언.
    Spring Bean이 DB의 데이터를 참조 , 수정 시 사용.

    。주로 DB와 상호작용을 담당하는 DAO(Data Access Obejct : DB에 접근하는 역할을 수행하는 객체)를 구현하는 class에 선언.
    @Repository가 선언된 Class를 Spring의 Data Access 예외변환 기능과 함께 Spring Bean으로 등록하는 역할을 수행.

  • EntityManager :
@PersistenceContext // 선언 시 `EntityManager` Class의 instance를 주입
private EntityManager em;

JPA를 활용해서 DB Table과 Mapping된 DB Entity 간 CRUD 작업을 수행하기 위한 method 제공.
▶ Spring JDBC에서의 JdbcTemplate와 유사한 기능 수행.

。사전에 @Entity를 선언한 DB Entity(ex : Course class)를 관리하는 역할 수행.
▶ 사전에 DB Entity내 Field가 모두 DB와 Mapping이 되어있어야함.
DB Entity의 영속성관리, life-cycle을 관리.

  • DB Entity를 정의한 객체를 DB에 삽입(merge) / 탐색(find) / 삭제(remove) 시 사용
    • EntityManager객체.merge(DBEntityInstance) :
      DB Entity instance내 data를 insert.

    • EntityManager객체.find(DBEntity.class , id(primary key)) :
      。해당 DBEntity Class로 생성된 instance를 조회하는 select와 동일한 기능 수행.
      ▶ 해당 id에 해당하는 DBEntity를 반환.

    • EntityManager객체.remove(DBEntityInstance) :
      。DB에 해당 DB Entity Instance로 insert된 데이터를 Delete하는 기능 수행
      ▶ 보통 EntityManager객체.find(DBEntity.class , id)를 통해 해당 id의 DBEntity를 획득 후 EntityManager객체.remove에 입력하여 DB에서 삭제 수행.


  • @PersistenceContext :
    DB Entity 개체에 대하여 Spring Bean의 @Autowired 역할을 수행.
    EntityManager Class의 instance를 주입하는데 사용하는 Annotation.
@Autowired // Field-base Dependency Injection 수행.
private JdbcTemplate jdbcTemplate;
@PersistenceContext 
private EntityManager em;

.

  • @Transactional : 선언적 트랜잭션
    。DB와 관련된 작업( insert, select, update 등 )을 일관성 있게 처리하기 위한 개념.
    。개별 Class 혹은 Method에 선언 시 내부 DB작업을 하나의 Transaction으로 묶어주는 역할을 수행.
    단위 Transaction 내 DB관련작업이 성공하면 Transaction을 Commit하여 작업을 영구적 저장, 하나라도 실패 시 Rollback 하여 이전 상태로 복구
    • 트랜잭션(Transaction) :
      。DB의 트랜잭션의 경우 DBMS에서의 상태변화의 한 주기.
      주기 : 직전 Commit부터 다음 Commit까지의 주기.

      。트랙잭션 시작 ▶ DB관련작업 ▶ 트랜잭션 종료
      트랜잭션 종료 : 단위 Transaction 내 DB관련작업이 성공하면 Transaction을 Commit하여 작업을 영구적 저장, 하나라도 실패 시 Rollback 하여 이전 상태로 복구.


  • JPA 사용 시, Background에서 어떤 SQL query가 생성되는지 확인하는 방법
    JpaRepository<>를 활용한 Spring JPA의 method(save(), findById() )의 Query 확인용도로도 사용됨.
    。다음 구문을 application.properties로 추가
    spring.jpa.show-sql=true

    。웹페이지 상에서 DB와 관련된 작업을 수행하면 콘솔창에 해당 작업의 query문이 표시됨.
    ▶ Spring Data JPA 활용 시 사용자가 SQL을 사용하진 않지만, 백그라운드상에서는 SQL이 활용됨.

Spring Data JPA
。 기존 JPA방식에서 EntityManager를 활용하지 않는 방식.
▶ 따로 EntityManager를 통해 JPA를 구현한 Class를 생성하지 않고 간단한 JpaRepository만 상속하는 Interface만 생성

  • JpaRepository<DBEntity class, id data Type>를 상속하는 Interface를 생성.
    // CourseSpringDataJpaRepository interface
public interface CourseSpirngDataJpaRepository extends JpaRepository<Course,Long> 
  • JpaRepository를 상속한 Interface를 Field-based Injection 및 DB Entity를 이용해 DB와 상호작용.
    // CourseCommandRunner.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class CourseCommandRunner implements CommandLineRunner {
    @Autowired
    private CourseSpringDataJpaRepository rp;
    public void run(String... args) throws Exception {
        rp.save(new Course2(1,"Learn AWS jpa","wjdtn747"));
        rp.save(new Course2(2,"Learn JDBC jpa","wjdtn747"));
        rp.save(new Course2(3,"Learn JPA jpa","wjdtn747"));
        rp.deleteById(2l); // Integer를 Long type으로 형변환.
        System.out.println(rp.findById(1l));
        System.out.println(rp.findById(3l));
        System.out.println(rp.findAll());
        System.out.println(rp.count());
    }
}

private CourseSpringDataJpaRepository rp : JpaRepository를 상속한 Interface를 @Autowired로 Field-based Injection
Interface객체.count() : 특정 DB Table의 DB Entity의 갯수 확인
Interface객체.findAll() : JpaRepository를 통해 실행된 Query문을 Console에 표현.

다음과 같이 결과값 출력!

  • 임의의 Custom Method 생성하기
    JpaRepository를 상속한 Interface에 생성할 Custom Method를 서식.
    Custom method 작성 시 SpringDataJpa Interface 내에서 명명 규칙을 지켜서 작성.
    Custom Method 명명규칙 :
    findByname1()의 매개변수의 이름은 반드시 DB Entity Class의 Field명(name1)과 동일.
    。Field명이 username인 경우, findByusername(String username)

    Custom Method의 매개변수를 Spring Bean의 Field명과 동일하게 설정 시 Spring Data JPA가 자동으로 검색

    JpaRepository는 DB의 Id로 검색하는 findById()는 기본적으로 제공하지만, Field명으로 검색하는 기능은 구현이 되지 않았으므로, Custom Method를 구현.
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface CourseSpringDataJpaRepository extends JpaRepository<Course2, Long> {
    List<Course2> findByAuthor(String author);
    List<Course2> findByname1(String name1);
}
        // CourseCommandRunner.java
public void run(String... args) throws Exception {
        rp.save(new Course2(1,"Learn AWS jpa","wjdtn747"));
        rp.save(new Course2(2,"Learn JDBC jpa","wjdtn747"));
        rp.save(new Course2(3,"Learn JPA jpa","wjdtn747"));
        System.out.println(rp.findByname1("Learn AWS jpa"));
        System.out.println(rp.findByAuthor("wjdtn747"));
}


rp.findByName1("Learn AWS jpa")을 통해 해당하는 Entity를 총 1개 return.
rp.findByAuthor("wjdtn747")을 통해 해당하는 Entity를 총 3개 return.

  • JpaRepository<DBEntity class, id data Type> :
    。Spring Data JPA에서 제공하는 Interface로서 Spring JPAORM을 활용한 자동으로 Query를 생성하는 DB 상호작용 Method를 제공.
    Spring JDBCJdbcTemplate객체의 경우 직접 SQL을 작성하여 JdbcTemplate객체.update(SQL)로 상호작용한다면 JpaRepository는 자동으로 Query가 생성되는 Method를 제공.
    public interface CourseSpirngDataJpaRepository extends JpaRepository<Course,Long> 
    @Autowired
    private CourseSpringDataJpaRepository rp;

▶ 해당 Interface를 상속받는 Interface를 정의 후, 해당 Interface를 구현한 Field는 JPA에서 제공하는 Method를 사용 가능.

JpaRepository<DBEntity class, id data Type> 구현 Method :

  • JpaRepository객체.save(DBEntity객체) :
    INSERTUPDATE 역할을 수행
    ▶ 기존 DB EntityUPDATE , 새로운 DB EntityINSERT

  • JpaRepository객체.deleteById(Id)
    DELETE 역할 수행

  • JpaRepository객체.findById(Id)
    SELECT 역할 수행
    。DB에서 조회한 Data를 Optional<Bean>으로 return.
    JpaRepository객체.findById(Id).get()으로 Spring Bean instance로 가져올 수 있다.

    JpaRepositoryfindById() 사용 시 Optional<T>로 return하는 이유?
    ▶ 조회한 DB Entity가 존재하지 않을 수 있으므로 ( null 방지용 )

    Optional<T> : null값을 직접 다루는 대신 값이 존재할 수 있고 없을 수 있는 상황을 명확히 표현하는데 사용하는 Class
Optional<User> user = userRepository.findById(id);
User user1 = userRepository.findById(id).get();

JpaRepository객체.findById(ID).get() : findById()로 조회된 Data를 Spring Bean instance를 return

JpaRepository는 DB의 Id로 검색하는 findById()는 기본적으로 제공하지만, Field명으로 검색하는 기능은 구현이 되지 않았으므로, Custom Method를 구현해야함.

List<Course2> findByname1(String name1)

.

  • JpaRepository객체.count() : 특정 DB Table의 DB Entity의 갯수 확인

  • JpaRepository객체.findAll() :
    @Entity를 통해 특정 Spring Bean과 Mapping된 DB Table에 존재하는 모든 Data를 해당 Spring Bean Class의 Spring Bean intance로서 List<SpringBean>로 return.
    JpaRepository를 통해 실행된 Query문을 Console에 표현하는 역할도 수행.
@GetMapping(path="/jpa/users")
    public List<User> ListAllUsers(){
        List<User> users = userRepository.findAll();
        return users;
    }
  • JpaRepository객체.getReferenceById() :
    。DB Table에서 ID에 해당하는 Spring Bean을 return.
  • EmbeddedDatabaseBuilder :
    Spring JDBC에서 Embedded DB를 쉽게 구축 및 구성하는데 사용되는 Class.
    ▶ 주로 Test 환경에서 가벼운 Embedded DB를 설정 시 사용됨.

    @Configuration을 선언한 Configuration Class에서 DataSource type으로 EmbeddedDatabaseBuilder 객체를 build하여 반환하는 @Bean Method 정의.
	@Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.H2)
                .addScript(JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION)
                .build();
    }

Spring SecurityJDBC 기반 사용자인증을 위한 기본 테이블을 H2-DB에 자동으로 생성.

  • EmbeddedDatabase :
    Application 내부에서 실행되는 가벼운 DB로서 별도의 설치없이 사용이 가능.
    ▶ 대표적으로 H2, Derby , HSQLDB등이 존재.

    。빠르게 DB를 초기화 가능하여 TEST용으로 적합하지만, 영구저장되지 않고, 대량의 데이터를 다루기에는 적합하지않은 단점이 존재.
    ▶ 운영환경에서는 부적합.

  • DataSource : javax.sql.DataSource
    JDBC에서 DB Connection을 관리하는 Interface.

  • JdbcDaoImpl :
    Spring Security에서 JDBC 기반으로 사용자정보를 가져오는 기본 구현체.
    DB에서 사용자정보를 읽어 Authentication을 처리하는 용도로 사용됨.
    • JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION :
      Spring Security에서 기본적으로 제공하는 라이브러리 내 포함된 users.ddl의 파일 경로.

      public static final String DEFAULT_USER_SCHEMA_DDL_LOCATION = "classpath:org/springframework/security/core/userdetails/jdbc/users.ddl";

    • users.ddl :
      JDBC 기반 사용자인증을 수행 시 기본적으로 필요한 테이블을 생성하는 SQL이 포함됨.
    CREATE TABLE users (
        username VARCHAR(50) NOT NULL PRIMARY KEY,
        password VARCHAR(500) NOT NULL,
        enabled BOOLEAN NOT NULL
    );
    CREATE TABLE authorities (
        username VARCHAR(50) NOT NULL,
        authority VARCHAR(50) NOT NULL,
        CONSTRAINT fk_authorities_users FOREIGN KEY(username) REFERENCES users(username)
    );
    CREATE UNIQUE INDEX ix_auth_username ON authorities (username, authority);

JDBC / Spring JDBC / JPA / Spring Data JPA 차이

  • JDBC :
    。SQL을 많이 작성하면서 Java 코드가 많다.
    ▶ 하드코딩이 많음.

  • Spring JDBC :
    。SQL을 많이 작성
    JdbcTemplate instance를 통해 Java코드는 적게 사용.

  • JPA :
    。SQL을 작성하지않음
    EntityManager을 통해 DB Entity를 DB Table로 Mapping 하는 과정 필요.

  • Spring JPA :
    SQL , EntityManager을 사용하지 않는다.
    EntityManager 대신 JpaRepository<Entity class, ID> Interface를 상속한 Interface를 Instance로 생성하여 활용.

    Spring JDBC , Spring JPA를 사용할 경우 PostgreSQL DB , MySQL DB , H2-DB 등에 관계없이 구축된 DB Entity을 매우 쉽게 적용할 수 있다.

    Spring JDBCJdbcTemplate객체의 경우 직접 SQL을 작성하여 JdbcTemplate객체.update(SQL)로 상호작용한다면 JpaRepository는 자동으로 Query가 생성되는 Method를 제공.
profile
공부기록 블로그

0개의 댓글