반응형
반응형

 

📝전체 URL 적용

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
            .allowedOrigins("*") // 모든 origin 허용 (보안상 주의)
            .allowedMethods("GET", "POST", "PUT", "DELETE")
            .allowedHeaders("*");
    }
}

 

  • addMapping
    • 내가 작성한 어떤 라우팅에 대해서 CORS를 허용할 지
  • allowedOrigins
    • 어떤 요청자에 대해서 허용시킬 것인지
  • allowedMethods
    • 어떤 HTTP 메소드에 대해서 허용할지
  • allowedHeaders
    • 어떤 헤더 값에 대해서 허용할지

 

📝부분 라우팅 적용

@CrossOrigin(origins = "*")
@GetMapping("/next")
public @ResponseBody HashMap<String, Object> next() throws InterruptedException {

	System.out.println("next2 call!!");
	HashMap<String, Object> map = new HashMap<String, Object>();
	map.put("id", "Next 테스트");
	
	Thread.sleep(3000);
	
	return map;
}
반응형
반응형

 

 

 

반응형
반응형

Enum은 열거형이라는 뜻을 가지고 있다 Enum이라는 타입을 사용할 시 Enum에서 설정한 데이터만 사용할 수 있다 예를 들면 '요일' "월, 화, 수, 목, 금, 토, 일"이 있다 해당 요일 외에 데이터는 받지 않기 때문에 컴파일 에러로 사전에 버그를 잡아낼 수 있다

 

Enum에 다양한 메소드가 존재한다

 

📝name()

enum Week {
    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY,
    SUNDAY
}



Week week = Week.FRIDAY;

String weekName = week.name();
System.out.println(weekName); // FRIDAY

enum의 값을 String으로 반환해준다

 

📝예제

import java.util.Arrays;

public enum AreaEnum {

    SEOUL("서울",1),
    INCHEON("인천",2),
    GYEONGGI("경기",31);

    private final String name;
    private final int code;


    AreaEnum(String name, int code) {
        this.name = name;
        this.code = code;
    }

    public String getName() {
        return this.name;
    }

    public int getCode() {
        return this.code;
    }


    public static String getByCode(int code) {
        Arrays.stream(values())
                .filter(area -> area.getCode() == code)
                .findFirst()
                .map(AreaEnum::getName)
                .orElse(null);
    }

}
반응형
반응형

 

📝Boolean OR절 축약

int areaCode = detail.get("areaCode").asInt();
boolean isAreaCodeValid = areaCode == 1 || areaCode == 2 || areaCode == 31;

 

반응형
반응형
public class SwitchExample {
    public static void main(String[] args) {
        int dayOfWeek = 3;

        String dayType = switch (dayOfWeek) {
            case 1, 2, 3, 4, 5 -> "Weekday";
            case 6, 7 -> "Weekend";
            default -> throw new IllegalArgumentException("Invalid day of the week: " + dayOfWeek);
        };

        System.out.println("Day type: " + dayType);
    }
}

Java17부터 →을 이용해 각각 break를 안 사용해도 되고 가독성을 높여주는 Syntactic Sugar이다

반응형
반응형

 

📝@Rollback

@Transactional
@Rollback(value = false)
class MemberRepositoryTest {
    ...
}

@Transactional에서 장애 발생시 롤백 시키는 걸 할지 말지에 대한 것이다. 일반적으로 사용하지 않으며 테스트 코드 작성해서 디비 데이터 값을 확인해야한다면 사용한다 (테스트 코드 작동한 후 종료된 뒤에 바로 롤백 시켜서 디비에서 확인하기가 어려움)

 

📝@PostConstruct

// 초기 데이터 INSERT
@PostConstruct
public void insertData() throws Exception {

    IntStream.range(0, 100).forEach((i) -> {
        Member member = new Member("username" + i);
        memberRepository.save(member);
    });
}

종속성 주입이 완료된 후 실행되어야 하는 메서드에 사용한다 여기에서는 정상으로 스프링부트가 기동 된다면 해당 메소드가 동작해 초기 데이터를 INSERT하는 작업이다.

 

