Life Developer
인생 개발자
전체 글 (141)
[typescript]interface-optional property

interface Person{

    name:string,

    age?:number,

    [props:string]:any;

}

 

function hello(param:Person):void{

    console.log(`안녕? ${param.name}    입니다`);

}

 

const p1:Person={

    name:'mark',

    age:35,

}

 

const p2:Person={

    name:'anna',

    syster:['sung','chan','pang'],

}

 

const p3:Person={

    name:'gibeom',

    father:p1,

    maoter:p2,

}

 

console.log(p1,p2,p3);

  Comments,     Trackbacks
[JPA]변경감지와 병합 - 아주 중요함(진심)

준영속 엔티티가 무엇인가

 

영속성 컨텍스트가 더는 관리하지 않는 엔티티를 말한다.

 

그냥 평범하게 객체를 하나 만들었다 쳐도 기존 식별자를 set하면 (가지고 있으면)

 

준영속 상태의 엔티티,,,즉 , 객체로 볼수 있는 것이다.

 

 

그럼 준영속 엔티티를 수정하는 방법은 뭘까?

1. 변경감지 기능 사용

2. 병합 사용(merge)

 

일단 변경감지 기능을 간략히 말한다.

find로 찾아와서 그 찾아온 엔티티를 set하면 persist할필요가 있다 없다?

없다.

 

@Transactional

트랜잭션이 commit을 하는 순간 어? 영속성컨텍스트에 있는 엔티티중에 이놈이 바뀐것같네?

내가 관리하니까 수정된 내용을 DB에 insert박아야지!!!!!!!!!

 

끝.

 

이게 정석.

 

한가지 방법이 더있다 .

그것은 병합.

 

병합은 준영속 상태의 엔티티를 영속 상태로 변경할때 사용하는 기능임.

 

쉽게 말한다.

 

객체를 만든다. 이때는 걍 객체다.

객체에 정보를 set하고 em.merge(객체)를 준다.

그럼 merge는 받는 객체의 식별자, 즉 ID로 1차캐시에서 찾는다.

없네???

없으면 그냥 DB에서 똑같은 엔티티를 식별자를 이용하여 걍 조회해서 가져온다.

그럼 그 조회한 엔티티(값)를(뭐 find 한 결과(엔티티)라 생각하면됨) 영속성 컨텍스트에 담긴다.

이 엔티티에 처음에 merge가 받은 객체의 정보를 대조해 모두 set을 하여 정보를 밀어넣는다.

이 영속상태인 객체를 반환한다.

 

그리고!!!!!!!!!!! 이때 처음의 객체는 영속 상태가 아닌 객체이고!!!!

반환된 놈만 영속상태다!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

 

끝.

 

 

둘중에 왜 병합이 더 위험할까?

 

변경감지는 일단 원하는 값을 set하면 원하는 것만 바뀐다.

 

그러나 병합은 완전히 다 갈아서 대체하는것이기 때문에 객체의 값이 모두 채워져있어야 한다.

만약에 값을 안넣고 merge를 해버리면!!!!!!!!!!!!!!!!!!!!!기존 데이터는 날라가고 null이 DB에 드간다!!!!!!!!!!!

 

 

merge 안쓰면된다!!!!!!!!!!!!!!!!!!

 

