반응형

오류

QueryDSL적용중 QuerySyntaxException: {Entity} is not mapped 오류 발생
원인을 찾기 위해 이것 저것 찾아보고 별걸 다 해보다가 포기..
다음날 맑은 정신으로 보니 바로 보이는 문제점..

원인

결론부터 말하면 Multi Datasource구성 때문이였다

  • 쿼리를 만들 때 사용하는 JPAQueryFactory에서 EntityManager를 사용하는데 이때 기본으로 primary Datasource의 EntityManager를 사용한다.
    primary EntityManager에서  secondary의 entity 조회하는 요청을 받다보니 발생하던 오류였던것
    • gradle 설정부터 property 설정 repositoryImpl 다시 구현 등등.. 해봐도 해결이 안됨
    • secondary Datasource를 먼저 queryDSL로 변경하다보니 본인이 뭘 잘못 설정한게 아닐까 하는 생각으로 다른곳에서 원인을 찾고 있었다.

 

해결 방법

- Multi Datasource 구성에서는 datasource별로 다른 EntityManager를 사용하기 때문에
   각 datasource의 EntityManager로 JPAQueryFactory를 빈으로 등록

- RepositoryImpl.java에서도 올바른 JPAQueryFactory를 가져다가 사용 

QueryDslConfig.java

import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Configuration
public class QueryDslConfig {


    @PersistenceContext(unitName = "firstEntityManager")
    private EntityManager firstEntityManager;

    @PersistenceContext(unitName = "secondaryEntityManager")
    private EntityManager secondaryEntityManager;

    @Primary
    @Bean
    public JPAQueryFactory firstJpaQueryFactory() {
        return new JPAQueryFactory(firstEntityManager);
    }

    @Bean("secondaryJpaQueryFactory")
    @Qualifier("secondaryJpaQueryFactory")
    public JPAQueryFactory secondaryJpaQueryFactory() {
        return new JPAQueryFactory(thirdEntityManager);
    }

}

??RepositoryImpl.java

@Repository
public class ??RepositoryImpl implements ??Repository {

    private final JPAQueryFactory secondaryJpaQueryFactory;

    public ??RepositoryImpl(@Qualifier("secondaryJpaQueryFactory") JPAQueryFactory secondaryJpaQueryFactory) {
        this.secondaryJpaQueryFactory = secondaryJpaQueryFactory;
    }
...
}

 

build.gradle 설정

plugins {
...
	id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"

}

...

dependencies {
...
	implementation "com.querydsl:querydsl-jpa"
...
}
...
/*
 * queryDSL 설정 추가
 */
// queryDSL에서 사용할 경로 설정
def queryDlsDir = "$buildDir/generated/querydsl"

// queryDSL 설정 블록
// QueryDSL 설정 블록에서는 JPA 사용 여부와 QueryDSL 소스 코드를 생성할 디렉토리를 지정
// - jpa = true: JPA 엔터티를 기반으로 QueryDSL 클래스를 생성할 것임을 나타냄
// - querydslSourcesDir = querydslDir: QueryDSL 소스 코드를 저장할 디렉토리를 설정.
querydsl {
	jpa = true
	querydslSourcesDir = queryDlsDir
}
// SourceSet 설정: 소스셋에 QueryDSL 디렉토리를 추가하여 빌드 시 해당 디렉토리의 소스 코드가 컴파일되도록 함
sourceSets {
	main.java.srcDir queryDlsDir
}
// configurations 설정
// - compileOnly 구성에 annotationProcessor를 추가하여 annotation processor를 컴파일 시에만 사용하도록 설정
// - querydsl.extendsFrom compileClasspath: QueryDSL이 컴파일될 때 compileClasspath를 상속하도록 설정
//   이는 QueryDSL이 프로젝트 내의 다른 클래스 및 의존성을 참조할 수 있게 
configurations {
	querydsl.extendsFrom compileClasspath
}
// compileQuerydsl 설정
// - QueryDSL을 컴파일할 때 필요한 옵션을 설정
//   여기서는 annotationProcessorPath를 설정하여 QueryDSL의 annotation processor를 사용하도록 
compileQuerydsl {
	options.annotationProcessorPath = configurations.querydsl
}
반응형
반응형

JAVA는 JVM을 통해 여러 OS에 종속되지 않고 실행될 수 있는 코드를 작성할 수 있음

하지만 이러한 특징 때문에 특정 OS에만 존재하는 기능을 JVM에 담지 못해 사용하지 못한다는 단점이 있다

 

JNI(Java Native Interface)

앞서 말한 단점을 해결하기 위해 OS의 고유 기능을 C, C++로 함수를 만들고
Java와 연결해 OS의 기능을 사용할 수 있도록 하는 기술

반응형
반응형

max connection 확인

SHOW VARIABLES LIKE '%max_connection%';

현재 connection 확인

SHOW STATUS LIKE 'Threads_connected';

max connection 수정

SET GLOBAL max_connections = 200;
반응형
반응형

https://reflectoring.io/dont-use-spring-profile-annotation/

 

Don't Use the @Profile Annotation in a Spring Boot App!

