前言

  在上篇博客中:SpringBoot系列——Spring-Data-JPA:
https://www.cnblogs.com/huanzi-qch/p/9970545.html
<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; } }
  注:BeanMap类引入的是:org.apache.commons.beanutils.BeanMap;

  引入这两个jar
<!-- Vo与实体的转换工具类需要用到 --> <dependency> <groupId>commons-beanutils</groupId> <
artifactId>commons-beanutils</artifactId> <version>1.8.0</version> </dependency>
<dependency> <groupId>commons-collections</groupId> <artifactId>
commons-collections</artifactId> <version>3.2.2</version> </dependency>
 

  

 

  通用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;//注入实体类仓库 public 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)); } @Overridepublic 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)); } @Overridepublic
Result<V> save(V entityVo) { E e =
commonRepository.save(FastCopy.copy(entityVo, entityClass));return
Result.of(FastCopy.copy(e, entityVoClass)); } @Overridepublic 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> { }
   2019-05-13更新

    jpa实现局部更新

  注意:
jpa原生的save方法,更新的时候是全属性进行updata,如果实体类的属性没有值它会帮你更新成null,如果你想更新部分字段请在通用CommonServiceImpl使用这个save方法,我这里是在调用save之前先查询数据库获取完整对象,将要更新的值复制到最终传入save方法的对象中,从而实现局部更新


 
@Override public Result<V> save(V entityVo) { //传进来的对象(属性可能残缺) E entity =
CopyUtil.copy(entityVo, entityClass);//最终要保存的对象 E entityFull = entity; //
为空的属性值,忽略属性,BeanUtils复制的时候用到 List<String> ignoreProperties = new
ArrayList<String>(); //获取最新数据,解决部分更新时jpa其他字段设置null问题 try { //
反射获取Class的属性(Field表示类中的成员变量) for (Field field :
entity.getClass().getDeclaredFields()) {//获取授权 field.setAccessible(true); //属性名称
String fieldName = field.getName(); //属性的值 Object fieldValue =
field.get(entity);//找出Id主键 if (field.isAnnotationPresent(Id.class) && !
StringUtils.isEmpty(fieldValue)) { Optional<E> one =
commonRepository.findById((T) fieldValue);if (one.isPresent()) { entityFull =
one.get(); } }//找出值为空的属性,值为空则为忽略属性,我们复制的时候不进行赋值 if(null == fieldValue){
ignoreProperties.add(fieldName); } }/* org.springframework.beans
BeanUtils.copyProperties(A,B); 是A中的值付给B org.apache.commons.beanutils;
BeanUtils.copyProperties(A,B);是B中的值付给A 把entity的值赋给entityFull,第三个参数是忽略属性,表示不进行赋值
*/ BeanUtils.copyProperties(entity, entityFull, ignoreProperties.toArray(new
String[0])); } catch (IllegalAccessException e) { e.printStackTrace(); } E e =
commonRepository.save(entityFull);return Result.of(CopyUtil.copy(e,
entityVoClass)); }
 

 

 

  单表使用

  单表继承通用代码,实现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 {
@Autowiredprivate 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
@Transactional 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,所有看起来会比较杂。更新接口依旧跟上一篇的一样,接收到的是什么就保存什么。

 

  后记

  单表的增删改查接口,直接继承这一套通用代码即可实现,无需再重复编写,大大提升开发效率。

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信