反射方法操作


Sep 26, 2023

透過反射操作共同方法

可以讓大家透過統一方式進行操作

  • EntityOperationUtils 共用方法
  • OperationObj 操作細項
  • JpaEnum JPA的操作步驟
  • PathConstant 靜態字串 放入實際物件的檔案結構
  1. 先準備細項
    OperationObj
    /
    @param repositoryName 操作資料庫的物件名稱
    @param method 操作資料庫的方法枚舉
    @param parameters 操作資料庫物件方法要處理的紀錄資訊
    @param entityName 紀錄名稱
    /

    @NoArgsConstructor
    @Data
    public class OperationObj {
    
     private String repositoryName;
    
     private Enum<?> method;
    
     private String params;
    
     private String entityName;
    
     private final Gson gson = new Gson();
    
     public OperationObj(String repositoryName, Enum<?> method, Object params, String entityName) {
         this.repositoryName = repositoryName;
         this.method = method;
         this.params = gson.toJson(params);
         this.entityName = entityName;
     }
    }
    

JpaEnum
必須要為repository內有的方法(與jpa有關聯)

public enum JpaEnum {
    saveAndFlush;
}

PathConstant

public class PathConstant {

    public static final String BASE_PACKAGE = "com.testModule.life";
    public static final String ENTITY_NAME = "com.testModule.life.entity."; // 尋找entity物件的完整路徑
    public static final String REPOSITORY_NAME = "com.testModule.life.repository.main."; // 尋找repository物件的完整路徑
}
  1. 核心方法
    EntityOperationUtils
    *多executeJpa來決定要操作資料庫物件處理資料表資料(調整一下命名其實也適用於各類自行建立的物件方法)

*doJpaSth明定JPA方法來處理資校 其實MethodUtils.invokeMethod已經實作了

@Component
public class EntityOperationUtils {

    @Autowired
    ApplicationContext applicationContext;

    private final Gson gson = new Gson();

    public Boolean executeJpa(List<OperationObj> opObjList) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        for (OperationObj opObj :opObjList) {
        String fullRepositoryPath=String.join("",PathConstant.REPOSITORY_MAIN_NAME,opObj.getRepositoryName());
        Class<?> repositoryClass = Class.forName(fullRepositoryPath);
        Object repository = applicationContext.getBean(repositoryClass);
        Method method = repository.getClass().getMethod(opObj.getMethod().toString(),Object.class);
        String fullEntityPath = String.join("",PathConstant.ENTITY_NAME + opObj.getEntityName());
        Class<?> entityClass = Class.forName(fullEntityPath); MethodUtils.invokeMethod(repository,true,method.getName(),gson.fromJson(opObj.getParams(), entityClass));
        }
        return true;
    }

public static Optional<?> doJpaSth(JpaRepository<?, ?> repository, String methodName, Object input, Class<?> argumentClassType) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        Gson gson = new Gson();
        String inputStr = gson.toJson(input);
        Object inputObj = new Gson().fromJson(inputStr, argumentClassType);
        Method method = repository.getClass().getMethod(methodName, Object.class);
        Object obj = method.invoke(repository,inputObj);
        if (obj instanceof Optional) {
            return (Optional<?>) obj;
        }
        return Optional.of(obj);
    }

}

實際應用

 @Test
    void find() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        int pk = 1;
        Optional<Country> res = countryRepository.findById(pk);

        TypeReference<?> typeReference = new TypeReference<Country>() {
        };
        Class<?> classType = objectMapper.getTypeFactory().constructType(typeReference).getRawClass();

        // 可以根據需求放入 list/map/collection...的型別
        Optional<?> res2 = EntityOperationUtils.doJpaSth(countryRepository, "findById", pk, Integer.class);
        Object res2Opt = MethodUtils.invokeMethod(countryRepository, "findById", pk);
        if (res2Opt instanceof Optional<?>){
            res2=(Optional<?>) res2Opt;
        }
        log.info(new Gson().toJson(res2.get()));
        Country resO = (Country) res2.get();
        Assertions.assertEquals(res.get().getId(), resO.getId());
    }

片段程式碼

 Country updated = this.findByCountryId(id);
            if (Objects.isNull(updated)) {
                return Collections.emptyMap();
            }
 Country origin = (Country) SerialUtil.cloneObject(updated);
 updated.setDescription(description);
 OperationObj updateOp = new OperationObj("CountryRepository", JpaEnum.saveAndFlush, updated, "Country"); 
 // 異動實體表格
 List<OperationObj> updateOpList = Collections.singletonList(updateOp);
 entityOperationUtils.executeJpa(updateOpList);

看到這樣應用可能會覺得為什麼要多此一舉直接注入呼叫Jpa/自定義物件就好了
那我這邊列舉可能會用到的情境

  1. 當下沒有直接要對資料庫異動。例如:批次、審放、統一作法、觸發情境

這不是一個給新手的一個教學過程,也寫的不是很完整
希望大家多多包涵囉~

主要是給自己的一個紀錄,也分享給有需要的夥伴

這是一個心血來潮,產生的文章

若有喜歡或交流的部分都歡迎在下方留言,多多關照。

#reflaction #java







你可能感興趣的文章

Why you should or shouldn't use Google DNS?

Why you should or shouldn't use Google DNS?

Remote Tool 指令

Remote Tool 指令

JS30 Day 15 筆記

JS30 Day 15 筆記






留言討論