네로개발일기

개발자 네로의 개발 일기, 자바를 좋아합니다 !

'전체 글'에 해당되는 글 194건


반응형

 

현재 변경사항을 다른 브랜치에 커밋하는 방법

 

개발을 하다가 커밋을 하려고 보면 다른 브랜치인 것을 확인할 때가 있다.

 

git stash를 사용하여 다른 브랜치에 커밋할 수 있다.

$ git stash
$ git checkout other-branch
$ git stash pop

git stash는 커밋하지 않은 변경사항을 임시로 저장한다.

git checkout 명령어로 브랜치를 옮긴 뒤

git stash pop 명령어로 앞서 저장한 내용을 가져온다.

 

 

$ git stash -m "임시저장"
Saved working directory and index state WIP on master: 451c825 Add index.html

git stash 는 커밋처럼 -m 옵션을 사용하면 변경사항에 메시지를 붙일 수 있다.

 

git stash list 로 현재 저장소에 임시 저장된 전체 목록을 확인할 수 있다.

$ git stash list
stash@{0}: On master: 임시 저장

 

참고

https://www.lainyzine.com/ko/article/git-stash-usage-saving-changes-without-commit/

 

git stash 사용법: 커밋하지 않고 변경사항 저장하는 방법

git stash 명령어으로 Git 저장소의 변경사항을 임시로 스택에 쌓아둘 수 있습니다. 이 글에서는 stash를 사용해 변경사항을 저장하고 다시 꺼내오는 방법에 대해서 소개합니다.

www.lainyzine.com

 

728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

- Spring Data JPA

- Spring Boot

 

 

application.yml

spring:
  datasource:
    hikari:
      bootdb1:
        driver-class-name: [driver-name]
        jdbc-url: [url]
        username: [username]
        password: [password]
      bootdb2:
        driver-class-name: [driver-name]
        jdbc-url: [url]
        username: [username]
        password: [password]

 

* 정의해야 하는 것

- DataSource

- EntityManagerFactory

- TransactionManager

=> DBConfig를 만들어주자.

 

LegacyDBConfig.java

package com.jiyoon.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy;
import org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "legacyEntityManager",
        transactionManagerRef = "legacyTransactionManager",
        basePackages = "com.jiyoon.repository.legacy"
)
public class LegacyDBConfig { 

    @Bean
    @ConfigurationProperties("spring.datasource.hikari.bootdb2")
    public DataSource legacyDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    public PlatformTransactionManager legacyTransactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(legacyEntityManager().getObject());

        return transactionManager;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean legacyEntityManager() {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(legacyDataSource());
        em.setPackagesToScan("com.jiyoon.model.legacy");

        HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(adapter);
        Map<String, Object> properties = new HashMap<>();
        properties.put("hibernate.physical_naming_strategy", SpringPhysicalNamingStrategy.class.getName());
        properties.put("hibernate.implicit_naming_strategy", SpringImplicitNamingStrategy.class.getName());
        em.setJpaPropertyMap(properties);

        return em;
    }
}

 

TargetDBConfig.java

package com.jiyoon.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy;
import org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "targetEntityManager",
        transactionManagerRef = "targetTransactionManager",
        basePackages = "com.jiyoon.repository.target"
)
public class TargetDBConfig {

    @Bean
    @Primary
    @ConfigurationProperties("spring.datasource.hikari.bootdb1")
    public DataSource targetDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @Primary
    public PlatformTransactionManager targetTransactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(targetEntityManager().getObject());

        return transactionManager;
    }

    @Bean
    @Primary
    public LocalContainerEntityManagerFactoryBean targetEntityManager() {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(targetDataSource());
        em.setPackagesToScan("com.jiyoon.model.target");

        HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(adapter);
        Map<String, Object> properties = new HashMap<>();
        properties.put("hibernate.physical_naming_strategy", SpringPhysicalNamingStrategy.class.getName());
        properties.put("hibernate.implicit_naming_strategy", SpringImplicitNamingStrategy.class.getName());
        em.setJpaPropertyMap(properties);

        return em;
    }
}

 

