SMALL

View

데이터베이스에서 View는 가상 테이블이다.

View를 사용하는 이유로는 여러 테이블을 조인하는 경우와 같이 복잡한 SQL을 간소화시키기 위해서입니다.

 

이러한 View를 Spring JPA에서 어떻게 사용하는지 알아보도록 합시다.

 

@Table

 

View 생성

CREATE OR REPLACE VIEW ROLE AS 
SELECT u.user_id,
       ut.user_id AS u_id
FROM   USER u
       JOIN user_team ut
         ON u.id = ut.user_id;

 

Entity 생성

@Entity
@Immutable
@Table(name = "role")
@Getter
public class Role {

    private String userId;
    
    @Id
    private UUID uId;

    private UUID teamId;

}

@Table, @Immutable Annotation을 추가하여 Entity 생성

@Table에는 View를 생성한 name을 작성

@Immutable - 해당 Entity에 매핑되는 것은 View이기 때문에 조회만 하고 수정은 존재하지 않는다.

 

이렇게 SQL View에 매핑되는 Entity를 작성할 수 있다.

추가적으로, @Subselect Annotation을 통해서도 가능하다.

 

@Subselect

해당 Annotation은 SQL View를 생성하지 않고 View를 생성할 때 사용하는 Join query를 Annotation에 설정하는 방식이다.

 

@Entity
@Immutable
@Subselect("SELECT u.user_id,\r\n" + 
        "       ut.user_id AS u_id,\r\n" + 
        "FROM   USER u\r\n" + 
        "       JOIN user_team ut\r\n" + 
        "         ON u.id = ut.user_id")
@Getter
public class Role {

    private String userId;
    
    @Id
    private UUID uId;

    private UUID teamId;

}

 

복합키 View ( @IdClass + @Table )

추가적으로, View를 생성하면 여러 테이블을 join 하여 생성하므로 일반적인 Entity와 같이 PrimaryKey가 하나 이상일 경우가 존재한다.

이러한 경우에는 @IdClass Annotation을 사용하여서 여러 Column을 조합하여 Primary Key를 설정할 수 있다.

 

View 생성

CREATE OR REPLACE VIEW ROLE AS 
SELECT u.user_id,
       ut.user_id AS u_id,
       tpr.team_id,
       tpr.page_id
FROM   USER u
       JOIN user_team ut
         ON u.id = ut.user_id
       JOIN uteam t
         ON t.id = ut.team_id
       JOIN team_page_role tpr
         ON t.id = tpr.team_id;

 

Entity 생성

@Entity
@Immutable
@Table(name = "role")
@Getter
@IdClass(RoleId.class)
public class Role {

    private String userId;
    private UUID uId;

    @Id
    private UUID teamId;

    @Id
    private UUID pageId;

}

이러한 식으로 Entity에 @Id를 한 개 이상 추가할 수 있다.

@IdClass Annotation 추가 해당 class 생성

 

 

@IdClass

@NoArgsConstructor
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class RoleId implements Serializable {

    private static final long serialVersionUID = 1122593031541339037L;

    @EqualsAndHashCode.Include
    UUID teamId;

    @EqualsAndHashCode.Include
    UUID pageId;
}

@IdClass를 사용하는 자세한 내용은 아래의 포스팅을 통해 확인할 수 있다. ( JPA 복합키 생성 )

https://joomn11.tistory.com/105

 

LIST
SMALL

Entity를 생성하다 보면 PK(primary key)가 여러 개가 되는 경우가 발생한다.

이러한 경우에 어떠한 방식으로 Entity를 작성해야 하는지 알아보도록 하자.

 

JPA에서는 두 가지 방식을 제공한다.

@IdClass, @EmbeddedId

각각의 사용법과 특징을 알아보도록 하자.

 

@IdClass

@Getter
@NoArgsConstructor
@Entity
@IdClass(MemberId.class)
public class Member {
	@Column
	private String name;
	
	@Id
	@Column
	private String teamId;
	
	@Id
	@Column
	private String teamName;
}

복합키가 존재하는 Entity에 @IdClass Annotation을 추가해주고, 복합키를 설정할 class명을 정해준다.

또한, 복합키로 지정할 컬럼에 @Id Annotation을 추가한다.

 

@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@NoArgsConstructor
public class MemberId implements Serializable {

	private static final long serialVersionUID = 5735022999922182249L;

