جاوا و تکنولوژی های آن

java programming language

در این وبلاگ به بررسی نکات موجود در جاوا و تکنولوژی های آن می پردازیم

طبقه بندی موضوعی


در حوزه ORM مفهوم Database Auditing به معنی دنبال کردن و ردیابی ، Log کردن event های مربوط به Persistence Entity میباشد مزایای استفاده از Database Auditing همانند استفاده از Source Version Control است که در اینجا به معرفی سه روش از Database Auditing میپردازیم


نخست استفاده از JPA استاندارد و بعد با استفاده از JPA و یکبار همراه با Hibernate و دیگری همراه با Spring Data خواهیم پرداخت



طی مثال ها دو کلاس Foo و Bar که entity های ما هستند را فرض کنید که کلاس Foo یک فیلد از نوع Bar دارد 



Auditing With JPA:


JPA بتنهایی قابلیتی برای Auditing ندارد ولی ما با استفاده از امکانات موجود آنرا شبیه سازی میکنیم 


Annotation های PrePersist ، @PreUpdate ، @PreRemove@ میتوانند روی متد هایی قرار بگیریند و نقش callback در عملیات insert , update , delete را بر عهده بگیرند این متد ها باید بدون آرگومان ورودی و مقدار برگشتی void داشته باشند و static نباشند  


@Entity
public class Bar {
       
    @PrePersist
    public void onPrePersist() { ... }
       
    @PreUpdate
    public void onPreUpdate() { ... }
       
    @PreRemove
    public void onPreRemove() { ... }
       
}


* این متد ها نباید در lifecycle خود کوئری ای را اجرا کنند یا از EntityManager استفاده کنند یا به Entity  دیگری دسترسی داشته باشند یا روابط را همان Persistence Context تغییر دهند 



اگر برای Auditing از فریم ورک خاصی استفاده نمیکنیم باید Domain model و Database Schema را بصورت دستی نگهداری کنیم که برای ساده سازی ما دو ویژگی جدید را اضافه میکنیم یکی ذخیره کردن نام عملیات و دیگری زمان آن :


@Entity
public class Bar {
      
    //...
      
    @Column(name = "operation")
    private String operation;
      
    @Column(name = "timestamp")
    private long timestamp;
      
    //...
      
    // standard setters and getters for the new properties
      
    //...
      
    @PrePersist
    public void onPrePersist() {
        audit("INSERT");
    }
      
    @PreUpdate
    public void onPreUpdate() {
        audit("UPDATE");
    }
      
    @PreRemove
    public void onPreRemove() {
        audit("DELETE");
    }
      
    private void audit(String operation) {
        setOperation(operation);
        setTimestamp((new Date()).getTime());
    }
      
}




اگر چندین کلاس Entity وجود داشته باشد که نیاز به Auditing داشته باشد میتوانیم با استفاده از EntityListeners@ عملیات Auditing را در یک کلاس متمرکز کنیم :


public class AuditListener {
     
    @PrePersist
    @PreUpdate
    @PreRemove
    private void beforeAnyOperation(Object object) { ... }
     
}


و در کلاس entity انرا معرفی کنیم :


@EntityListeners(AuditListener.class)
@Entity
public class Bar { ... }







Auditing با Hibernate :


برای Auditing در Hibernate میتوانیم از Interceptors و EventListeners استفاده کنیم اما Hibernate ماژول دیگری برای اینکار معرفی کرده است به نام Envers  :


<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-envers</artifactId>
    <version>${hibernate.version}</version>
</dependency>


و کافی است در کلاس های entity از Audited@ استفاده کنیم و یا اگر نیاز بود روی Columns@ خاصی Auditing داشته باشیم همراه با آن استفاده کنیم 


@Entity
@Audited
public class Bar { ... }



همانطور که در ابتدا توضیح داده شده هر Bar میتواند با تعدادی Foo رابطه داشته باشد (one to many) و باید در کلاس Foo هم از Audited@ استفاده کنیم و یا از NotAudited@ در رابط آن استفاده کنیم :


@OneToMany(mappedBy = "bar")
@NotAudited
private Set<Foo> fooSet;






ساخت جدولی برای لاگ گرفتن Audit ها :


برای ساخت این جدول راه هایی وجود دارد :


- ست کردن hibernate.hbm2ddl.auto به یک از مقادیر create , create-drop , update تا Envers بتواند آن جدول را اتوماتیک بسازد 

- استفاده از org.hibernate.tool.EnversSchemaGenerator برای استخراج database schema با کدنویسی 

- برای تولید DDL ها از Ant task استفاده کنیم 

- با استفاده از یکی از پلاگین های Maven مانند Juplo اسکیمای دیتابیس ها را با توجه به روابط انها بسازیم و در envers استفاده کنیم 


روش اول راحت ترین راه است ولی برای استفاده در محصول تجاری ایمن نیست 


