반응형

@Component

개발자가 직접 작성한 class 전체를 Spring Bean 으로 등록

하위로 @Controller, @Service, @Repository 어노테이션이 있다

@Component
public class MyComponent {
// 클래스 내용
}

 

@Configuration

스프링에게 해당 클래스가 Bean 구성 Class 임을 알리는 어노테이션이다

즉, Bean 등록용 class로 사용할 수 있고 일반 메소드 또한 만들수있지만 주로 Bean 을 보조하는 역할을 한다

 

@Bean

외부 라이브러리 등 반환되는 객체를 Bean 으로 등록한다

쉽게말하면 @Bean 어노테이션을 사용하면 new ~ 로  생성해서 import 하지않아도 된다

 

스프링 간 객체가 의존관계를 관리하도록 하는 것에 가장 큰 목적이다

new 로 생성하면 생성자를 그득그득 붙여서 변경가능성이 있는데, Bean 을 사용하면 변경이 안되겠지

@Configuration //여기는 @Bean 등록 전용 class!
public class TestConfig {

    @Bean
    public MyBean myBean() {
      return new MyBean();
    }
    
    @Bean(name="myMethod")
    public MyBean myBean() {
      return new MyBean();
    }
    
    // @Bean 어노테이션이 없는 메소드
    public SomeConfiguration someConfiguration() {
      // 구성 정보 반환
      return new SomeConfiguration();
    }
}
728x90
반응형

@RequestParam

Client 요청 URI 에서 전달되는 파라미터를 메소드 인자와 매칭
휴먼에러 방지를 위해 name 속성을 같이 쓰는게 좋음!

@GetMapping(value = "/main")
public String index(@RequestParam String nickname, 
                    @RequestParam(name="age") String age) {
        return "";
}

 

@RequestBody

Client 에서 서버로 요청 시 복수의 parameter 를 전달할 때 사용
json 기반의 Http Body 를 컨트롤러에서 자바객체로 변환 (Client 에서 보낸 json body를 객체형태로 매핑)

Client 에서 보내고싶은 데이터가 여러개 있을 경우 주로 사용된다.

아래와 같이 param 안에 데이터를 넣고

var param = {
      title : "1111",
      content : "hello",
      userName : "admin"
  }

 

 

해당 어노테이션을 넣으면 컨트롤러에서 해당 데이터를 객체로 자동 매핑한다

이때 유의할 점은 PostDto 에서 선언된 변수명과 param 에 들어있는 변수명이 완벽하게 같아야 한다

public class PostDto {
    private String title;
    private String content;
    private String userName;
}

/////
@PostMapping(value = "/index-request")
public String indexRequest(@RequestBody PostDto postDto) {
        return "/user/index";
}

 

만약 변수명이 같지 않은 경우 별도로 에러는 발생하지 않으며, 해당 변수만 해당 객체에 매핑되지 않는다

 

 

@ResponseBody

Client 로 data 반환 시 사용하며 객체 data 를 json 으로 바꿔 반환한다

해당 어노테이션의 위치는 return type 객체 앞에 써도 되지만, 나는 개인적으로 방법 2를 선호한다

// 방법 1
@GetMapping(value = "/index-rest")
public @ResponseBody ResponseEntity indexRest() {
    //json type으로 반환하기 위하여 @ResponseBody 어노테이션이 필요함
    return ApiResponseEntity
            .builder()
            .ok();
}

// 방법 2
@ResponseBody
@GetMapping(value = "/index-rest2")
public ResponseEntity indexRest2() {
    //@ResponseBody 어노테이션은 return type 앞에 위치한것보다 현재의 형태가 제일 깔끔해 보인다
    return ApiResponseEntity
            .builder()
            .ok();
}

 

위와 같은 코드로 컨트롤러에서 데이터를 보내면 Client 에서는 아래와 같은 형태로 받게된다

var response = {
        "data": {
            "loginId": "admin"
        },
        "status": 200
  }

 

