728x90

Error 1

현상 @Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();}

등록시 경로지정을 안해줬다는 에러발생.
찾은것 stackoverflow.com/questions/64822250/illegalstateexception-after-upgrading-web-app-to-spring-boot-2-4
결론 원인은 스프링에서도 component-scan 을 재지정해줌
스프링 부트 그대로 사용하기위해 xml 빈을 안만드려고 다른방법 찾아다니다. 하기 방법발견. 
@Bean
WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> enableDefaultServlet() {
return (factory) -> factory.setRegisterDefaultServlet(true);
}
728x90

GIT : github.com/miseongshin/diary/tree/forValidationInBlog


INDEX

 

1. @Valitated

  • CustomerController.java

2. Custom Validate Annotation

  • CustomerSignUpData.java
  • SameValue.java
  • SameValueValidator.java

3. Validation Annotation, BindingResult

  • CustomerController.java
  • CustomerSignUpData.java
  • DiaryValidErrorException.java
  • DiaryExceptionHandlers.java
  • ValidErrorData.java
  • ValidErrorResultData.java

4. Sequences(or order)

  • CustomerController.java
  • CustomerSignUpData.java
  • Sequences.java

 


1.  pom.xml 

org.hibernate.validator is  for the Validated field

com.google.cod.gson is for JUnit5

        <!-- valid annotation for  @Valid  -->
        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>6.0.13.Final</version>
        </dependency>
        <!-- for json parameter for java bean  -->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

2. CustomerController.java

@Valitated(Sequence.class) is for Sequences(or order)

@Validated(Sequences.class) @RequestBody CustomerSignUpData customerSignUpData, BindingResult bindingResult) throws DiaryValidErrorException

and

if (bindingResult.hasErrors()) {
throw new DiaryValidErrorException(bindingResult, messageSource);
}

are for @Valitated, BindingResult

    @PostMapping("/signUp/ajax")
    @ResponseStatus(HttpStatus.CREATED)
    public ResponseEntity<Map> signUpAjax(HttpServletRequest request, @Validated(Sequences.class) @RequestBody CustomerSignUpData customerSignUpData, BindingResult bindingResult) throws DiaryValidErrorException {

        if (bindingResult.hasErrors()) {
            throw new DiaryValidErrorException(bindingResult, messageSource);
        }

        Map result = new HashMap<>();

        return ResponseEntity.ok(result);
    }

3. CustomerSignUpData.java

@SameValue(field = "password", fieldMatch = "confirmPassword") is for Custom Validation

groups= Sequences.OrderXX.class is Sequences(or order),

package com.today10sec.diary.customize.dto;

import com.today10sec.diary.customize.validator.SameValue;
import com.today10sec.diary.customize.validator.Sequences;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;

@Setter
@Getter
@AllArgsConstructor
@ToString(exclude = {"password", "confirmPassword"})
@SameValue(field = "password", fieldMatch = "confirmPassword", groups= Sequences.Order6.class)
public class CustomerSignUpData {


    @NotEmpty(groups = Sequences.Order1.class)
    @Length(max=320, groups= Sequences.Order2.class)
    @Email(groups= Sequences.Order2.class)
    private String email;

    @NotEmpty(groups= Sequences.Order3.class)
    @Length(min= 6, max= 12, groups= Sequences.Order4.class)
    @Pattern(regexp="[a-zA-Z1-9]{6,12}",groups= Sequences.Order4.class)
    private String password;


    @NotEmpty(groups= Sequences.Order5.class)
    @Length(min= 6, max= 12, groups= Sequences.Order7.class)
    private String confirmPassword;

}

4. SameValue.java

this source for Custom Validation

package com.today10sec.diary.customize.validator;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Constraint(validatedBy = SameValueValidator.class)
public @interface SameValue {

    String field();

    String fieldMatch();

    String message() default "{come.today10sec.diary.error.valid.samePassword}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

}

5. SameValueValidator.java

this source for Custom Validation

package com.today10sec.diary.customize.validator;

import org.springframework.beans.BeanWrapperImpl;
import org.springframework.stereotype.Component;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

@Component
public class SameValueValidator implements ConstraintValidator<SameValue,Object> {

    private String field;
    private String fieldMatch;

    @Override
    public void initialize(SameValue constraintAnnotation) {
        this.field = constraintAnnotation.field();
        this.fieldMatch = constraintAnnotation.fieldMatch();

    }

    @Override
    public boolean isValid(Object object, ConstraintValidatorContext constraintValidatorContext) {

        String fieldValue = String.valueOf(new BeanWrapperImpl(object).getPropertyValue(field));
        String fieldMatchValue = String.valueOf(new BeanWrapperImpl(object).getPropertyValue(fieldMatch));

        if (fieldValue == null) {
            throw new RuntimeException("fieldValue can not be null");
        }else if(fieldMatchValue ==null ){
            throw new RuntimeException("fieldMatchValue can not be null");
        }

        if (fieldValue.equals(fieldMatchValue)){
            return true;
        } else{

            constraintValidatorContext.disableDefaultConstraintViolation();
            constraintValidatorContext.buildConstraintViolationWithTemplate("message")
                    .addPropertyNode(fieldMatch)
                    .addConstraintViolation();
            return false;
        }
    }
}

6. Sequences.java

this source for Sequences(or order)

package com.today10sec.diary.customize.validator;

import javax.validation.GroupSequence;
import javax.validation.groups.Default;

@GroupSequence(value = {Sequences.Order1.class, Sequences.Order2.class, Sequences.Order3.class, Sequences.Order4.class, Sequences.Order5.class, Sequences.Order6.class, Sequences.Order7.class, Sequences.Order8.class, Sequences.Order9.class, Sequences.Order10.class, Default.class})
public interface Sequences {

    public @interface Order1 {
    }

    public @interface Order2 {
    }

    public @interface Order3 {
    }

    public @interface Order4 {
    }

    public @interface Order5 {
    }

    public @interface Order6 {
    }

    public @interface Order7 {
    }

    public @interface Order8 {
    }

    public @interface Order9 {
    }

    public @interface Order10 {
    }
}

7. DiaryValidErrorException.java

this source for Custom Validation BindingResult

package com.today10sec.diary.customize.exception;

import lombok.Getter;
import org.springframework.context.MessageSource;
import org.springframework.http.HttpStatus;
import org.springframework.util.Assert;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ResponseStatus;

/**
 * Validation Exception for Common Diary 
 */
@ResponseStatus(value= HttpStatus.BAD_REQUEST)
@Getter
public class DiaryValidErrorException extends Exception {

    private final BindingResult bindingResult;
    private final MessageSource messageSource;

    public DiaryValidErrorException(BindingResult bindingResult, MessageSource messageSource) {
        Assert.notNull(bindingResult, "BindingResult must not be null");
        Assert.notNull(bindingResult, "BindingResult must not be null");
        this.bindingResult = bindingResult;
        this.messageSource = messageSource;
    }
}

8. ValidErrorExceptionHandler.java

this source for Custom Validation, BindingResult

package com.today10sec.diary.config;

import com.today10sec.diary.customize.dto.ValidErrorData;
import com.today10sec.diary.customize.dto.ValidErrorResultData;
import com.today10sec.diary.customize.exception.DiaryValidErrorException;
import org.springframework.context.MessageSource;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import java.util.List;
import java.util.stream.Collectors;

@ControllerAdvice
public class DiaryExceptionHandlers {

    @ResponseBody
    @ExceptionHandler(DiaryValidErrorException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ValidErrorResultData ValidErrorExceptionHandler (DiaryValidErrorException diaryValidErrorException){

        MessageSource messageSource = diaryValidErrorException.getMessageSource();
        BindingResult bindingResult = diaryValidErrorException.getBindingResult();

        List<ValidErrorData> errorDataList = bindingResult.getFieldErrors()
                .stream().map(error-> new ValidErrorData(error, messageSource)
                ).collect(Collectors.toList());

        return new ValidErrorResultData(errorDataList);
    }
}

9. ValidErrorData.java

this source for Custom Validation, BindingResult

package com.today10sec.diary.customize.dto;

import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.validation.FieldError;

import java.util.Locale;

@Getter
public class ValidErrorData {

    Locale locale;

    private String fieldName;
    private String message;

    public ValidErrorData(FieldError fieldError, MessageSource messageSource){
        this.fieldName = fieldError.getField();
        this.message = messageSource.getMessage(fieldError.getCodes()[0],fieldError.getArguments(),fieldError.getDefaultMessage()+"["+fieldError.getCodes()[0]+"]",locale);
    }

    @Autowired
    public void setLocale(Locale locale) {
        this.locale = locale;
    }
}

10. ValidErrorResultData.java

this source for Custom Validation, BindingResult

package com.today10sec.diary.customize.dto;


import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.List;

@AllArgsConstructor
@Getter
public class ValidErrorResultData {
    private final Boolean validData = true;
    private List<ValidErrorData> errors;
}

 

11. CustomerControllerTest.java

this source for Custom Validation

package com.today10sec.diary.mvc.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.google.gson.Gson;
import com.today10sec.diary.customize.dto.CustomerSignUpData;
import com.today10sec.diary.customize.dto.ValidErrorResultData;
import com.today10sec.diary.customize.validator.SameValue;
import com.today10sec.diary.customize.validator.SameValueValidator;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
@ExtendWith(SpringExtension.class)
@WebMvcTest({CustomerController.class, SameValueValidator.class, SameValue.class, CustomerSignUpData.class})
class CustomerControllerTest {

    @Autowired
    MockMvc mockMvc;

