フィールド自動入力 (JPA)
0. はじめに
最近、プロジェクトはフィールドの自動入力機能を実装する必要があります。全体的な要件は次のとおりです: エンティティ クラスが挿入または変更されるたびに、値がパブリック フィールドに自動的に割り当てられます。パブリック フィールドには作成者 ID、作成者名、修飾子 ID 、修飾子名、作成時刻、および変更時刻。このうち作成者 ID、作成者名、修飾子 ID、および修飾子名はトークンから解析して取得されます。
当初、私のアイデアは次のようなものでした。カスタム アノテーションを作成し、このアノテーションを自動的に挿入する必要があるフィールドに追加してから、AOP クラスを作成し、各エンティティが操作を挿入および変更しようとしているときに割り込んで、次に、リフレクションを使用してフィールドの割り当てを完了します。
プロジェクトの永続化フレームワークは JPA であるため、関連情報を確認し、最終的に永続化操作のための JPA のライフサイクル アノテーション (保存前は @PrePersist、変更前は @PreUpdate、これは JPA が提供するものと同等) を見つけました。 AOP 操作) をカスタム アノテーションとリフレクションと組み合わせて、最終的に自動フィールド充填機能を実現します。
その後、プロジェクトはエンティティ クラスに追加のアノテーションを使用したくないため、列挙型クラスとリフレクションを使用して自動フィールド充填機能を実現します。
1. カスタム注釈 + リフレクション
1.1. 注意事項
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.FIELD})
public @interface LoadingField {
boolean isTime() default false;
boolean isModified() default false;
}
1.2. エンティティクラス
@EntityListeners(FieldListener.class)
public class example {
@LoadingField
private Long creatorId;
@LoadingField
private String creatorName;
@LoadingField(isTime = true)
private Timestamp createTime;
@LoadingField(isModified = true)
private Long modifierId;
@LoadingField(isModified = true)
private String modifierName;
@LoadingField(isTime = true, isModified = true)
private Timestamp modifyTime;
}
1.3、リスナー
@Slf4j
public class FieldListener extends BasicListener {
private static final String TOKEN_KEY = "access_token";
private static final Map<String, String> fieldMap = new HashMap<>();
private SysUserVO sysUserVo;
// 保存之前,为创建时间和创建人赋值
@PrePersist
public void prePersist(Object object) {
this.init();
if (sysUserVo == null) {
return;
}
if (object != null) {
Field[] fields = object.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(LoadingField.class)) {
boolean isTime = field.getAnnotation(LoadingField.class).isTime();
fieldFill(field, object, isTime);
}
}
}
}
// 修改之前,为创建时间和创建人赋值
@PreUpdate
public void preUpdate(Object object) {
this.init();
if (sysUserVo == null) {
return;
}
if (object != null) {
Field[] fields = object.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(LoadingField.class) && field.getAnnotation(LoadingField.class).isModified()) {
boolean isTime = field.getAnnotation(LoadingField.class).isTime();
fieldFill(field, object, isTime);
}
}
}
}
private void fieldFill(Field field, Object object, boolean isTime) {
try {
if (isTime) {
field.setAccessible(true);
field.set(object, new Timestamp(new Date().getTime()));
} else {
String fieldName = fieldMap.containsKey(field.getName()) ? fieldMap.get(field.getName()) : field.getName();
Field fieldSource = sysUserVo.getClass().getDeclaredField(fieldName);
fieldSource.setAccessible(true);
field.setAccessible(true);
field.set(object, fieldSource.get(sysUserVo));
}
} catch (NoSuchFieldException | IllegalAccessException e) {
log.debug(e.getMessage());
}
}
private void init() {
// SysUserVO sysUserVO = new SysUserVO();
// sysUserVO.setSysUserId(1L);
// sysUserVO.setUserName("admin");
// this.sysUserVo = sysUserVO;
this.sysUserVo = parseToken();
fieldMap.put("creatorId", "sysUserId");
fieldMap.put("creatorName", "userName");
fieldMap.put("modifierId", "sysUserId");
fieldMap.put("modifierName", "userName");
}
// 解析 Token
private SysUserVO parseToken() {
HttpServletRequest request = RequestAndResponseContextHolder.request();
if (request != null) {
Cookie tokenCookie = CookieUtils.getCookie(TOKEN_KEY);
if (tokenCookie != null) {
return JwtUtils.parserCredential(tokenCookie.getValue(), SysUserVO.class);
}
}
return null;
}
}
public class BasicListener implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
2. 列挙型クラス + リフレクション
2.1、列挙型クラス
public enum PublicField {
CREATOR_ID("creatorId", "sysUserId", false, false),
CREATOR_NAME("creatorName", "userName", false, false),
CREATE_TIME("createTime", "createTime", true, false),
MODIFIER_ID("modifierId", "sysUserId", false, true),
MODIFIER_NAME("modifierName", "userName", false, true),
MODIFY_TIME("modifyTime", "modifyTime", true, true)
;
private final String fieldName;
private final String mappingName;
private final boolean isTime;
private final boolean isModified;
PublicField(String fieldName, String mappingName, boolean isTime, boolean isModified) {
this.fieldName = fieldName;
this.mappingName = mappingName;
this.isTime = isTime;
this.isModified = isModified;
}
public String getFieldName() {
return fieldName;
}
public String getMappingName() {
return mappingName;
}
public boolean isTime() {
return isTime;
}
public boolean isModified() {
return isModified;
}
}
public class BasicListener implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
2.2. エンティティクラス
@EntityListeners(FieldListener.class)
public class example {
private Long creatorId;
private String creatorName;
private Timestamp createTime;
private Long modifierId;
private String modifierName;
private Timestamp modifyTime;
}
2.3、リスナー
@Slf4j
public class FieldListener extends BasicListener {
private static final String TOKEN_KEY = "access_token";
private SysUserVO sysUserVo;
// 保存之前,为创建时间和创建人赋值
@PrePersist
public void prePersist(Object object) throws NoSuchFieldException {
this.sysUserVo = parseToken();
if (this.sysUserVo == null) {
return;
}
if (object != null) {
for (PublicField publicField : PublicField.values()) {
Field field = object.getClass().getDeclaredField(publicField.getFieldName());
fieldFill(field, publicField.getMappingName(), object, publicField.isTime());
}
}
}
// 修改之前,为创建时间和创建人赋值
@PreUpdate
public void preUpdate(Object object) throws NoSuchFieldException {
this.sysUserVo = parseToken();
if (sysUserVo == null) {
return;
}
if (object != null) {
for (PublicField publicField : PublicField.values()) {
if (publicField.isModified()) {
Field field = object.getClass().getDeclaredField(publicField.getFieldName());
fieldFill(field, publicField.getMappingName(), object, publicField.isTime());
}
}
}
}
private void fieldFill(Field field, String mappingName, Object object, boolean isTime) {
try {
if (isTime) {
field.setAccessible(true);
field.set(object, new Timestamp(new Date().getTime()));
} else {
Field fieldSource = sysUserVo.getClass().getDeclaredField(mappingName);
fieldSource.setAccessible(true);
field.setAccessible(true);
field.set(object, fieldSource.get(sysUserVo));
}
} catch (NoSuchFieldException | IllegalAccessException e) {
log.debug(e.getMessage());
}
}
private SysUserVO parseToken() {
HttpServletRequest request = RequestAndResponseContextHolder.request();
if (request != null) {
Cookie tokenCookie = CookieUtils.getCookie(TOKEN_KEY);
if (tokenCookie != null) {
return JwtUtils.parserCredential(tokenCookie.getValue(), SysUserVO.class);
}
}
return null;
}
}
3.効果
挿入する場合:
変更する場合: