Developer Note/국비과정 수업내용 정리&저장

24년 11월 21일

DH_PARK 2024. 11. 25. 04:42

뜬금지식 :

bean 이름 등록할 때 클래스명에서 앞글자만 소문자로 하는 이유 ?

그냥 큰 의미같은건 없고 이름 따로 지정안하면 앞글자가 소문자로 바뀐 클래스 이름이 bean 이름으로 설정이 된다.

오전

우선 인텔리제이에서 이클립스에 있던 코드들을 사용해야 하기 때문에 옮겨오는 작업을 이것저것 진행했다. IDE 들의 구조가 완전 다르기 때문에 코드만 복사해서 붙여오는 식으로 테스트 해가면서 진행중이다.

근데 너무 재미가 없다 ㅋㅋ


스프링부트에서의 설정파일

  • build.gradle : 기존 이클립스에서 사용하던 pom.xml 이다. (이클립스가 아니라 스프링부트라서 그런건가?) 암튼 의존성은 여기다가 추가하는데 maven 에서 받을 떄는 gradle 모드로 받는다.
  • application.properties : root-context.xml 과 servlet-context.xml 파일을 합쳐놓은 듯한. 전체적인 설정을 하는 파일이다.
# JSP Setting
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
# JSP Auto-Configuration
server.servlet.jsp.init-parameters.development=true

이런식의 perfix 같은 설정도 여기서 한다.

9번 폴더 까지는 기존에 사용하던 파일들이 여기서도 잘 되는지 확인하는 작업이었고

JPA 부터 제대로 된 스프링부트 시작이다.


프로그램 실행 중에 memoDao 를 실행시키는데 실행이 안되길래 왜그런가 봤더니

namespace 변수에 경로지정을 조금 잘못했다.

마지막에 . 하나를 안붙여서 실행이 안됐었는데 왜그런지 살펴보자.

	@Autowired
	private SqlSession sqlSession;

	private static String namespace = "com.example.demo.domain.mapper.MemoMapper.";

	public int insert(MemoDto memoDto) {
		sqlSession.insert(namespace + "Insert",memoDto);
		return memoDto.getId();
	}

여기서 namespace에 . 이 들어가고 안들어가고의 차이 :

마지막에 . 이 들어가야 뒤에 함수를 구분 지을수 있기 때문에 매우 유의해야한다.

. 이 안들어가면 MemoMapperInsert 가 되어버리기 때문에 구분자가 없어져서

함수가 실행이 안됨.

주의. !


JPA

JPA는 자바의 ORM기술을 쉽게 구현하도록 도와주는 API이다.JpaRepository를 상속하는 인터페이스에 메서드 이름만 적어놓으면알아서 다 처리(구현체 생성, 쿼리문 구현 등)해주는 좋은 ORM이다.

출처:

https://ccomccomhan.tistory.com/131

[[꼼꼼한 개발자] 꼼코더:티스토리]

컨트롤 시프트 티 = 테스트 파일 만드는 단축키(라이브러리 키 인것 같음)

JPA 사용법

우선 maven repository 에서 jpa 의존성을 다운받는다

**Spring Boot Starter Data JPA 검색**

// <https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-jpa>
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '3.4.0'
		

build.gradle 에 추가해준다. 그리고 중요한 코끼리 버튼.

그리고 applcation.properties 파일에서 DB 연결 정보를 추가해야한다.

database 연결 정보 추가하기

## DATASOURCE
#spring.datasource.url=jdbc:mysql://localhost:3306/bookdb
#spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#spring.datasource.username=root
#spring.datasource.password=1234

데이터베이스에 대한 설정을 추가해준다.