📝@CrossOrign

@CrossOrigin(origins = "*")
@GetMapping("/next")
public @ResponseBody HashMap<String, Object> next() throws InterruptedException {

	System.out.println("next2 call!!");
	HashMap<String, Object> map = new HashMap<String, Object>();
	map.put("id", "Next 테스트");
	
	Thread.sleep(3000);
	
	return map;
}

어떤 라우팅에 대해서 URL 요청을 허용할 지

반응형
반응형

📝@EnableJpaAuditing, @EntityListeneres, @CreateBy, @LastModifiedBy, @CreateDate, @LastModifiedDate

@EnableJpaAuditing // 필수
@SpringBootApplication
public class DataJpaApplication {

	public static void main(String[] args) {
		SpringApplication.run(DataJpaApplication.class, args);
	}

	@Bean
	public AuditorAware<String> auditorProvider() {
		return () -> Optional.of(UUID.randomUUID().toString());
		// 실무에서는 세션 정보나, 스프링 시큐리티 로그인 정보에서 ID를 받음
	}

}

@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Getter
public class BaseEntity {

    @CreatedBy
    @Column(updatable = false)
    private String createdBy;

    @LastModifiedBy
    private String lastModifiedBy;

}

@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Getter
public class BaseTimeEntity {

    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createDate;

    @LastModifiedDate
    private LocalDateTime lastModifiedDate;

}


@Entity
public class Member extends BaseEntity {

    @Id
    @GeneratedValue
    private String id;
    
    private String username;

}

 

  • @EnableJpaAuditing
    • 아래 어노테이션을 쓰기 위해 필수 기능이다
  • @EntityListeneres
    • 해당 엔터티는 JPA Entity에 이벤트 발생시 콜백 처리하라는 의미이다.
  • @CreateBy
    • 생성자
  • @LastModifiedBy
    • 수정자
  • @CreateDate
    • 등록일자
  • @LastModifiedDate
    • 수정일자

 

📝@PersistenceContext

@PersistenceContext
EntityManager em;

스프링 부트에서 생성한 EntityManager를 DI시켜준다

반응형
반응형

📝@NamedQuery, @Query

@Entity
@NamedQuery(
    name="Member.findByUsername", // Key
    query="select m from Member m where m.username = :username" // Value
)
public class Member {

    @Id @GeneratedValue
    @Column(name = "member_id")
    private Long id;

    private String username;
    private int age;

}

public interface MemberRepository extends JpaRepository<Member, Long> {

    /** ----- 방법1 (@Query) ----- **/
    @Query(name ="Member.findByUsername")
    List<Member> findByUsername(@Param("username") String username);
    
    /** ----- 기본 예제 ----- **/
    @Query("select m from Member m where m.username = :username and m.age = :age")
    List<Member> findUser(@Param("username") String username, @Param("age") int age);

}

조건절 조회하는 임의의 쿼리가 필요할 때 Entity에 @NamedQuery를 이용해 JpaRepository에서 {키:값} 형태로 찾아 사용이 가능하다

이 방법보단 JpaRepository에서 바로 @Query를 이용해 바로 작성하는 걸 더 많이 사용한다

 

📝@Modifying

public interface MemberRepository extends JpaRepository<Member, Long> {

    @Modifying(clearAutomatically = true)
    @Query("update Member m set m.age = m.age + 1 where m.age >= :age")
    int bulkAgePlus(@Param("age") int age);
 
}

Update한 후에 Update 항목을 조회하는 경우가 있을 때 영속성 컨텍스트 캐시를 날리고 데이터 조회하기 때문에 디비에서 조회하게 되고  데이터의 정합성을 맞춘다

 

📝@EntityGraph

public interface MemberRepository extends JpaRepository<Member, Long> {

    @EntityGraph(attributePaths = {"team"})
    @Query("select m from Member m")
    // 위에 코드는 EntityGraph + Query로 아래와 같은 코드이다. (team Fetch Join)

    // @Query("select m from Member m left join fetch m.team")
    List<Member> findMemberEntityGraph();
}

