真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

SpringDataJpa怎么實體對象增強設(shè)計

本篇內(nèi)容主要講解“SpringDataJpa怎么實體對象增強設(shè)計”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“SpringDataJpa怎么實體對象增強設(shè)計”吧!

站在用戶的角度思考問題,與客戶深入溝通,找到甘孜州網(wǎng)站設(shè)計與甘孜州網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗,讓設(shè)計與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個性化、用戶體驗好的作品,建站類型包括:成都網(wǎng)站設(shè)計、成都做網(wǎng)站、企業(yè)官網(wǎng)、英文網(wǎng)站、手機端網(wǎng)站、網(wǎng)站推廣、域名注冊、網(wǎng)絡(luò)空間、企業(yè)郵箱。業(yè)務(wù)覆蓋甘孜州地區(qū)。

實體增強插件第一版

需求

在日常的 Java-web 開發(fā)過程中時常需要做一些單表的數(shù)據(jù)操作,常用操作有:單表的新增、單表根據(jù)ID查詢,單表根據(jù)ID刪除,單表根據(jù)ID修改。對于這四種單表的基本操作時常需要編寫很多重復(fù)代碼如何避免編寫重復(fù)編寫這類代碼成了一個問題。面對這樣的一個問題我們常規(guī)的解決方案有代碼生成器,代碼生成器可以通過數(shù)據(jù)庫建表語句直接得到Controoler、service、dao三者從而避免重復(fù)編寫。除此之外筆者思考了另一種處理方式,不通過代碼生成器通過一個注解來完成上述操作。

設(shè)計

首先需要考慮的是四種單表操作的API設(shè)計,一般情況下筆者會定義這樣的幾個API,下面是關(guān)于新增的API筆者會設(shè)計出如下接口。

  • 以用戶做新增舉例

POST http://host:port/user
Content-Type: application/json

// 添加參數(shù)
{}
  • 以部門為例做新增舉例

POST http://host:port/dept
Content-Type: application/json

// 添加參數(shù)
{}

對于上述兩個API設(shè)計可以看到一些大同小異的地方,相同的有都是通過POST進行請求,不同的是后面的路由地址和參數(shù),對于這樣兩組接口可以抽象為下面一個接口

  • 抽象后的新增接口

POST http://host:port/{entity_name}
Content-Type: application/json

// 添加參數(shù)
{}

同樣的其他3個操作也可以通過類似的方式進行抽象。

  • 根據(jù)ID查詢接口

GET http://host:port/{entity_name}/{id}
  • 修改接口

PUT http://host:port/{entity_name}
Content-Type: application/json

// 修改參數(shù)
{}
  • 根據(jù)ID刪除接口

DELETE http://host:port/{entity_name}/{id}

基礎(chǔ)接口設(shè)計完成,可以先將基本的Controller代碼編寫完成。

@RestController
public class EntityPluginController {

  @GetMapping("/{entityPluginName}/{id}")
  public ResponseEntity findById(
      @PathVariable("entityPluginName") String entityPluginName,
      @PathVariable("id") String id
  ) {
    return null;
  }


  @PostMapping("/{entityPluginName}")
  public ResponseEntity save(
      @PathVariable("entityPluginName") String entityPluginName,
      @RequestBody Object insertParam
  ) {
    return null;
  }

  @PutMapping("/{entityPluginName}")
  public ResponseEntity update(
      @PathVariable("entityPluginName") String entityPluginName,
      @RequestBody Object updateParam
  ) {
	return null;
  }

  @DeleteMapping("/{entityPluginName}/{id}")
  public ResponseEntity deleteById(
      @PathVariable("entityPluginName") String entityPluginName,
      @PathVariable("id") String id
  ) {

    return null;
  }
}

本文使用JPA作為數(shù)據(jù)交互層,以JAP作為交互會有2個關(guān)鍵對象,第一個是數(shù)據(jù)庫實體,第二個是Repository接口。通常情況下會選擇CrudRepository接口來作為數(shù)據(jù)交互層的根對象,也有會選擇JpaRepository接口來作為數(shù)據(jù)交互的根對象,這兩種存在間接引用,類圖如下

SpringDataJpa怎么實體對象增強設(shè)計

了解了常用的JPA操作對象后來看一個Entity對象

@Entity
@Table(name = "oauth_client", schema = "shands_uc_3_back", catalog = "")
public class OauthClientEntity {

  private Long id;
  private String clientId;
  private String clientSecurity;
  private String redirectUri;
  private Long version;
    // 省略getter&setter
}

在單表開發(fā)過程中我們做的所有行為都是圍繞這個數(shù)據(jù)庫實體進行操作,比如在新增的時候?qū)⑿略鰠?shù)轉(zhuǎn)換成數(shù)據(jù)庫對象,在更新的是將更新參數(shù)轉(zhuǎn)換成數(shù)據(jù)庫對象,在根據(jù)ID查詢的時候?qū)⒉樵兘Y(jié)果(數(shù)據(jù)庫對象)轉(zhuǎn)換為返回結(jié)果對象,總共存在三種數(shù)據(jù)庫對象的轉(zhuǎn)換,這三種轉(zhuǎn)換是必不可少的,當(dāng)然也可以用一個數(shù)據(jù)庫對象直接來滿足這個操作從而減少代碼量(不建議這么做),對于這三種轉(zhuǎn)換先來定義一個接口,該接口表示了三種對象的轉(zhuǎn)換過程。

  • 數(shù)據(jù)庫對象的三種轉(zhuǎn)換

public interface EntityConvert {

  /**
   * convert data from insert param db entity
   *
   * @param insType insert param
   * @return db entity
   */
  EntityType fromInsType(InsType insType);

  /**
   * convert data from update param to db entity
   *
   * @param upType update param
   * @return db entity
   */
  EntityType fromUpType(UpType upType);

  /**
   * convert data from db entity to response entity
   *
   * @param entityType db entity
   * @return response entity
   */
  ResType fromEntity(EntityType entityType);

}

EntityConvert 接口中定義了4個泛型,含義如下

  1. InsType:新增時的參數(shù)類型

  2. UpType:修改時的參數(shù)類型

  3. ResType:返回時的參數(shù)類型

  4. EntityType:數(shù)據(jù)庫實體類型

完成接口定義后需要將這個接口的實現(xiàn)類和實體對象綁定,最簡單的一種綁定模式就是通過注解來表示,注解定義如下

@java.lang.annotation.Target({ElementType.TYPE})
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Documented
@java.lang.annotation.Inherited
public @interface EntityPlugin {

  /**
   * name
   *
   * @return name
   */
  String name();

  /**
   * {@link EntityConvert} class
   *
   * @return class
   */
  Class convertClass() default EntityConvert.class;
}

注解兩個函數(shù)的含義:

  1. name表示實體名稱,

  2. convertClass表示轉(zhuǎn)換器實現(xiàn)類

下面將注解和實體類進行綁定,具體代碼如下

@EntityPlugin(name = "oauthClient")
@Entity
@Table(name = "oauth_client", schema = "shands_uc_3_back", catalog = "")
public class OauthClientEntity {}

注意:筆者在這里沒有自定義實現(xiàn) EntityConvert 接口,采用的是默認方式,即參數(shù)等于數(shù)據(jù)庫對象

在完成實體對象和轉(zhuǎn)換對象之間的關(guān)系綁定后后需要做到的事情是如何調(diào)用JPA框架將數(shù)據(jù)插入。解決這個問題首先需要從JPA接口入手,在JPA接口中都需要定義兩個泛型,第一個泛型是實體對象,第二個泛型是ID類型,我們需要通過實體對象來獲取前文所編寫的注解信息,使用ID泛型為根據(jù)ID查詢提供參數(shù)支持。下面是存儲上述信息的對象。