	@EqualsAndHashCode.Include
	public String teamId;
	
	@EqualsAndHashCode.Include
	public String teamName;
}

IdClass의 작성시 지켜야 할 사항들이 존재한다.

  • Serializable을 implements 받아야 한다.
  • equal, hashCode method를 구현해야 한다. 위의 예제에서는 lombok을 이용하였다.
  • 기본 생성자가 존재해야 한다.
  • public class 이여야 한다.
  • Entity Class의 필드명과 동일한 필드를 가지고 있어야 한다. 

 

public interface MemberRepository extends JpaRepository<Member, MemberId>{

}

Repository를 작성 시에는 JpaRepository의 제너릭 타입에 Entity Class와 Entity Class의 Key값을 넣는데 복합키를 사용한 경우에는 위와 같이 넣어주어야 한다.

 

@EmbeddedId

@Getter
@NoArgsConstructor
@Entity
public class Member {
	@Column
	private String name;
	
	@EmbeddedId
	private MemberId memberId;
}

 

@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@NoArgsConstructor
@Embeddable
public class MemberId implements Serializable {

	private static final long serialVersionUID = 5735022999922182249L;

	@EqualsAndHashCode.Include
	public String teamId;
	
	@EqualsAndHashCode.Include
	public String teamName;
}

 

이렇게 두 가지의 형식이 존재하는데 각각의 선언하는 방식도 다르지만 실제로 쿼리를 조회한 후에 값을 조회할 때도 차이가 존재한다.

 

@IdClass의 경우에는 member.getTeamId()와 같이 사용

@EmbeddedId의 경우에는 member.getMemberId().getTeamId()와 같이 사용해야 한다. 

LIST
SMALL

정규표현식(Regular Expression)

정규표현식(Regular Expression)은 특정한 규칙을 가진 문자열의 집합을 표현하는 데 사용하는 형식 언어이다.

정규 표현식은 이메일, 전화번호 등 사용자가 입력한 값을 검증할 때 사용된다. (유효성 검사)

 

정규 표현식 문법

 

Character 

Character explain
\ 특수 문자가 아닌 문자
. 어떤 문자 1개를 의미
\d digit 숫자
\D digit 숫자 아님
\w [a-zA-Z_]와 동일 ( 모든 알파벳 + _ )
\W [^a-zA-Z_]와 동일
\s space 공백
\S space 공백 아님

 

Quantifiers

Character explain
? 1 or 0
* 0번 이상
+ 1번 이상
{n} n번 반복
{min,} min이상 반복
{min,max} min이상 max이하 반복

 

Boundary-Type

Character explain example
\b 단어 경계(boundary) \btest\b : test tests test 
\B 단어 경계 아님 \Btest\B : test Gtests tests
^ 문장의 시작 ^test : tests aaa test
$ 문장의 끝  

 

Groups and Ranges

Character explain
|  or
( )  그룹
[ ] 문자셋, 괄호안의 어떤 문자든
[^ ] 부정 문자셋

 

연습 

https://regexr.com/

해당 사이트를 이용해 실시간으로 테스트 가능 및 reference 정보 제공 

 

public class TestAA {

    public static void main(String[] args) {
        String result;
        result = "The cat sat on the mat. and the cat".replaceAll("^sat", "*");
        System.out.println(result);
        
        String pattern1 = "[abc][vz]";
        assertTrue("av".matches(pattern1));
        assertFalse("ac".matches(pattern1));

    }
}

Test용 클래스를 작성하여 java application을 실행하며 결괏값 확인 

 

자주 사용되는 정규 표현식

이메일

\w+@\w+\.\w+(\.\w+)?

해당 정규식을 분해해서 보면 

\w+ _를 포함한 알파벳 한개 이상 
@ @
\w+ _를 포함한 알파벳 한개 이상 
\. 정규 표현식에 .이 존재하므로 해당 내용이 아닌 문자.을 나타내기위해서 \.으로 표현
\w+ _를 포함한 알파벳 한개 이상 
(\.\w+)? .co.kr과 같이 .kr이 존재하는 경우를 커버 
?을 이용해 0 or 1개 존재 

 

^.*$ ( 모든 문자 선택) 

^ 문장의 시작
.* 모든 문자 0개 이상 
$ 문장의 끝

 

특수 Case

 

*? or +?

?는 수량자의 역할을 한다고 소개하였다. ( 0 or 1)

