8 Şubat 2021 Pazartesi

SpringData JPA Auditing Kullanımı

Giriş
1. Entity sınıfına gerekli SpringData JPA Auditing anotasyonları eklenir
2. Entity nesnesine @EntityListeners anotasyonu ile bir AuditingEntityListener takılır.
3. Spring Security kullanmıyorsak, AuditorAware arayüzünü gerçekleştiren bean kodlarız. Spring Security kullanıyorsak bir şey yapmaya gerek yok.
4. Veri tabanında sütunlar yoksa yeni sütunları eklemek gerekir. Nihayetinde şöyle 4 tane sütun olmalı.
CREATE TABLE user (
  ...
  created_by VARCHAR(50),
  created_date DATE,
  last_modified_by VARCHAR(50),
  last_modified_date DATE
);
1. SpringData JPA Anotasyonları
Bunlar şöyle
... Spring provides four annotations in the spring-data-commons module for auditing features. They are:
@CreatedBy represents the principal that created the entity containing the field.
@CreatedDate represents the date the entity containing the field was created.
@LastModifiedBy represents the principal that recently modified the entity containing the field.
@LastModifiedDate represents the date the entity containing the field was recently modified.

Besides the four mentioned annotations, Spring also provides @EnableJpaAuditing that aims enable auditing feature.
@CreatedBy ve @LastModifiedBy kullanıcı bilgisini saklar. Kullanıcı bilgisi için String kullanılır. Entity sınıfına yeni bir alan ekleriz. Şöyle yaparız
@CreatedBy
@Column(name = "created_by")
private String createdBy;
@LastModifiedBy
@Column(name = "last_modified_by")
private String lastModifiedBy;
@CreatedDate ve @LastModifiedDate tarih bilgisini saklar. Tarih bilgisi için Instant kullanılır.  
Entity sınıfına yeni bir alan ekleriz. Şöyle yaparız.
@CreatedDate
@Column(name = "created_date")
private Instant createdDate;

@LastModifiedDate
@Column(name = "last_modified_date")
private Instant updatedAt;
2. AuditingEntityListener 
Entity nesnesine @EntityListeners anotasyonu ile bir AuditingEntityListener takılır. Açıklaması şöyle
The AuditingEntityListener is one of the key factors of auditing features in Spring. Its responsibility is to listen to an update or a creation of an entity then perform necessary tasks to fill auditing information to the entity based on the annotated annotation of the entity. This class relies on the JPA Entity lifecycle event.
Bu arayüzün iki tane metodu var. Açıklaması şöyle
Take a look at the AuditingEntityListener we can see two main methods touchForCreate and touchForUpdate one is decorated with @PrePersist and @PreUpdate respectively.
Örnek - Ata Sınıf
Şöyle yaparız. Bu örnekte her Entity nesnesine teker teker @EntityListeners anotasyonunu eklememek için bir ata sınıf yaratılıyor. Ayrıca örnek biraz eski olduğu için tarih bilgisi alanlarında Instant yerine Date kullanılıyor.
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import java.util.Date;

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class AuditableEntity {
  @CreatedBy
  @Column(name = "CREATED_BY")
  private String createdBy;
  @CreatedDate
  @Column(name = "CREATED_DATE")
  private Date createdDate;
  @LastModifiedBy
  @Column(name = "LAST_MODIFIED_BY")
  private String lastModifiedBy;
  @LastModifiedDate
  @Column(name = "LAST_MODIFIED_DATE")
  private Date lastModifiedDate;

  //Getters and Setters are omitted

}
Burada kullanılan AuditingEntityListener, JPA Entity lifecycle event'lerini dinler ve yukarıdaki 4 tane anotasyona sahip nesnelere değerler atar. 

Örnek
Şöyle yaparız. Burada yine Instant yerine LocalDateTime kullanılıyor.
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class AuditorEntity {

  @CreatedDate
  @Column(name = "CreatedOn")
  private LocalDateTime createdOn;

  @CreatedBy
  @Column(name = "CreatedBy", length = 50)
  private String createdBy;


  @LastModifiedDate
  @Column(name = "UpdatedOn")
  private LocalDateTime updatedOn;

  @LastModifiedBy
  @Column(name = "UpdatedBy", length = 50)
  private String updatedBy;
  ...
}

@Entity
@Table(name = "user")
public class User extends AuditorEntity {
  ...
}
3. AuditorAware Arayüzü
Açıklaması şöyle
AuditorAware is an interface provided by Spring Data JPA that is used to dynamically determine the current auditor (user) for auditing purposes.

The AuditorAware interface has a single method: getCurrentAuditor(). This method is responsible for providing the current auditor's identification, such as the username or ID. The returned value is then automatically set in the corresponding auditing fields of the audited entities.
Eğer SpringSecurity kullanıyorsak açıklaması şöyle. Yani eğer SpringSecurity kullanıyorsak, Spring bize SpringSecurityAuditorAware isimli bir bean sağlar ve bu bean kullanıcı ismini döner. Bir şey yapmaya gerek yok.
... spring doesn’t know how to fill the principal therefore we have to define a bean that has the implementation for org.springframework.data.domain.AuditorAware interface. At this time, I know that Spring has an implement in the spring security module. The name of the class is SpringSecurityAuditorAware. In case we don’t want to apply the SpringSecurityAuditorAware we have to make our own.
Örnek - Kendi Sınıfımız
Şöyle yaparız
public class AuditorAwareImpl implements AuditorAware<String> {
  @Override
  public Optional<String> getCurrentAuditor() {
    Authentication authentication = SecurityContextHolder.getContext()
      .getAuthentication();
    // todo: add checks if authentication is present and user is not null
    SignedUser user = (SignedUser) authentication.getPrincipal();
    return Optional.of(user.getUserId().toString());
  }
}

@Configuration
@EnableJpaAuditing(auditorAwareRef = "auditorProvider")
public class Config {
  @Bean
  public AuditorAware<String> auditorProvider() {
    // our implementation of AuditorAware
    return new AuditorAwareImpl();
  }
}
Örnek - Kendi Sınıfımız
Eğer kullanıcı ismini dönen kendi sınıfımızı sağlamak istersek şöyle yaparız. Burada @EnableJpaAuditing anotasyonunda kendi bean'imizi belirtiyoruz
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@Configuration
@EnableJpaAuditing(auditorAwareRef ="auditorAware")
public class AuditingConfiguration {
  @Bean
  public AuditorAwareImpl auditorAware(){
    return new AuditorAwareImpl();
  }
}
public class AuditorAwareImpl implements AuditorAware {
  @Override
  public Optional getCurrentAuditor() {
    return Optional.of(System.getProperty("user.name"));
  }
}

Hiç yorum yok:

Yorum Gönder