基于Java的泛型和反射实现通用的增删改查(4)——通用增删改查操作的优雅实现

本文探讨如何优雅地实现基于Java的泛型和反射的通用增删改查操作。通过Spring注入避免手动从ApplicationContext获取Service,利用泛型实现依赖注入,简化代码并提高代码规范性。文章提及了BaseAbstractController与BaseAbstractService的改进,并指出了项目目前存在的问题,如条件查询的实现、update操作的安全隐患等,预告后续将深入讲解反射的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、回顾
 在上一节,我们讲到了通用增删改查的简单实现,在简单实现中,获取对应的service或repository时,采用的是从ApplicationContext中手动获取的方式,这应该也是最容易想到的方式,因为只要使用了Spring,那么Spring就会为我们创建相应的实例并将其置于Spring容器中,我们直接从容器中获取当然是可行的。可行但不优雅,而且对于有些方法实现起来也不是很方便。这一节我们说一下怎么优雅的实现通用的增删改查。其实,非常简单,通过Spring注入。
 在这之前,再回顾一下在BaseAbstractController中是怎么获取到对应泛型类的service的:
  1️⃣获取到泛型类对象的简单类名
  2️⃣将简单类名的首字母小写
  3️⃣再将简单类名拼接上特定的"Service"后缀,获取到泛型类对象的相应Service在Spring容器中的bean的id
  4️⃣据bean的id从Spring容器中获取到这个bean,并将其类型转换为BaseService
 通过以上4个步骤我们拿到了对应泛型类的Service,但是如果你不小心将某一个Service的名称写错了,那就获取不到了,所以这种方式不但不够优雅,而且要求规范命名。
 这些问题,Spring已经为我们解决了,其实我们不需要自己从Spring的容器中获取对应泛型类的service或repository,可以直接注入。但是父类怎么区分具体应该使用哪个子类的Service呢,总不能将所有的子类的Service一个一个的注入到BaseAbstractController中吧。当然不用,那怎么办呢?依然是使用泛型,Spring是支持带泛型的依赖注入的,这样我们就可以优雅的实现所有设计的方法了。

二、优雅实现
 BaseAbstractService的优雅实现:

public abstract class BaseAbstractService<T extends BaseEntity> implements BaseService<T> {

    private static final Logger LOGGER = LoggerFactory.getLogger(BaseAbstractService.class);

    /**
     * 在Spring容器中repository对象名称的后缀
     */
//    private static final String REPOSITORY_SUFFIX = "Repository";

    @Autowired
    private BaseRepository<T> baseRepository;


    @Override
    public T saveOrUpdate(T t) {
        return baseRepository.save(t);
    }

    @Override
    public Iterable<T> saveOrUpdateBatch(List<T> list) {
        return baseRepository.save(list);
    }

    @Override
    public void delete(T t) {
        baseRepository.delete(t);
    }

    @Override
    public void deleteBatch(List<T> list) {
        baseRepository.delete(list);
    }

    @Override
    public T findById(T t) {
        return baseRepository.findOne(t.getId());
    }

    @Override
    public T findOne(T t) {
        Iterable<T> byEntity = findByEntity(t);
        if (byEntity != null) {
            return byEntity.iterator().next();
        }
        return null;
    }

    @Override
    public Iterable<T> findByEntity(T t) {
        return null;
    }

    @Override
    public Iterable<T> queryAll(T t) {
        return baseRepository.findAll();
    }

    @Override
    public Page<T> queryByPage(T t) {
        return null;
    }

    /**
     * 从Spring容器中获取对应泛型类的repository
     *
     * @param t
     * @return
     */
//    private BaseRepository<T> getRepository(T t) {
//        String simpleName = t.getClass().getSimpleName();
//        String prefix = StringUtil.firstLetterToLowerCase(simpleName);
//        String beanName = prefix + REPOSITORY_SUFFIX;
//        return (BaseRepository) SpringContextUtil.getBean(beanName);
//    }
}

 注:JPA虽然提供了delete(T t),但其实并不支持没有id的删除操作,这大概是为了安全起见吧。
 BaseAbstractController的优雅实现:

public abstract class BaseAbstractController<T extends BaseEntity> extends BaseController<T> {

    private static final Logger LOGGER = LoggerFactory.getLogger(BaseAbstractController.class);

