관리 메뉴

CASSIE'S BLOG

102-1강 스프링 부트와 JPA v1 본문

PROGRAMMING/슈퍼코딩 강의 정리

102-1강 스프링 부트와 JPA v1

ITSCASSIE1107 2024. 5. 1. 20:11

 
 

 
매번 INSERT할 때도 물음표 적어야하고,  FIND 할 때는 TRY CATCH 다 해야하고, 그리고 ROW MAPPER 하는게 상당히 번거로웠다. (BUILRDER로 개선하긴 했지만) 
 
 
JDBC를 사용하여 데이터베이스에서 데이터를 가져올 때, 처음에는 ResultSet을 직접 다루는 것이 일반적이었습니다. 그러나 이는 코드를 복잡하게 만들고 유지보수를 어렵게 했습니다.
그래서 나중에 Spring 프레임워크에서는 이러한 문제를 해결하기 위해 RowMapper 인터페이스를 도입했습니다. RowMapper를 사용하면 ResultSet의 각 행을 자바 객체로 매핑할 수 있습니다.
따라서 RowMapper는 ResultSet에서 데이터를 추출하고 그것을 원하는 형태의 자바 객체로 매핑하는 역할을 합니다. 이를 통해 코드의 가독성과 유지보수성을 향상시킬 수 있습니다.
 
BUILDER: 
BUILDER는 객체를 만들 때 사용하는 도구입니다. 이 도구를 사용하면 객체를 만들 때 필요한 정보를 단계적으로 입력할 수 있습니다. 이렇게 하면 객체를 만들 때 필요한 정보를 놓치지 않고 정확하게 입력할 수 있습니다.
예를 들어, 데이터베이스에서 정보를 가져와서 자바 객체로 바꿀 때 사용하는 ROW MAPPER를 생각해봅시다. ROW MAPPER를 만들 때 BUILDER를 사용하면 ROW MAPPER를 쉽게 만들 수 있습니다.
예를 들어, 사용자 정보를 데이터베이스에서 가져와서 자바 객체로 바꾸는 ROW MAPPER를 만든다고 해봅시다. BUILDER를 사용하면 이런 코드를 작성할 수 있습니다.
 
public class User {
    private final int id;
    private final String name;
    private final String email;
    
    private User(Builder builder) {
        this.id = builder.id;
        this.name = builder.name;
        this.email = builder.email;
    }
    
    public static class Builder {
        private int id;
        private String name;
        private String email;
        
        public Builder id(int id) {
            this.id = id;
            return this;
        }
        
        public Builder name(String name) {
            this.name = name;
            return this;
        }
        
        public Builder email(String email) {
            this.email = email;
            return this;
        }
        
        public User build() {
            return new User(this);
        }
    }
}
 
 
이제 이 BUILDER를 사용해서 ROW MAPPER를 만들 수 있습니다.
 
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;

public class UserRowMapper implements RowMapper<User> {
    @Override
    public User mapRow(ResultSet rs, int rowNum) throws SQLException {
        return new User.Builder()
            .id(rs.getInt("id"))
            .name(rs.getString("name"))
            .email(rs.getString("email"))
            .build();
    }
}
 
 
BUILDER를 사용하면 코드가 더 깔끔해지고, 필요한 정보를 놓치지 않고 입력할 수 있습니다. 이렇게 하면 코드를 이해하기 쉬워지고, 유지보수하기도 편해집니다.
 
 
 
결론적으로 말하고싶었던게 내부에서는 JDBC를 사용한다는 점임.
 

 
Rdbs와 자바는 패러다임이 다름
Rdbs: 데이터 지향, 관계 지향, 선언적
자바: 객체지향적, 행위 지향 패러다임

본질적으로 왜이런 문제가 나타나냐면

자바는 코드 재사용성과 다형성 위해 개체상속을 지원하는데 그에반해

Rdbms는 1:1, 1:n, n:m 이런 식으로 지원하고 상속 개념이 없다.

그래서 달라서 개발자들이 애로사항이 많다.

그래서 패러다임 불일치 해결책인  orm이란 무엇인가?

Orm (Object Relation Mapping) : 객체 지향 언어 <-> RDB 자동 변환기술

