1) 데이터 접근 기술 – 시작

데이터 액세스 기술

SQL 매퍼

  • Jdbc 템플릿
  • 마이바티스

ORM 관련 기술

(ORM ==> 개체 관계형 매핑)

  • JPA, 최대 절전 모드
  • 스프링 데이터 JPA
  • 쿼리 dsl

SQL 매퍼의 주요 기능

개발자가 SQL을 작성하고 있을 때 SQL Mapper 기술은 SQL의 결과를 개체에 편리하게 매핑합니다.

JDBC를 직접 사용할 때 발생하는 각종 중복을 제거하고 다른 개발자에게 유용한 다양한 기능을 제공합니다.

ORM 주요기능

Jdbc 템플릿또는 마이바티스 똑같은 것 SQL 매퍼 기술개발자가 직접 작성한 SQL해야 하지만

JPA사용 기본 SQL은 대신 JPA에서 작성하고 처리합니다.그렇습니다

개발자 저장하려는 객체를 Java 컬렉션에 저장한 다음 검색하는 것처럼 사용합니다.만약에

ORM 기술은 데이터베이스에 개체를 저장하고 검색합니다.그렇습니다

JPA~이다 자바 캠프 ORM 표준자아, 최대 절전 모드는 가장 일반적으로 사용되는 JPA 구현입니다.오전.

자바에서 ORM사용할 때 JPA 인터페이스그리고 구체화~처럼 최대 절전 모드를 사용하는 것으로 생각하십시오.

스프링 데이터 JPA, 쿼리 dslJPA를 보다 편리하게 사용할 수 있도록 도와주는 프로젝트오전.

실제로 JPA이 프로젝트를 사용한다면 당신도 꼭! 함께 사용하기 좋습니다.

나는 개인적으로 그것이 거의 필수적이라고 생각합니다.

프로젝트 살펴보기

DTO(데이터 전송 개체)

데이터 전송 개체

DTO는 기능이 없고 데이터 전송에만 사용되는 개체입니다.

참고로 DTO에는 기능이 없어야 하나요? 그렇지 않습니다.

개체의 주요 목적이 데이터 전송인 경우 DTO호출할 수 있습니다

개체 이름에 DTO를 추가할 필요는 없습니다.

대신 붙여넣을 때 사용법을 감지할 수 있다는 이점이 있습니다.

앞에서 설명한 ItemSearchCond도 DTO 역할을 하지만

이 프로젝트에서는 Cond가 검색 조건으로 사용된다는 규칙을 만들었습니다. 따라서 DTO를 연결할 필요가 없습니다.

ItemSearchCondDto 그것은 일을 너무 복잡하게 만듭니다.

그리고 Cond를 보면 그 목적을 알 수 있습니다.

참고로 이와 같이 정해진 규칙이 없기 때문에 프로젝트 내에서 일관되게 규칙을 설정할 수 있습니다.

메모리 저장

저장소로 카드. 서버의 전원이 꺼지면 데이터가 사라지는 스토리지 저장소입니다.

아이디 발급 및 회원 찾기

@Override
public Optional<Item> findById(Long id) {
    return Optional.ofNullable(store.get(id));
}

findById ~이다 선택 과목 다시 돌아와야 하기 때문에 선택적.ofNullable 필요한

조건 검색

항목 검색 조건 객체를 받아 조건이 있으면 입력하고 검색하여 결과를 일치시킵니다.

@Override
public List<Item> findAll(ItemSearchCond cond) {
    String itemName = cond.getItemName();
    Integer maxPrice = cond.getMaxPrice();
    return store.values().stream()
            .filter(item -> {
                if (ObjectUtils.isEmpty(itemName)) {
                    return true;
                }
                return item.getItemName().contains(itemName);
            }).filter(item -> {
                if (maxPrice == null) {
                    return true;
                }
                return item.getPrice() <= maxPrice;
            })
            .collect(Collectors.toList());
}

(자바 스트림 포함)

itemName 또는 maxPrice가 null이거나 비어 있으면 조건이 무시됩니다.

itemName 또는 maxPrice에 값이 있는 경우에만 해당 조건으로 수행되는 필터 기능입니다.

검색 조건이 비어 있으면 무조건 가져옵니다. 즉, true

검색 조건이 존재하면 검색 조건이 성공하면 true, 그렇지 않으면 false

즉, 두 필터가 true로 반환하는 목록을 반환하는 메서드입니다.

스프링 부트 구성 – 메모리 구성

구성 파일

@Configuration
public class MemoryConfig {

    @Bean
    public ItemService itemService() {
        return new ItemServiceV1(itemRepository());
    }

    @Bean
    public ItemRepository itemRepository() {
        return new MemoryItemRepository();
    }

}