    /**
     * 在Spring容器中service对象名称的后缀
     */
//    private static final String SERVICE_SUFFIX = "Service";

    @Autowired
    private BaseService<T> baseService;


    @PostMapping("/saveOrUpdate")
    public RespDto saveOrUpdate(T t) {
        try {
            T t1 = baseService.saveOrUpdate(t);
            return success(t1);
        } catch (Exception e) {
            LOGGER.error("saveOrUpdate error!", e);
            return error("操作异常!");
        }
    }

    @PostMapping("/saveOrUpdateBatch")
    public RespDto saveOrUpdateBatch(List<T> list) {
        try {
            Iterable<T> ts = baseService.saveOrUpdateBatch(list);
            return success(ts);
        } catch (Exception e) {
            LOGGER.error("saveOrUpdate error!", e);
            return error("操作异常!");
        }
    }

    @PostMapping("/delete")
    public RespDto delete(T t) {
        try {
            baseService.delete(t);
            return success();
        } catch (Exception e) {
            LOGGER.error("saveOrUpdate error!", e);
            return error("操作异常!");
        }
    }

    @PostMapping("/deleteBatch")
    public RespDto deleteBatch(List<T> list) {
        try {
            baseService.deleteBatch(list);
            return success();
        } catch (Exception e) {
            LOGGER.error("saveOrUpdate error!", e);
            return error("操作异常!");
        }
    }

    @PostMapping("/findById")
    public RespDto findById(T t) {
        try {
            T t1 = baseService.findById(t);
            return success(t1);
        } catch (Exception e) {
            LOGGER.error("saveOrUpdate error!", e);
            return error("操作异常!");
        }
    }

    @PostMapping("/findOne")
    public RespDto findOne(T t) {
        try {
            T t1 = baseService.findOne(t);
            return success(t1);
        } catch (Exception e) {
            LOGGER.error("saveOrUpdate error!", e);
            return error("操作异常!");
        }
    }

    @PostMapping("/findByEntity")
    public RespDto findByEntity(T t) {
        try {
            Iterable<T> byEntity = baseService.findByEntity(t);
            return success(byEntity);
        } catch (Exception e) {
            LOGGER.error("saveOrUpdate error!", e);
            return error("操作异常!");
        }
    }

    @PostMapping("/queryAll")
    public RespDto queryAll(T t) {
        try {
            Iterable<T> ts = baseService.queryAll(t);
            return success(ts);
        } catch (Exception e) {
            LOGGER.error("saveOrUpdate error!", e);
            return error("操作异常!");
        }
    }

    @PostMapping("/queryByPage")
    public RespDto queryByPage(T t) {
        try {
            Page<T> page = baseService.queryByPage(t);
            return success(page);
        } catch (Exception e) {
            LOGGER.error("saveOrUpdate error!", e);
            return error("操作异常!");
        }
    }

    /**
     * 从Spring容器中获取对应泛型类的service
     *
     * @param t
     * @return
     */
//    private BaseService<T> getService(T t) {
//        String simpleName = t.getClass().getSimpleName();
//        String prefix = StringUtil.firstLetterToLowerCase(simpleName);
//        String beanName = prefix + SERVICE_SUFFIX;
//        return (BaseService) SpringContextUtil.getBean(beanName);
//    }
}

三、遗留问题
 从项目搭建开始至此,整个项目的雏形就完成了。但这仅仅是一个雏形,还完全达不到生产使用时的要求。存在的主要问题如下:
 1️⃣BaseAbstractService中的findByEntity和queryByPage没有实现,因为这两个方法通常都是带条件查询的,而JPA的带条件查询实现起来有些复杂,而且我们要想获取到查询条件就必须使用反射,因此我把它放在下一节
 2️⃣由于JPA自身的原因,这里的saveOrUpdate方法在新增时没问题,但是在update时很可能会导致属性值丢失,除非每次都将查询出的对象的所有属性都带着。但即使这样,也会有很大的安全风险,因为update的接口最终是要暴露出来的,倘若有人恶意攻击的话,是很容易导致数据被侵犯的
 要想解决这些问题,就必须要使用反射,反射的使用是这个专栏的核心,也是该专栏最关注的内容,从下一节开始,将会进入到反射相关的讲解。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值