摘要:以下內(nèi)容基于如果你使用的也是相同的技術(shù)棧可以繼續(xù)往下閱讀,如果不是可以當(dāng)作參考。編寫的四種方式裸寫最簡單最粗暴也是使用最多的一種方式,在寫的多了之后可以用生成工具生成。
導(dǎo)讀
在目前接觸過的項(xiàng)目中大多數(shù)的項(xiàng)目都會涉及到: crud相關(guān)的操作, 哪如何優(yōu)雅的編寫crud操作呢?
帶著這個問題,我們發(fā)現(xiàn)項(xiàng)目中大量的操作多是 創(chuàng)建實(shí)體 、刪除實(shí)例、 修改實(shí)體、 查詢單個實(shí)體、 分頁查詢多個實(shí)體, 我們有沒有好的方式解決呢?
下面我給出crud編寫的四種方式 循序漸進(jìn) ,并分析其優(yōu)勢劣勢,希望有一種能適合你,如果你有其他方式可以留言討論,在此權(quán)當(dāng)拋磚引玉。
以下內(nèi)容基于Spring Boot 、Spring MVC、 Spring Data JPA 如果你使用的也是相同的技術(shù)棧可以繼續(xù)往下閱讀,如果不是可以當(dāng)作參考。
crud編寫的四種方式 1 裸寫crud最簡單最粗暴也是使用最多的一種方式,在寫的多了之后可以用生成工具生成。
import lombok.AllArgsConstructor; import org.springframework.beans.BeanUtils; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; /** * @author yangrd * @date 2019/3/4 **/ @AllArgsConstructor @RestController @RequestMapping("/api/banner") public class BannerController { private BannerRepository repository; @PostMapping @ResponseStatus(HttpStatus.CREATED) public Banner save(Banner banner) { return repository.save(banner); } @DeleteMapping("/{id}") @ResponseStatus(HttpStatus.NO_CONTENT) public void delete(@PathVariable Long id) { repository.deleteById(id); } @PutMapping("/{id}") public void update(@PathVariable("id") Banner db, @RequestBody Banner banner) { BeanUtils.copyProperties(banner, db); repository.save(db); } @GetMapping public PagefindAll(Pageable pageable) { return repository.findAll(pageable); } @GetMapping("/{id}") public Banner finOne(@PathVariable("id") Banner banner) { return banner; } }
優(yōu)勢:能控制到代碼的每一行并非所有的增刪改查都需要
劣勢:在業(yè)務(wù)簡單的情況下會編寫大量的類似代碼 這個時(shí)候我們可以用泛型與繼承解決 引出 AbstractCrudController
2 extend BaseCrudController使用抽象的能力,通過抽象類對相同的代碼進(jìn)行封裝,減少子類繼續(xù)編寫重復(fù)的代碼。
import com.st.cms.common.spring.jpa.AbstractEntity; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; import java.util.UUID; /** * @author yangrd * @date 2019/3/1 **/ public abstract class BaseCrudController> { @Autowired protected D repository; @PostMapping @ResponseStatus(HttpStatus.CREATED) public T save(@RequestBody T t) { return repository.save(t); } @DeleteMapping("/{id}") @ResponseStatus(HttpStatus.NO_CONTENT) public void delete(@PathVariable("id") String id) { repository.deleteById(id); } @PutMapping("/{id}") @ResponseStatus(HttpStatus.NO_CONTENT) public void update(@PathVariable("id") T dbData, @RequestBody T t) { BeanUtils.copyProperties(t, dbData,"id"); repository.saveAndFlush(dbData); } @GetMapping("/{id}") public T get(@PathVariable("id") T t) { return t; } }
-
import com.st.cms.common.spring.mvc.BaseCrudController; import lombok.AllArgsConstructor; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author yangrd * @date 2019/3/4 **/ @AllArgsConstructor @RestController @RequestMapping("/api/banner") public class BannerController extends BaseCrudController{ }
優(yōu)勢:在簡單的crud操作中通過泛型與繼承減少編寫大量增刪改查的方法
劣勢:在findAll方法中入?yún)?shù)不好控制,通過HttpServletRequest可以解決這個問題 但有會引入大量的獲取值的方法 因此BaseCrudController中不提供 findAll 方法 由用戶編寫
3 spring data rest引入spring-boot-starter-data-rest,crud操作可以直接http調(diào)用 ,感興趣的可以翻看官方文檔
org.springframework.boot spring-boot-starter-data-rest
優(yōu)勢:spring 家的東西 可以很好的與spring boot 整合 只需引入一個依賴即可
劣勢:和之前業(yè)務(wù)中返回的數(shù)據(jù)格式內(nèi)容不同 (此處也是好處 更統(tǒng)一規(guī)范 ,如果一開始前后端約定好數(shù)據(jù)格式就沒有什么太大的問題)
4 ControllerHelper重點(diǎn)來了哈哈 直接上代碼
import com.alibaba.fastjson.JSON; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.util.Assert; import org.springframework.web.bind.annotation.*; import javax.persistence.EntityNotFoundException; import java.beans.PropertyDescriptor; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.*; /** * @author yangrd * @date 2019/3/22 **/ @Slf4j @RestController @RequestMapping("/api") public class ControllerHelper implements ApplicationContextAware { private MappingManager mappingManager; @PostMapping("/{repository}") public ResponseEntity create(@PathVariable String repository, @RequestBody String reqJSON) { return mappingManager.getJpaRepository(repository).map(repo -> { Object object = mappingManager.getEntityObj(repository); Object req = JSON.parseObject(reqJSON, object.getClass()); BeanUtils.copyProperties(req, object); return ResponseEntity.status(HttpStatus.CREATED).body(repo.saveAndFlush(object)); }). orElseGet(() -> ResponseEntity.notFound().build()); } @DeleteMapping("/{repository}/{id}") public ResponseEntity delete(@PathVariable String repository, @PathVariable Long id) { return mappingManager.getJpaRepository(repository).map(repo -> { repo.deleteById(id); return ResponseEntity.noContent().build(); }). orElseGet(() -> ResponseEntity.notFound().build()); } @PutMapping("/{repository}/{id}") public ResponseEntity update(@PathVariable String repository, @PathVariable Long id, @RequestBody String reqJSON) { return mappingManager.getJpaRepository(repository).map(repo -> { repo.findById(id).ifPresent(db -> { Object req = JSON.parseObject(reqJSON, db.getClass()); BeanUtils.copyProperties(req, db); repo.saveAndFlush(db); }); return ResponseEntity.noContent().build(); }). orElseGet(() -> ResponseEntity.notFound().build()); } @GetMapping("/{repository}/{id}") public ResponseEntity get(@PathVariable String repository, @PathVariable Long id) { return mappingManager.getJpaRepository(repository).map(repo -> ResponseEntity.ok(repo.findById(id))). orElseGet(() -> ResponseEntity.notFound().build()); } @GetMapping("/{repository}") public ResponseEntity get(@PathVariable String repository, Pageable pageable) { return mappingManager.getJpaRepository(repository).map(repo -> ResponseEntity.ok(repo.findAll(pageable))). orElseGet(() -> ResponseEntity.notFound().build()); } class MappingManager { private Mapentity4Repositories = new HashMap<>(); private Map entity4Class = new HashMap<>(); MappingManager(ApplicationContext applicationContext) { Map repositoryBeans = applicationContext.getBeansOfType(JpaRepository.class); repositoryBeans.forEach((repositoryName, repositoryBean) -> { Class entityClass = MappingSupport.getEntityClass(repositoryBean); String entityClassName = MappingSupport.getEntityName(entityClass); entity4Repositories.put(entityClassName, repositoryBean); entity4Class.put(entityClassName, entityClass); }); } public Optional getJpaRepository(String repository) { return Optional.ofNullable(entity4Repositories.get(repository)); } public Object getEntityObj(String repository) { return Optional.ofNullable(entity4Class.get(repository)).map(clazz -> { try { return clazz.newInstance(); } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); } return null; }).orElseThrow(EntityNotFoundException::new); } } static class MappingSupport { static Class getEntityClass(JpaRepository jpaRepository) { Type[] jpaInterfacesTypes = jpaRepository.getClass().getGenericInterfaces(); Type[] type = ((ParameterizedType) ((Class) jpaInterfacesTypes[0]).getGenericInterfaces()[0]).getActualTypeArguments(); return (Class) type[0]; } static String getEntityName(Class clazz) { String simpleName = clazz.getSimpleName(); return simpleName.substring(0, 1).toLowerCase() + simpleName.substring(1); } } /** * @author yangrd * @date 2018/8/30 **/ static class BeanUtils { /** * 只拷貝不為null的屬性 * * @param source the source bean * @param target the target bean * @throws BeansException if the copying failed */ public static void copyProperties(Object source, Object target) throws BeansException { org.springframework.beans.BeanUtils.copyProperties(source, target, getNullPropertyNames(source)); } private static String[] getNullPropertyNames(Object source) { final BeanWrapper src = new BeanWrapperImpl(source); PropertyDescriptor[] pds = src.getPropertyDescriptors(); Set emptyNames = new HashSet<>(); for (PropertyDescriptor pd : pds) { Object srcValue = src.getPropertyValue(pd.getName()); if (srcValue == null) { emptyNames.add(pd.getName()); } } String[] result = new String[emptyNames.size()]; return emptyNames.toArray(result); } } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { Assert.notNull(applicationContext, ""); this.mappingManager = new MappingManager(applicationContext); } }
優(yōu)勢:對spring data rest弱勢的補(bǔ)充可以在不改變 之前習(xí)慣的數(shù)據(jù)格式的情況下狠方便的前移, 最重要的是相比于 Abstract 方法可以被覆蓋 如 findAll 如果你使用 如 api/user/{id} spring mvc會優(yōu)先匹配它 而不是 api/{repository}/{id} ,后期可以根據(jù)自身業(yè)務(wù)需要打成jar包放在私服上面方便其他項(xiàng)目使用
劣勢: 也是除了 第一種方式 之外其余三種都存在的問題 如果并不需要常用所有的五種操作 如何禁用 哈哈 解決方法是有的 我們下次再說 >_<
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/73512.html
摘要:哪吒社區(qū)技能樹打卡打卡貼函數(shù)式接口簡介領(lǐng)域優(yōu)質(zhì)創(chuàng)作者哪吒公眾號作者架構(gòu)師奮斗者掃描主頁左側(cè)二維碼,加入群聊,一起學(xué)習(xí)一起進(jìn)步歡迎點(diǎn)贊收藏留言前情提要無意間聽到領(lǐng)導(dǎo)們的談話,現(xiàn)在公司的現(xiàn)狀是碼農(nóng)太多,但能獨(dú)立帶隊(duì)的人太少,簡而言之,不缺干 ? 哪吒社區(qū)Java技能樹打卡?【打卡貼 day2...
摘要:本項(xiàng)目將使用配合最簡單的邏輯來展示一個基于的微服務(wù)全棧快速開發(fā)實(shí)踐的。提供一系列大型項(xiàng)目常用的非功能性特征,比如內(nèi)嵌服務(wù)器,安全,指標(biāo),健康檢測,外部化配置。 SprintBoot-Vue SpringBoot + 前端MVVM 基于Java的微服務(wù)全棧快速開發(fā)實(shí)踐 showImg(https://segmentfault.com/img/remote/1460000010167913...
摘要:本項(xiàng)目將使用配合最簡單的邏輯來展示一個基于的微服務(wù)全棧快速開發(fā)實(shí)踐的。提供一系列大型項(xiàng)目常用的非功能性特征,比如內(nèi)嵌服務(wù)器,安全,指標(biāo),健康檢測,外部化配置。 SprintBoot-Vue SpringBoot + 前端MVVM 基于Java的微服務(wù)全棧快速開發(fā)實(shí)踐 showImg(https://segmentfault.com/img/remote/1460000010167913...
摘要:前兩篇已經(jīng)構(gòu)建了標(biāo)準(zhǔn)工程實(shí)例,也整合了實(shí)現(xiàn)了簡單數(shù)據(jù)庫訪問,本篇主要更深入的學(xué)習(xí)下,實(shí)現(xiàn)較為完整的數(shù)據(jù)庫的標(biāo)準(zhǔn)服務(wù)。到這里,最復(fù)雜的數(shù)據(jù)訪問基本就算編寫完了。 前兩篇已經(jīng)構(gòu)建了RESTful API標(biāo)準(zhǔn)工程實(shí)例,也整合了MyBatis實(shí)現(xiàn)了簡單數(shù)據(jù)庫訪問,本篇主要更深入的學(xué)習(xí)下,實(shí)現(xiàn)較為完整的數(shù)據(jù)庫CRUD的標(biāo)準(zhǔn)服務(wù)。 首先看下要實(shí)現(xiàn)的效果吧,完成下面截圖部分的API,除了CRUD之外...
摘要:的作用可以看到,它給我們提供了一些核心的功能代碼生成器和現(xiàn)成的接口以及可以結(jié)合的條件構(gòu)造器使我們的代碼變得足夠優(yōu)雅,分頁的使用也是相當(dāng)?shù)姆奖悖约疤峁┝瞬煌闹麈I生成策略。 簡介 Mybatis-Plus是在Mybatis的基礎(chǔ)上,國人開發(fā)的一款持久層框架。 showImg(https://segmentfault.com/img/bVbvFk4?w=2022&h=862); 并且榮獲...
閱讀 2949·2021-11-24 09:39
閱讀 2858·2021-09-29 09:34
閱讀 3549·2021-09-24 10:23
閱讀 1731·2021-09-22 15:41
閱讀 1690·2019-08-30 15:55
閱讀 3506·2019-08-30 13:58
閱讀 2614·2019-08-30 13:11
閱讀 1662·2019-08-29 12:31