ItemServiceV1, MemoryItemRepository 스프링 빈으로 등록하고 생성자를 통해 종속성을 주입합니다.하다.

(spring bean 이름은 메소드 이름, spring bean 객체는 반환 값)

참고로 여기서는 서비스와 리포지토리의 구현을 편리하게 변경하기 위해 이와 같은 빈을 수동으로 등록합니다.했다.

(자동 bean 등록을 위해서는 @Service와 @Repository를 붙일 수 있다. @Repository를 사용하고 싶은 리포지토리에 붙이거나, 사용하지 않는 리포지토리에서 @Repository를 삭제하거나, 빈 우선순위를 확고히 설정하면 된다. 이것이 가장 좋은 것 같다. 사례.)

컨트롤러는 구성 요소 스캔을 사용합니다.하다.

저장소 저장소에 대한 테스트 데이터 자동 생성

@Slf4j
@RequiredArgsConstructor
public class TestDataInit {

    private final ItemRepository itemRepository;

    /**
     * 확인용 초기 데이터 추가
     */
    @EventListener(ApplicationReadyEvent.class)
    public void initData() {
        log.info("test data init");
        itemRepository.save(new Item("itemA", 10000, 10));
        itemRepository.save(new Item("itemB", 20000, 20));
    }

}

애플리케이션 실행 시 초기 데이터를 저장합니다.

데이터가 목록에 잘 도착했는지 편리하게 확인할 때 사용합니다.

이 기능이 없으면 목록에 나타나기 위해 서버를 시작할 때마다 데이터를 입력해야 합니다.

(스토리지이기 때문에 서버가 종료되면 데이터가 삭제됩니다.)

@EventListener(ApplicationReadyEvent.class)

(@EventListner는 파라미터로 전달된 이벤트가 종료되면 해당 메소드를 실행시키는 주석이다)

Spring 컨테이너가 초기화를 완료하고 실행할 준비가 되면 발생하는 이벤트입니다.오전.

Spring은 이 시점에서 주석 처리된 initData() 메서드를 호출합니다.

참고 이 함수 대신 @PostConstruct 사용

AOP와 같은 부분은 아직 처리되지 않은 지점에서 호출될 수 있기 때문에 때때로 문제가 발생할 수 있습니다.

예를 들어 @Transactional 컨텍스트에서 AOP를 적용하지 않고 호출할 수 있습니다.

@EventListener(ApplicationReadyEvent.class) ~이다

이 문제는 AOP를 포함하는 Spring 컨테이너가 완전히 초기화된 후에 호출되기 때문에 발생하지 않는다.

항목 서비스 신청

@Import(MemoryConfig.class)
@SpringBootApplication(scanBasePackages = "hello.itemservice.web")
public class ItemServiceApplication {

   public static void main(String() args) {
      SpringApplication.run(ItemServiceApplication.class, args);
   }

   @Bean
   @Profile("local")
   public TestDataInit testDataInit(ItemRepository itemRepository) {
      return new TestDataInit(itemRepository);
   }

}
@SpringBootApplication(scanBasePackages = "hello.itemservice.web")

구성 요소 스캔 범위는 패키지 및 해당 하위 패키지로 설정됩니다.

(여기서는 컨트롤러만 컴포넌트 스캔을 사용하고 나머지는 수동으로 등록합니다.

그래서 구성 요소 스캔 경로를 hello.itemservice.web의 하위로 설정했습니다.)

(설정하지 않으면 현재 ItemServiceApplication 및 해당 하위 패키지가 상주하는 패키지로 설정됩니다.

(패키지 hello.itemservice;))

지정된 경로가 아닌 패키지의 클래스는 @Bean 및 @Configuration을 사용하더라도 구성 요소 검사 대상이 아닙니다.

@Import(MemoryConfig.class)

따라서 @import를 사용하여 설정을 추가하여 hello.itemservice.config 패키지에 MemoryConfig.class를 추가하십시오. https://pangtrue.76

(Component 검사 대상에 MemoryConfig의 위치가 포함되어 있지 않기 때문에)

@Bean
@Profile("local")

특정 프로필에만 이 스프링 빈을 등록합니다.(@Profile과 함께)

여기서는 local이라는 프로필을 사용할 때만 testDataInit이라는 Spring bean을 등록합니다.

이전에 이 bean을 본 적이 있지만 단순성을 위해 초기 데이터를 생성하고 저장하는 bean입니다.

프로필

Spring은 로드 시간에 application.properties의 spring.profiles.active 속성을 읽어 프로파일로 사용합니다.하다.

이 프로파일은 로컬(내 PC), 운영 환경, 테스트 실행 등 환경에 따라 다른 설정을 지정할 때 사용하는 정보입니다.