@ConfigurationProperties 과 관련 글

https://frogand.tistory.com/131

https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config

 

Core Features

Spring Boot lets you externalize your configuration so that you can work with the same application code in different environments. You can use a variety of external configuration sources, include Java properties files, YAML files, environment variables, an

docs.spring.io

 

* 트랜잭션

여러 단계를 수행할 때, 하나라도 실패하면 모두 취소되어야 하며 데이터의 무결성을 보장한다.

 

* 스프링의 트랜잭션 지원

선언적 트랜잭션을 지원한다. 트랜잭션의 범위를 코드 수준으로 정의 가능하며 설정 파일 또는 어노테이션을 이용하여 규칙 및 범위를 설정할 수 있다.

 

- PlatformTransactionManager

트랜잭션은 PlatformTransactionManager 인터페이스를 이용해 추상화했다. DB 연동 기술에 따라 각각의 구현 클래스가 제공된다.

실제 트랜잭션을 처리할 때 PlatformTransactionManager를 사용하진 않는다. 선언적 트랜잭션 방식으로 처리한다.

- JDBC 기반의 트랜잭션 설정

JDBC, MyBatis 등의 JDBC를 이용하는 경우 DataSourceTransactionManager를 관리자로 등록한다.

dataSource 프로퍼티를 통해 전달받은 Connection으로 commit, rollback을 수행하면서 관리한다.

- JPA 트랜잭션 설정

JPA를 사용하는 경우 JpaTransactionManager를 사용한다.

 

 

엔티티

package com.jiyoon.model.legacy

@Entity
@Table(schema = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    
    private String name;
}

 

JPA 레포지토리

package com.jiyoon.repository.legacy;

public interface UserRepository extends JpaRepository<User, Integer> {}

 

 

출처

https://www.baeldung.com/spring-data-jpa-multiple-databases

 

728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

@ConfigurationProperties

Spring Boot에서 *.properties나 *.yml 파일에 있는 property를 자바 클래스에 값을 바인딩하여 사용할 수 있게 해주는 어노테이션

 

Spring Boot에서는 운영에 필요한 정보를 프로퍼티 파일에서 Key-Value 형태로 저장하여 관리한다.

 

다음과 같은 properties 파일이 있다고 가정할 때 @Value를 사용하여 바인딩할 수 있다.

site-url.naver=https://www.naver.com
site-url.google=https://www.google.com
@Value("${site-url.naver}")
private String naver;

@Value("${site-url.google}")
private String google;

 

@ConfigurationProperties의 좋은점은

프로퍼티를 바인딩할 때 완화된 규칙을 적용하여 다음과 같은 변형도 모두 같게 취급한다.

mail.hostName    // CamelCase
mail.hostname
mail.host-name   // *.properties와 *.yml에 권장되는 표기 방법
mail.host_name   
mail.HOST_NAME   // 시스템 환경변수를 사용할 때 권장

 

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import lombok.Data;

@Component
@ConfigurationProperties(prefix = "site-url")
@Data
public class siteUrlProperties {
	private String naver;
	private String google;
	
}

@Component로 bean을 등록하고

@ConfigurationProperties에 prefix를 선정한다. 

properties 파일에 site-url.*에 대해 바인딩한다.

 

Spring Boot 2.2

스프링 부트 2.2에서는 @ConfigurationProperties 클래스들을 모두 찾아서 등록해주므로 @Component, 혹은 @Comfiguration 과 같은 어노테이션이나 @EnableConfigurationProperties를 붙일 필요가 없다.

 

참고

https://www.baeldung.com/configuration-properties-in-spring-boot

https://sgc109.github.io/2020/07/07/spring-boot-configuration-properties/

 

Spring Boot 의 @ConfigurationProperties

@ConfigurationProperties 는 Spring Boot 에서 properties 파일에 정의된 프로퍼티 중 주어진 prefix 를 가지는 프로퍼티들을 POJO 에 매핑하여 Bean 으로 만들수 있게 해주는 어노테이션이다. 그럼 @ConfigurationPrope