Controller에서 어설프게 엔티티 생성하지 마라!!!!!!!!!!!!!!!!!!!!!!!!!!(set 쓰지말고 서비스에 메서드 만들던가

수정할 변수가 많으면 DTO클래스 그냥 하나 만들자!!!!!!!!!!!

 

트랜잭션이 있는 서비스 계층에 식별자(id)와 변경할 데이터를 명확하게 전달해라!!!!

전달해서 서비스에서 id로 find해서 select하고 그 영속엔티티에 set을 박아서 변경감지 작업해라!!!!!!

  Comments,     Trackbacks
도메인 모델 패턴, 트랜잭션 스크립트 패턴

엔티티가 비지니스 로직을 가지고 객체 지향의 특성을 적극 활용하는 것을 도메인 모델 패턴이라고 한다.

 

반대로 엔티티에는 비지니스 로직이 거의 없고 서비스 계층에서 대부분의 비지니스 로직을 처리하는

 

것을 트랜잭션 스크립트 패턴이라고 한다.

  Comments,     Trackbacks
[JPA]@NoArgsConstructor

@NoArgsConstructor(access = AccessLevel.PRIVATE)

를 클래스 위에 정의해주면

 

private Class명(){ }

 

처럼 생성자를 protected으로 만든다는 의미이다.

 

JPA에서 protected를 사용하면 걍 쓰지말란 얘기다.

  Comments,     Trackbacks
[JPA실전]memory로 테스트하기

테스트할때 메모리에 테이블 만들어서 테스트 할수 있다.

 

그리고 일단 test 에 resources 폴더를 만들어서 

 

이렇게 yml 파일을 주면 main의 resources 보다 우선적으로 읽어서 test할때는 저 yml 파일을 읽는다.

 

그래서 yml 파일의 내용을 맘대로 바꿔서 테스트 할수있다.

 

예를 들어 url이라던지?

 

 

그런데 신기한건 Spring Boot 에서 아래에서 연한 글씨의 코드를 다 빼고 실행하면

 

Spring Boot 자체적으로 메모리로 실행되게끔 디폴트가 되어있어서 메모리로 돈다.

 

spring:
datasource:
url: jdbc:h2:mem:test
username: sa
password:
driver-class-name: org.h2.Driver

jpa:

hibernate:
ddl-auto: create
properties:

hibernate:
# show_sql: true
format_sql: true


logging:
level:
org.hibernate.SQL: debug
org.hibernate.type: trace

 

 

심지어 알아서 url도 알아서 만들어서  실행해준다.ㅡㅡ

 

 

개쩐다

  Comments,     Trackbacks
[JPA실전]@RequiredArgsConstructor - EntityManager에 사용하기

@RequiredArgsConstructor 를 Repository에도 사용할수있다.

 

===================================================

처음에 이렇게 되어있는것을

 

@PersistenceContext
private EntityManager em;

public MemberRepository(EntityManager em) {
this.em = em;
}

 

 

===================================================

 

이렇게 바꿀수가 있고,

 

private EntityManager em;

@Autowired
public MemberRepository(EntityManager em) {
this.em = em;
}

 

===================================================

 

또 다시 위에 코드를 이렇게 바꿀수가 있다.

 

 

@Repository
@RequiredArgsConstructor
public class MemberRepository {

private final EntityManager em;

public void save(Member member) {
em.persist(member);
}

 

이게 되는 이유는 Spring Boot에서는 EntityManager는 @PersistenceContext 를 사용하여 관리가 되는데

 

@Autowired 를 사용해도 똑같아서 가능한것이다.

 

Spring Boot에서만 이렇게 사용할수 있다는것.

  Comments,     Trackbacks
[JPA실전]@RequiredArgsConstructor-final 변수들을 생성자로

@RequiredArgsConstructor를 사용하면 이 클래스의 멤버변수 중 final로 선언된 것만 

 

찾아서 모두 생성자에 박아버린다.

 

public MemberService(Member Repository memberRepository){

            this.memberRepository = memberRepository;

}

 

이것은 이렇게 자동으로 생긴다는 것이지 입력하는게 아니다.

 

@RequiredArgsConstructor 쓰면 자동으로 생성자가 저렇게 만들어진다는 거다.

 

setter로 주입받아도 되고 생성자를 그냥 AutoWired로 받아도 된다.

 

근데 이게 제일 안전하다.

 

 

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class MemberService {

private final MemberRepository memberRepository;

 

public MemberService(Member Repository memberRepository){

            this.memberRepository = memberRepository;

}

//가입
@Transactional
public Long join(Member member) {
validateDuplicateMember(member);
memberRepository.save(member);
return member.getId();
}

private void validateDuplicateMember(Member member) {
List<Member> findMembers = memberRepository.findByName(member.getName());

if (!findMembers.isEmpty()) {
throw new IllegalStateException("이미 존재하는 회원입니다");
}
}

//회원 전체 조회
public List<Member> findMembers() {
return memberRepository.findAll();
}

//회원 단건 조회
public Member findOne(Long memberId) {
return memberRepository.findOne(memberId);
}
}

  Comments,     Trackbacks
[JPA실전]파라미터 로그 표시

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-devtools'
implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.5.6'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

  Comments,     Trackbacks