본문 바로가기
Back-End/Spring

TIL 230727 : 튜터님 예제 보며 정리한 마이크로 팁들

by 우인입니다 2023. 7. 28.

JPA심화 주차로 들어섰다.

앞서 배웠던 내용들을 다시 훑기도 하면서 뼈와 살이 될것같은 개선점들이 크고많게 있었다.

하나둘 기록해두고 복습하며 체화시켜보기로 한다.

 


어노테이션도 분류해주기 lombok / jpa

현업에서 자주 쓰인다고 한다.

 

  • @ToString → 필드명 같이 출력. @Exclude 연관관계 매핑한건 출력 제외할때

 


List말고 Set으로 중복제거하고 LinkedHashSet으로 생성해주면 순서까지 보장해준다

 

내가 썼던 코드

지금 생각해보면 객체가 중복으로 들어올 일이 없기도 하고 중복으로 오면 손해였는데

관성적으로 List를 사용했다.

 

Set타입으로 선언하고 생성은 LinkedHashSet

위처럼 LinkedHashSet을 생성하면 중복도 제거하고 순서도 보장이 된다.

이에 대해 자세히 성능차이를 볼 수 있는 자료나 이를 테스트해볼 기회가 되면 해보기로 한다.


 

@Builder 어노테이션 활용한 생성자

 

@Builder라는 걸 처음봤다.

아래처럼 생성자 위에 추가해준다.

  @Builder
  public User(String username, String password) {
    this.username = username;
    this.password = password;
  }

 

그러면 아래처럼 {엔티티클래스명}.bilder() 메소드를 호출할 수 있고 각 필드값을 빌더형태로 만들어 줄 수 있다.

User newUser = User.builder().username("new_user").password("new-pass").build();
  	newChannel.joinUser(newUser);

 

@Builder 장점

- 불필요한 생성자의 제거

- 데이터의 순서에 상관없이 객체생성 가능

- 명시적 선언으로 이해하기가 쉽고 각 인자가 어떤 의미인지 알기 쉽다.(가독성)

- setter 메서드가 없으므로 변경 불가능한 객체를 만들수있다.(객체불변성)

- 한번에 객체를 생성하므로 객체 일관성이 깨지지 않는다.

- build() 함수가 null인지 체크해주므로 검증이 가능한다. 

 

Builder에 static 클래스가 생기는 것이 살짝 맘에 걸리기도 한데, 파라미터가 많거나 변경이슈가 적어보일때 활용하면 더 좋을 듯하다.

 

 

 


영속성 전이 최강 조합 : orphanRemoval=true + Cascade.ALL

  • 위 2개를 함께 설정하면 자식 엔티티의 라이프 사이클이 부모 엔티티와 동일해지며, 직접 자식 엔티티의 생명주기를 관리할 수 있게 되므로 자식 엔티티의 Repository 조차 없어도 된다. (따라서, 매핑 테이블에서 많이 쓰임)

 

 


Repository에서 사용하지 않는, 막아야 하는 기능 막기

 

비밀번호와 같은 곳에 접근하는 것을 막을 수도 있다.

캡슐화, 은닉성을 살려줄 수 있는 동작이다.

 

크게 두가지 방법으로 가능하다.

1. @RepositoryDefinition(domainClass = {받고자 하는 객체 클래스}.class, {PK id의 클래스} = Long.class)

: 원래 JpaRepository를 extends해오는 것이 아닌 인터페이스에 어노테이션을 추가해주고 클래스 안에 주입받아올 메소드를 선언해줄 수 있다.

 

 

@RepositoryDefinition(domainClass = Comment.class, idClass = Long.class)
public interface CommentRepository {

    Comment save(Comment comment);
    List<Comment> findAll();
}

 

 

 

2. @NoRepositoryBean 어노테이션을 추가한 중간 인터페이스를 만들어 사용할 메소드만 선언 해준다.

  • 아래처럼 만든 중간 인터페이스를 원래 사용하려던 ex) UserRepository에 extends해서 사용한다. (원래 JpaRepository를 extends하던 자리에 대신 상속)
@NoRepositoryBean
public interface MyRepository<T, ID extends Serializable> extends Repository<T, ID> {

    <E extends T> E save(E entity);
    List<T> findAll();
}
public interface UserRepository extends MyRepository<User, Long> {
}

이렇게 되면 UserRepository의 빈객체에서는 save와 findAll 이 두 메소드만 사용할 수 있게 된다.

 

 

 


Repository 에 기능 추가, 수정하기

 

CASE - JpaRepository에서 delete 메소드는 영속성을 확인하는 과정을 거친다.

SimpleJpaRepository 구현체 클래스의 delete메소드 중

 

기존 코드

  • contains()는 영속성 상태인 지 보는 메소드.
  • contains(e) ? e : merge(e); 이 뜻은 영속성이면 그냥 가져오고 아니면 merge해서 가져온다는 뜻.
  • 저렇게 되어있는 이유는 CASCADE, OrphanRemoval을 위해서 영속성 체크.

 

  • 영속성이 아니어도 되는 경우에서 바로 delete쿼리만 날리고 싶을 때 오버라이드해서 기능을 추가해 줄 수 있다.

 

메소드 수정하는 법

1. 인터페이스 생성, 오버라이드할 메소드 선언

public interface MyRepository {
	...여기다가 추가할 메소드 선언...
}

 

2. 인터페이스 구현 클래스 생성

@Repository
@Transactional
public class MyRepositoryImpl implements MyRepository {

	@Autowired
	EntityManager entityManager;

	@Override
	public void delete(User user) {
		entityManager.remove(user);
  }
}
  • @Repository, @Transactional을 붙여준다.
  • 인터페이스를 implements해준다.
  • 메소드를 Override해서 수정해준다.
    • EntityManager를 @Autowired해준다. : 여기서 remove메소드를 가지고 있어서.
    • 바로 EntityManager가 remove해주도록 변경.