Home > Enterprise >  How can I handle any field change in Entity (Spring Boot)
How can I handle any field change in Entity (Spring Boot)

Time:08-24

For example I have User entity class.
This User have a status field.
When status is changed I need to call method from my UserService.

I have this code, but I think injecting service into entity is very bad.

@Entity
public class User {

    private String status;

    @Transient
    private String lastStatus;
    
    @Transient
    @Autowired
    private UserService userService;

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        lastStatus = this.status;
        this.status = status;
    }
    
    @PreUpdate
    public void handleChangeStatus() {
        if (!status.equals(lastStatus)) {
            userService.doSomething();
        }
    }
}

CodePudding user response:

One way to do this is to publish domain events from your User entity class and then to create a listener for this events.
First, let's create an event:

@AllArgsConstructor
@Getter
public class UserStatusChangeEvent {
    private final UUID userId;    
    private final String lastStatus;
    private final String newStatus;
}

Then your User entity class should implement AbstractAggregateRoot interface with default domain event publishing mechanism to let your entity publish events and publish one in your setStatus method:

@Entity
@Getter
public class User implements AbstractAggregateRoot<User> {
    private UUID userId;
    private String status;

    @Transient
    private String lastStatus;

    public void setStatus(String status) {
        lastStatus = this.status;
        this.status = status;
        registerEvent(new UserStatusChangeEvent(userId, lastStatus, status));
    }
}

Then create a separate class with a listener, define it as a bean (@Component) and inject there your UserService:

@RequiredArgsConstructor
@Component
public class UserStatusListener {
    private final UserService userService;

    @EventListener
    public void onStatusChange(UserStatusChangeEvent statusChangeEvent) {
        if (!statusChangeEvent.getNewStatus().equals(statusChangeEvent.getLastStatus())) {
            userService.doSomething();
        }
    }
}

Spring will do the "magic" for you - publish your event as a application event and register your listener on startup.

Note, that spring-data will publish your domain events only after save or saveAll method called on a repository of your entity, so no save - no events.

Also instead of @EventListener you can use @TransactionalEventListener with different transaction phase if you want your listener to work depending on the transaction (success, failure, before or after, etc.).

P.S. I used Lombok annotations on classes to simplify code and inject fields through the constructor.

  • Related