Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

지석이의 일기

H2 DB + QueryDsl 테스트 해보자 본문

Java

H2 DB + QueryDsl 테스트 해보자

91년도에 철산에서 태어난 최지석 2023. 11. 12. 14:59

간단한 프로젝트를 테스트 하기 위해 DB를 설치하고 설정하기가 여간 귀찮지 않을 수 없다.

물론 추후에 배울 도커를 사용한다면 바로 DB환경을 만들 수 있겠지만, 그건 또 서버가 있어야 가능한 소리이다.

 

그래서 바로 설정이 가능한 H2 데이터 베이스를 사용 하자. 

H2 데이터 베이스는 간단하게 말하자면 내가 지금 작동중인 프로젝트 메모리 안에 작은 경량 DB를 만들어 그 안에서 사용하는것이다.

그러므로 매우 가볍고(경량화), 매우 빠르고(고성능) 라는 장점이 있다.

물론 단점으론, 큰 프로젝트에는 사용하지 못하고, 일관성이 유지 되기 힘들다는 점이 있다고 한다.

그러므로 테스트로만 사용하고 실질적인 프로젝트에는 다른 DBMS를 사용하자!

 

아래는 인터넷에서 찾아 검색한 H2의 장단점 요약이다.

장점:
경량화: H2는 매우 작은 크기의 라이브러리 (약 2MB)로, 복잡한 설치 과정 없이 간단하게 사용할 수 있습니다.
고성능: H2는 메모리 데이터베이스로서 빠른 데이터 처리 속도를 제공합니다. 따라서 작은 규모의 프로젝트에서 뛰어난 성능을 발휘합니다.
다양한 모드 지원: H2는 임베디드 모드와 서버 모드 등 다양한 동작 모드를 지원합니다. 이를 통해 사용자의 환경에 맞게 유연하게 설정할 수 있습니다.


단점:
일관성 문제: H2는 다수의 동시 사용자가 있을 때 일관성(Consistency)을 유지하는 데 어려움이 있을 수 있습니다. 이는 트랜잭션 처리에서 문제를 일으킬 수 있습니다.
대용량 데이터 처리 제한: H2는 중대형 규모의 데이터베이스에 비해 많은 양의 데이터를 처리하는 데 제한이 있는 편입니다.

 

기본적으로 H2는 프로젝트를 띄운 뒤에 종료 시, 저장된 데이터가 모조리 삭제된다.

계속 데이터를 살리기 위해서는 디스크 기반의 모드에서 추가 작업을 해야하는데, 그 정도 까지 할꺼면 그냥 다른 DBMS를 쓰자.

 

Gradle 추가

gradle에 QueryDsl을 설치하기에는 쫌 많은 작업이 필요하다.  의존성 주입에 h2도 같이 넣어준다.

plugins {
    id 'java'
    id 'org.springframework.boot' version '2.7.15'
    id 'io.spring.dependency-management' version '1.1.3'
    id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10' // Querydsl 플러그인 추가
}

 

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

 

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'

    // Spring Boot Starter Data JPA
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    // H2 Database
    runtimeOnly 'com.h2database:h2'
    // Querydsl 관련 의존성
    implementation 'com.querydsl:querydsl-jpa:5.0.0'
    annotationProcessor 'com.querydsl:querydsl-apt:5.0.0'
}

 

//querydsl 추가 시작
def querydslDir = "$buildDir/generated/querydsl"

querydsl {
    jpa = true
    querydslSourcesDir = querydslDir
}
sourceSets {
    main.java.srcDir querydslDir
}
compileQuerydsl{
    options.annotationProcessorPath = configurations.querydsl
}
configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
    querydsl.extendsFrom compileClasspath
}
//querydsl 추가 끝

 

application.properties 에 아래와 같이 넣어보자.

# H2 Database 설정
spring.datasource.url=jdbc:h2:mem:testdb;
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=jiseok
spring.datasource.password=jiseok
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console