    @Test
    void SIGN_UP_AJAX_VALID_CONFIRM_PW() throws Exception {

        String expectedResult = "{\"validData\":true,\"errors\":[{\"locale\":null,\"fieldName\":\"confirmPassword\",\"message\":\"비밀번호와 비밀번호확인값이 일치하지 않습니다. \"}]}";
        CustomerSignUpData customerSignUpData = new CustomerSignUpData("a@a.com","111111","222222" );
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, false);
        ObjectWriter ow = mapper.writer().withDefaultPrettyPrinter();
        String requestJson=ow.writeValueAsString(customerSignUpData);

        MvcResult mvcResult= mockMvc.perform(
                post("/customer/signUp/ajax")
                        .contentType(MediaType.APPLICATION_JSON)
                        .accept(MediaType.APPLICATION_JSON)
                        .content(requestJson))
                .andDo(print())
                .andExpect(status().isBadRequest())
                .andReturn();

        String result = mvcResult.getResponse().getContentAsString();
        Gson gson = new Gson();
        ValidErrorResultData resultData = gson.fromJson(result, ValidErrorResultData.class);
        Assertions.assertEquals("confirmPassword",resultData.getErrors().get(0).getFieldName(),"confirmPassword not contian");

    }

}

 

 

>> 도움이 많이 되었는데 예전버전이라 이것저것하면서 적용

***Spring REST Validation Example mkyong.com/spring-boot/spring-rest-validation-example

**Spring Boot Validation 순서 정하기 & 테스트 코드 dncjf64.tistory.com/302

www.baeldung.com/spring-mvc-custom-validator

mkyong.com/java/how-do-convert-java-object-to-from-json-format-gson-api/

728x90

error 1.

문제 Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
검색 에러 발생 시 Eclipse Run Configurations에서 Arguments에 --debug 추가  출처  kamsi76.tistory.com/entry/%EC%8B%9C%EC%9E%91
해결 application.properties 에 설정추가. 
logging.level.org.springframework= debug
logging.level.org.springframework.web= debug

error 2. 

문제 defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]
해결 DB 서버 재기동 & 상태점검 >> 도커재기동  restart docker

 

error3

문제 org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction

중략
Caused by: javax.validation.ConstraintViolationException: Validation failed for classes [com.today10sec.diary.customize.model.Customer] during persist time for groups [javax.validation.groups.Default, ] List of constraint violations:[ ConstraintViolationImpl{interpolatedMessage='반드시 최소값 6과(와) 최대값 12 사이의 길이이어야 합니다.', propertyPath=password, rootBeanClass=class com.today10sec.diary.customize.model.Customer, messageTemplate='{org.hibernate.validator.constraints.Length.message}'} ] at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.validate(BeanValidationEventListener.java:140) 
해결 스프링시큐리티 적용하면서 password 길이수가 늘었는데 생각못해서. 수정
validate range  change

error4

문제 jpa count NonUniqueResultException: query did not return a unique result: 31

중략

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'XXXController': Unsatisfied dependency expressed through field 'XXXControllerService'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'defaultXXXControllerService': Injection of autowired dependencies failed; nested exception is org.springframework.dao.IncorrectResultSizeDataAccessException: query did not return a unique result: 31; nested exception is javax.persistence.NonUniqueResultException: query did not return a unique result: 31

해결 count 를 사용하려고 했는데  이렇게 인간적인 jpa 일쭐이야.
count 밖에 쓸일이 없는 쿼리라 count로 정의 하려고 했는데.. 고민될정도로 count사용이 지저분했다.  
1. 리턴값은 List<long> 
그리고 조건을 group by 한 거였는데   list 의 길이가 내가 원한 값(그룹바이한 조건 값 31)이고  리스트 안에 long 은 반복되는 횟수(3)였다.   

error5

문제 Invocation of init method failed; nested exception is org.hibernate.AnnotationException: A Foreign key refering co   
중략  
has the wrong number of column. should be 2

해결 멀티키를 가진 model 을 join 할때 해당 멀티키를 각각  @joinColumn 선언필요
... 우선선언은했는데.. sn 한 컬럼만조인하면되는데.. 쓰레기 컬럼이 생겨버림...

error6

문제 Unable to find column with logical name XXXX:  in org.hibernate.mapping.Table(XXXX) and its related supertables and secondary tables
해결 @column @joinColumn 선언 검토 필요
테이블 구조를 변경하면서 오타발생

error7  >> 추가 검토 필요. 

문제 2022-06-18 17:40:42.159 ERROR 17664 --- [p-nio-80-exec-5] o.h.engine.jdbc.spi.SqlExceptionHelper   : HikariPool-1 - Connection is not available, request timed out after 30012ms.
해결 https://techblog.woowahan.com/2664/
쓰레드 1개, 히카리 Maximum pool size 1개 -> 하나의 task 에 동시 요구되는 connection 2개

풀싸이즈 = 트랜젝션 x  (커넥션-1) +1
풀사이즈 = 트랜젝션 (커넥션-1) + 트랜젝션/2

  java.lang.Exception: Apparent connection leak detected
  spring.datasource.hikari.leak-detection-threshold=240000

클일일세... https://docs.openkm.com/kcenter/view/okmkb/warning-in-update-or-creation-stage-javalangexception-apparent-connection-leak-detected.html

https://do-study.tistory.com/97

 

728x90

 

package com.today10sec.diary.etc;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;

@Component
@Transactional
public class JpaRunner implements ApplicationRunner {

    @PersistenceContext
    EntityManager entityManager;

    @Autowired
    Environment environment;

    @Override
    public void run(ApplicationArguments args) throws Exception {

    }
}
728x90
쿼리 만들기 스프링 데이터 저장소의 메소드 이름으로 쿼리 만드는 방법
메소드 이름을 분석해서 쿼리 만들기 (CREATE)
미리 정의해 둔 쿼리 찾아 사용하기 (USE_DECLARED_QUERY)
미리 정의한 쿼리 찾아보고 없으면 만들기 (CREATE_IF_NOT_FOUND)  >> 기본값

@SpringBootApplication
//@EnableJpaRepositories(queryLookupStrategy = QueryLookupStrategy.Key.CREATE)
@EnableJpaRepositories(queryLookupStrategy = QueryLookupStrategy.Key.CREATE_IF_NOT_FOUND)
//@EnableJpaRepositories(queryLookupStrategy = QueryLookupStrategy.Key.USE_DECLARED_QUERY)
public class DiaryApplication {

리턴타입 
{접두어}{도입부}By{프로퍼티 표현식}(조건식)[(And|Or){프로퍼티 표현식}(조건식)]{정렬 조건} (매개변수)

 
리턴타입 E, Optional<E>, List<E>, Page<E>, Slice<E> Stream<E>
{접두어} Find, Get, Query, Count
{도입부} Distinct, First(N), Top(N)
By  
{프로퍼티 표현식} Person Address ZipCode => findPersonByAddress_ZipCode()
{조건식} IgnoreCase, Between, LessThan, GreaterThan, Like, Contains
[(And|Or)]  
{프로퍼티 표현식}  
{조건식}  
{정렬조건} OrderBy프로퍼티Asc|Desc
(매개변수) Pageable, solt
 
쿼리 찾는 방법 메소드 이름으로 쿼리를 표현하기 힘든 경우에 사용.
저장소 기술에 따라 다름.
JPA: @Query @NamedQuery
@Query  @Query(SELECT c FROM Comment AS C" nativeQuery=true)
@Async 비동기 쿼리
@Async Future<User> findByFirstname(String firstname);  
@Async CompletableFuture<User> findOneByFirstname(String firstname);
@Async ListenableFuture<User> findOneByLastname(String lastname); 
- 해당 메소드를 스프링 TaskExecutor에 전달해서 별도의 쓰레드에서 실행함.
- Reactive랑은 다른 것임

권장하지 않는 이유
테스트 코드 작성이 어려움.
코드 복잡도 증가.
성능상 이득이 없음. 
DB 부하는 결국 같고.
메인 쓰레드 대신 백드라운드 쓰레드가 일하는 정도의 차이.
단, 백그라운드로 실행하고 결과를 받을 필요가 없는 작업이라면 @Async를 사용해서 응답 속도를 향상 시킬 수는 있다.

커스텀 리포지토리 쿼리 메소드(쿼리 생성과 쿼리 찾아쓰기)로 해결이 되지 않는 경우 직접 코딩으로 구현 가능.
스프링 데이터 리포지토리 인터페이스에 기능 추가.
스프링 데이터 리포지토리 기본 기능 덮어쓰기 가능.
구현 방법
커스텀 리포지토리 인터페이스 정의 
인터페이스 구현 클래스 만들기 (기본 접미어는 Impl)
엔티티 리포지토리에 커스텀 리포지토리 인터페이스 추가

기능 추가하기

기본 기능 덮어쓰기

접미어 설정하기
기본 리포지토리 커스터마이징 모든 리포지토리에 공통적으로 추가하고 싶은 기능이 있거나 덮어쓰고 싶은 기본 기능이 있다면 

JpaRepository를 상속 받는 인터페이스 정의
@NoRepositoryBean
기본 구현체를 상속 받는 커스텀 구현체 만들기
@EnableJpaRepositories에 설정
repositoryBaseClass

@NoRepositoryBean public interface MyRepository<T, ID extends Serializable> extends JpaRepository<T, ID> { boolean contains(T entity); }
도메인 이벤트 도메인 관련 이벤트를 발생시키기

스프링 프레임워크의 이벤트 관련 기능
https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#context-functionality-events
ApplicationContext extends ApplicationEventPublisher
이벤트: extends ApplicationEvent
리스너
  - implements ApplicationListener  E extends ApplicationEvent
  - @EventListener

스프링 데이터의 도메인 이벤트 Publisher
-@DomainEvents
-@AfterDomainEventPublication
-extends AbstractAggregateRoot<E>
-현재는 save() 할 때만 발생 합니다.

QueryDSL  

Distinct 분명한 : 중복없이 불러옴.

alt F7 사용된 위치 탐색

ctrl + alt + B  자세하게 추적. 

Pageable -> paging 으로 받아야 누락없음. 

             -> sorting 관련 파라메터도 넣을수 있음. 

resolveQuery 처리 : 쿼리 > 프로시져 > 네임드 쿼리 

like sql 키워드  변수 사용불가. 혹은 별명처리.. 

더보기
public final class JpaQueryLookupStrategy {

중략
        protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, NamedQueries namedQueries) {
            RepositoryQuery query = JpaQueryFactory.INSTANCE.fromQueryAnnotation(method, em, this.evaluationContextProvider);
            if (null != query) {
                return query;
            } else {
                RepositoryQuery query = JpaQueryFactory.INSTANCE.fromProcedureAnnotation(method, em);
                if (null != query) {
                    return query;
                } else {
                    String name = method.getNamedQueryName();
                    if (namedQueries.hasQuery(name)) {
                        return JpaQueryFactory.INSTANCE.fromMethodWithQueryString(method, em, namedQueries.getQuery(name), this.evaluationContextProvider);
                    } else {
                        RepositoryQuery query = NamedQuery.lookupFrom(method, em);
                        if (null != query) {
                            return query;
                        } else {
                            throw new IllegalStateException(String.format("Did neither find a NamedQuery nor an annotated query for method %s!", method));
                        }
                    }
                }
            }
        }
    }

쿼리관련 참고

더보기

기본 예제

 

List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);