Object <-> ORM <-> Table

기존 Entity -> 간접매핑 (근데 rowmapper가 또 처리를 해주는 거였음)

ORM 적용한 Entity는 직접매핑을 지원한 것임

각각 어노테이션들이 실제 데이터베이스와 1:1 로 완전히 매칭됨

엔티티가 테이블에 완전히 귀속되는 그래서 테이블의 영속화라고 합니다.

**이런 엔티티들은 RDB테이블의 영속화**

ORM과 JPA
Java는 JPA
Node.js 는 Sequalize.js다.

Jpa구성요소 중에서

중요한 게
Entity Manager Factory
Query

——
Entity Manager
Entity Transaction

 



 데이터베이스 탭 사용하는 방법

 

인텔리제이에서 바로 볼 수 있음. 

 

db에 있는 것들 그대로 그냥 정보 넣어줌. 

 

 

지금 java config를 2개를 갖고 있잖아

 

package com.github.supercoding.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

@Configuration
public class JdbcConfig {

@Bean
public DataSource dataSource1(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUsername("root");
dataSource.setPassword("12341234");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); //이 스트링 값 넣어줘야 MySQL 드라이버 등록됨
dataSource.setUrl("jdbc:mysql://localhost:3306/chapter_96");
return dataSource;
}

@Bean
public DataSource dataSource2(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUsername("root");
dataSource.setPassword("12341234");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); //이 스트링 값 넣어줘야 MySQL 드라이버 등록됨
dataSource.setUrl("jdbc:mysql://localhost:3306/chapter_97");
return dataSource;
}

@Bean
public JdbcTemplate jdbcTemplate1(){return new JdbcTemplate(dataSource1());};

@Bean
public JdbcTemplate jdbcTemplate2(){return new JdbcTemplate(dataSource2());};

@Bean(name="tm1")
public PlatformTransactionManager transactionManager1(){return new DataSourceTransactionManager(dataSource1());}

@Bean(name="tm2")
public PlatformTransactionManager transactionManager2(){return new DataSourceTransactionManager(dataSource2());}

};


 

97도 있으니 똑같이 설정해준다.

 

 

mapstruct 밑에 

//JPA 

해당 설치해준다.

implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

testImplementation 'org.springframework.boot:spring-boot-starter-test'

 

그 다음 application-local.yaml에 들어가서

level를 설정을 해서 로그를 자세히 볼 예정 

 

before

server:
port: 9090

logging:
config: classpath:logback-spring-local.xml

 

after 

 

server:
port: 9090

logging:
config: classpath:logback-spring-local.xml
level:
org:
hibernate:
SQL: DEBUG

 

application-local.yaml 에 가서 구현하는게 하이버네이트라서 그 부분도 설정해주고 그 다음에 SQL: DEBUG 설정해준다. 

 

logback-spring-local.xml 이 파일에 이렇게 설정이 되어있잖아. 

 

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 콘솔 로그 출력 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{36}) - %msg%n</pattern>
</encoder>
</appender>

<!-- 루트 로거 설정 -->
<root level="info">
<appender-ref ref="CONSOLE" />
</root>
</configuration>

 

logback-spring-local.xml 파일에는 로깅에 대한 세부 설정이 들어 있으며, 이를 통해 로그 레벨, 로그 출력 형식, 로그 파일 위치 등을 설정할 수 있습니다.

 

org.hibernate는 프로젝트의 패키지 구조가 아니라 Hibernate 프레임워크의 패키지 구조.

logging.level.org.hibernate.SQL: DEBUG는 Hibernate의 SQL 쿼리에 대한 로깅 레벨을 DEBUG로 설정하는 것이지요.

여기서 org는 패키지 이름의 시작을 나타내는 것입니다. Hibernate는 org.hibernate 패키지에 속하므로, org.hibernate 패키지에 속한 모든 클래스에 대해 로깅 레벨을 설정하는 것입니다.

만약 org.hibernate 패키지 안에 있는 다른 클래스들에 대해서도 동일한 로그 레벨을 적용하려면 org.hibernate를 통해 패키지 전체에 대한 로깅 레벨을 설정할 수 있습니다.