با این حال اگر ما کلاس Foo را Audited@ کنیم باید جداول bar_AUD و foo_AUD بصورت اتوماتیک ساخته شوند در این جداول کلیه فیلد های کلاس های entity و دو فیلد اضافه REVTYPE و REV وجود خواهد داشت که مقادیر REVTYPE میتواند 0 برای insert و 1 برای update و 2 برای delete ثبت شود 

علاوه بر جدول اختصاص یافته به هر Entity یک جدول دیگر بنام REVINFO بصورت پیش فرض ساخته میشود که دو فیلد مهم در ان است REV و REVTSTMP که زمان را برای هر ورژن از entity ثبت میکند 

و foo_AUD.REV  و bar_AUD.REV کلید خارجی به REVINFO.REV هستند 





تنظیمات Envers :


در این تنظیمات میتوانیم پیشوند جداول ساخته شده را تغییر دهیم 

Properties hibernateProperties = new Properties(); 
hibernateProperties.setProperty(
  "org.hibernate.envers.audit_table_suffix", "_AUDIT_LOG"); 
sessionFactory.setHibernateProperties(hibernateProperties);

دیگر تنظیمات در این لینک قابل مطالعه است 





دسترسی به تاریخچه Entity ها :


مشابه استفاده از Criteria API میتوانیم تاریخچه entity ها را با استفاده از اینترفیس AuditReader  و کلاس سازنده آن AuditReaderFactory مشاهده کنیم :


AuditReader reader = AuditReaderFactory.get(session);


حال میتوانیم روی ابجکت موجود تاریخچه entity ها را ببینیم مثلا تغییرات در ورژن 2 که bar_AUDIT_LOG.REV = 2 است 

AuditQuery query = reader.createQuery()
  .forEntitiesAtRevision(Bar.class, 2)


اگر بخواهیم لیستی از کلیه Entity در کلیه ورژن ها داشته باشیم میتوانی بدین صورت دسترسی پیدا کنیم :


AuditQuery query = reader.createQuery()
  .forRevisionsOfEntity(Bar.class, true, true);

اگر پارامتر دوم را false کنیم با جدول REVINFO  نتیجه join میشود و اگر true باشد فقط Entity ها برگشت داده میشوند و پارامتر آخر مشخص میکند که آیا entity های حذف شده را هم برگرداند یا خیر


بعضی محدودیت ها را توسط AuditEntity  میتوان اعمال کرد :

query.addOrder(AuditEntity.revisionNumber().desc());







Audit کردن با Spring Data JPA :


ابتدا آنرا با EnableJpaAuditing@ در کلاس Configuration@ فعال میکنیم :


@Configuration
@EnableTransactionManagement
@EnableJpaRepositories
@EnableJpaAuditing
public class PersistenceConfig { ... }




اضافه کردن کلاس  Listener callback برای entity ها :


@Entity
@EntityListeners(AuditingEntityListener.class)
public class Bar { ... }



اضافه کردن فیلدهایی برای Tracking و ذخیره کردن تاریخ ایجاد و تاریخ تغییرات که مقادیر آنها اتوماتیک پر میشود :


@Entity
@EntityListeners(AuditingEntityListener.class)
public class Bar {
     
    //...
     
    @Column(name = "created_date", nullable = false, updatable = false)
    @CreatedDate
    private long createdDate;
 
    @Column(name = "modified_date")
    @LastModifiedDate
    private long modifiedDate;
     
    //...
     
}



* ما متیوانیم یک کلاس پدر که با MappedSuperClass@ مشخص شده است داشته باشیم که کلیه Audited Entity های ما از آن ارث بری میکنند و این فیلد ها را به آنجا منتقل کنیم 




ثبت Author تغییرات با Spring Security :


@Entity
@EntityListeners(AuditingEntityListener.class)
public class Bar {
     
    //...
     
    @Column(name = "created_by")
    @CreatedBy
    private String createdBy;
 
    @Column(name = "modified_by")
    @LastModifiedBy
    private String modifiedBy;
     
    //...
     
}



نام Author میتواند از طریق Spring Security Context بدست آید ولی اگر نیاز داریم که این اسم را بصورت سفارشی وارد کنیم میتوانیم با استفاده از  پیاده سازی اینترفیس AuditAware آنرا ثبت کنیم :


public class AuditorAwareImpl implements AuditorAware<String> {
  
    @Override
    public String getCurrentAuditor() {
        // your custom logic
    }
 
}


و انرا به EnableJpaAuditing@ در قالب یک Bean معرفی کنیم پس باید یک Bean هم برای این منظور در نظر بگیریم که مقدار برگشتی آن از نوع AuditorAware باشد :


@EnableJpaAuditing(auditorAwareRef="auditorProvider")
public class PersistenceConfig {
     
    //...
     
    @Bean
    AuditorAware<String> auditorProvider() {
        return new AuditorAwareImpl();
    }
     
    //...
     
}










نظرات  (۰)

هیچ نظری هنوز ثبت نشده است

ارسال نظر

ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
تجدید کد امنیتی