패치조인을 더 쉽게할 수 있도록 스프링 데이터 JPA에서 제공해준다.

 

📝엔터티 라이프 사이클 컨트롤 어노테이션

@PrePersist : 새로운 엔티티에 대해 persist가 호출되기 전
@PreUpdate : 엔티티 업데이트 작업 전
@PreRemove : 엔티티가 제거되기 전
@PostPersist : 새로운 엔티티에 대해 persist가 호출된 후
@PostUpdate : 엔티티가 업데이트된 후
@PostRemove : 엔티티가 삭제된 후
@PostLoad : Select조회가 일어난 직후에 실행

 

 

 

반응형
반응형

📝@Entity, @Id, @GeneratedValue, @Temporal, @Lob

public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // IDENTITY 전략
    // @GeneratedValue(strategy = GenerationType.SEQUENCE) // SEQUENCE 전략
    @Column(name="member_id")
    private Long id;

    private String name;
    private int age;
    private String address; // 필드만 추가해주면 끝

    // @Enumerated(EnumType.ORDINAL) // enum 타입 매핑 [숫자] (금지)
    @Enumerated(EnumType.STRING) // enum 타입 매핑 [문자] (사용 OK)
    private CategoryEnum name;

    // 날짜 타입 매핑 [아래 LocalDateTime을 쓰면 알아서 YYYYMMDDHHMMSS 설정]
    @Temporal(TemporalType.DATE) 
    private Date date;
    
    private LocalDateTime createDate
    
    @Lob // BLOB, CLOB 매핑
    private String description;
}

 

  • @Entity 
    • JPA에서 영속성 컨텍스트가 관리할 테이블이라는 의미이다.
  • @Id
    • 해당 필드를 PK로 지정하겠다는 의미이다.
  • @GeneratedValue
    • PK AutoIncrement설정이다. IDENTITY로 설정할지 SEQUENCE로 지정할지 정할 수 있다.
  • @Temporal
    • 날짜 필드라는 걸 알리는 의미이다.
  • @Lob
    • BLOB, CLOB타입이라는 걸 알리는 의미이다.

 

📝@JoinColumn, @ManyToOne, @OneToMany, @OneToOne, @ManyToMany

@Entity
public class Member {

    @Id // PK로 설정할 컬럼
    @GeneratedValue
    private long id;

    @Column(nullable = false, length = 10)
    private String name;

    @ManyToOne
    @JoinColumn(name = "TEAM_ID") // Team 테이블[클래스]에 연결할 필드명을 알려주세요 
                                  // (JoinColumn은 FK가 있는 테이블에 들어간다)
    private Team team;

}

@Entity
public class Team {

    @Id @GeneratedValue
    @Column(name = "TEAM_ID")
    private Long id;
    private String name;

}
  • @JoinColumn
    • 연결할 테이블에 필드를 지정한다
  • @ManyToOne
    • 다대일 관계로 연결하겠다는 의미
  • @OneToMany
    • 일대다 관계로 연결하겠다는 의미
  • @OneToOne
    • 일대일 관계로 연결하겠다는 의미
  • @ManyToMany (사용 X)
    • 다대다 관계로 연결하겠다는 의미 (다대다 관계는 무조건 다대일로 풀어내야한다)

 

 

📝@Inheritance, @DiscriminatorColumn, @DiscriminatorValue

@Inheritance(strategy = InheritanceType.JOINED) // 조인 전략
@DiscriminatorColumn 
@Entity
public class Item {

    @Id
    @GeneratedValue
    private long id;

    private String name;
    private int price;
}

@Entity
// @DiscriminatorValue("this is movie")
public class Movie extends Item{

    private String director;
    private String actor;

}

public class Main {
    public static void main(String[] args) {

        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");

        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();

        tx.begin();

        try{

            Movie movie = new Movie();
            movie.setName("바람과 함께 사라지다");
            movie.setPrice(10000);
            movie.setActor("이민호");
            movie.setDirector("봉준호");

            em.persist(movie);

            tx.commit();

        }catch (Exception e){
            e.printStackTrace();
        }finally{
            emf.close();
            em.close();
        }
    }
}

 