자세한 코드는 아래 깃헙 주소에서 basic폴더의 README 참고

https://github.com/Daseul727/BackEnd-Skill-Up.git

 

728x90
반응형

@Controller

- 일반적인 Spring MVC 컨트롤러

- 주로 view 를 반환하기 위하여 사용됨

 

View 반환

Return Type 은 String 으로 설정한다

return 에 들어오는 경로의 화면을 View Resolver 를 통해 반환한다

@Controller
@RequestMapping("/main")
public class MainController {

    @GetMapping(value = "/index")
    public String index(){
        return "/user/index";
    }
}

 

요청 처리 과정

1. Client 에서 URI 형식으로 요청

2. DispatcherServlet 에서 요청을 처리할 대상 조회

3. HandlerAdapter 를 통해 요청을 Controller 로 위임

4. Controller 에서 서비스로직 실행 후 view 반환

5. DispatcherServletViewResolver 를 통해 View 를 찾아 반환

 

View + Data 반환

Return Type 은 String 으로 설정한다

model 에 들어있는 data를 view와 함께 반환한다

@Controller
@RequestMapping("/main")
public class MainController {

    @GetMapping(value = "/index-model")
    public String indexModel(Model model){
        model.addAttribute("user", "");
        return "/user/index";
    }

    @GetMapping(value = "/index-param")
    public String indexParam(Model model, @RequestParam("userName") String userName){
        model.addAttribute("user", userName);
        return "/user/index";
    }

}

 

요청 처리 과정

1. Client 에서 URI 형식으로 요청

2. DispatcherServlet 에서 요청을 처리할 대상 조회

3. HandlerAdapter 를 통해 요청을 Controller 로 위임

4. Controller 에서 서비스로직 실행 후 viewData 를 함께 반환

 

Data 반환

페이지 이동없이 데이터만 Json 객체로 반환한다

@Controller
@RequestMapping("/main")
public class MainController {

    @GetMapping(value = "/index-rest")
    public @ResponseBody ResponseEntity indexRest() {
        //json type으로 반환하기 위하여 @ResponseBody 어노테이션이 필요함
        return ApiResponseEntity
                .builder()
                .ok();
    }
}

 

하지만 이런 경우 @Controller 를 쓰기보단 아래의 @RestController 어노테이션을 사용한다

 

@RestController

- RestFul 웹서비스의 컨트롤러

- @Controller + @RequestBody 의 기능을 한다

- ResponseEntity 형식으로 return

   ㄴ HttpStatus, data 를 Json 객체로 반환

 

 

Data 반환

페이지이동 없이 Data 만 Json 객체로 반환한다

아래 코드의 경

@RestController
@RequestMapping("/api/main")
public class MainRestController {

    @ResponseBody
    @GetMapping("")
    public ResponseEntity index() {
        return ResponseEntity
                .ok();
    }
}

 

요청 처리 과정

1. Client 에서 URI 형식으로 요청

2. DispatcherServlet 이 요청을 처리

3. HandlerAdapter 를 통해 요청을 Controller로 위임

4. Controller 에서 서비스로직 실행 후 객체 반환

5. 반환 Json 으로 Serialize 되어 Client 반환

 

 

viewResolver 필요한 프로젝트는 @Controller

api기능만 사용할 프로젝트는 @RestController 사용하면 된다!

728x90
반응형

아래와 같은 관계도가 있는 테이블이 있다.

 

위에 그림 두개는 DB 관계도, 아래의 이미지는 자바단 부모 @Entity 쪽 @OneToMany 설정이다.

 

원래는 LAZY를 쓰고싶지만 회사 ..사정상 EAGER를 쓰고있다.

 

 

편의상 OP_REGULAR_ROUND => OP_ROUND

OP_USER_REGULAR_ROUND -> OP_ROUND_ORDER 로 바꿔서 설명하겠다.

 

1. JPA N+1 이슈

 

내가원하는 SQL문은 아래와 같다.