// distinct

List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);

List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);

// ignoring case

List<Person> findByLastnameIgnoreCase(String lastname);

// ignoring case

List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);

 

정렬

 

List<Person> findByLastnameOrderByFirstnameAsc(String lastname);

List<Person> findByLastnameOrderByFirstnameDesc(String lastname);

 

페이징

 

Page<User> findByLastname(String lastname, Pageable pageable);

Slice<User> findByLastname(String lastname, Pageable pageable);

List<User> findByLastname(String lastname, Sort sort);

List<User> findByLastname(String lastname, Pageable pageable);

 

스트리밍

 

Stream<User> readAllByFirstnameNotNull();

  • try-with-resource 사용할 것. (Stream을 다 쓴다음에 close() 해야 함)

 

728x90
스프링 데이터 Common Repository
CrudRepository
PagingAndSortingRepository
스프링 데이터 JPA JpaRepository -> PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T>
CrudRepository PagingAndSortingRepository -> CrudRepository

<S extends T> S save(S var1);
<S extends T> Iterable<S> saveAll(Iterable<S> var1);
Optional<T> findById(ID var1);
boolean existsById(ID var1);
Iterable<T> findAll();
Iterable<T> findAllById(Iterable<ID> var1);
long count();
void deleteById(ID var1);
void delete(T var1);
void deleteAll(Iterable<? extends T> var1);
void deleteAll();
@Rollback(false) 롤백안함
트렌젝션  + 테스트 -->  하이버네이트가 롤백할 커리여서 테스트에서 insert 안나라감.
PagingAndSortingRepository public interface Page<T> extends Slice<T> {
    static <T> Page<T> empty() { 
        return empty(Pageable.unpaged());
    }
    static <T> Page<T> empty(Pageable pageable) {
        return new PageImpl(Collections.emptyList(), pageable, 0L);
    }
    int getTotalPages();
    long getTotalElements();
    <U> Page<U> map(Function<? super T, ? extends U> var1);
}
테스트용 h2 DB설정  1. h2 test 추가. 
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>test</scope>
</dependency>

2. @DataJpaTest 추가. 
@ExtendWith(SpringExtension.class) 
@DataJpaTest
class DiaryRepositoryTest {
Repository 인터페이스 정의 특정레포지터리 제작
@RepositoryDefinition(domainClass = Study.class, idClass = Integer.class)
public interface PersonalRepository {
    Study save(Study study);
    List<Study> findAll();
}
@NoRepositoryBean @NoRepositoryBean  > 레퍼지토리아님. 상속받아 사용. 
public interface MyRepository<T, ID extends Serializable> extends Repository<T, ID> {    
    <E extends T> E save(E entity);
    
    List<T> findAll();

}
Null 처리  리턴값 1개일떄 
java 8 Optional
<E extends T> Optional<E> findByID(ID id);
spring 5
@NonNullApi, @NonNull, @Nullable.
@Nullable
<E extends T> E save(E entity);     
런타임 체크 지원 함.
JSR 305 애노테이션을 메타 애노테이션으로 가지고 있음. (IDE 및 빌드 툴 지원)
인텔리제이 : Build, Execution, Deployment > Compiler
> Add runtime assertion for notnull-annotated methods and parameters
> Configure annotations 클릭 > 위에박스 + nullable 스프링추가, 아랫박스 NoneNull 추가
 

 >> 반영

 

@NonNullApi
package com.today10sec.diary.repository;;
Optional 단일값 받을 때 추천. .. 최근추세  == null  안씀... 
Optional<Diary> byId= diaryRepository.findById(100);
assertThat(byId).isEmpty();
List의 경우 스프링데이터에서 빈 컬렉션이 나옴
isEmpty(); 체크만. 
 쿼리 만들기 개요.   

Paging and sorting repository , crudRepository

더보기
package com.today10sec.diary.repository;

import com.today10sec.diary.model.Diary;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

@ExtendWith(SpringExtension.class)
@DataJpaTest
class DiaryRepositoryTest {

    @Autowired
    DiaryRepository diaryRepository;

    @Test
    @Rollback(false)
    public void crud_Repository(){
        //Given
        Diary diary = new Diary();
        diary.setName("aaaaa");
        assertThat(diary.getId()).isNull();
        //when
        Diary newDiary = diaryRepository.save(diary);
        //then
        assertThat(newDiary.getId()).isNotNull();
        //when
        List<Diary> diaries = diaryRepository.findAll();  //이것만 JPA REpository
        //then
        assertThat(diaries.size()).isEqualTo(1);
        assertThat(diaries).contains(newDiary);

    }

    @Test
    @Rollback(false)
    public void PagingAndSorting_Repository(){
        //Given
        Diary diary = new Diary();
        diary.setName("aaaaa");
        Diary newDiary = diaryRepository.save(diary);
        //when
        Page<Diary> page =diaryRepository.findAll(PageRequest.of(0, 10));
        //then
        assertThat(page.getTotalElements()).isEqualTo(1);
        assertThat(page.getNumber()).isEqualTo(0);
        assertThat(page.getSize()).isEqualTo(10);
        assertThat(page.getNumberOfElements()).isEqualTo(1);

        //when
        page = diaryRepository.findByNameContains("aaaaa", PageRequest.of(0,10));
        //then
        assertThat(page.getTotalElements()).isEqualTo(1);
        assertThat(page.getNumber()).isEqualTo(0);
        assertThat(page.getSize()).isEqualTo(10);
        assertThat(page.getNumberOfElements()).isEqualTo(1);
    }



}
728x90

 

JpaRepository

JpaRepository<Entity, Id> 인터페이스
매직인터페이스 
@Repository가 없어도 빈으로 등록해 줌

public interface DiaryRepository extends JpaRepository<Diary, Integer> { }
@EnableJpaRepositories (스프링부트에서 지원, 생략)  컨피규레이션 위에 올림. 
스프링부트 시작은 @Import(JpaRepositoriesRegistrar.class)   
핵심은 ImportBeanDefinitionRegistrar 인터페이스 : 스프링프레임워크 
: 빈을 프로그래밍을 통해 등록가능. 
public class DiaryRegistrar implements ImportBeanDefinitionRegistrar {
    @Override public void registerBeanDefinitions(AnnotationMetadata  importingClassMetadata, BeanDefinitionRegistry registry) {
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(Diary.class);         
        beanDefinition.getPropertyValues().add("name","오잉");
        registry.registerBeanDefinition("day", beanDefinition); }
}
jdbc  
패러다임 불일치 릴레이션 - java
단위, 관계표현, 식별자 , 상속관계표현 => ORM 해결 매핑 메타데이터 기반. 
생산성, 유지보수성, 성능.  
jpa 주의 반드시 발생하는 SQL 확인
? 출력
logging.level.org.hibernate.SQL=debug logging.level.org.hibernate.type.descriptor.sql=trace

binding parameter [2] as [VARCHAR] - [null]
binding parameter [3] as [TIMESTAMP] - [null]
binding parameter [4] as [VARCHAR] - [오늘이다.]
binding parameter [5] as [BOOLEAN] - [true]
binding parameter [6] as [INTEGER] - [3]
스프링 데이터 SQL & NoSQL 저장소, 지원프로젝트의 묶음

spring.io/projects/spring-data

스프링 데이터 모듈 : 

스프링데이터 Common : 여러 저장소 지원 프로젝트의 공통기능 제공
스프링데이터 REST: 저장소의 데이터를 하이퍼미디어 기반 Http 리소스로 REST API 제공
---------------------------
스프링데이터 JPA      : 스프링 데이터 Common 이 제공하는 기능에 jpa 관련 기능 추가. 
스프링데이터 JDBC
스프링데이터 KeyValue
---------------------------
스프링데이터 MongoDB
스프링데이터 Redis.......

 

non-invasive 논인베이시브 비침투적. 스프링 코드를 최대한 노출 시키지 않겠다. 

 

* DB 조회 변경 과정

더보기
@Repository @Transactional
public class AccountRepository {
    @PersistenceContext EntityManager entityManager;
    public Account add(Account account){
     entityManager.persist(account);
     return account;
 }
  -> @Repository @Transactional
public class AccountRepository extends GenericRepository<Account, Long> {
-> JPA package com.today10sec.diary.repository;
import com.today10sec.diary.model.Diary;
import org.springframework.data.jpa.repository.JpaRepository;

public interface DiaryRepository extends JpaRepository<Diary, Integer> {
}

package com.today10sec.diary.etc;

import com.today10sec.diary.model.Diary;
import com.today10sec.diary.repository.DiaryRepository;
import org.hibernate.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;
import java.util.HashSet;
import java.util.Set;

@Component
@Transactional
public class JpaRunner implements ApplicationRunner {