## JPA
## Hibernate가 데이터베이스 스키마를 자동으로 생성 또는 업데이트할 때의 동작을 설정하는 데 사용
## update : 데이터베이스 스키마를 변경하지 않고, 새로운 필드를 추가하거나 기존 필드를 수정하는 등의 작업이 발생할 때 이를 반영
## validate : Hibernate는 현재 데이터베이스 스키마와 엔터티 클래스를 비교하고 일치하지 않으면 예외를 발생
## create : Hibernate는 현재 엔터티 클래스를 기반으로 데이터베이스 스키마를 새로 생성합니다. 기존의 데이터베이스 스키마는 삭제
## create-drop : 애플리케이션 시작 시에 데이터베이스 스키마를 생성하고, 애플리케이션 종료 시에는 해당 스키마를 삭제합니다. 주로 테스트 환경에서 사용
## none : Hibernate은 자동으로 스키마를 생성 또는 업데이트하지 않습니다. 개발자가 수동으로 데이터베이스 스키마를 관리해야 합니다
#spring.jpa.hibernate.ddl-auto=update
## Hibernate가 생성하는 SQL 쿼리를 콘솔에 출력
#spring.jpa.properties.hibernate.format_sql=true
## Hibernate에서 JDBC 배치 처리를 사용할 때 한 번에 실행되는 JDBC 명령의 수를 지정(1000 == 1000개의 SQL명령어)
#spring.jpa.properties.hibernate.jdbc.batch_size=1000
## Hibernate가 INSERT 쿼리를 일괄 처리할 때, 데이터베이스에 대해 명시적인 순서를 유지
#spring.jpa.properties.hibernate.order_inserts=true
## Hibernate가 UPDATE 쿼리를 일괄 처리할 때, 데이터베이스에 대해 명시적인 순서를 유지
#spring.jpa.properties.hibernate.order_updates=true
##  Hibernate가 일괄 처리로 데이터를 업데이트할 때, 버전화된(Versioned) 엔터티(두개 이상 트랜잭션이 동일한 엔터티를 동시에 수정하려고 할때 버전 정보를 통해 충돌을 감지하고 처리하는 매커니즘)에 대해 특정한 최적화를 적용하도록 하는 옵션
#spring.jpa.properties.hibernate.jdbc.batch_versioned_data=true

JPA 설정에 관한 설명이다.

 

 

package com.example.demo.config;

import 는 지웠습니다. 길어서

@Configuration
@EntityScan(basePackages = {"com.example.demo.domain.entity"})  //이게 엔티티를 스캔
@EnableJpaRepositories(basePackages = {"com.example.demo.domain.repository"},
                                    transactionManagerRef = "jpaTransactionManager") //이건 리포지토리 인터페이스를 스캔
public class JpaConfig {
    @Autowired
    private DataSource dataSource;

    @Bean
    LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(dataSource);
        entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        entityManagerFactoryBean.setPackagesToScan("com.example.demo.domain.entity");

        Properties jpaProperties = new Properties();

        Map<String, Object> properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto", "update");                         // 필요에 따라 'create', 'validate' 등으로 변경
        properties.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect"); // 사용 중인 DB에 맞게 변경
        properties.put("hibernate.show_sql", true);
        properties.put("hibernate.format_sql", true);

        properties.put("hibernate.hibernate.jdbc.batch_size", 1000);
        properties.put("hibernate.hibernate.order_inserts", true);
        properties.put("hibernate.order_updates", true);
        properties.put("hibernate.jdbc.batch_versioned_data", true);
        entityManagerFactoryBean.setJpaPropertyMap(properties);

        return entityManagerFactoryBean;
    }

    //애플리케이션 시작 시 데이터베이스 초기화
    @Bean
    public DataSourceInitializer dataSourceInitializer() {
        DataSourceInitializer initializer = new DataSourceInitializer();
        initializer.setDataSource(dataSource);
        initializer.setDatabasePopulator(databasePopulator());
        return initializer;
    }

    //schema.sql과 data.sql 스크립트를 실행
    private DatabasePopulator databasePopulator() {
        //Spring Framework에서 제공하는 클래스로, 외부 리소스에 정의된 SQL 스크립트를 사용하여 데이터베이스를 초기화하거나 정리하는 데 사용
        ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
        //src/main/resources 디렉토리에 위치한 SQL 스크립트를 로드
        Resource schemaScript = new ClassPathResource("schema.sql");
        Resource dataScript = new ClassPathResource("data.sql");

        populator.addScript(schemaScript);
        populator.addScript(dataScript);
        return populator;
    }

}

