原創(chuàng)性聲明:本文完全為筆者原創(chuàng)缸逃,請(qǐng)尊重筆者勞動(dòng)力休讳。轉(zhuǎn)載務(wù)必注明原文地址。
這是一個(gè)很常見的需求 :
例如另锋。簡(jiǎn)書數(shù)據(jù)庫中,文章表(articles)中的某條記錄是被那個(gè)賬號(hào)創(chuàng)建的(
@CreateBy
)狭归,最后的一次更新是哪個(gè)賬號(hào)干的(@LastModifiedBy
)夭坪,以及創(chuàng)建的時(shí)間是什么(@CreateDate
),最后更新的時(shí)間又是什么(@LastModifiedDate
)过椎?
這既可以作為用戶所需要的字段信息室梅,在一定程度上也是一種操作記錄的體現(xiàn)。當(dāng)然,這樣的功能亡鼠,完全可以自己在CRUD
操作的時(shí)候去實(shí)現(xiàn)赏殃,但是秉持著程序的精簡(jiǎn)性和可靠性,用Spring 的@Audited
自然是一種更好的方法了间涵。
Spring審計(jì)功能簡(jiǎn)單易用(我的項(xiàng)目建立在Spring boot之上)仁热。
假設(shè)我們的domain
包下,有一個(gè)抽象的審計(jì)超類——AbstractAuditingEntity
勾哩,其他的實(shí)體類都繼承它抗蠢。
@MappedSuperclass
@Audited
@EntityListeners(AuditingEntityListener.class)
public abstract class AbstractAuditingEntity {
@CreatedBy
@Column(name = "created_by", nullable = false, length = 50, updatable = false)
@JsonIgnore
private String createdBy;
@CreatedDate
@Column(name = "created_date", nullable = false, updatable = false)
@JsonIgnore
private LocalDateTime createdDate = LocalDateTime.now();
@LastModifiedBy
@Column(name = "last_modified_by", length = 50)
@JsonIgnore
private String lastModifiedBy;
@LastModifiedDate
@Column(name = "last_modified_date")
@JsonIgnore
private LocalDateTime lastModifiedDate = LocalDateTime.now();
// 省略對(duì)應(yīng)的 set/get 方法......
}
簡(jiǎn)單講一下類上三個(gè)注解的作用。
@MappedSuperclass
API文檔是這么寫的: 指定一個(gè)類思劳,其映射信息應(yīng)用于從其繼承的實(shí)體迅矛。 映射的超類沒有為它定義的單獨(dú)的表。使用MappedSuperclass注釋指定的類可以以與實(shí)體相同的方式進(jìn)行映射敢艰,除了映射將僅應(yīng)用于其子類诬乞,因?yàn)橛成涞某惐旧頉]有表。(谷歌翻譯)翻譯的雖然不是非常順口钠导,但大致還是能看懂的震嫉。意思就是說,應(yīng)用這個(gè)注解的類(比如上面例子的AbstractAuditingEntity
)在數(shù)據(jù)庫里是沒有表與它對(duì)應(yīng)的牡属,但是它的屬性(這里的四個(gè)屬性)票堵,將會(huì)被繼承的子類所繼承,并映射到子類在數(shù)據(jù)庫中對(duì)應(yīng)的這四個(gè)字段逮栅。那么顯而易見悴势,這個(gè)注解可以把眾多實(shí)體類的公共屬性提取出來,這也是此處的作用措伐。
@Audited
API文檔是這么寫的: 當(dāng)應(yīng)用于類時(shí)特纤,表示其所有屬性都應(yīng)該被審計(jì)。 當(dāng)應(yīng)用于一個(gè)字段時(shí)侥加,表示該字段應(yīng)該被審計(jì)捧存。沒什么好解釋的,谷歌這個(gè)翻譯担败,很6昔穴。
@EntityListeners(AuditingEntityListener.class)
指定要用于實(shí)體或映射超類的回調(diào)偵聽器類。 此注釋可以應(yīng)用于實(shí)體類或映射超類提前。相當(dāng)于給當(dāng)前的超類注冊(cè)一個(gè)監(jiān)聽器吗货,注冊(cè)給誰呢,就是Spring提供的AuditingEntityListener
這個(gè)類狈网,從名字上看就大概揣測(cè)一二這個(gè)監(jiān)聽器的作用宙搬,監(jiān)聽一個(gè)實(shí)體創(chuàng)建或更新時(shí)的審計(jì)用的笨腥。
顯然這三個(gè)注解都是必不可少的,當(dāng)然如果不把公共實(shí)體抽取出來勇垛,而是在某個(gè)具體的類上(如Article
)使用這些注解和聲明定義這四個(gè)屬性扇雕,那么@MappedSuperclass
就沒有必要了。
至于@CreatedBy
窥摄、@CreatedDate
、@LastModifiedBy
和@LastModifiedDate
,那就對(duì)號(hào)入座了础淤。
@JsonIgnore
: 忽略改字段的序列化和反序列化崭放。這么做,客戶端將不會(huì)獲得該字段鸽凶。
這么做還是不夠的币砂,如果我們沒有在JPA操作中啟用審計(jì)功能的話。啟用的方法很簡(jiǎn)單玻侥,只需要在主類上加上啟用的注解即可:
@SpringBootApplication
@EnableJpaAuditing //在JPA操作中啟用審計(jì)功能
public class Application
{
public static void main( String[] args )
{
SpringApplication.run(Application.class, args);
}
}
跑起來我們會(huì)發(fā)現(xiàn)决摧。createBy
和 lastModifiedBy
的值并不會(huì)被相應(yīng)地自動(dòng)填上。因此我們還少了一個(gè)步驟——實(shí)現(xiàn)AuditorAware
接口凑兰,并實(shí)現(xiàn)getCurrentAuditor
方法掌桩,不然程序不知道創(chuàng)建者和最后修改者怎么給值。
@Component
public class SpringSecurityAuditorAware implements AuditorAware<String> {
@Override
public String getCurrentAuditor() {
// TODO 返回當(dāng)前session會(huì)話中的賬戶名即可
}
}
getCurrentAuditor
方法中也體現(xiàn)了記錄createdBy
和lastModifiedBy
的策略(包括是保存操作者用戶名姑食,還是電話號(hào)碼或其他信息)波岛,一般情況下,當(dāng)然是返回session中的賬戶信息(通常是賬戶名)音半,因?yàn)榭隙ㄊ沁@個(gè)賬戶修改的對(duì)應(yīng)數(shù)據(jù)则拷。
如此一來,在對(duì)普通實(shí)體類的CRUD
操作時(shí)曹鸠,便不用操心某條記錄是誰創(chuàng)建的煌茬、啥時(shí)候創(chuàng)建的,又是誰最后一次更新的彻桃,最后一次更新是啥時(shí)候這樣的破事兒了坛善。