public class EntityPluginCache {

  private String name;
  private Class convertClass;
  private CrudRepository crudRepository;
  private Class self;
  private Class idClass;
}
  1. name表示注解EntityPlugin的name屬性

  2. convertClass 表示EventConvert實現(xiàn)類的類型

  3. crudRepository 表示JPA數(shù)據(jù)庫操作對象

  4. self 表示實體類類型

  5. idClass 表示實體類的ID數(shù)據(jù)類型

完成這些后我們需要解決的問題就是如何從JPA接口提取類和ID類型,如下面代碼所示,我們需要提取CrudRepository的兩個泛型

@Repository
public interface OauthClientRepo extends CrudRepository {

}

這里需要使用反射,具體操作代碼如下:

public class InterfaceReflectUtils {

  private InterfaceReflectUtils() {

  }

  public static List> getInterfaceGenericLasses(Class check, Class targetClass) {

    if (check == null || targetClass == null) {
      return Collections.emptyList();
    }
    List> res = new ArrayList<>();

    Class cur = check;

    while (cur != null && cur != Object.class) {
      Type[] types = cur.getGenericInterfaces();
      for (Type type : types) {

        // todo: 修改為可以根據(jù)類型進行推論
        if (type.getTypeName().contains(targetClass.getName())) {
          Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments();
          for (Type typeArgument : typeArguments) {
            if (typeArgument instanceof Class) {
              res.add((Class) typeArgument);
            }
          }
          break;

        }
      }
      Class[] interfaces = cur.getInterfaces();
      if (interfaces != null) {
        for (Class inter : interfaces) {
          List> result = getInterfaceGenericLasses(inter, targetClass);
          if (result != null) {
            res.addAll(result);
          }
        }
      }
      cur = cur.getSuperclass();
    }

    return res;
  }


}

在得到兩個泛型數(shù)據(jù)后需要進行數(shù)據(jù)解析和對象組裝并將數(shù)據(jù)存儲,數(shù)據(jù)存儲對象如下

public class EntityPluginCacheBean {

  public Map getCacheMap() {
    return cacheMap;
  }

  private final Map cacheMap = new ConcurrentHashMap<>(64);

}

接口解析代碼如下:

@Component
public class EntityPluginRunner implements ApplicationRunner, ApplicationContextAware, Ordered {

  private static final Logger log = LoggerFactory.getLogger(EntityPluginRunner.class);
  @Autowired
  private ApplicationContext context;
  @Autowired
  private EntityPluginCacheBean entityPluginCacheBean;

  @Override
  public void run(ApplicationArguments args) throws Exception {
    Map crudRepositoryMap
        = context.getBeansOfType(CrudRepository.class);


    crudRepositoryMap.forEach((k, v) -> {
      Class[] repositoryInterfaces = AopProxyUtils.proxiedUserInterfaces(v);
      for (Class repositoryInterface : repositoryInterfaces) {
        List> interfaceGenericLasses = InterfaceReflectUtils
            .getInterfaceGenericLasses(repositoryInterface,
                CrudRepository.class);
        if (!CollectionUtils.isEmpty(interfaceGenericLasses)) {
          // entity class
          Class entityClass = interfaceGenericLasses.get(0);
          EntityPlugin annotation = entityClass.getAnnotation(EntityPlugin.class);
          if (annotation != null) {

            Map cacheMap = entityPluginCacheBean.getCacheMap();
            EntityPluginCache value = new EntityPluginCache();
            value.setName(annotation.name());
            value.setSelf(entityClass);
            value.setIdClass(interfaceGenericLasses.get(1));
            value.setConvertClass(annotation.convertClass());
            value.setCrudRepository(v);
            if (cacheMap.containsKey(annotation.name())) {
              try {
                if (log.isErrorEnabled()) {
                  log.error("不允許出現(xiàn)相同的EntityPlugin名稱 ,entity = [{}]", entityClass);
                }
                throw new Exception("不允許出現(xiàn)相同的EntityPlugin名稱");
              } catch (Exception e) {
                e.printStackTrace();
              }
            }
            cacheMap.put(annotation.name(), value);
          }
        }
      }

    });
  }

  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    this.context = applicationContext;
  }

  @Override
  public int getOrder() {
    return Ordered.LOWEST_PRECEDENCE;
  }
}

注意本例中只支持CrudRepository接口暫時不支持它的子類接口,子類接口實現(xiàn)會作為后續(xù)開發(fā)方向。

至此數(shù)據(jù)準(zhǔn)備都已經(jīng)完成,接下來就是將Controller開發(fā)完成,首先定義一個對應(yīng)Controller的Service

public interface EntityPluginCoreService {

  Object findById(String entityPluginName, String id);

  Object save(String entityPluginName, Object insertParam);

  Object update(String entityPluginName, Object updateParam);

  Boolean deleteById(String entityPluginName, String id);

}

該Service對應(yīng)了四種操作模式,下面以保存作為一個實例進行說明。保存的Controller相關(guān)代碼如下

@PostMapping("/{entityPluginName}")
public ResponseEntity save(
    @PathVariable("entityPluginName") String entityPluginName,
    @RequestBody Object insertParam
) {
  EntityPluginCache entityPluginCache = entityPluginCacheBean.getCacheMap().get(entityPluginName);
  Class convertClass = entityPluginCache.getConvertClass();
  if (convertClass != EntityConvert.class) {
    Object save = coreService.save(entityPluginName, insertParam);
    return ResponseEntity.ok(save);
  } else {
    Object o = gson.fromJson(gson.toJson(insertParam), entityPluginCache.getSelf());
    Object save = coreService.save(entityPluginName, o);
    return ResponseEntity.ok(save);

  }
}

在Controller這段代碼中可以看到有兩個分支,這里兩個分支的判斷是注解EntityPlugin中的convertClass屬性是否為EntityConvert.class,如果是說明沒有轉(zhuǎn)換過程,即數(shù)據(jù)庫對象就是參數(shù)對象,因此可以直接做出下面的轉(zhuǎn)換,請求參數(shù)轉(zhuǎn)換成JSON字符串,再通過JSON字符串轉(zhuǎn)換成實體類本身,如果不是則進入核心實現(xiàn)類。核心實現(xiàn)類的相關(guān)代碼如下

@Override
public Object save(String entityPluginName, Object insertParam) {
  EntityPluginCache entityPluginCache = entityPluginCacheBean.getCacheMap().get(entityPluginName);
  CrudRepository crudRepository = entityPluginCache.getCrudRepository();

  Class convertClass = entityPluginCache.getConvertClass();
  if (convertClass == EntityConvert.class) {
    return crudRepository.save(insertParam);
  }
  // 存在轉(zhuǎn)換類的情況下
  if (convertClass != null) {

    String[] beanNamesForType = context.getBeanNamesForType(convertClass);
    // 在 Spring 中能夠搜索到
    if (beanNamesForType.length > 0) {
      String beanName = beanNamesForType[0];
      EntityConvert bean = context.getBean(beanName, convertClass);
      // 轉(zhuǎn)換成數(shù)據(jù)庫實體對象
      Object insertDbData = bean.fromInsType(insertParam);
      // 執(zhí)行插入
      return crudRepository.save(insertDbData);
    }
    // 不能再 Spring 容器中搜索
    else {
      EntityConvert entityConvert;
      try {
        entityConvert = newInstanceFromEntityConvertClass(
            convertClass);
      } catch (Exception e) {
        if (log.isErrorEnabled()) {
          log.error("無參構(gòu)造初始化失敗,{}" + e);
        }
        return null;
      }
      Object insertDbData = entityConvert.fromInsType(insertParam);
      return crudRepository.save(insertDbData);
    }
  }
  // 如果不存在轉(zhuǎn)換器類直接進行插入
  else {
    return crudRepository.save(insertParam);
  }
}

