JPA

JPA save() Domain Event 추가하기

du.study 2020. 12. 13. 21:22
728x90

JPA 를 사용하면서 save동작시, event를 등록하는 방법에 대해 기록하려합니다.

특정 entity가 저장이 될때, event호출을 통하여 특정 동작을 수행하게 할 수 있습니다. 

 

코드로 간략하게 보면 다음과 같습니다.

 

Event 코드

public class AccountEvent extends ApplicationEvent {

    private final Account account;

    public AccountEvent(Object source) {
        super(source);
        this.account = (Account) source;
    }

    public Account getAccount() {
        return account;
    }
}

 

Listener 코드 

@Component
public class AccountSaveListener implements ApplicationListener<AccountEvent> {

    @Override
    public void onApplicationEvent(AccountEvent accountEvent) {
        System.out.println("event occurred");
        Account account = accountEvent.getAccount();
        System.out.println(account.getUsername() +","+account.getPassword());
    }
}

Entity 코드

@Entity
public class Account extends AbstractAggregateRoot<Account> {

    @Id
    @GeneratedValue
    private Long id;

    private String username;

    private String password;

	.... 생략

    public Account publish(){
        this.registerEvent(new AccountEvent(this));
        return this;
    }
}


 

테스트를 위한 Runner코드

@Component
public class JpaRunner implements ApplicationRunner {

    @Autowired
    private AccountRepository accountRepository;

    @Override
    public void run(ApplicationArguments args) throws Exception {
		Account account = new Account();
        account.setUsername("DUSANG");
        account.setPassword("PWD");
        accountRepository.save(account.publish());
    }
}

 

 

간단하게 정리하면 동작 순서는 다음과 같게됩니다.

 

1. 사전세팅을 진행한다.

- Event 작성

- Event를 받는 Listener 작성 ( bean으로 등록)

- Entity에 AbstractAggregateRoot<{Entity name}> 상속.

- save() 시 registerEvent 코드가 같이 등록될 함수 어딘가에 저장.

2. save를 하면서 특정 registerEvent 를 같이 등록하는 함수 호출

3. 이벤트 리스너 호출.

 

다음과 같이 동작하게됩니다.

 

 

추가)

save과정에서 코드를 따라가다 보면 다음 EventPublishingRepositoryProxyPostProcessor 클레스에서 해당 함수가 호출되게 됩니다. 

public void publishEventsFrom(@Nullable Object object, ApplicationEventPublisher publisher) {

  if (object == null) {
    return;
  }

  for (Object aggregateRoot : asCollection(object)) {

    for (Object event : asCollection(ReflectionUtils.invokeMethod(publishingMethod, aggregateRoot))) {
      publisher.publishEvent(event);
    }

    if (clearingMethod != null) {
      ReflectionUtils.invokeMethod(clearingMethod, aggregateRoot);
    }
  }
}

해당 코드에서 publisher.publishEvent(event); 를 보고 여러 event를 등록할 수있다는것을 알 수있습니다.

한코드에 모든것을 때려박는 짓을 피할 수 있습니다. 

728x90