SELECT RR.ROUND, OR.*
FROM OP_ROUND OR
         INNER JOIN OP_ROUND_ORDER RR ON OR.CODE = RR.CODE
WHERE OR.CODE = '1111'
ORDER BY RR.ROUND DESC;

조인을 하고 코드를 파라미터로 넘겨준다.

 

1) service와 같은 root 에 repository를 생성 - 나는 일단 impl 생성했다.

public interface RoundCustom {
    /**
     * 설명 ~~~~
     * @return
     */
    Round getDetail(String code);
}
import static saleson.model.QRound.round;
import static saleson.model.QRoundOrder.roundOrder;

@RequiredArgsConstructor
public class RoundCustomImpl implements RoundCustom{
    private final JPAQueryFactory jpaQueryFactory;

    @Override
    public Round getDetail(String roundCode) {
    }
}

querydsl에서 사용할 q클래스와 JPAQeuryFactory 를 선언해줬다.

 

그리고 리포지토리에 extends 추가해준다.

사실 커스텀안하고 별도 리포지토리를 생성하고 거기에 관리해도된다.

public interface RoundRepository extends JpaRepository<Round, Long>, QuerydslPredicateExecutor<Round>, RoundCustom {
    Optional<Round> findByCode(String code);
}

 

 

2) QUERY DSL를 사용 할 메소드 생성

다시 RoundCustomImpl에 돌아와서..

public Round getDetail(String code) {
    
    //builder 를 따로 뺀 이유는, 이후에 where 조건이 추가될수도 있기 때문입니다.
    BooleanBuilder builder = new BooleanBuilder();
    builder.and(round.code.eq(code));

    }

querydsl 생성에 앞서 먼저 builder 를 만들어준다. 

 

where 조건에 들어갈 부분을 넣으면 된다.

 

public Round getDetail(String code) {
    
    //builder 를 따로 뺀 이유는, 이후에 where 조건이 추가될수도 있기 때문입니다.
    BooleanBuilder builder = new BooleanBuilder();
    builder.and(round.code.eq(code));

    return jpaQueryFactory
            .select(round)
            .from(round)
            .innerJoin(round.list,roundOrder) //oneToMany
            .fetchJoin()
            .where(builder)
            .orderBy(roundOrder.round.desc())
            .fetchOne();
    }

이게 완성된 로직인데, innerjoin에 들어가는 파라미터는, 부모@Entity에 @OneToMany로 걸려있는 변수를, 뒤에는 조인할 q클래스명을 입력해줬다.

 

이렇게 돌리면