예를 들어, 로컬 PC에서 로컬 PC에 설치된 데이터베이스에 접근해야 하는 경우,

프로덕션 환경에서 프로덕션 데이터베이스에 액세스해야 하는 경우 구성 정보가 달라야 합니다.

환경에 따라 다른 Spring Bean을 등록해야 할 수도 있습니다.

프로필은 이 문제에 대한 명확한 솔루션을 제공합니다.

https://keeeeeepgoing.171

메인 프로필

/소스/메인/자원 부하 application.properties

이 위치의 application.properties는 /src/main(주로 main())에서 Java 객체를 실행할 때 실행되는 Spring 구성입니다.

spring.profiles.active = local

라는 코드로 프로필을 설정할 수 있습니다. (위의 코드는 프로필을 로컬로 설정하는 것을 보여줍니다.)

이것이 설정되면 스프링은 해당 프로파일과 함께 작동합니다.

따라서 앞에서 설명한 @Profile(“local”)이 작동하고 testDataInit가 Spring Bean으로 등록됩니다.

실행하면 아래와 같은 로그를 볼 수 있습니다.


1) 데이터 접근 기술 - 시작 1

프로필을 지정하지 않으면 기본 프로필이 실행됩니다.


1) 데이터 접근 기술 - 시작 2

테스트 프로필

/소스/시험/자원 부하 애플리케이션.형질

spring.profiles.active=test

이 위치의 application.properties는 /src/test의 Java 개체가 실행될 때 실행될 Spring 구성입니다.

테스트 케이스를 실행할 때 주로 작동합니다.

spring.profiles.active=test가 설정되면 Spring은 test라는 프로필로 실행됩니다.

이 경우 프로필 정보가 일치하지 않기 때문에 앞에서 설명한 @Profile(“local”)이 작동하지 않습니다.

따라서 testDataInit라는 Spring 호출은 등록되지 않으며 초기 데이터를 추가하지 않습니다.


1) 데이터 접근 기술 - 시작 3
구성된 테스트 프로필로 실행

기본값?


1) 데이터 접근 기술 - 시작 4
기본적으로 동일합니다.

프로파일 기능을 사용하여 Spring으로 로컬에서 웹 애플리케이션을 실행할 때,

testDataInit는 Spring Bean으로 등록됩니다.

따라서 등록된 초기화 데이터를 편리하게 확인할 수 있습니다.

초기화 데이터는 편리하지만 테스트 사례를 실행할 때 문제가 될 수 있습니다.

테스트에 이러한 데이터가 포함되어 있으면 오류가 발생할 수 있습니다.

예를 들어, 데이터를 저장하고 총 개수를 확인하려면,

1이 아니라 testDataInit 때문에 2개의 데이터가 추가되어 3이 됩니다.

프로파일 기능 덕분에 테스트 케이스에서 테스트 프로파일이 실행됩니다.

따라서 TestDataInit은 Spring Bean으로 추가되지 않으므로 초기 데이터가 추가되지 않습니다.

테스트 페이지에서 ApplicationTests를 실행해도

메인 페이지의 ItemServiceApplication이 실행 중입니다.

ItemServiceApplicationTests 내부 코드에 TestDataInit를 bean으로 추가하는 코드가 없기 때문에 테스트 프로필이 로컬인지는 중요하지 않을 수 있지만 ItemServiceApplicationTests가 실행될 때 메인 페이지에서 ItemServiceApplication이 실행됩니다.

따라서 테스트 프로필이 로컬인 경우 테스트 데이터는 TestDataInit 빈이 생성될 때 추가됩니다.


1) 데이터 접근 기술 - 시작 5


1) 데이터 접근 기술 - 시작 6

테스트 코드

다음은 테스트 코드의 일부입니다.

@AfterEach
void afterEach() {
    //MemoryItemRepository 의 경우 제한적으로 사용
    if (itemRepository instanceof MemoryItemRepository) {
        ((MemoryItemRepository) itemRepository).clearStore();
    }
}

매번 후에 :

테스트는 서로 영향을 주어서는 안 됩니다. 따라서 저장된 데이터는 각 테스트 후에 삭제해야 합니다.

@매번 각 테스트 실행이 끝날 때 호출됩니다.

이때 메모리를 완전히 지워서 다음 테스트에 영향을 주지 않도록 초기화한다.

인터페이스에 clearStore()가 없기 때문에 MemoryItemRepository인 경우에만 지독한을 통해

데이터를 초기화합니다.

나중에 배우게 됩니다

실제 DB를 사용하는 경우 테스트 완료 후 트랜잭션을 롤백하여 데이터를 초기화할 수 있습니다.