    @PersistenceContext
    EntityManager entityManager;

    @Autowired
    DiaryRepository diaryRepository;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        Diary diary = new Diary();
        diary.setUse(true);
        diary.setName("오늘이다.");

        Session session = entityManager.unwrap(Session.class);
        session.save(diary);

        diaryRepository.findAll().forEach(diary1 -> System.out.println(diary1.getName()) );

    }
}
728x90

ERROR1.  

error creating bean with name 'entitymanagerfactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/hibernatejpaconfiguration.class]: invocation of init method failed; nested exception is org.hibernate.service.spi.serviceexception: unable to create requested service [org.hibernate.engine.jdbc.env.spi.jdbcenvironment]

두번째 서버 올렸을 때 postgresql 서버가 내려가있다는 메시지 와서 도커 재시작 해결. 

 


JPA 에서 EntityManager   

@Entity “엔티티”는 객체 세상 릴레이션. 
보통 클래스와 같은 이름을 사용하기 때문에 값을 변경하지 않음.
엔티티의 이름은 JQL에서 쓰임.
@Entity(name="myAccount")
@Table
public class Account
...기존 postgres 에서 user 가 키워드여서 user 테이블 사용불가 syntax에러 or. Entity 변경 사용  
@Table “릴레이션" 세상에서 엔티티
@Entity의 이름이 default .
테이블의 이름은 SQL에서 쓰임.
@Id 엔티티의 주키를 맵핑할 때 사용.
자바의 모든 primitive 타입과 그 랩퍼 타입을 사용할 수 있음_ 임의값과 구별과능 
-Date랑 BigDecimal, BigInteger도 사용 가능.
복합키를 만드는 맵핑하는 방법도 있지만 그건 논외로..
@GeneratedValue 주키의 생성 방법을 맵핑하는 애노테이션
생성 전략과 생성기를 설정할 수 있다.
- 기본 전략은 AUTO: DB 에따라 기본값 다름 postgres 시퀀스(생성전략다름, 명시가능 strategy)
-
TABLE, SEQUENCE, IDENTITY 중 하나.
@Column unique
nullable    : default true
length
columnDefinition
@Temporal 날짜데이터
현재 JPA 2.1까지는 Date와 Calendar만 지원.
@Transient 데이터 컬럼으로 맵핑하고 싶지 않은 멤버 변수에 사용.
application.properties spring.jpa.show-sql=true

spring.jpa.properties.hibernate.format_sql=true   // 보기좋게 포멧

Value 타입 종류 기본 : String, Date, Boolean..
Composite Value 타입
Collection Value 타입- 기본타입 콜렉션, 컴포짓 타입 콜렉션
<
Composite Value 타입 맵핑 @Embeddable
@Embedded
@AttributeOverrides
@AttributeOverride
Entity 타입
Value 타입
@Entity
public class Account{    >> 엔티티타입
    @Embedded
    private Address address ;      >>> 벨류타입.........Account 엔티티 통해서 접근. 
    @Embedded
    @Attribute
    private Address homeAddress ;
}
@Embeddable @Embeddable   > 새로운 타입값 지정. 
public class Address{
    private String street;
    private String etc;
}

    @Embedded
    @AttributeOverrides({   >> 새로운 타입값에 일부 변경
            @AttributeOverride(name = "etc", column = @Column(name = "etc2"))
    })
    private Address address;

1대다 단방향_@ManyToOne public class Study {
    @ManyToOne
    private Account one;   >> Study 가 주인._ 관계 값을 정의 위치. 
1대다 단방향
_@OneToMany
many 로 끝나야 컬렉션. >> account_studies 에 관계값 정의 
public class Account {
    @OneToMany
    private Set<Study> studies;

1대다 양방향 public class Account {
    @OneToMany(mappedBy = "owner")
    private Set<Study> studies = new HashSet<>();
public class Study {
    @ManyToOne                             >>> 이쪽에 관계값 정의 
    private Account owner;

소스작성
account.getStudies().add(study);
study.setOwner(account);
Session session = entityManager.unwrap(Session.class);
session.save(study);
session.save(account);
엔티티 상태 변화 -new Object()->  Transient: JPA가 모르는 상태

(from Transient )-session.save()-> Persistent: JPA가 관리중 상태
(fromDetached)-session.update()
                        session.marge();
                        session.saveOrUpdate()-> Persistent
Persistent: (1차 캐시, Dirty Checking, Write Behind, ...) : 변경사항 계속 모니터링
session.get()
session.load()
Query.Iterate()

(from Persistent) ->session.evict();  
                           session.clear();

                            session.close(); ->Detached: JPA가 더이상 관리하지 않는 상태.



-session.delete();->Removed: JPA가 관리하긴 하지만 삭제하기로 한 상태.
Cascade - @ManyToOne 엔티티 상태값 전이: 부모자식관계.에 쓰는 것을 추천.   
class Post{
@OneToMany(mappedBy = "post", cascade = CascadeType.PERSIST)
 private Comment comment;
}
post만 저장해도 comment 도 같이 저장.
cascade = {CascadeType.PERSIST, CascadeType.REMOVE}) 
cascade = CascadeType.All
Dirty Checking 상태변경 검사
(영속-managed-상태, Transaction 엔티티변경T@Transactional, Entity Transaction.  경우)
1) 엔티티 메니저에서 수정에 해당하는 부분에 더티 체킹 지원
2) 엔티티 데이터 변경부분 체크 DB 반영 
* 데이터 변경 시점: transaction commit, Entitymanager flush, jpql 사용시점
import org.hibernate.Session
Session session = entityManager.unwrap(Session.class);//jpa감싸는 hibernate api 그대로이용
session.save(study); session.save(account); 

Account k = session.load(Account.class, account.getId());   // null 일때 예외 
// session.get(Post.calss, 4l);  null 일 때null 가져옴

k.setUsername("ddd");
k.setUsername("eee");
System.out.println(k.getUsername());
Write Behind
DB 엔티티 변경사항을 최대한 필요한 시점에 반영하는것.
@DynamicUpdate 변경된 필드만 저장
Fetch *성능영향 연관 관계의 엔티티를 어떻게 가져올 것이냐... 지금 (Eager)? 나중에(Lazy)?
@OneToMany의 기본값은 Lazy
post 조회시 comment 를 가져오지 않음
@ManyToOne의 기본값은 Eager
comment 조회시 post 를 가져옴. 
Query JPQL, Criteria, Native Query 가 있다. 
JPQL (= HQL)  Java Persistence Query Language / Hivernate Query Language
데이터베이스 테이블이 아닌 엔티티 객체 모델 기반으로 쿼리 작성
JPA 하이버네이트가 해당쿼리 SQL로 변환 실행 , 타입세이프 하지 안음
TypedQuery<Post> query                                     // 지정해준 타입으로 나옴
= entityManager.
createQuery("SELECT p FROM Post As p", Post.class);  
List<Post> posts = query.getResultList();
https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#hql
Criteria 타입 세이프 관리
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery criteria = builder.createQuery(Post.class);
Root root = criteria.from(Post.class);
criteria.select(root);
List posts = entityManager.createQuery(criteria).getResultList();
Native Query SQL 쿼리 실행
List<Post> posts = entityManager .createNativeQuery("SELECT * FROM Post", Post.class) .getResultList();

Composite    합성의 ... 컴포짓한 벨류타입. 

Transient   일시적인, 순간적인, 단기체류의  트랜지언트 

evict         쫓아내다. 이빅트

Detached   분리됨

캐쉬가 되었다. - PersistentContext 에 넣음. 

영향이 있는 테이블만 삭제하고 다시만듬

Model . getter setter 안만들어도 됨. 

Eager 열심인

Lazy  게으른

Criteria  criterion기준 복수  크리테리아. 

728x90

스프링 코어

IoC 컨테이너와 빈

Environment, MessageSource, ApplicationEventPublisher, ResourceLoader

Validation, 데이터 바인딩.

SpEL, AOP,Null-safety

 

 

 

 

 

 

 

 

728x90

스프링목차

SpEL 스브링EL
객체 그래프를 조회, 조작하는 기능
Unified EL과 비슷하지만, 메소드 호출 지원, 문자열템플릿 기능도 제공. 
OGNL MVEL, JBOss EL 등 자바에서 사용할 수 있는 여러 EL 이 있지만 , SpEL은 모든 스프링 프로젝트 전반에 걸처 사용할 EL로 만듬.  스프링 3.0 부터. 
ExpressionParser parser = new SpelExpressionParser() 
StandardEvaluationContext context = new StandardEvaluationContext(bean) >> 함수. 가능. 
Expression expression = parser.parseExpression(“SpEL 표현식”)
String value = expression.getvalue(context, String.class)

#{“표현식"}  "#{1+1}" 
${“프로퍼티"}
docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions-language-ref

@Value 애노테이션
@ConditionalOnExpression 애노테이션
스프링 시큐리티
 - 메소드 시큐리티, @PreAuthorize, @PostAuthorize, @PreFilter, @PostFilter
  - XML 인터셉터 URL 설정
스프링 데이터  : @Query 애노테이션
Thymeleaf  : blog.outsider.ne.kr/997
docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions-language-ref
AOP Aspect-oriendted Programming  :  OOP 보완, 흩어진 Aspect를 모듈화 
스프링은 AspectJ 연동, 혹은 따로구현. 
OOP 에 나타난... 흩어진 관심(비슷한코드)사 : Crosscutting Concerns : 유지보수 어려움. 
ClassA {    1,     2,     3 }
ClassB {    1,     3,     3 }
ClassC {    1,     2}
>> AOP 적용
ClassA   , ClassB,  ClassC                                   
AspectX : A, B, C          1소스 부분
AspectY: A, C              2소스 부분
AspectZ: A, B              3소스부분
   PointCut           +    advice            = Aspect
  어디에 적용              해야할 일            하나의 모듈
Target 적용대상  ClassA   , ClassB,  ClassC
Join point 합류 지점 ex) 생성자 호출전, 호출때, 필드 호출전, 
그중에 더 자세한내용은 PointCut
AOP 적용방법 - 컴파일(java > .class)- 
- 로드타임 위빙(컴파일 후)-                                                        >> AspectJ  경우에 따라
- 런타임(빈을 만들때 A타입 프록시 빈을 만듬... 프록시 실행후 A실행)   >> 스프링 AOP 주로
빈만들때 약간의 성능 부하, 다양한 기능적용(AspectJ)
AOP 구현체  en.wikipedia.org/wiki/Aspect-oriented_programming
Java AOP 구현체 AspectJ  : 기능 다양
스프링 AOP  : 국한적. 
프록시 패턴 Client    임의 인터페이스
                      ↑
            프록시   →   임의 클래스
프록시 객체가 실제 사용할 클래스를 감싸서 프로그램실행 : 접근제어, 부가기능 추가. 
문제점
 매번 프록시 클래스를 작성해야 하는가?
 여러 클래스 여러 메소드에 적용하려면?
 객체들 관계도 복잡하고...
스프링AOP 프록시 기반 AOP 구현체
스프링 빈에만 AOP를 적용할 수 있다.
모든 AOP 기능을 제공하는 것이 목적이 아니라, 스프링 IoC와 연동하여 엔터프라이즈
애플리케이션에서 가장 흔한 문제에 대한 해결책을 제공하는 것이 목적.

스프링 IoC 컨테이너가 제공하는 기반 시설과 Dynamic 프록시를 사용하여 여러
복잡한 문제 해결.
동적 프록시: 동적으로 프록시 객체 생성하는 방법
- 자바가 제공하는 방법은 인터페이스 기반 프록시 생성.
- CGlib은 클래스 기반 프록시도 지원.
스프링 IoC: 기존 빈을 대체하는 동적 프록시 빈을 만들어 등록 시켜준다.
- 클라이언트 코드 변경 없음.
- AbstractAutoProxyCreator implements BeanPostProcessor
@Aspect 의존성 추가. spring-boot-starter-aop
@Component
@Aspect
public class PerfAspect{
     //@Around("execution( * com.example.demo.*.EventService.*(..))") //포인트컷 표현식.
     @Around("@annotation(PerfLogging)")
     public Object logPerf(ProceedingJoinPoint pip)throws Throwable{
         Object reVal = pip.proceed();
         return reVal;
     }
}
@Pointcut @Pointcut(표현식)
- execution
- @annotation
- bean
포인트컷 조합
-&&, ||, !
어드바이스 정의 @Before
@AfterReturning
@AfterThrowing
@Around
Null-safety 스프링 프레임워크 5에 추가된 Null 관련 애노테이션
@NonNull                                     >>매개변수 널허용안됨.
@Nullable
@NonNullApi (패키지 레벨 설정)
@NonNullFields (패키지 레벨 설정)
목적 : (툴의 지원을 받아) 컴파일 시점에 최대한 NullPointerException을 방지하는 것
 컴파일러 옵션.  configuration  스프링어노테이션 추가해야 쓸수 있음. 스프링데이터쪽에서 쓴다고함
패키지 레벨... NonNull.. 허용하는대만  Nullable 사용. 

 

@Retention(RetentionPolicy.CLASS)// 언제까지 유지할것인가.

docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-pointcuts

 

 

프록시 패턴구현_ 동적, 어노테이션

더보기
package com.example.demo.event;

import java.lang.annotation.*;
/**
 * 이 어노테이션은 성릉을 로깅해줍니다.. 
 */
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)// 언제까지 유지할것인가. 명시.. 기본값
public @interface PerfLogging {
}
package com.example.demo.event;

import org.springframework.stereotype.Service;

@Service
public class SimpleEventService implements EventService {
    @Override
    @PerfLogging
    public void createEvent() {
        System.out.println("Created an event");
    }