select regularrou0_.ID                         as id1_47_0_,
       userregula1_.ID                         as id1_60_1_,
       regularrou0_.CREATED                    as created2_47_0_,
       regularrou0_.CREATED_BY                 as created_3_47_0_,
       regularrou0_.UPDATED                    as updated4_47_0_,
       regularrou0_.UPDATED_BY                 as updated_5_47_0_,
       regularrou0_.BUYER_EMAIL                as buyer_em6_47_0_,
       regularrou0_.BUYER_MOBILE               as buyer_mo7_47_0_,
       regularrou0_.BUYER_NAME                 as buyer_na8_47_0_,
       regularrou0_.CARD_NAME                  as card_nam9_47_0_,
       regularrou0_.INDEX_ROUND                as index_r10_47_0_,
       regularrou0_.ITEM_NAME                  as item_na11_47_0_,
       regularrou0_.ITEM_USER_CODE             as item_us12_47_0_,
       regularrou0_.RECEIVE_NAME               as receive13_47_0_,
       regularrou0_.REGULAR_ROUND_END_DATE     as regular14_47_0_,
       regularrou0_.REGULAR_ROUND_START_DATE   as regular15_47_0_,
       regularrou0_.STATUS                     as status16_47_0_,
       regularrou0_.TOTAL_ROUND                as total_r17_47_0_,
       regularrou0_.USER_ID                    as user_id18_47_0_,
       regularrou0_.USER_REGULAR_CARD_ID       as user_re19_47_0_,
       regularrou0_.USER_REGULAR_ROUND_CODE    as user_re20_47_0_,
       userregula1_.CREATED                    as created2_60_1_,
       userregula1_.CREATED_BY                 as created_3_60_1_,
       userregula1_.UPDATED                    as updated4_60_1_,
       userregula1_.UPDATED_BY                 as updated_5_60_1_,
       userregula1_.DISCOUNT_RATE              as discount6_60_1_,
       userregula1_.FAIL_COUNT                 as fail_cou7_60_1_,
       userregula1_.ITEM_ID                    as item_id8_60_1_,
       userregula1_.ITEM_NAME                  as item_nam9_60_1_,
       userregula1_.ITEM_SEQUENCE              as item_se10_60_1_,
       userregula1_.ORDER_CODE                 as order_c11_60_1_,
       userregula1_.ORDER_DATE                 as order_d12_60_1_,
       userregula1_.ORDER_SEQUENCE             as order_s13_60_1_,
       userregula1_.ORDER_STATUS               as order_s14_60_1_,
       userregula1_.PAY_AMOUNT                 as pay_amo15_60_1_,
       userregula1_.PAY_DATE                   as pay_dat16_60_1_,
       userregula1_.PAYMENT_INTERVAL           as payment17_60_1_,
       userregula1_.QUANTITY                   as quantit18_60_1_,
       userregula1_.ROUND                      as round19_60_1_,
       userregula1_.SHIPPING_CANCEL_END_DATE   as shippin20_60_1_,
       userregula1_.SHIPPING_CANCEL_START_DATE as shippin21_60_1_,
       userregula1_.SHIPPING_READY_END_DATE    as shippin22_60_1_,
       userregula1_.SHIPPING_READY_START_DATE  as shippin23_60_1_,
       userregula1_.USE_FLAG                   as use_fla24_60_1_,
       userregula1_.USER_DELIVERY_ID           as user_de25_60_1_,
       userregula1_.USER_ID                    as user_id26_60_1_,
       userregula1_.USER_REGULAR_CARD_ID       as user_re27_60_1_,
       userregula1_.USER_REGULAR_ROUND_CODE    as user_re28_60_1_,
       userregula1_.USER_REGULAR_ROUND_CODE    as user_re28_60_0__,
       userregula1_.ID                         as id1_60_0__
from OP_ROUND regularrou0_
         inner join OP_ROUND_ORDER userregula1_
                    on regularrou0_.CODE = userregula1_.code
where regularrou0_.CODE = '1111'
order by userregula1_.ROUND desc

 

이런식으로 내가원하던 조인문이 나온다.

 

 

이때 fetchJoin을 안쓰면

select regularrou0_.ID                       as id1_47_,
       regularrou0_.CREATED                  as created2_47_,
       regularrou0_.CREATED_BY               as created_3_47_,
       regularrou0_.UPDATED                  as updated4_47_,
       regularrou0_.UPDATED_BY               as updated_5_47_,
       regularrou0_.BUYER_EMAIL              as buyer_em6_47_,
       regularrou0_.BUYER_MOBILE             as buyer_mo7_47_,
       regularrou0_.BUYER_NAME               as buyer_na8_47_,
       regularrou0_.CARD_NAME                as card_nam9_47_,
       regularrou0_.INDEX_ROUND              as index_r10_47_,
       regularrou0_.ITEM_NAME                as item_na11_47_,
       regularrou0_.ITEM_USER_CODE           as item_us12_47_,
       regularrou0_.RECEIVE_NAME             as receive13_47_,
       regularrou0_.REGULAR_ROUND_END_DATE   as regular14_47_,
       regularrou0_.REGULAR_ROUND_START_DATE as regular15_47_,
       regularrou0_.STATUS                   as status16_47_,
       regularrou0_.TOTAL_ROUND              as total_r17_47_,
       regularrou0_.USER_ID                  as user_id18_47_,
       regularrou0_.USER_REGULAR_CARD_ID     as user_re19_47_,
       regularrou0_.USER_REGULAR_ROUND_CODE  as user_re20_47_