Why using Spring's @Profile annotation is a bad idea and what to do instead.

reflectoring.io


@Profile에 따라 Service인터페이스 구현체를 사용하려고 찾아보던중 발견한 내용입니다.

원래 계획은 아래 샘플과 같이 use 프로파일을 사용할경우 UseService, no일경우 NoService를 사용하는것이였다. 

public interface interfaceService {
	void test();
}

@Profile("use")
@Service
public class useService implement interfaceService {

	@Overide
    void test() {
    	// ...
    }
}


@Profile("no")
@Service
public class noService implement interfaceService {

	@Overide
    void test() {
    	// ...
    }
}

위 블로그에선 이 어노테이션을 사용하면 유지보수가 어렵다는 문제를 얘기했는데 만약 프로젝트를 수정하기 위해 해당 profile이 영향을 주는 범위를 찾는다면 어떻게 해야될까? 상상만해도 어렵다.. 의도를 파악하기도 어려울듯하다.

특정 프로필이 영향을 주는 범위를 한눈에 보기 어렵다 보니 Profile 어노테이션이 사용되는 영역을 하나하나 찾아서 확인해야 된다는 문제가 있다.

블로그에서는 이 문제를 해결하기위해 사용할 수 있는 어노테이션을 소개해줬는데 바로 @ConfigurationOnProperty 이다.

프로퍼티 값에 따라 bean이 생성되며 값이 없을경우 생성되지 않는 어노테이션
"application.test.using" 프로퍼티가 있을 때 해당 값에 따라 다르게 bean을 생성할 수 있다.

...
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class BeanConfiguration {

    @ConditionalOnProperty(name = "application.test.use", havingValue = "true")
    class testUseConfiguration {

        @Bean
        public YesService service() {
            return new YesService();
        }
    }

    @ConditionalOnProperty(name = "application.test.use", havingValue = "false")
    class testNotUseConfiguration {

        @Bean
        public NoService service() {
            return new NoService();
        }
    }
}

 

Test

@AutoConfigureMockMvc
@SpringBootTest
public class ConfigurationOnPropertyTest {

    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    TestService service;

    @Value("${application.test.use}")
    String use;

    @Test
    public void test() {
        logger.info("application.test.use: {}", use);
        logger.info("service: {}", service.getClass().getName());
    }
}

application.test.use가 true일 경우

application.test.use가 false일 경우

 

반응형
반응형

마이그레이션 참고 문서

https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Migration-Guide

 

GitHub - spring-projects/spring-boot: Spring Boot

Spring Boot. Contribute to spring-projects/spring-boot development by creating an account on GitHub.

github.com

 

Spring boot 3.0대로 올라가며 Java 17 버전을 베이스로 동작을 한다고 합니다.

그래서 project에 사용하는 Java 8.0과 Spring boot 2.3.*의 버전을 업그레이드를 진행하게 됐습니다.

이 글은 변경을 진행하며 발견한 이슈를 정리하는 글이며,, 계속해서 업데이트해갈 예정입니다...!
 * 2.3 ~ 3.0 사이에 변경된 내용들을 정리할 예정입니다.

 

업그레이드를 하기전 왜 많은 기업에서 8.0버전을 사용하는 이유를 짧게 찾아봤다.
다른 이유들도 많겠지만 LTS(Long Term Support)라 사용해왔다는 글들을 발견했다. 

17버전 부터는 8.0버전과 지원기간이 차이가 많이 나지 않고,
Spring boot 3.0이 17버전을 베이스로하기 때문에 업데이트를 진행하게 됐다..

Spring boot Version 변경 (Gradle)

더보기

build.gradle 설정 변경

...
plugins {
    id 'org.springframework.boot' version '3.0.0' // 3.0.0으로 변경
    ...
}
...

// Java version 변경
sourceCompatibility=17
targetCompatibility=17

assert System.properties["java.specification.version"] == "17"
...

gradle 버전업
gradle-wrapper.properties 설정 변경

distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip

 

 

1. properties에러

더보기

active profile 설정 방법이 2.4 버전부터 변경됨

https://spring.io/blog/2020/08/14/config-file-processing-in-spring-boot-2-4#profile-activation

  • profiles 설정 방법: spring.profiles에서 spring.config.on-profile로 변경됨
  • include 설정 방법: spring.profiles.include에서 spring.profile.group로 설정

 

서버 실행 명령어

java -jar -Dspring.profiles.active=dev {SPRING_PROJECT}.jar
  • 기존 application-dev.yml 설정
spring:
  profiles: dev
  profiles.include:
      - swagger
  • 변경
# application.yml
spring:
  profiles:
    group:
      dev: swagger,debug
      test: swagger
---
# application-dev.yml
spring:
  config:
    activate:
      on-profile: dev
  datasource:
    ...
---
# application-test.yml
spring:
  config:
    activate:
      on-profile: test
  datasource:
    ...
---
# application-prod.yml
spring:
  config:
    activate:
      on-profile: prod
  datasource:
    ...

 

2. cannot find javaee.*

더보기

