1. 程式人生 > >SpringBoot系列——Spring-Data-JPA(升級版)

SpringBoot系列——Spring-Data-JPA(升級版)

  前言

  在上篇部落格中:SpringBoot系列——Spring-Data-JPA:https://www.cnblogs.com/huanzi-qch/p/9970545.html,我們實現了單表的基礎get、save(插入/更新)、list、page、delete介面,但是這樣每個單表都要寫著一套程式碼,重複而繁雜,那能不能寫成一套通用common程式碼,每個單表去繼承從而實現這套基礎介面呢?同時,我們應該用Vo去接收、傳輸資料,實體負責與資料庫表對映。

 

  common程式碼

  Vo與實體轉換

/**
 * 實體轉換工具
 */
public class FastCopy {

    
/** * 型別轉換:實體Vo <->實體 例如:UserVo <-> User * 支援一級複雜物件複製 */ public static <T> T copy(Object src, Class<T> targetType) { T target = null; try { target = targetType.newInstance(); BeanWrapper targetBean = new BeanWrapperImpl(target); BeanMap srcBean
= new BeanMap(src); for (Object key : srcBean.keySet()) { try { String srcPropertyName = key + ""; Object srcPropertyVal = srcBean.get(key); //&& StringUtils.isEmpty(targetBean.getPropertyValue(srcPropertyName))
if (!StringUtils.isEmpty(srcPropertyVal) && !"class".equals(srcPropertyName)) { Class srcPropertyType = srcBean.getType(srcPropertyName); Class targetPropertyType = targetBean.getPropertyType(srcPropertyName); if (targetPropertyType != null) { if (srcPropertyType == targetPropertyType) { targetBean.setPropertyValue(srcPropertyName, srcPropertyVal); } else { Object targetPropertyVal = targetPropertyType.newInstance(); BeanUtils.copyProperties(srcPropertyVal, targetPropertyVal); targetBean.setPropertyValue(srcPropertyName, targetPropertyVal); BeanWrapper targetBean2 = new BeanWrapperImpl(targetPropertyVal); BeanMap srcBean2 = new BeanMap(srcPropertyVal); srcBean2.keySet().forEach((srcPropertyName2) -> { Class srcPropertyType2 = srcBean2.getType((String) srcPropertyName2); Class targetPropertyType2 = targetBean2.getPropertyType((String) srcPropertyName2); if (targetPropertyType2 != null && srcPropertyType2 != targetPropertyType2 && srcBean2.get(srcPropertyName2) != null && !"class".equals(srcPropertyName2)) { Object targetPropertyVal2 = null; try { targetPropertyVal2 = targetPropertyType2.newInstance(); } catch (Exception e) { e.printStackTrace(); } BeanUtils.copyProperties(srcBean2.get(srcPropertyName2), targetPropertyVal2); targetBean2.setPropertyValue((String) srcPropertyName2, targetPropertyVal2); } }); } } } } catch (Exception e) { e.printStackTrace(); } } } catch (Exception e) { e.printStackTrace(); } return target; } /** * 型別轉換:實體Vo <->實體 例如:List<UserVo> <-> List<User> */ public static <T> List<T> copyList(List srcList, Class<T> targetType) { List<T> newList = new ArrayList<>(); for (Object entity : srcList) { newList.add(copy(entity, targetType)); } return newList; } /** * 獲取/過濾物件的空屬性 */ public static String[] getNullProperties(Object src) { BeanWrapper srcBean = new BeanWrapperImpl(src); //1.獲取Bean Set<String> properties = new HashSet<>(); //3.獲取Bean的空屬性 for (PropertyDescriptor p : srcBean.getPropertyDescriptors()) { String propertyName = p.getName(); Object srcValue = srcBean.getPropertyValue(propertyName); if (StringUtils.isEmpty(srcValue)) { srcBean.setPropertyValue(propertyName, null); properties.add(propertyName); } } String[] result = new String[properties.size()]; return properties.toArray(result); } /** * 獲取物件的非空屬性 */ public static Map<String, Object> getNotNullProperties(Object src) { BeanWrapper srcBean = new BeanWrapperImpl(src); //1.獲取Bean PropertyDescriptor[] pds = srcBean.getPropertyDescriptors(); //2.獲取Bean的屬性描述 Map<String, Object> properties = new LinkedHashMap<>(); //3.獲取Bean的非空屬性 for (PropertyDescriptor p : pds) { String key = p.getName(); Object value = srcBean.getPropertyValue(key); if (!StringUtils.isEmpty(value) && !"class".equals(key)) { properties.put(key, value); } } return properties; } /** * 將Object陣列轉為實體類VO */ public static <V> List<V> getEntityVo(List<Object[]> propertyArrayList, Class<V> voClass) { List<V> list = new ArrayList<>(); try { if (propertyArrayList != null) { for (Object[] propertyArray : propertyArrayList) { V vo = voClass.newInstance(); Field[] fields = vo.getClass().getDeclaredFields(); for (int i = 0; i < propertyArray.length; i++) { Field voField = fields[i]; Object queryVal = propertyArray[i]; if (voField.getType() == String.class && queryVal instanceof BigDecimal) { queryVal = String.valueOf(queryVal); } voField.setAccessible(true);//獲取授權 voField.set(vo, queryVal); } list.add(vo); } } } catch (Exception e) { throw new RuntimeException(e); } return list; } }

  

  通用service、repository

/**
 * 通用Service
 *
 * @param <V> 實體類Vo
 * @param <E> 實體類
 * @param <T> id主鍵型別
 */
public interface CommonService<V, E,T> {

    Result<PageInfo<V>> page(V entityVo);

    Result<List<V>> list(V entityVo);

    Result<V> get(T id);

    Result<V> save(V entityVo);

    Result<T> delete(T id);
}
/**
 * 通用Service實現類
 *
 * @param <V> 實體類Vo
 * @param <E> 實體類
 * @param <T> id主鍵型別
 */
public class CommonServiceImpl<V, E, T> implements CommonService<V, E, T> {

    private Class<V> entityVoClass;//實體類Vo

    private Class<E> entityClass;//實體類

    @Autowired
    private CommonRepository<E, T> commonRepository;//注入實體類倉庫

    CommonServiceImpl() {
        Type[] types = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments();
        this.entityVoClass = (Class<V>) types[0];
        this.entityClass = (Class<E>) types[1];
    }

    @Override
    public Result<PageInfo<V>> page(V entityVo) {
        //實體類缺失分頁資訊
        if (!(entityVo instanceof PageCondition)) {
            throw new RuntimeException("實體類" + entityClass.getName() + "未繼承PageCondition。");
        }
        PageCondition pageCondition = (PageCondition) entityVo;
        Page<E> page = commonRepository.findAll(Example.of(FastCopy.copy(entityVo, entityClass)), pageCondition.getPageable());
        return Result.of(PageInfo.of(page, entityVoClass));
    }

    @Override
    public Result<List<V>> list(V entityVo) {
        List<E> entityList = commonRepository.findAll(Example.of(FastCopy.copy(entityVo, entityClass)));
        List<V> entityModelList = FastCopy.copyList(entityList, entityVoClass);
        return Result.of(entityModelList);
    }

    @Override
    public Result<V> get(T id) {
        Optional<E> optionalE = commonRepository.findById(id);
        if (!optionalE.isPresent()) {
            throw new RuntimeException("ID不存在!");
        }
        return Result.of(FastCopy.copy(optionalE.get(), entityVoClass));
    }

    @Override
    public Result<V> save(V entityVo) {
        E e = commonRepository.save(FastCopy.copy(entityVo, entityClass));
        return Result.of(FastCopy.copy(e, entityVoClass));
    }

    @Override
    public Result<T> delete(T id) {
        commonRepository.deleteById(id);
        return Result.of(id);
    }
}
/**
 * 通用Repository
 *
 * @param <E> 實體類
 * @param <T> id主鍵型別
 */
@NoRepositoryBean
public interface CommonRepository<E,T> extends JpaRepository<E,T>, JpaSpecificationExecutor<E> {
}

 

  單表使用

  單表繼承通用程式碼,實現get、save(插入/更新)、list、page、delete介面

  Vo

/**
 * 使用者類Vo
 */
@Data
public class UserVo extends PageCondition implements Serializable {

    private Integer id;

    private String username;

    private String password;

    private Date created;

    private String descriptionId;

    //機架型別資訊
    private DescriptionVo description;
}
/**
 * 使用者描述類Vo
 */
@Data
public class DescriptionVo implements Serializable {
    private Integer id;

    private String userId;

    private String description;
}

 

  controller、service、repository

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping("/getAllUser")
    public ModelAndView getAllUser(){
        Result result=userService.getAllUser();
        ModelAndView mv=new ModelAndView();
        mv.addObject("userList",result.getData());
        mv.setViewName("index.html");
        return mv;
    }

    /*
        CRUD、分頁、排序
     */

    @RequestMapping("page")
    public Result<PageInfo<UserVo>> page(UserVo entityVo) {
        return userService.page(entityVo);
    }

    @RequestMapping("list")
    public Result<List<UserVo>> list(UserVo entityVo) {
        return userService.list(entityVo);
    }

    @RequestMapping("get/{id}")
    public Result<UserVo> get(@PathVariable("id") Integer id) {
        return userService.get(id);
    }

    @RequestMapping("save")
    public Result<UserVo> save(UserVo entityVo) {
        return userService.save(entityVo);
    }

    @RequestMapping("delete/{id}")
    public Result<Integer> delete(@PathVariable("id") Integer id) {
        return userService.delete(id);
    }
}
public interface UserService extends CommonService<UserVo, User,Integer>{

    Result getAllUser();
}
@Service
public class UserServiceImpl extends CommonServiceImpl<UserVo, User,Integer> implements UserService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public Result getAllUser() {
        List<User> userList = userRepository.getAllUser();
        if(userList != null && userList.size()>0){
            ArrayList<UserVo> userVos = new ArrayList<>();
            for(User user : userList){
                userVos.add(FastCopy.copy(user, UserVo.class));
            }
            return Result.of(userVos);
        }else {
            return Result.of(userList,false,"獲取失敗!");
        }
    }
}
@Repository
public interface UserRepository extends CommonRepository<User, Integer> {

    @Query(value = "from User") //HQL
//    @Query(value = "select * from tb_user",nativeQuery = true)//原生SQL
    List<User> getAllUser();

}

 

  經測試,所有的介面都可以使用,資料傳輸正常,因為傳輸的Vo,分頁資訊跟雜七雜八的欄位、資料都在Vo,所有看起來會比較雜。更新介面依舊跟上一篇的一樣,接收到的是什麼就儲存什麼。

 

  後記

  單表的增刪改查介面,直接繼承這一套通用程式碼即可實現,無需再重複編寫,大大提升開發效率。