from OP_ROUND regularrou0_
         inner join OP_ROUND_ORDER userregula1_
                    on (regularrou0_.CODE = userregula1_.CODE)
where regularrou0_.CODE = '1111'
order by userregula1_.ROUND desc;

select userregula0_.USER_REGULAR_ROUND_CODE    as user_re28_60_0_,
       userregula0_.ID                         as id1_60_0_,
       userregula0_.ID                         as id1_60_1_,
       userregula0_.CREATED                    as created2_60_1_,
       userregula0_.CREATED_BY                 as created_3_60_1_,
       userregula0_.UPDATED                    as updated4_60_1_,
       userregula0_.UPDATED_BY                 as updated_5_60_1_,
       userregula0_.DISCOUNT_RATE              as discount6_60_1_,
       userregula0_.FAIL_COUNT                 as fail_cou7_60_1_,
       userregula0_.ITEM_ID                    as item_id8_60_1_,
       userregula0_.ITEM_NAME                  as item_nam9_60_1_,
       userregula0_.ITEM_SEQUENCE              as item_se10_60_1_,
       userregula0_.ORDER_CODE                 as order_c11_60_1_,
       userregula0_.ORDER_DATE                 as order_d12_60_1_,
       userregula0_.ORDER_SEQUENCE             as order_s13_60_1_,
       userregula0_.ORDER_STATUS               as order_s14_60_1_,
       userregula0_.PAY_AMOUNT                 as pay_amo15_60_1_,
       userregula0_.PAY_DATE                   as pay_dat16_60_1_,
       userregula0_.PAYMENT_INTERVAL           as payment17_60_1_,
       userregula0_.QUANTITY                   as quantit18_60_1_,
       userregula0_.ROUND                      as round19_60_1_,
       userregula0_.SHIPPING_CANCEL_END_DATE   as shippin20_60_1_,
       userregula0_.SHIPPING_CANCEL_START_DATE as shippin21_60_1_,
       userregula0_.SHIPPING_READY_END_DATE    as shippin22_60_1_,
       userregula0_.SHIPPING_READY_START_DATE  as shippin23_60_1_,
       userregula0_.USE_FLAG                   as use_fla24_60_1_,
       userregula0_.USER_DELIVERY_ID           as user_de25_60_1_,
       userregula0_.USER_ID                    as user_id26_60_1_,
       userregula0_.USER_REGULAR_CARD_ID       as user_re27_60_1_,
       userregula0_.USER_REGULAR_ROUND_CODE    as user_re28_60_1_
from OP_ROUND_ORDER userregula0_
where userregula0_.CODE = 'R20230216100309';

 

이런식으로 두번나온다.

 

하지만 fetchJoin이 만사형통은 아니다.. 페이징은 쓸수없기에....

 

참고로 해당 sql문은 콘솔에서 확인할수있는데

이런식으로 쓱- 보고 지나가기 쉽다. 하지만 꼭 작업할때 긁어서 실제로 sql문을 육안으로 확인해보는 습관을 길러야한다.

 

2. querydsl 결과를 특정 DTO에 리턴, 서브쿼리받는법

 

내가 받고싶은 sql문

select RR.USER_ID,
       RR.USER_REGULAR_ROUND_CODE,
       RR.ITEM_NAME,
       RR.REGULAR_ROUND_START_DATE,
       RR.TOTAL_ROUND,
       RR.BUYER_NAME,
       RR.CREATED,
       min(OURR.PAY_DATE),
       min(OURR.PAYMENT_INTERVAL),
       OURR.SHIPPING_READY_START_DATE
from OP_ROUND RR
inner join OP_ROUND_ORDER OURR
        on RR.CODE = OURR.CODE