따라서 org.hibernate.SQL: DEBUG라고 적어줌으로써 Hibernate의 SQL 쿼리에 대한 로그 레벨을 DEBUG로 설정합니다..

 

JpaConfig 설정을 하려면 어노테이션 몇개를 붙여야한다고함. 

basePackages는 어떤거를 둘중에 하나를 선택할거냐 설정하는 부분이라고함. 

 

copy path and reference package에 오른쪽 클릭해서 할 수 있음  그 다음 그냥 copy reference 하면 됨 

 

아까 말한 EntityManager를 빈으로 등록해줘야함. 그게 LocalContainerEntityManagerFactoryBean

 

그게 JdbcConfig에 외부에 데이터 소스가 있기 때문에 
지우고 Qualifier 해서 넣어줘야한다고함.

설정하는거는 그냥 다른코드들도 다 똑같은데
벤더라는 어댑터를 설정해야하는게 좀 어렵다고함.

또 설정을 몇개 추가를 해줘야한다. (하이버네이트와 관련된 것)

 

https://github.com/MangwonCassie/SuperCoding-Backend-Edited/tree/class102-1

 

GitHub - MangwonCassie/SuperCoding-Backend-Edited

Contribute to MangwonCassie/SuperCoding-Backend-Edited development by creating an account on GitHub.

github.com

 

코드 내용은 이거 보면 되는데, 

 

Jpa랑 데이터 소스 다 설정한건데 이거는 기본으로 까는거고 이제 JPA Entity를 만들어줘야함

 

 

 

@GeneratedValue(strategy = GenerationType.IDENTITY)는 자동으로 생성되는 기본 키를 관리하는 방법 중 하나입니다. 이 전략은 대부분의 RDBMS에서 지원됩니다.

GenerationType.IDENTITY 전략은 데이터베이스의 자동 증가(auto-increment) 기능을 활용하여 기본 키를 생성합니다. 이 경우, 데이터베이스가 기본 키 값을 자동으로 할당하고, 자바 코드에서는 이 값을 받아와서 사용하게 됩니다.

이 뜻 자체가 자바에서 결정하는 것이 아니라 RDB가 결정하도록 둔다는 것임.

 

feat: ItemEntity를 JpaEntity로 변경

 

before

 

package com.github.supercoding.repository.items;

import lombok.*;

import java.util.Objects;

@Setter
@Getter
@AllArgsConstructor
@EqualsAndHashCode(of = "id")
@ToString
@Builder
public class ItemEntity {
private Integer id;
private String name;
private String type;
private Integer price;
private Integer storeId;
private Integer stock;
private String cpu;
private String capacity;

public ItemEntity(Integer id, String name, String type, Integer price, String cpu, String capacity) {
this.id = id;
this.name = name;
this.type = type;
this.price = price;
this.storeId = null;
this.stock = 0;
this.cpu = cpu;
this.capacity = capacity;
}
}

 

after

 

 

테이블 설정할 떄는 실제 테이블 이름과 이름이 똑같아야한다고함.

 

빨간줄이 뜨는 이유는 id를 설정하라는 뜻이라고함.

 

 

after

package com.github.supercoding.repository.items;

import lombok.*;

import javax.persistence.*;
import java.util.Objects;


@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "id")
@ToString
@Builder
@Entity
@Table(name = "item")
public class ItemEntity {

@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;

@Column(name = "name", length = 50, nullable = false, unique = true)
private String name;

@Column(name = "type", length = 20, nullable = false)
private String type;
@Column(name ="price")
private Integer price;

@Column(name = "store_id")
private Integer storeId;

@Column(name = "stock", columnDefinition = "DEFAULT 0 CHECK(stock) >= 0")
private Integer stock;

@Column(name = "cpu", length = 30)
private String cpu;

@Column(name = "capacity", length = 30)
private String capacity;

public ItemEntity(Integer id, String name, String type, Integer price, String cpu, String capacity) {
this.id = id;
this.name = name;
this.type = type;
this.price = price;
this.storeId = null;
this.stock = 0;
this.cpu = cpu;
this.capacity = capacity;
}


}
 

 

 

기능이 많다. 이런식으로 확인할 수 있음

 

반응형