# Querydsl 설정
spring.jpa.properties.hibernate.physical_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
spring.jpa.properties.hibernate.hbm2ddl.auto=create
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.jpa.show-sql=true

 

프로젝트 구조는 다음과 같다.

QueryDslConfig에서 entitymanager를 bean 등록해준다.

@Configuration
public class QueryDslConfig {

    @PersistenceContext
    private EntityManager entityManager;

    @Bean
    public JPAQueryFactory jpaQueryFactory() {
        return new JPAQueryFactory(entityManager);
    }
}

 

RestController에서 단순 데이터 접수 창구를 만들어준다.

@org.springframework.web.bind.annotation.RestController
@RequiredArgsConstructor
public class RestController {

    private final RestService restService;

    @RequestMapping("/GetAllUserCnt")
    public int GetAllUserCnt(){
        int cnt = restService.GetAllUserCnt();
        return cnt;
    }

    @RequestMapping("/UserSaveProc")
    public long UserSaveProc(String name){

        // 방금 삽입한 유저의 pk번호가 반환된다.
        long userId = restService.UserSaveProc(0, name);
        return userId;
    }

    @RequestMapping("/UserSearch")
    public List<User> UserSearch(User userSearch){

        List<User> userlist = restService.UserSearch(userSearch);
        return userlist;
    }
}

 

service 에서는 service 역활을 해준다.

@org.springframework.stereotype.Service
@RequiredArgsConstructor
public class RestService {

  private final RestInterface restInterface;

    public int GetAllUserCnt(){ 
      return (int)restInterface.GetAllUserCnt();
    }

    public long UserSaveProc(long userid , String name) {
      //이름만 받고 나이는 랜덤.
      User user = new User(userid, name, (int) (Math.random()*100));
      User result = restInterface.save(user);
      return  result.getId();
    }

  public List<User> UserSearch(User userSearch) { 
      return restInterface.UserSearch(userSearch);
  }
}

 

RestInterface에서 기본 JPA 레파지토리와 새로 만든 Querydsl용 custom을 상속 받아준다.

 

@Repository
public interface RestInterface extends JpaRepository<User, Long>, RestInterfaceCustom {


}

JpaRepository은 기본으로 제공되는 것이며, RestInterfaceCustom은 새로 만들어줘야한다.

public interface RestInterfaceCustom {

      long GetAllUserCnt();

      List<User> UserSearch(User userSearch);
}

@Repository
public class RestInterfaceCustomImpl implements   RestInterfaceCustom {
    private final JPAQueryFactory  jpaQueryFactory;

    public RestInterfaceCustomImpl(JPAQueryFactory jpaQueryFactory) {
        this.jpaQueryFactory = jpaQueryFactory;
    }

    @Override
    public long GetAllUserCnt(){
        QUser qUser = QUser.user;
        return   jpaQueryFactory
                        .select(qUser.count())
                        .from(qUser)
                        .fetchOne();
    }

    @Override
    public List<User> UserSearch(User user) {
        QUser qUser = QUser.user;
        BooleanBuilder builder = new BooleanBuilder();

        if (user.getName() != null) {
            builder.and(qUser.name.eq(user.getName()));
        }

        if (user.getAge() != 0) {
            builder.and(qUser.age.eq(user.getAge()));
        }

        return jpaQueryFactory
                .select(qUser)
                .from(qUser)
                .where(builder)
                .fetch();
    }
}

 

저기서 주구장창 사용하는 User 모델은 아래와 같다.

@Entity
@Data
@Table(name = "TB_USER")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private int age;

    public User(long ID,  String name, int age) {
        this.id = ID;
        this.name = name;
        this.age = age;
    }

    public User() {

    }
}

 

H2 DB구조에 의해 프로젝트 구동시 ,TB_USER라는 테이블이 생성되고, 유저 생성시 인메모리식으로 등록된다.

 

github uri : https://github.com/choichoi1233/practice1