一、回顾
在上一节,我们讲到了通用增删改查的简单实现,在简单实现中,获取对应的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的接口最终是要暴露出来的,倘若有人恶意攻击的话,是很容易导致数据被侵犯的
要想解决这些问题,就必须要使用反射,反射的使用是这个专栏的核心,也是该专栏最关注的内容,从下一节开始,将会进入到反射相关的讲解。