    @Override
    @PerfLogging
    public void publishEvent() {
        System.out.println("published an event");
    }

    @Override
    public void deleteEvent(){
        System.out.println("Delete an event");
    }
}
package com.example.demo.event;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class PerfAspect{

    //@Around("execution( * com.example.demo.*.EventService.*(..))")  //포인트컷 표현식.
    @Around("@annotation(PerfLogging)")
    public Object logPerf(ProceedingJoinPoint pip)throws Throwable{
        long begin = System.currentTimeMillis();
        Object reVal = pip.proceed();
        System.out.println(System.currentTimeMillis()-begin);
        return reVal;
    }
}

 

 

프록시 패턴구현 _ 기본이해 

더보기
package com.example.demo.event;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

@Primary
@Service
public class ProxySimpleEventService implements EventService{

    @Autowired
    SimpleEventService simpleEventService;

    @Override
    public void createEvent(){
        long begin = System.currentTimeMillis();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        simpleEventService.createEvent();
        System.out.println(System.currentTimeMillis()-begin);
    }

    @Override
    public void publishEvent(){
        long begin = System.currentTimeMillis();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        simpleEventService.publishEvent();
        System.out.println(System.currentTimeMillis()-begin);
    }

    @Override
    public void deleteEvent(){
        simpleEventService.deleteEvent();
    }
}
package com.example.demo.event;

public interface EventService{
    void createEvent();
    void publishEvent();
    void deleteEvent();
}
package com.example.demo.event;

import org.springframework.stereotype.Service;

@Service
public class SimpleEventService implements EventService{
    @Override
    public void createEvent(){
        System.out.println("Created an event");
    }

    @Override
    public void publishEvent(){
        System.out.println("published an event");
    }

    @Override
    public void deleteEvent(){
        System.out.println("Delete an event");
    }
}

 

 

package com.example.demo.event;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Component
public class AppRunner implements ApplicationRunner {

    EventService eventService;

    @Override
    public void run(ApplicationArguments args) throws Exception {

        eventService.createEvent();
        eventService.publishEvent();
        eventService.deleteEvent();

    }

    @Autowired
    public void setEventService(EventService eventService) {
        this.eventService = eventService;
    }
}

.

추가 스프링부트 웹 안쓸때

SpringApplication app = new SpringApplication(DemoApplication.class);
        app.setWebApplicationType(WebApplicationType.NONE);
        app.run(args);
728x90

import static 단축키 

1.  settings > Editor > General > Auto Import

Add unambiguous imports on the fly 

Optimize imports on the fly (for current project)

두개 체크 후 저장

2. 

그리고 status() 뒤  alt + enter 로사용. **() 뒤에서 알트엔터

출처 :hjjungdev.tistory.com/102


스프링목차

스프링 validator org.springframework.validation.Validator;

객체 검증 인터페이스. 
- supports : 검증할수 있는 클래스인지 확인. 
public boolean supports(Class<?> clazz){
    return Event.class.equals(clazz);
}
- validate
public void validate(Object target Errors errors){
    ValidationUtils.rejectIfEmptyOrWhitespace(error, "분별 키","공통 키값", "없을때 메시지 ");
    Event event = (Event)target;
    if(event.getTitle()==null){
         errors.reject("필드", "메시지");...
     }
}

--소스
eventValidator.validate(event, errors);
event.hasErros()
error.getAllErrors().forEach(e->{ Arrays.stream(e.getCodes()).forEach(System.out::println)})
- 스프링부트 2.0.5 이상 스프링 LocalValidatorFactoryBean 자동등록.   - 
@Validator validator;
빈에 추가. 
@NotNull @Min(0) @Size(min=0) @NotEmpty(message = "{email.notempty}")
>> 기본메시지가 출력.  
Bean Validation javax.xml.validation.Validator;
JAVA EE 기능.
beanvalidation.org/
데이터 바인딩 프로퍼티 값을 타겟 객체에 설정하는 기능
사용자 관점 : 사용자 입력값을 애플리케이션 도메인 모델에 동적으로 변환해 넣어주는 기능
해석하자면, 입력값은 대부분 문자열, 그값을 객체가 가지고 있는 타입으로 변환해서 넣어주는 기능. 
PropertyEditor 스프링 3.0 이전까지 DataBinder가 변환 작업 사용하는 인터페이스. 
쓰레드 세이프하지 않음. -상태정보 저장하고 있음, 빈으로 등록해서.. 쓰면... 안됨. 
Object String 간의 변환만 할수 있어 사용범위가 제한적임
public class EventPropertyEditor extends PropertyEditorSupport {
>> 후에 Converter 사용. 
Converter S 타입을 T 타입으로 변환할 수 있는 매우 일반적인 변환기.
상태 정보 없음 == Stateless == 쓰레드세이프
ConverterRegistry에 등록해서 사용
@Component
public class StringToEventConverter implements Converter<String, Event> {
    @Override
    public Event convert(String source) {
        Event event = new Event();
        event.setId(Integer.parseInt(source));
        return event; }
}

public class WebConfig implements WebMvcConfigurer {  >>빈 등록시 부트는 등록안해도됨
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new StringToEventConverter());
    }
}
기본적인것은 자동변환..해줌. 
 Formatter PropertyEditor 대체제
Object와 String 간의 변환을 담당한다.
문자열을 Locale에 따라 다국화하는 기능도 제공한다. (optional)
FormatterRegistry에 등록해서 사용
@Component
public class EventFormatter implements Formatter<Event> {
    @Autowired
    MessageSource messagesource;                                  >> bean 등록 가능. 

    @Override
    public Event parse(String text, Locale locale) throws ParseException {
        Event event = new Event();
        int id = Integer.parseInt(text);
        event.setId(id); return event;
    }
    @Override
    public String print(Event object, Locale locale) {
        return object.getId().toString();
    }
}

public class WebConfig implements WebMvcConfigurer {>>빈 등록시 부트는 등록안해도됨
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addFormatter(new EventFomatter());
    }
}
ConversionService 실제 변환 작업은 이 인터페이스를 통해서 쓰레드-세이프하게 사용할 수 있음.
스프링 MVC, 빈 (value) 설정, SpEL에서 사용한다.
 Default
Formatting
ConversionService
; FormatterRegistry,ConversionService상속. 
- FormatterRegistry(포멧터등록) <-  컨퍼터 ConverterRegistry(컨버터등록) 상속. 

- 여러 기본 컴버터와 포매터 등록 해 줌.
JPA 엔티티에 컨버터가 들어있음. 들어있는
컨버터 전부 보는방법. ConversionService  출력 
스프링 부트 - 웹 애플리케이션인 경우에 DefaultFormattingConversionSerivce를 상속하여 만든
WebConversionService를 빈으로 등록해 준다.
- Formatter와 Converter 빈을 찾아 자동으로 등록해 준다.

 

PropertyEditor

더보기
package com.example.demo;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@ExtendWith(SpringExtension.class)
@WebMvcTest
class DemoControllerTest {
    @Autowired
    MockMvc mockMvc;

    @Test
    void getTest() throws Exception {
        mockMvc.perform(get("/event/1"))
                .andExpect(status().isOk())
                .andExpect(content().string("1"));

    }
}

 

package com.example.demo;

import com.example.demo.event.Event;
import com.example.demo.event.EventEditor;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    @InitBinder
    public void init(WebDataBinder webDataBinder){
        webDataBinder.registerCustomEditor(Event.class, new EventEditor());
    }

    @GetMapping("/event/{event}")
    public String getEvent(@PathVariable Event event){
        System.out.println(event);
        return event.getId().toString();
    }


}
package com.example.demo.event;

public class Event {
    private Integer id;

    public Event(Integer id) {
        this.id = id;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }
}
package com.example.demo.event;

import java.beans.PropertyEditorSupport;

public class EventEditor extends PropertyEditorSupport {
    @Override
    public String getAsText() {
        Event evnet = (Event)getValue();
        return evnet.getId().toString();
    }

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        setValue(new Event(Integer.parseInt(text)));

    }
}
728x90

스프링목차

Environment

프로파일과 프로퍼티를 다루는 인터페이스

 

Environment ApplicationContext extends EnvironmentCapable
profile 활성화여부 확인 및 설정
프로퍼티 소스 설정 및 프로퍼티 값 가져오기
profile Environment 에서 프로파일 확인 및설정. 
-클래스-
@Configuration @Profile(“test”)
@Component @Profile(“test”)
-메소드- 
@Bean @Profile(“test”)

사용 설정추가. 
-Dspring.profiles.avtive=”test,A,B,...”
테스트 ; ActiveProfiles

설정방법
! (not)
& (and)
 | (or)
Property StandardServletEnvironment의 우선순위
- ServletConfig 매개변수
- ServletContext 매개변수
- JNDI (java:comp/env/)
- JVM 시스템 프로퍼티 (-Dapp.key=”value”)   enviroment.getProperty("app.key");
- JVM 시스템 환경 변수 (운영 체제 환경 변수)
@PropertySource Environment를 통해 프로퍼티 추가하는 방법
@PropertySource("classpath:/app.properties") >> 스프링 부트 어플리케이션 지정한 클래스에 추가
>> enviroment에 추가됨. 
스프링 부트 프로퍼티 기본 프로퍼티 소스 지원 (application.properties)
프로파일까지 고려한 계층형 프로퍼티 우선 순위 제공 (yml)
MessageSource 국제화(i18n) 기능 제공하는 인터페이스
ApplicationContext extends MessageSource
message.properties messages_ko_kr.properties   >> 스프링 프로퍼티스. 
@Bean
public MessageSource messageSource() {
var messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:/messages");
messageSource.setDefaultEncoding("UTF-8");
messageSource.setCacheSeconds(3);
return messageSource;
}
운영중 빌드해주면.. 바로 반영. 
ApplicationEventPublisher 이벤트 프로그래밍에 필요한 인터페이스 제공. 옵저버 패턴 구현체.
ApplicationContext extends ApplicationEventPublisher
publishEvent(ApplicationEvent event)
이벤트 처리하는 방법
● ApplicationListener<이벤트> 구현한 클래스 만들어서 빈으로 등록하기.
● 스프링 4.2 부터는 @EventListener를 사용해서 빈의 메소드에 사용할 수 있다.
● 기본적으로는 synchronized.
● 순서를 정하고 싶다면 @Order와 함께 사용.
● 비동기적으로 실행하고 싶다면 @Async와 함께 사용.
스프링이 제공하는 기본 이벤트
● ContextRefreshedEvent: ApplicationContext를 초기화 했더나 리프래시 했을 때 발생.
● ContextStartedEvent: ApplicationContext를 start()하여 라이프사이클 빈들이 시작
신호를 받은 시점에 발생.
● ContextStoppedEvent: ApplicationContext를 stop()하여 라이프사이클 빈들이 정지
신호를 받은 시점에 발생.
● ContextClosedEvent: ApplicationContext를 close()하여 싱글톤 빈 소멸되는 시점에
발생.
● RequestHandledEvent: HTTP 요청을 처리했을 때 발생.
ResourceLoader 리소스를 읽어오는 기능을 제공하는 인터페이스
ApplicationContext extends ResourceLoader
리소스 읽어오기 Resource resource = resourceLoader.getResource("classpath:test.txt");
● 파일 시스템에서 읽어오기 
● 클래스패스에서 읽어오기
● URL로 읽어오기
● 상대/절대 경로로 읽어오기
 Resource 추상화  java.net.URL 추상화., 스프링 내부 많이 사용 인터페이스
org.springFramework.core.io.Resouce
- 클래스 패스 기준 읽어오는 기능 부재
- ServletContet를 기준으로 상대경로로 읽어오는 기능 부재
- 새로운 핸들러 등록 URL 접미사 만들면 구현복잡. 
>> 불러오는 방법 통일. 
○ getInputStream()
○ exitst()                       !!! 항상체크해서 사용. 
○ isOpen()
○ getDescription(): 전체 경로 포함한 파일 이름 또는 실제 URL
구현체
● UrlResource: java.net.URL 참고.  
● ClassPathResource: 지원하는 접두어 classpath:
● FileSystemResource
● ServletContextResource: 웹 애플리케이션 루트에서 상대 경로로 리소스 찾는다.
리소스 읽어오기
● Resource의 타입은 locaion 문자열과 ApplicationContext의 타입에 따라 결정 된다.
○ ClassPathXmlApplicationContext -> ClassPathResource
○ FileSystemXmlApplicationContext -> FileSystemResource
○ WebApplicationContext -> ServletContextResource
● ApplicationContext의 타입에 상관없이 리소스 타입을 강제하려면 java.net.URL 접두어(+ classpath:)중 하나를 사용할 수 있다.
○ classpath:me/whiteship/config.xml -> ClassPathResource
○ file:///some/resource/path/config.xml -> FileSystemResource
*접두어 추천. 다양한개발자들을 위해.. 명시. 

 

728x90

스프링목차