where OURR.ORDER_CODE is null
  and RR.CODE = (select CODE
                  from OP_ROUND_ORDER
                  where ORDER_CODE = '2222')

 

특정 결과만 dto에 리턴받고싶을때

 

@Override
    public dtoA method2(String orderCode){

		//서브쿼리에 넣을 where 절
        BooleanBuilder subBuilder = new BooleanBuilder();
        subBuilder.and(roundOrder.orderCode.eq(orderCode));

		//메인쿼리에 넣을 where 절
        BooleanBuilder builder = new BooleanBuilder();
        builder.and(roundOrder.orderCode.isNull());
        builder.and(round.code.eq(
                JPAExpressions.select(roundOrder.code)
                        .from(roundOrder)
                        .where(subBuilder)
        ));

        return jpaQueryFactory
                .select(Projections.constructor(dtoA.class,
                        round.userId,
                        round.userRegularRoundCode,
                        round.itemName,
                        round.regularRoundStartDate,
                        round.totalRound,
                        round.buyerName,
                        round.created,
                        roundOrder.payDate.min(),
                        roundOrder.paymentInterval.min())) //그룹함수는 여기서
                .from(round)
                .join(round.list,roundOrder)
                .where(builder)
                .fetchOne();
    }

서브쿼리는 JPAExpressions 를 사용해서 조인시킨다.

역시 이때도 where 조건이 많아지면 너무 산만하기때문에 상단에 따로 선언하고 사용하였다.

 

Projections.contructor 사용하면 dto로 받을수있다.

첫 파라미터에 리턴받고싶은 dto 클래스를 추가하고 뒤에는 전부 내가받고싶은 컬럼명 나열한다.

 

이때 해당 dto에 해당 컬럼을 받는 생성자가 선언되어있어야한다.

그룹함수는 select할때 바로 쓰면된다.

 

728x90
반응형

주어진배열에서 제일 많이 나온 값을 도출

array = {1,3,3,4,4,4,4,5}

 

= 4

 

int[] array = {1};

        int answer = 0;
        HashMap<Integer, Integer> map = new HashMap<>();
		
        if (array.length == 1) return array[0];
        
        int[] list = array;
        int t = 1;
        for (int i = 0 ; i<array.length ; i++) {
            for(int j = 1 ; j < list.length-1 ; j ++) {
                if (list[j] == array[i]) {
                    t++;
                    map.put(list[j],t);
                }
            }
            t = 0;
        }

        int test = 0;
        for(int key : map.keySet()) {
            if (map.get(key) > test) {
                test = map.get(key);
                answer = key;
            } else if (map.get(key) == test) {
                answer = -1;
            }
        }
        return answer;

이렇게 했는데 한 케이스만 안됐다.

 

맵으로 어케안되나? 생각하던 중 아래 케이스를 발견했다.

 

성공적인 케이스

import java.util.*;
class Solution {
    public int solution(int[] array) {
        int maxCount = 0;
        int answer = 0;
        Map<Integer, Integer> map = new HashMap<>();
        for(int number : array){
        
           // count = 맵에 저장된 number의 카운트+1 혹은 0 +1
            int count = map.getOrDefault(number, 0) + 1;
            
            //최초 & 기존에 저장된 number를 다시만났을때 오는 곳
            if(count > maxCount){
                maxCount = count;
                answer = number;
            }
            else  if(count == maxCount){
            //count = 1 즉 해당글자가 첫번째 글자일때 들어오는 공간
                answer = -1;
            }
            map.put(number, count);
        }
        return answer;
    }
}

728x90

'BackEnd > 알고리즘' 카테고리의 다른 글

leetcode 21. Merge Two Sorted Lists  (0) 2024.07.31
leetcode 234. Palindrome Linked List  (0) 2024.07.27
[LeetCode] 1. Two Sum  (0) 2024.07.10
[LeetCode] 1598. Crawler Log Folder  (0) 2024.07.10
대소문자 변환  (0) 2023.02.20

+ Recent posts