sgc109.github.io

 

728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

# 아이템26. 로타입은 사용하지 마라

 

## 용어 정리
public class<T> Example {
  private T member;
}
- 제네릭 클래스[인터페이스]: 클래스[인터페이스] 선언에 타입 매개변수(type parameter)가 쓰인다.
- 제네릭 타입(Generic Type): 제네릭 클래스와 제네릭 인터페이스를 통틀어 말함. Example<T>
- 매개변수화 타입(parameterized Type): 각각의 제네릭 타입은 parameterized Type을 선언함. Example<String>
- 타입 매개변수(Type parameter): 제네릭 선언에 사용된 매개변수 <T>
- formalType: Example<E>
- actualType: Exampe<String>
- 로타입(raw tyep): 제네릭 타입에서 타입 매개변수를 전혀 사용하지 않았을 때

 

## 제네릭의 타입 안정성
오류는 가능한 한 발생 즉시, 이상적으로는 컴파일 타임에 발견하는 것이 좋다.

 

### 1. 로타입의 단점: 컴파일 타임에 타입 정보를 알지 못한다.
ClassCastException과 같은 런타임에야 알아챌 수 있는 에러를 만들기 쉽다.
아래 예제는 컴파일 타임에는 문제가 없으나, 런타임에서 ClassCastException이 발샌한다.
public class Raw {
    public static void main(String[] args) {
        List<String> strings = new ArrayList<>();
        unsafeAdd(strings, Integer.valueOf(42));
        String s = strings.get(0); // 컴파일러가 자동으로 형변환 코드를 넣어준다.
    }

    private static void unsafeAdd(List list, Object o) {
        list.add(o);
    }
}

 

### 2. 제네릭의 장점: 컴파일 타임에 타입 선언이 녹아있다.
컴파일러가 타입 선언에 대해 인지하고 있기 때문에, 아무런 경고 없이 컴파일되면 의도대로 동작할 것임을 보장한다.
public class Raw {
    public static void main(String[] args) {
        List<String> strings = new ArrayList<>();
        unsafeAdd(strings, "42");   // 컴파일 타임에 에러를 체크해주어서 바꿀 수 있다.
        String s = strings.get(0);
    }

    private static <T> void unsafeAdd(List<T> list, T o) { // 제네릭 선언
        list.add(o);
    }
}
컴파일러가 컬렉션에서 원소를 넣는 모든 곳에 보이지 않은 형변환을 추가하여 절대 실패하지 않음을 보장하였다.

 

## 로타입은 절대로 사용하지 말자
제네릭이 안겨주는 안정성과 표현력을 모두 잃게 된다.
### 로타입이 만들어진 이유 - 호환성
기존 코드를 모두 수용하면서 제네릭을 사용하는 새로운 코드와 맞물려 돌아가게 하기 위해서이다.
호환성: 로타입 지원 + 제네릭 구현 시, 소거(erasure) 방식을 이용

 

** List VS List<Object>
- List: 로타입
- List<Object>: 임의 객체를 허용하는 매개변수화 타입 - 타입에 대한 정보를 컴파일러에 알려주었다.

 

로타입을 사용하면 타입 안정성을 잃게된다.

 

## 타입 안전 + 유연: 비한정 와일드카 타입
- 비한정 와일드카드 타입(unbounded wildcard type): 제네릭 타입을 쓰고 싶지만, 실제 타입 매개변수가 무엇인지 신경쓰고 싶지 않을 대 사용한다. Set<E>의 비한정 와일드카드 타입은 Set<?>
static int numElementsInCommon(Set<?> s1, Set<?> s2) {...}
와일드카드 타입은 안전하지만 로타입은 안전하지 않다.

 

- 로타입: 아무 원소나 넣을 수 있어 타입 불변식을 훼손하기 쉽다.
- 와일드카드 타입: Collection<?>에는 어떤 원소도 넣을 수 없다. (null 외에는) 컴파일 타임에 오류 메시지를 볼 수 있고, 컬렉션의 타입 불변식을 훼손하지 못하게 막고, 컬렉션에서 꺼낼 수 있는 객체의 타입도 알 수 없게 한다.
 