JPA 의 어노테이션 해석

  • @Table : 엔티티와 매핑할 테이블을 지정한다.
    • 기본적으로는 엔티티와 동일한 이름이 매핑되지만 , 이름을 다르게 할 경우 name 속성으로 매핑할 테이블을 지정해야 한다.
  • @Column : 테이블 컬럼을 매핑함.
    • 이 태그를 생략하면 기본값으로 엔티티 변수 이름과 동일한 칼럼이 매핑된다.
    • name 과 NULL 여부를 결정하는 nullable 정도를 자주 사용한다!
    • 그 외에는 length 정도 ?
  • SpringBootTest : 스프링부트 테스트 파일이란것을 명시
  • ID : 테이블의 기본 키를 매핑해준다.
    • PK 값 말하는거임 !
  • Entity : 클래스를 엔티티로 지정해주는 어노테이션
    • name 속성을 생략하면 클래스 이름이 엔티티 이름이 된다.
    • JPA는 엔티티라고 설정해놓은 객체만 엔티티로 인지하고 사용한다.
  • @ElementCollection, @CollectionTable :
    • ElementCollection : 컬렉션 객체임을 JPA가 알 수 있게 하게 한다.
      • 엔티티가 아닌 값 타입, 임베디드 타입에 대한 테이블을 생성하고 1대다 관계로 다룬다.
    • @CollectionTable :
      • 값 타입 컬렉션을 매핑할 테이블에 대한 역할을 지정하는 데 사용한다.
      • 테이블의 이름과 조인정보를 적어줘야 한다.

JPA 에서 테이블 Entity 설계하기

DB 에서 테이블을 생성하거나 할 필요없이 JPA 에서 테이블을 간단하게 정의하고 생성할 수 있다.

package com.example.demo.domain.entity;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity //엔티티 라는것을 명시
@Table(name="book") //테이블명 설정
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder // 빌더 패턴
public class Book {     //테이블이 알아서 생김   //이걸 토대로 테이블을 만들어낸다.
    @Id // PK 값으로 설정
//    @GeneratedValue(strategy = GenerationType.IDENTITY)     //Auto Increment 를 사용한다는 어노테이션
    @Column(name = "bookcode")
    private Long bookCode;  //나중에 쿼리를 사용할 때를 대비해서 카멜표기법 안쓰는게 좋다고 함 안그럼 book_name 이렇게 들어간다고함
   // db column name
   @Column(name = "bookname" , length = 1024) //varchar(1024) 이렇게 됨
    private String bookName;
    @Column(name="publisher")
    private String publisher;
    @Column(name="author")
    private String author;
    @Column(name="isbn")
    private String isbn;

}

외래키가 있는 Entity설계

package com.example.demo.domain.entity;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDate;

@Entity
@Table(name="lend")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Lend {     //N 대 M 관계 테이블 매핑하고 만들기
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY) //1 대 N 관계   user와 .      EAGER : 즉시로딩
    @JoinColumn(name = "username", foreignKey = @ForeignKey(name="FK_LEND_USER",
    foreignKeyDefinition = "FOREIGN KEY(username) REFERENCES user(username) ON DELETE CASCADE ON UPDATE CASCADE"))
    private User user;

    @ManyToOne(fetch = FetchType.LAZY)  //지연로딩 LAZY
    @JoinColumn(name = "bookcode", foreignKey = @ForeignKey(name="FK_LEND_BOOK",
            foreignKeyDefinition = "FOREIGN KEY(bookcode) REFERENCES book(bookcode) ON DELETE CASCADE ON UPDATE CASCADE"))
    private Book book;

    @Column(name="lenddate")
    private LocalDate lendDate;     //대여 날짜
    @Column(name="returndate")
    private LocalDate returnDate;   //반납날짜
    
}

JpaRepository 란?

Spring Data JPA에서 제공하는 인터페이스 중 하나로, JPA를 사용하여 데이터베이스를 조작하기 위한 메서드들을 제공합니다.

JPARepository 인터페이스를 상속받는 인터페이스를 정의하면, 해당 인터페이스를 구현하는 클래스는 JPA에서 제공하는 메서드들을 사용할 수 있습니다.

데이터베이스의 추가, 조회, 수정, 삭제의 findAll(), findById(), save() 등의 메서드들을 사용할 수 있습니다. 제공되는 메서드들 이용하여 쉽고 간편하게 CRUD 조작을 할 수 있습니다. 즉, JpaRepository를 사용하면, 복잡한 JDBC(Java DataBase Connectivity) 코드를 작성하지 않아도 간단하게 DB와의 데이터 접근 작업을 처리할 수 있습니다.