하지만 + or *과 같이 사용되면 다른 용도로 사용이 된다. (*? or +? )

lazy로 표현되며 처음에 발견했을 때 쉬고 다시 찾는다.

Hi, Ut, Et, At, Tt.
,.*, 와 같은 정규 표현식을 찾는다고 해보자

결과는 위와 같다.

이 상황에서 *뒤에 ?를 추가한다면 -> *?

결과는 이렇게 달라진다.

 

(?:)

다음으로 Grouping 관련 ? 의 예시이다.

(?:)와 같이 사용되는 경우에는 정규표현식을 통해 찾지만 기억하지는 않음을 나타낸다.

 

위와 같이 ()를 통해 그룹핑한 정보를 가지고 있다.

 

?:를 추가하면 group정보가 나타나지 않는다.

 

java code를 예시로 보면

?: 를 사용하지 않은 경우에는 group으로 지정한 정보를 mather.group(index)를 통해서 조회 가능하다.

?:를 사용하게 되면 group정보가 저장되지 않아 해당 정보를 가져올수없게 된다.

LIST
SMALL

DTO 데이터 검증이 필요한 이유 

보통 Web Appliation을 작성하면, Front-End에서 사용자가 입력한 정보를 검증하고, 검증이 완료된 데이터를 Back-End에게 넘겨주게 된다.

하지만, 보통의 경우가 아닌 상황에서는 Back-End에서는 입력값으로 받은 DTO를 신뢰할 수가 없게 된다.

( Web Application UI를 통해 요청된 Request가 아닌 경우 - curl, postman 등등..)

이러한 상황에 대한 대비로 Back-End에서도 Request로부터 전달받은 DTO에 대한 검증이 필요하다.

 

@Valid

Spring에서는 DTO 필드에 조건과 메세지를 작성해주면, @Valid를 이용해 데이터 검증을 할 수 있다.

 

@Valid는 JSR-303, 380 표준 스펙으로

제약 조건이 부여된 객체에 대해 Beam Validator를 이용하여 검증하도록 지시하는 Annotation이다.

Spring에서는 LocalValidatorFactoryBean을 이용해 JSR 표준의 검증 기능을 사용한다.

 

SpringBoot 2.3 이상의 버전 부터는 spring-boot-starter-web 의존성 내부에 있던 validation이 사라졌다.

spring-boot-starter-validation 의존성을 따로 추가해 주어야 한다. 

 

Dto

@Getter
@NoArgsConstructor
public class UserSaveRequestDto {

    @NotBlank(message = "Please, insert UserID!")
    private String userId;

    @NotBlank(message = "Please, insert UserName!")
    private String userName;

    @Email(message = "Please, insert valid e-mail!",
            regexp = "^(?=.{1,64}@)[A-Za-z0-9_-]+(\\.[A-Za-z0-9_-]+)*@[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$")
    private String email;

    @NotBlank(message = "Please, insert password!")
    private String password;

    @Size(min = 1, message = "User must be mapped with Group")
    @NotNull(message = "Please, insert GroupIdList!")
    private Set<String> groupIdList;
}

javax.validation.constraints에서 제공하는 valid 관련 Annotation 

https://docs.jboss.org/hibernate/beanvalidation/spec/2.0/api/

 

 

Controller

@RequiredArgsConstructor
@RestController
public class UserController {
    private final UserService userService;

    @PostMapping("/user")
    public String save(@RequestBody @Valid UserSaveRequestDto requestDto) throws Exception {
        return userService.save(requestDto);
    }
}

검증을 원하는 @RequestBody argument에 @Valid Annotation을 추가해 주면 된다.

 

Exception Handling

@ControllerAdvice
public class CommonExceptionHandler<T> {

    @ExceptionHandler(value = {MethodArgumentNotValidException.class})
    protected ResponseEntity<Response<T>> handleMethodArgNotValidException(
            MethodArgumentNotValidException exception) {

        Response<T> response = new Response<>();
        response.addErrorMsgToResponse(
                exception.getBindingResult().getAllErrors().get(0).getDefaultMessage()
        );

        return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE).body(response);
    }
}

@RequestBody로 부터 받은 파라미터에 Error가 발생한 경우에는 MethodArgumentNotValidException이 발생한다.

Spring의 Exception Handling 관련 Annotation을 이용하여 해당 Exception에 대한 처리를 추가해 주면 된다.

LIST

+ Recent posts