spring boot 3.0부터는 javax.*이 대신 Jakarta EE 9 API()를 사용

 

javax를 jakarta로 변경해주기만 하면 됨

// 기존
import javax.servlet.??

// 변경
import jakarta.servlet.??

 

3. spring-security-without-the-websecurityconfigureradapter WebSecurityConfigurerAdapter Deprecated

더보기
In Spring Security 5.7.0-M2 we deprecated the WebSecurityConfigurerAdapter,
as we encourage users to move towards a component-based security configuration.

Spring Security 5.7.0-M2부터 WebSecurityConfigurerAdapter을 더이상 사용할 수 없음
자세한 해결 방법은 아래 링크 참조하면 되며 간단히 수정한 내용을 작성함

https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter

 

1. WebSecurity config 설정 변경
WebSecurityCustomizerbean을 등록하는 것입니다.
* 5.8에서 변경된 내용 web.ignoring().antMatchers -> web.ignoring().requestMatchers

// 기존
@Override
public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
}

// 변경
@Bean
public WebSecurityCustomizer configure() throws Exception {
    // web.ignoring().antMatchers(HttpMethod, "")
	// -> web.ignoring().requestMatchers(HttpMethod, "")
    return (web) -> web.ignoring().requestMatchers(HttpMethod.OPTIONS, "/**");

}

2. HttpSecurity config 설정 변경  (수정필요)
SecurityFilterChainbean을 직접 등록하는 방식으로 변경
 * 5.5에서 변경된 내용 HttpSecurity.authorizeRequests() -> HttpSecurity.authorizeHttpRequests()

// 기존
@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests((authorize) -> authorize
         // .antMatchers(ApiConstants.SAMPLE).hasAnyAuthority(AuthEnum.ADMIN.getName)
            .anyRequest().authenticated()
        )
        .httpBasic(withDefaults());
}

// 변경
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests((authorize) -> authorize
         // .requestMatchers(ApiConstants.SAMPLE).hasAnyAuthority(AuthEnum.ADMIN.getName)
            .anyRequest().authenticated()
        )
        .httpBasic(withDefaults());
    return http.build();
}

 

3. UrlAuthorizationConfigurer.AuthorizedUrl.access -> AuthorizeHttpRequestsConfigurer.AuthorizedUrl.access
attribute 문자열을 통해 권한을 부여하는 방식이 변경됨
access(String attribute) -> access(AuthorizationManager<RequestAuthorizationContext> manager)

API별 권한 부여방식이 변경됨
antMatchers -> requestMatchers

// 기존
@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        ...
        .authorizeRequests()
        	.antMatchers("/api/v1").access("permitAll()")
        ...
 }
// 변경
@Bean
protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        ...
        .authorizeHttpRequests((authorize) ->
			authorize.requestMatchers("/api/v1")
				.access(new WebExpressionAuthorizationManager("permitAll()"))
        ...
	return http.build();
 }

 

4. Jasypt 오류 - auto configuration 설정방식 변경

더보기

properties 암호화로 사용하던 Jasypt가 동작을 안하는 오류가 발생했다..

 

원인은 3.0버전부터 auto-configuration 설정파일의 형식이 변경됐기 때문
기존: META-INF/spring.factories에 원하는 configuration 정의
변경: META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports에 정의

3.0에서 변경된 auto-configuration 등록 방법에 대응하기 위해선 라이브러리에 변경사항이 반영돼야 함

다음 이슈를 참고해 강제로 auto-configuration을 등록해 해결함
- https://github.com/ulisesbocchio/jasypt-spring-boot/issues/342

@ImportAutoConfiguration(classes = JasyptSpringBootAutoConfiguration.class) // Remove when Jasypt is released for spring boot 3 (or at least 2.7)
@SpringBootApplication
public class SampleApplication {
    ...
}

 

 

5. swagger-ui 404

더보기

Spring boot 3.0으로 마이그레이션하니 swagger-ui가 동작하지 않게됨
springfox git을 가보니 나만 그런게 아니라 안심이 됐다(?)

 

https://springdoc.org/

springdoc 공식 사이트에선 springdoc-openapi v2를 사용하면 3.0에서 사용 가능하다고하니
갈아탈 준비를 해야겠다..
우선 swagger-ui를 사용한 API doc 수정은 우선순위를 미루기로 결정


반응형
반응형
만 나이 계산기입니당



생년월일 입력:
만 
<div>만 나이 계산기입니당</div>
<p data-ke-size="size16"><br /><br /></p>
<div>생년월일 입력: <input id="birthdate" type="date" value="1990-01-01" /> <br /><button id="btn">나이 계산</button>만&amp;nbsp<span></span></div>
<p data-ke-size="size16">&nbsp;</p>
<script>
  const myAge = function() {
		let date = new Date();
        let birth = new Date(document.getElementById( 'birthdate' ).value);
        let age = date.getFullYear() - birth.getFullYear();
        if (birth.setFullYear(birth.getFullYear()+age) > date ) {
            age -= 1;
        }
    
        document.getElementById("age").innerText = age + "세 입니다.";
  }
</script>
반응형

+ Recent posts