ORM은 결국 Java 객체와 DB Record간에 연결관계를 맺어주는 것입니다. 때문에 JpaRepository를 이용하여 메소드를 실행시키면 최종적으로 동작하는 것은 SQL 쿼리가 됩니다. application.yml에 jpa.show-sql 속성을 true로 주는 경우 실제 실행되는 쿼리를 확인할 수 있습니다.

  @Query("SELECT l FROM Lend AS l JOIN FETCH l.user JOIN FETCH l.book")
    List<Lend> findLendsByUserAndBook();

여기서 Lend 엔티티를 조회한다. 반환은 Lend 엔터티 객체로 매핑된다.

Lend 별칭을 l 로 지정한다.

join fetch : JOIN FETCH는 지연 로딩(lazy loading)을 방지하고, **즉시 로딩(eager loading)**으로 관련 데이터를 가져옵니다.

이 쿼리는 Lend와 연관된 user 데이터를 함께 가져옵니다.

이 함수의 반환값은 List<Lend> 로 반환된다.

Lend 엔터티를 조회하면서 user와 book 데이터를 동시에 가져옵니다.

이 쿼리문은 JPQL 이란 언어이다.


JPQL이란 ?

Java Persistance Query Langauge

JPA의 쿼리 메소드만으로는 조회가 불가능한 경우가 있는데 자세한 정보를 조회하고 싶을 경우에 사용하는 객체지향 쿼리문

SQL 과의 차이점 ?

SQL 은 테이블을 대상으로 쿼리를 하는데 반면

JPQL 은 엔티티 객체를 대상으로 쿼리를 작성한다.

그러니까 select * from table명 같이 테이블을 대상으로 컬럼이나 그런걸 검색하는 sql 에 비해서

JPQL 은

"select m from Member as m where m.name = 'coco'";

처럼 엔티티 객체를 대상을 쿼리를 작성한다.

사용하는 어노테이션

  • Query : 더 구체적인 쿼리 메서드를 작성하기 위해 사용하는 메서드의 custom 버전

Fetch 종류의 차이 ?

FetchType.EAGER (즉시 로딩)

연관된 엔터티를 즉시 가져옵니다.

연관 데이터는 부모 엔터티를 조회하는 시점에 함께 로드됩니다.

장점 :

즉시 데이터를 사용할 수 있으므로 추가적인 데이터베이스 호출이 필요 없습니다.

FetchType.LAZY (지연 로딩)

연관된 엔터티를 필요할 때 가져옵니다.

연관 데이터는 실제로 접근하기 전까지 데이터베이스 쿼리가 실행되지 않습니다.

장점 :

성능 최적화: 불필요한 데이터를 즉시 로드하지 않아 메모리 사용량과 초기 로딩 시간이 줄어듭니다.

둘을 간단히 설명하면 뭘까?

잘 이해가 안되서 비유로 설명을 해보자면

비교 요약

전략 비유 상황 특징

FetchType.LAZY 메인 요리만 먼저 주문하고, 필요할 때 사이드 메뉴 요청 초기에는 가볍고 빠르지만, 필요할 때마다 추가 요청 발생
FetchType.EAGER 메인 요리와 사이드 메뉴를 한 번에 주문 초기 로딩이 느릴 수 있지만, 추가 요청 없이 한 번에 사용 가능
JOIN FETCH 세트 메뉴를 주문해 필요한 음식만 한 번에 제공받음 필요한 데이터만 한 번에 가져오므로 최적화 가능

jpa 에서 테이블명 작성 팁 (중요)

jpa 에서 테이블을 만들때 Entity 테이블에서 테이블명을 카멜표기법으로 사용할 시에

테이블명 사이에 언더바가 붙게된다. 그래서 띄우지 않고 붙어있는 테이블명을 사용하려면

그냥 전부 소문자로 작성해야한다.

'Developer Note > 국비과정 수업내용 정리&저장' 카테고리의 다른 글

24년 11월 25일  (0) 2024.11.26
24년 11월 22일  (0) 2024.11.25
24년 11월 20일  (0) 2024.11.25
24년 11월 19일  (1) 2024.11.25
24년 11월 18일  (0) 2024.11.24