在這段代碼中處理流程如下:

  1. 情況一:注解EntityPlugin中的convertClass屬性是EntityConvert.class,直接進JPA相關(guān)操作,注意此時的參數(shù)已經(jīng)被Controller轉(zhuǎn)換成實際的數(shù)據(jù)庫對象。

  2. 情況二:注解EntityPlugin中的convertClass屬性不是EntityConvert.class,此時可能存在兩種分類,第一種convertClass 交給Spring管理,第二種convertClass 不是Spring管理,對應(yīng)這兩種情況分別做出如下兩種操作:

    1. 從Spring中根據(jù)類型找到所有的bean取第一個作為EntityConvert接口的實現(xiàn)類,通過得到的bean進行數(shù)據(jù)轉(zhuǎn)換在調(diào)用JPA相關(guān)操作。

    2. 直接通過反射創(chuàng)建EntityConvert實現(xiàn)類,注意必須要有一個無參構(gòu)造,本例使用無參構(gòu)造進行創(chuàng)建,創(chuàng)建EntityConvert實例對象后調(diào)用JPA相關(guān)操作。

其他代碼編寫同理,其他實現(xiàn)可以查看這個倉庫:https://gitee.com/pychfarm_admin/entity-plugin

測試

完成了各類編寫后進入測試階段。

新增API測試

  • 新增API測試

POST http://localhost:8080/oauthClient
Content-Type: application/json
// 參數(shù)
{
    "clientId":"asa",
    "clientSecurity":"123"
}
  • 返回結(jié)果

{
    "id": 10,
    "clientId": "asa",
    "clientSecurity": "123",
    "redirectUri": null,
    "version": null
}
  • 數(shù)據(jù)庫結(jié)果

SpringDataJpa怎么實體對象增強設(shè)計

修改API測試

  • 修改API測試

PUT http://localhost:8080/oauthClient
Content-Type: application/json
// 參數(shù)
{
    "id": 10,
    "clientId": "asa",
    "clientSecurity": "123123123",
    "redirectUri": null,
    "version": null
}
  • 返回結(jié)果

{
    "id": 10,
    "clientId": "asa",
    "clientSecurity": "123123123",
    "redirectUri": null,
    "version": null
}
  • 數(shù)據(jù)庫結(jié)果

SpringDataJpa怎么實體對象增強設(shè)計

查詢API測試

  • 修改API測試

GET http://localhost:8080/oauthClient/10
  • 返回結(jié)果

{
    "id": 10,
    "clientId": "asa",
    "clientSecurity": "123123123",
    "redirectUri": null,
    "version": null
}

刪除API測試

  • 修改API測試

DELETE http://localhost:8080/oauthClient/10
  • 返回結(jié)果

true
  • 數(shù)據(jù)庫結(jié)果

SpringDataJpa怎么實體對象增強設(shè)計

通過上述的測試用例對于數(shù)據(jù)庫對線作為參數(shù)的開發(fā)已經(jīng)符合測試用例后續(xù)還有一些其他規(guī)劃將在后續(xù)進行開發(fā),具體計劃如下

后續(xù)計劃

  1. EntityConvert 接口使用完善,目前只支持?jǐn)?shù)據(jù)庫對象直接使用,后續(xù)對EntityConvert 接口進行更好的應(yīng)用。

  2. 驗證器相關(guān)接入,沒以前還未做數(shù)據(jù)驗證相關(guān)操作,后續(xù)會接入驗證API。

  3. 緩存相關(guān),目前對于數(shù)據(jù)還未使用緩存,后續(xù)接入reds-hash組件

    1. 緩存接入后對序列化進行自定義。

到此,相信大家對“SpringDataJpa怎么實體對象增強設(shè)計”有了更深的了解,不妨來實際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!


網(wǎng)頁題目:SpringDataJpa怎么實體對象增強設(shè)計
鏈接URL:http://weahome.cn/article/pehido.html

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部