@Inheritance

관계형 데이터베이스에 상속관계라는 걸 JPA에서 표현했다

 

 

 

 

@DiscriminatorColumn

해당 어노테이션을 사용 안 하면 DTYPE이 따로 안 보이는데 이걸 명시적으로 표현해주는 어노테이션 어느 자식이 UPSERT 되어서 그런지 잘 모르니 해당 어노테이션을 사용하는게 좋다

 

 

 

 

@DiscriminatorValue

해당 어노테이션을 사용하면 DTYPE에 들어갈 걸 명시적으로 지정할 수 있다.

 

 

 

 

 

 

 

 

📝@MappedSuperclass

@MappedSuperclass
public abstract class BaseEntity {

    @Column(name = "create_date") // 부모 클래스에서 컨트롤 가능
    private LocalDateTime localDateTime;

    private LocalDateTime modi_date;
}

@Entity
public class Book extends BaseEntity{

    @Id
    @GeneratedValue
    private long id;

    private String name;
    private String author;
    private String ISBN;

}

Inheritance와 차이가 있는 점은 Inheritance은 부모 자식 테이블 따로따로 들어가게 되는데 MappedSuperclass의 경우 필드만 공유하고 하나의 테이블로 만들어진다.

 

 

📝@NamedQuery, @Query

@Entity
@NamedQuery(
    name="Member.findByUsername", // Key
    query="select m from Member m where m.username = :username" // Value
)
public class Member {

    @Id @GeneratedValue
    @Column(name = "member_id")
    private Long id;

    private String username;
    private int age;

}

public interface MemberRepository extends JpaRepository<Member, Long> {

    /** ----- 방법1 (@Query) ----- **/
    @Query(name ="Member.findByUsername")
    List<Member> findByUsername(@Param("username") String username);
    
    /** ----- 기본 예제 ----- **/
    @Query("select m from Member m where m.username = :username and m.age = :age")
    List<Member> findUser(@Param("username") String username, @Param("age") int age);

}

조건절 조회하는 임의의 쿼리가 필요할 때 Entity에 @NamedQuery를 이용해 JpaRepository에서 {키:값} 형태로 찾아 사용이 가능하다

이 방법보단 JpaRepository에서 바로 @Query를 이용해 바로 작성하는 걸 더 많이 사용한다

 

📝@Modifying

public interface MemberRepository extends JpaRepository<Member, Long> {

    @Modifying(clearAutomatically = true)
    @Query("update Member m set m.age = m.age + 1 where m.age >= :age")
    int bulkAgePlus(@Param("age") int age);
 
}

Update한 후에 Update 항목을 조회하는 경우가 있을 때 영속성 컨텍스트 캐시를 날리고 데이터 조회하기 때문에 디비에서 조회하게 되고  데이터의 정합성을 맞춘다

 

📝@EntityGraph

public interface MemberRepository extends JpaRepository<Member, Long> {

    @EntityGraph(attributePaths = {"team"})
    @Query("select m from Member m")
    // 위에 코드는 EntityGraph + Query로 아래와 같은 코드이다. (team Fetch Join)

    // @Query("select m from Member m left join fetch m.team")
    List<Member> findMemberEntityGraph();
}

패치조인을 더 쉽게할 수 있도록 스프링 데이터 JPA에서 제공해준다.

 

엔터티 라이프 사이클 컨트롤 어노테이션 (@PrePersist, @PreUpdate, @PreRemove, @PostPersist, @PostUpdate, @PostRemove, @PostLoad)

@PrePersist : 새로운 엔티티에 대해 persist가 호출되기 전
@PreUpdate : 엔티티 업데이트 작업 전
@PreRemove : 엔티티가 제거되기 전
@PostPersist : 새로운 엔티티에 대해 persist가 호출된 후
@PostUpdate : 엔티티가 업데이트된 후
@PostRemove : 엔티티가 삭제된 후
@PostLoad : Select조회가 일어난 직후에 실행
반응형