스프링 IoC Inversion of Control : 제어의 역전: 어떤 객체가 사용하는 의존객체를 직접 만들어 사용하는게 아니라 주입받아 사용. 
- BeanFactory  :스프링 IOC 가장 최상위 인터페이스. 
- 애플리케이션 컴포넌트 중앙저장소
- 빈설정 소스로 부터 빈 정의 읽어드리고 빈구성 제공.
ApplicationContext 
- BeanFactory, 메시지 소스 처리기능, 이벤트 발생기능, 리소스 로딩기능. 
스프링 IoC 컨테이너 빈 인스턴스 생성
의존관계 설정
빈 제공
DI DI(Depedency Injection)의존성 주입.
내부에서 만든 변수를 외부에서 넣어준다.  
객체 생성과 사용자의 관심 분리, 가독성과 재사용을 높혀준다. 
- 광범위한 역제어 테크닉의 한 형태. 
- 클래스는 더이상 객체생성에 대한 책임이 없으며 추상팩토리 디자인 패턴에서 처럼 팩토리객체로 생성을 위임할 필요가 없다. 
스프링 빈 스프링 IoC 컨테이너가 관리하는 객체
의존성 관리
라이프사이클 인터페이스 지원. - @PostConstruct  : 빈이 만들어진후 .호출. 
스코프 : 기본 스프링은 싱글톤으로 만들어 사용
ApplicationContext BeanFactory 상속 받는 인터페이스. 
ClassPathXmlApplicationContext , context:component-scan _ XML 로 스프링 빈생성해서 가져옴. 
--이후 > @Configuration , @Bean , AnnotationConfigApplicationContext();  JAVA로 스프링빈생성
--현재 > @ComponentScan(basePackages="컴포넌트 스캔 시작 클래스")
SpringBootApplication 에 ComponentScan, Configuration이 이미 셋팅. 
스프링 빈 라이프사이클 생성, 의존설정, 사용 소멸. 
- 생산, 설정- 
1. BeanNameAware's 
setBeanName
2. BeanClassLoaderAware's setBeanClassLoader
3. BeanFactoryAware's setBeanFactory
4. ResourceLoaderAware's setResourceLoader (only applicable when running in an application context)
5. ApplicationEventPublisherAware's setApplicationEventPublisher (only applicable when running in an application context)
6. MessageSourceAware's setMessageSource (only applicable when running in an application context)
7. ApplicationContextAware's setApplicationContext (only applicable when running in an application context)
8. ServletContextAware's setServletContext (only applicable when running in a web application context)
9. postProcessBeforeInitialization methods of BeanPostProcessors <<@Autowire
- 초기화-
10. InitializingBean's 
afterPropertiesSet  >> 앞뒤 설정 가능. 
11. a custom init-method definition
- 사용- 
12. postProcessAfterInitialization methods of BeanPostProcessors
- 소멸-
1. DisposableBean's destroy
2. a custom destroy-method definition

* BeamFactory 가 BeanPostProcessor 타입 빈을 찾음 그중 AutowiredAnnotationBeanPostProcessor 를 찾아서.. 일반적인 빈에 적용을 함. 
출처 : BeanFactory docs
@Autowire 필요한 의존 객체의 타입에 해당하는 빈찾아 주입. 
required 기본값 true. 못찾으면 구동실패. 
생성자, 세터, 필드 사용. 