https://madplay.github.io/post/java-upcasting-and-downcasting

@Test
void findItems() {
    //given
    Item item1 = new Item("itemA-1", 10000, 10);
    Item item2 = new Item("itemA-2", 20000, 20);
    Item item3 = new Item("itemB-1", 30000, 30);

    itemRepository.save(item1);
    itemRepository.save(item2);
    itemRepository.save(item3);

    //둘 다 없음 검증
    test(null, null, item1, item2, item3);
    test("", null, item1, item2, item3);

    //itemName 검증
    test("itemA", null, item1, item2);
    test("temA", null, item1, item2);
    test("itemB", null, item3);

    //maxPrice 검증
    test(null, 10000, item1);

    //둘 다 있음 검증
    test("itemA", 10000, item1);
}

void test(String itemName, Integer maxPrice, Item... items) {
    List<Item> result = itemRepository.findAll(new ItemSearchCond(itemName, maxPrice));
    assertThat(result).containsExactly(items);
}

이름이 지정된 변수 인수를 통해 여러 매개변수를 가져올 수 있습니다.

https://jekal82.47

(가변 인수로 매개변수를 입력할 때 순서도 중요합니다.)

(JAVA) 자바 … 매개변수 = varargs( varargs )

Java에서 매개변수를 전달할 때 다음 코드를 보았을 수 있습니다. public void test(string… parameter) { content } 이것은 문자 그대로 여러 매개변수를 사용할 수 있음을 의미하는 가변 인수입니다.

jekal82.tistory.com

인터페이스를 테스트해보자

여기에서는 MemoryItemRepository 구현을 테스트하지 않습니다.

ItemRepository 인터페이스를 테스트하는 것을 볼 수 있습니다.

인터페이스에 대해 테스트하면 향후 다른 구현으로 변경됩니다.

동일한 테스트로 구현이 잘 되는지 쉽게 확인할 수 있습니다.

식별자 선택을 위한 권장 전략

데이터베이스 기본 키는 다음 세 가지 조건을 모두 충족해야 합니다.

1. Null 값은 허용되지 않습니다.

2. 고유해야 합니다.

3. 변하지 않아야 한다

테이블의 기본 키를 선택하기 위한 두 가지 주요 전략이 있습니다.

자연 키

비즈니스에 적합한 키

예: 주민등록번호, 이메일, 전화번호

예비 키

비즈니스와 관련이 없는 임의로 생성된 키로 대체 키라고도 합니다.

예: 오라클 시퀀스, 자동 증가ID, 키 생성 테이블 사용

자연 키보다 대리 키 선호

자연키와 스페어키는 둘 중 하나지만 가능하면 예비 키 사용을 권장합니다.하다.

예를 들어 전화번호를 자연키인 기본키로 선택하면 그 번호는 고유할 수 있지만

전화번호가 없거나 전화번호가 변경되었을 수 있습니다.

따라서 기본 키로 적합하지 않습니다. 문제는 사회보장번호처럼 그럴듯해 보이는 값이다.

이 값은 null이 아님, 고유 및 변경 불가능의 세 가지 조건을 모두 충족하는 것으로 보입니다.

하지만 현실과 비즈니스 규칙은 생각보다 빠르게 변화하고 있습니다. 사회보장번호는 다양한 이유로 변경될 수도 있습니다.

비즈니스 환경은 언젠가 바뀔 것입니다.

주민등록번호를 기본 키로 사용했는데 정부 정책이 바뀌어 주민등록번호를 합법적으로 저장할 수 없다면?

많은 응용 프로그램 논리뿐만 아니라 테이블을 변경해야 합니다.

데이터베이스의 원래 설계에 따라 자연키인 등록번호 대신,

비즈니스와 관련이 없는 예비 키를 사용하는 경우 변경할 사항이 많지 않습니다.

현재와 ​​미래의 기본 키 조건을 만족하는 자연 키를 찾는 것은 쉽지 않습니다.

대리 키는 엔터프라이즈 독립적인 임의의 값이므로 필요에 따라 기본 키가 거의 변경되지 않습니다.

대리 키를 기본 키로 사용하지만 다음과 같이 자연 키 후보인 열 B. 주민등록번호 또는 이메일,

필요한 경우 고유 인덱스를 설정하여 사용하는 것이 좋습니다.

JPA는 모든 엔터티에서 서로게이트 키를 일관되게 사용할 것을 권장합니다.

비즈니스 요구 사항은 끊임없이 변화하며 한 번 정의된 테이블은 변경하기 어렵습니다.

그런 점에서 외부 풍파에 그리 쉽게 흔들리지 않는다. 예비 키는 일반적으로 좋은 선택입니다.나는 찾는다.