Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@CreatedBy and @LastModifiedBy set actual entity instead of id

I have an entity which looks like:

@Audited
@Data
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {

  public static final long UNSAVED = 0;

  @Id
  @GeneratedValue
  private long id;

  @CreatedDate
  @Column(name = "created_at", updatable = false)
  private ZonedDateTime createdAt;

  @CreatedBy
  @OneToOne(fetch = FetchType.EAGER)
  @JoinColumn(name = "created_by")
  private User createdBy;

  @LastModifiedDate
  private ZonedDateTime updatedAt;

  @OneToOne(fetch = FetchType.EAGER)
  @JoinColumn(name = "updated_by")
  @LastModifiedBy
  private User updatedBy;

}

I'm want @LastModifiedBy and @CreatedBy so that they set corresponding users. However, when I try to save the entity, I get an exception:

java.lang.ClassCastException: Cannot cast java.lang.Long to com.intranet.users.Users

So it seems to me that it tries to set not actual User, but it's id. Is there any way to make spring set actual User on the entity and not just it's id?

Thanks

like image 653
Andrey Yaskulsky Avatar asked Dec 23 '22 02:12

Andrey Yaskulsky


1 Answers

This seems to be answered pretty directly by the documentation:

In case you use either @CreatedBy or @LastModifiedBy, the auditing infrastructure somehow needs to become aware of the current principal. To do so, we provide an AuditorAware SPI interface that you have to implement to tell the infrastructure who the current user or system interacting with the application is. The generic type T defines what type the properties annotated with @CreatedBy or @LastModifiedBy have to be.

The following example shows an implementation of the interface that uses Spring Security’s Authentication object:

Example 104. Implementation of AuditorAware based on Spring Security

class SpringSecurityAuditorAware implements AuditorAware<User> {

  public Optional<User> getCurrentAuditor() {

    return Optional.ofNullable(SecurityContextHolder.getContext())
        .map(SecurityContext::getAuthentication)
        .filter(Authentication::isAuthenticated)
        .map(Authentication::getPrincipal)
        .map(User.class::cast);   
  } 
} 

The implementation accesses the Authentication object provided by Spring Security and looks up the custom UserDetails instance that you have created in your UserDetailsService implementation. We assume here that you are exposing the domain user through the UserDetails implementation but that, based on the Authentication found, you could also look it up from anywhere.

like image 120
Jens Schauder Avatar answered Dec 28 '22 06:12

Jens Schauder