같은타입 빈 여러개 
- @Primary      < 추천. 
- 해당타입 모두 주입 _List<타입> or 필드이름 동일한것을 찾아서 주입 
  BeanPostProcessor 빈초기화 전후>  AutowiredAnnotationBeanPostProcessor(Autowire 처리
- @Qualifer  (빈이름으로 주입 맨앞자 소문자. 변경)
@ComponentScan 스캔 위치 설정 , 필터, 어떤 애노테이션 스캔할지 하지 않을지. 
- basePackages, basePackageClasses
선언 패키지 기준 Class<?>[]basePackageClasses 에 담아 실행.   
- useDefaultFilters, includeFilters, excludeFilters
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
구동시간 bean 늘어남 늘어지는단점. .. 예민하면 펑션 등 고려
실제 스캐닝은 ConfigurationClassPostProcessor라는 BeanFactoryPostProcessor에서 다른빈 모두 등록 전에 처리 됨.
- lazyInit  true 시.. 보통은 바로 초기화,  해당클래스 사용시 초기화. 
@Component @Repository
@Service
@Controller
@Configuration
스프링 빈의 스코프 @Component @Scope("prototype") 
프로토타입 내부 싱글톤 문제없음. 항상동일
싱글톤 내부 프로토 빈 사용시 프로토가 같음... 싱글톤과.. 같은현상. , 기본값 프록시 사용안함
>>@Component @Scope("prototype", proxyMode=ScopeProxyMode.TARGET_CLASS) 
CG라이브러리 사용한 다이나믹 프록시 적용됨. 매번 바꿀수있는 프록시로 감싸도록함. 
ObjectProvider<클래스> 클래스변수이름;

singleton : 스프링 default bean scope
prototype : 애플리케이션 요청시 ( getBean() 메서드가 호출될 때마다) 스프링이 새 인스턴스를 생성합니다
request : HTTP 요청별로 인스턴스화 되며 요청이 끝나면 소멸됩니다
session : HTTP 세션별로 인스턴스화 되며 세션이 끝나면 소멸됩니다.
global session : 포틀릿 기반의 웹 애플리케이션 용도로 전역 세션 스코프가 빈과 같은 스프링 MVC를 사용한 포탈 애플리케이션 내의 모든 포틀릿 사이에 공유를 할 수 있습니다.
thread : 새 스레드에서 요청하면 새로운 bean 인스턴스를 생성합니다. 같은 스레드의 요청에는 항상 같은 인스턴스가 반환됩니다.
custom : org.pringframework.beans.factory.config.Scope를 구현하고 커스텀 스코프를 스프링의 설정에 등록하여 사용합니다.

출처 : [Spring] Bean Scope와 Bean Life Cycle ooeunz.tistory.com/107
POJO Plain Old Java Object :  오래된 방식 자바 오브젝트 : 객체지향 원리 충실  
스프링 PSA POJO 철학 반영, Portable Service Abstraction  환경 변화 관계없이 일관된 환경제공 추상화구조
proxy 인터넷상에서, 한번 요청된 데이터를 저장해 두고 반복 요청 시에 저장된 데이터를 빠르게 제공해 주는 서버.

 

 

 

Inversion of Control

더보기

private BookRepository bookRepository = new BookRepository();   <- 의존객체를 직접만들어 사용

@Autowired                                                                         <-의존객체를 주입받아 사용. 

private BookRepository bookRepository;

public BookService(BookRepository bookRepository){                    

   this.bookRepository = bookRepository;

}

스프링빈 vs 자바빈

더보기
public class Book{                  >> 자바빈
	private int id;
	public int getId(){
		return id ;
	}
    public void setId(int id){
    	this.id;
    }
}                     

@Repository
public class BookRepository{}        >> 스프링 빈   이경우 어노테이션으로 autoscan 으로 빈으로 민들어짐. 

@Configuration , @Bean, AnnotationConfigApplicationContext  사용해서 스프링빈 만들때. 

더보기
@Configuration
 public class  ApplicationConfig{
      @Bean
      public BookRepositoy bookRepository(){
          return new BookRepository();
      }
}
-----
ApplicationContext ac = new AnnotationConfigApplicationContext(ApplicationConfig.class);  <<이렇게 사용가능

 

728x90
JDBC 관계형 데이터 베이스와 자바의 연결고리  Java database connectivity
- DataSource/DriverManager
- Connection
- PreparedStatement
SQL DDL, DML
ORM Object–relational mapping  : 객체 관계 맵핑 
- 도메인 모델 사용. 
    > 객체지향 프로그램 장점 활용, 디자인패턴, 코드재사용, 비즈니스 로직 구현 테스트 편함. 

애플리케이션의 클래스와 SQL 데이터 베이스의 테이블 사이의 맵핑 정보를 기술한 메타데이터를 사용하며, 자바 애플리케이션의 객체를 SQL 데이터베이스의 테이블에 자동으로 영속화 해주는 기술. 
- Java Persistence with Hibernate, Second Edition
장점 : 생산성, 유지보수성, 성능, 밴더 독립성
단점 : 학습비용
릴레이션/애트리뷰트
튜플/카디날리티
도메인
테이블 / 속성(ex_사원번호, 이름, 전화번호, 부서, 최소 차수는 1)
레코드 / 튜플갯수, 0일수있다. 
릴레이션에 포함된 각각 속성들이 가질수 있는 값들의 집합.  (ex_성별의 의 도메인 남, 여)

ORM 패러다임 불일치.

문제 객체 릴레이션
밀도 다양한 크기 객체 만듬, 커스텀 타입 만들기 쉬움 테이블 기본 데이터 타입
UDT(Uniform Data Type) CPU 실행 않고 PG 저장 비추
서브타입 상속구조 만들기 쉽다, 다형성 상속관계없음, 표준기술 아님, 다형적관계표현안됨
식별성 문제 레퍼러스 동일성(==)
인스턴스 동일성(equals())
주키(Primary key)
관계 객체 레퍼런스로 관계표현, 근복적으로 방향존재
다대다 가능
public class Study {
    User  user;
}
public class User{
    List<Study> studyList;
}
외래키(Foreign key)
방향 의미 없음, Join, 다대다 불가, 
데이터
네비게이션
레퍼런스를 이용해서 다른 객체로 이동 가능
콜렉션을 순회 활 수 도 있음
getOwner().getStudyList.stream().forEAch(s->s.getOwner());
비효율적. 
데이터 요청 적게 할수록 성능좋다. Join사용
많이 한번에 가져오는 것도 성능 많이씀. 
그렇다고 lazy loading을 하자니 그것도 문제다. (n+1 select)

 

JPA 사용이유

1. 도메인 주도 개발... 비지니스 로직 구현 집중 가능. 소프트웨어 복잡도 최소화 

도메인 : 사용자가 사용하는것.   영어사전: 1. 영역, 2. 소유지. 

+ 단건조회의 경우 jpa가 느릴수 있으나 객체와 데이터 베이스 사이 캐시사용으로 트랜젝션안 불필요한 쿼리를 날리지 않음. 

도메인 주도개발  참고 : 도메인 주도 설계(Domain-Driven Design) in Real Project — 도메인 

 

PostgreSQL 설치 및 서버 실행 (docker)

더보기

잘못만들어 삭제시

C:\Users\miseong>docker container ps
CONTAINER ID   IMAGE      COMMAND                  CREATED          STATUS          PORTS                    NAMES
6fe0fabf1380   postgres   "docker-entrypoint.s…"   22 minutes ago   Up 22 minutes   0.0.0.0:5432->5432/tcp   postgres_boot

C:\Users\miseong>docker container stop 6fe0fabf1380
6fe0fabf1380

C:\Users\miseong>docker container rm 6fe0fabf1380
6fe0fabf1380

C:\Users\miseong>docker images ps
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE

C:\Users\miseong>docker images ps -a
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE

 

 

--------
C:\Users\miseong>docker imgaes
docker: 'imgaes' is not a docker command.
See 'docker --help'

C:\Users\miseong>docker images ps
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE

docker run -p 5432:5432 -e POSTGRES_PASSWORD=pass -e POSTGRES_USER=keesun -e POSTGRES_DB=springdata --name postgres_boot -d postgres

docker run -p 5432:5432 -e POSTGRES_PASSWORD=1234 -e POSTGRES_USER=day -e POSTGRES_DB=diarydata --name postgres_diary -d postgres


C:\Users\miseong>docker images
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
postgres     latest    26c8bcd8b719   2 weeks ago   314MB

C:\Users\miseong>docker ps -a
CONTAINER ID   IMAGE      COMMAND                  CREATED              STATUS              PORTS                    NAMES
6fe0fabf1380   postgres   "docker-entrypoint.s…"   About a minute ago   Up About a minute   0.0.0.0:5432->5432/tcp   postgres_boot


C:\Users\miseong>docker start postgres_diary
postgres_diary

 

C:\Users\miseong>docker exec -i -t postgres_diary bash
root@b1de21ce08f0:/# su - postgres
postgres@b1de21ce08f0:~$ psql --username day --dbname diarydata
psql (13.2 (Debian 13.2-1.pgdg100+1))
Type "help" for help.

diarydata=# CREATE TABLE ACCOUNT(id int, username varchar(255), password varchar(255));
CREATE TABLE
diarydata=# INSERT INTO ACCOUNT VALUES(1,'day', 'pass');
INSERT 0 1
diarydata=# SELECT * FROM ACCOuNT;
 id | username | password
----+----------+----------
  1 | day      | pass
(1 row)

데이터베이스 조회

diarydata=# list
diarydata-# ;
ERROR:  syntax error at or near "list"
LINE 1: list
        ^
diarydata=# \list
                             List of databases
   Name    | Owner | Encoding |  Collate   |   Ctype    | Access privileges
-----------+-------+----------+------------+------------+-------------------
 diarydata | day   | UTF8     | en_US.utf8 | en_US.utf8 |
 postgres  | day   | UTF8     | en_US.utf8 | en_US.utf8 |
 template0 | day   | UTF8     | en_US.utf8 | en_US.utf8 | =c/day           +
           |       |          |            |            | day=CTc/day
 template1 | day   | UTF8     | en_US.utf8 | en_US.utf8 | =c/day           +
           |       |          |            |            | day=CTc/day
(4 rows)

diarydata=#

 

테이블 조회

diarydata=# \dt
        List of relations
 Schema |  Name   | Type  | Owner
--------+---------+-------+-------
 public | account | table | day
(1 row)

잘못만들어 삭제시

C:\Users\miseong>docker container ps
CONTAINER ID   IMAGE      COMMAND                  CREATED          STATUS          PORTS                    NAMES
6fe0fabf1380   postgres   "docker-entrypoint.s…"   22 minutes ago   Up 22 minutes   0.0.0.0:5432->5432/tcp   postgres_boot

C:\Users\miseong>docker container stop 6fe0fabf1380
6fe0fabf1380

C:\Users\miseong>docker container rm 6fe0fabf1380
6fe0fabf1380

C:\Users\miseong>docker images ps
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE

C:\Users\miseong>docker images ps -a
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE

 

 

--------
C:\Users\miseong>docker imgaes
docker: 'imgaes' is not a docker command.
See 'docker --help'

C:\Users\miseong>docker images ps
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE

docker run -p 5432:5432 -e POSTGRES_PASSWORD=pass -e POSTGRES_USER=keesun -e POSTGRES_DB=springdata --name postgres_boot -d postgres

docker run -p 5432:5432 -e POSTGRES_PASSWORD=1234 -e POSTGRES_USER=day -e POSTGRES_DB=diarydata --name postgres_diary -d postgres


C:\Users\miseong>docker images
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
postgres     latest    26c8bcd8b719   2 weeks ago   314MB

C:\Users\miseong>docker ps -a
CONTAINER ID   IMAGE      COMMAND                  CREATED              STATUS              PORTS                    NAMES
6fe0fabf1380   postgres   "docker-entrypoint.s…"   About a minute ago   Up About a minute   0.0.0.0:5432->5432/tcp   postgres_boot


C:\Users\miseong>docker start postgres_diary
postgres_diary

 

C:\Users\miseong>docker exec -i -t postgres_diary bash
root@b1de21ce08f0:/# su - postgres
postgres@b1de21ce08f0:~$ psql --username day --dbname diarydata
psql (13.2 (Debian 13.2-1.pgdg100+1))
Type "help" for help.

 

diarydata=# CREATE TABLE ACCOUNT(id int, username varchar(255), password varchar(255));
CREATE TABLE
diarydata=# INSERT INTO ACCOUNT VALUES(1,'day', 'pass');
INSERT 0 1
diarydata=# SELECT * FROM ACCOuNT;
 id | username | password
----+----------+----------
  1 | day      | pass
(1 row)

 

데이터베이스 조회

diarydata=# list
diarydata-# ;
ERROR:  syntax error at or near "list"
LINE 1: list
        ^
diarydata=# \list
                             List of databases
   Name    | Owner | Encoding |  Collate   |   Ctype    | Access privileges
-----------+-------+----------+------------+------------+-------------------
 diarydata | day   | UTF8     | en_US.utf8 | en_US.utf8 |
 postgres  | day   | UTF8     | en_US.utf8 | en_US.utf8 |
 template0 | day   | UTF8     | en_US.utf8 | en_US.utf8 | =c/day           +
           |       |          |            |            | day=CTc/day
 template1 | day   | UTF8     | en_US.utf8 | en_US.utf8 | =c/day           +
           |       |          |            |            | day=CTc/day
(4 rows)

diarydata=#

 

테이블 조회

diarydata=# \dt
        List of relations
 Schema |  Name   | Type  | Owner
--------+---------+-------+-------
 public | account | table | day
(1 row)

 

 

 

 

 

jdbc 예제

더보기
package com.today10sec.diary.basic;

import java.sql.*;

public class DBConnectTest {

    public static void main(String[] args) throws SQLException {
        String url = "jdbc:mariadb://localhost:3306/today10secDB";
        String userName = "today";
        String pw = "1234";

        //try with resource java 7  //자동 자원정리 메소드
        try(Connection connection = DriverManager.getConnection(url, userName,pw)){
            //System.out.println("Connection Created"+ connection);
            //String sql = " CREATE TABLE ACCOUNT(id int, username varchar(255), password varchar(255))";
            //String sql = " INSERT INTO ACCOUNT VALUES(1,'day', 'pass')";
            String sql =" SELECT * FROM ACCOuNT";
            ResultSet rs ;
            try(PreparedStatement statement = connection.prepareStatement(sql)){
                rs = statement.executeQuery();
                //int result = statement.executeUpdate();
                //System.out.println(result);
                while (rs.next()){
                    System.out.println(rs.getInt("id")+rs.getString("username")+rs.getString("password"));
                }
            }

        }
        //JDBC : 커넥션 만드는 비용이 비쌈 - 오래걸림.
        //스프링 부트 : hicari : 커넥션 객체 미리 만들고 사용. 
    }
}

JPA 프로그래밍: 프로젝트 세팅

 

데이터베이스 실행

  • PostgreSQL 도커 컨테이너 재사용
  • docker start postgres_boot
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>

스프링 부트

  • 스프링 부트 v2.*
  • 스프링 프레임워크 v5.*

 

스프링 부트 스타터 JPA

  • JPA 프로그래밍에 필요한 의존성 추가
    • JPA v2.*
    • Hibernate v5.*
  • 자동 설정: HibernateJpaAutoConfiguration
    • 컨테이너가 관리하는 EntityManager (프록시) 빈 설정
    • PlatformTransactionManager 빈 설정
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

 

엔티티메니저.. JPA 스팩. 

엔티티매니저 내부적 하이버네이트 사용. 

강의 해선. 스프링데이터 JPA 를 사용

 

 

JDBC 설정

  • jdbc:postgresql://localhost:5432/springdata
  • keesun
  • pass

 

application.properties

  • spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
  • spring.jpa.hibernate.ddl-auto=create

#spring.jpa.hibernate.ddl-auto : create or update ( 매번 스키마 생성.  )for dev, validate(매핑안되면 에러) for prod,추천 

update..  스키마 변경시...  스키마가 추가가됨.  주의 

 

spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation  :

추가한 드라이버가 메시지를 구현안했을때 경고  >> true  없음. 

 

 

 

spring.jpa.hibernate.ddl-auto=create..  운영에서 소스 잘못 올라가면 살인날듯

 

 

entityManager 

 

 

기본예제

더보기
#basic db
spring.datasource.url=jdbc:postgresql://localhost:5432/diarydata
spring.datasource.password=1234
spring.datasource.username=day

#hibernate
spring.jpa.hibernate.ddl-auto=create
#spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
package com.today10sec.diary.etc;

import org.hibernate.Session;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;

@Component
@Transactional
public class JpaRunner implements ApplicationRunner {

    @PersistenceContext
    EntityManager entityManager;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        Account account = new Account();
        account.setUsername("day");
        account.setPassword("12345");
        Session session = entityManager.unwrap(Session.class);
        session.save(account);
        entityManager.persist(account);
        System.out.println("접근");
    }
}
package com.today10sec.diary.etc;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Account {

    @Id @GeneratedValue
    private Long id;

    @Column
    private String username;

    private String password;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

 

 

+ Recent posts