## 로타입 규칙 예외
### 1. class 리터럴에는 로타입을 사용한다.
자바 명세 자체가 class 리터럴에 매개변수화 타입을 사용하지 못하게 하였다.
- 허용: List.class, String[].class, int.class
- 불가: List<String>.class, List<?>.class

 

### 2. instanceof 연산자는 로타입을 사용한다.
instanceof 연산자는 비한정 와일드카드 타입 이외의 매개변수화 타입에는 적용할 수 없다.
로타입이든 비한정적 와일드타입이든 instanceof는 똑같이 동작한다.
if (o instanceof Set) { // 로타입
  Set<?> s = (Set<?>) o; // 와일드카드 타입
}
728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

 

# 아이템25. 톱레벨 클래스는 한 파일에 하나만 담아라.

## 한 파일에 톱레벨 클래스가 여러 개일 때 문제점
public class Main {
  public static void main(String[] args) {
    System.out.println(Utensil.NAME + Dessert.NAME);
  }
}
// Utensil.java
class Utensil {
  static final String NAME = "pan";
}

class Dessert {
  static final String NAME = "cake";
}
javac Main.java Utensil.java
> pancake
Main 클래스를 실행하면 pancake가 출력된다.
이 상태에서 똑같은 두 클래스를 담은 Dessert.java가 추가되면
class Utensil {
  static final String NAME = "pot";
}

class Dessert {
  static final String NAME = "pie";
}
javac Main.java Dessert.java
> Duplicate class found in the file '~/src/main/java/Dessert.java'
javac Main.java Dessert.java 명령어로 컴파일을 한다면 컴파일 오류가 발생하고 클래스가 중복 정의되었다고 알려준다.
컴파일은 가장먼저 Main.java를 컴파일하고, 그 안에서 Utensil 참조를 먼저 만나 Utensil.java 파일을 살펴서 Utensil, Dessert 클래스를 모두 찾아낸다. 그 다음 두번째로 참조되는 Dessert에 따라 Dessert.java를 처리하려고 할 때 같은 클래스의 정의가 이미 되어있음을 알게 된다.
javac Main.java
> pancake
javac Main.java Utensil.java
> pancake
javac Dessert.java Main.java
> potpie
이처럼 컴파일러에 어느 소스파일을 먼저 전달하느냐에 따라 동작이 달라진다.

 

## 해결책
톱클래스를 서로 다른 소스 파일로 분리하면 된다.
// Utensil.java
class Utensil {
  static final String NAME = "pan";
}
// Dessert.java
class Dessert {
  static final String NAME = "cake";
}

 

## 정적 멤버 클래스
굳이 한 파일에 담고싶다면 정적 멤버 클래스를 사용하는 방법을 고려하자. 부차적인 클래스를 정적 멤버 클래스로 만들면 가독성도 높아지고, private로 선언하여 접근 범위도 최소로 관리할 수 있다.
단, 정적 멤버 클래스는 보통 하나의 클래스에 딸린 부차적인 클래스를 추가하는데 사용되므로, 패턴을 유지하기 위해 부차적인 클래스를 추가하는 경우에만 사용하자.
// example
class Utensil {
  static final String NAME = "pan";

  private static class Dessert {
    static final String Name = "cake";
  }
}
class Test {
  public static void main(String[] args) {
    System.out.println(Utensil.NAME + Dessert.NAME);
  }

  private static class Utensil {
    static final String NAME = "fan";
  }

  private static class Dessert {
    static final String NAME = "cake";
  }
}
## 정리
소스 파일 하나에는 반드시 톱레벨 클래스(혹은 인터페이스)를 하나만 담자. 이 규칙만 따른다면 소스파일을 어떤 순서로 컴파일 하든 프로그램의 동작이 달라지지 않는다.

 

728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !