目录
这里我来分享以下参考mybatis-plus的格式来封装neo4j的持久层,和设计出一个服务层
持久层
mp
这里我们先看以下mp持久层的是如何操作的
可以发现mp抽取出了一个通用的BaseMapper接口,并使用泛型T代指对实体类对象的统一操作
java
public interface BaseMapper<T> extends Mapper<T> {
int insert(T entity);
int deleteById(Serializable id);
int deleteById(T entity);
...
}
然后我们就可以创建一个mapper类,并继承自BaseMapper接口,然后泛型指定为PayChannelEntity实体类,这个实体类就是使用@TableName注解与数据表一一对应的实体类
java
@Mapper
public interface PayChannelMapper extends BaseMapper<PayChannelEntity> {
}
总结,mp通过抽取出一个BaseMapper通用的接口,然后让我们自己创建的mapper类去继承BaseMapper,并指定要操作的实体类,这样一来,大多数的CURD操作我们就不用自己实现,并且我们自己创建的mapper类使用@Mapper注解,这个注解可以让Spring扫描并为其创建一个代理对象。
模仿:
1.抽取出通用的接口类
这个类继承Neo4jRepository接口,这个接口有neo4j提供的大多数CURD方法
并且也使用泛型,并且规定这个T泛型必须继承自BaseEntity类,还有另一个泛型ID,表示id属性的类型
这里使用@NoRepositoryBean注解的作用是不用给这个通用接口类生成bean,因为继承Neo4jRepository接口的接口都会被扫描生成代理对象,这里没必要生成,mp是使用了@Mapper注解才会生成代理对象
java
/**
* Repository基本方法的实现
*
*/
@NoRepositoryBean//让这个类不生成bean
public interface BaseRepository<T extends BaseEntity, ID> extends Neo4jRepository<T, ID> {
/**
* 根据业务id查询节点数据
*
* @param bid 业务id
* @return 节点数据
*/
Optional<T> findByBid(Long bid);
/**
* 根据业务id删除节点数据
*
* @param bid 业务id
* @return 删除的数据数量
*/
Long deleteByBid(Long bid);
}
2.创建自定义的repository接口
这里指定泛型为AgencyEntity类,和id属性为Long类
AgencyEntity类是与neo4j中AGENCY标签一一对应的实体类
通过实现getAgencyType()抽象方法来区别不同的机构实体类
java
/**
* 网点数据操作
*/
public interface AgencyRepository extends BaseRepository<AgencyEntity, Long> {
}
@Node("AGENCY")
@Data
@ToString(callSuper = true)
@SuperBuilder(toBuilder = true)
@NoArgsConstructor
public class AgencyEntity extends BaseEntity {
@Override
public OrganTypeEnum getAgencyType() {
return OrganTypeEnum.AGENCY;
}
}
@Data
@SuperBuilder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor
public abstract class BaseEntity {
@Id
@GeneratedValue
@ApiModelProperty(value = "Neo4j ID", hidden = true)
private Long id;
@ApiModelProperty(value = "父节点id", required = true)
private Long parentId;
@ApiModelProperty(value = "业务id", required = true)
private Long bid;
@ApiModelProperty(value = "名称", required = true)
private String name;
@ApiModelProperty(value = "负责人", required = true)
private String managerName;
@ApiModelProperty(value = "电话", required = true)
private String phone;
@ApiModelProperty(value = "地址", required = true)
private String address;
@ApiModelProperty(value = "位置坐标, x: 纬度,y: 经度", required = true)
private Point location;
@ApiModelProperty(value = "是否可用", required = true)
private Boolean status;
@ApiModelProperty(value = "扩展字段,以json格式存储")
private String extra;
//机构类型
public abstract OrganTypeEnum getAgencyType();
}
还有另外两个持久层接口类

服务层
mp
这里看一下mp的服务层实现
可以发现mp的服务层也抽取出了一个通用的服务接口类,也使用了泛型T来代指操作的实体类
java
public interface IService<T> {
int DEFAULT_BATCH_SIZE = 1000;
default boolean save(T entity) {
return SqlHelper.retBool(this.getBaseMapper().insert(entity));
}
@Transactional(
rollbackFor = {Exception.class}
)
default boolean saveBatch(Collection<T> entityList) {
return this.saveBatch(entityList, 1000);
}
boolean saveBatch(Collection<T> entityList, int batchSize);
BaseMapper<T> getBaseMapper();
Class<T> getEntityClass();
...
}
这个接口类是没有实现的,如果我们要自己创建一个服务类去实现这个接口,是十分麻烦的,所以mp提供了一个ServiceImpl类对这个接口进行了实现
这个实现类使用了一个泛型M代指继承了BaseMapper接口的类,还有泛型T代指实体类
这样一来就可以使用M来实现IService接口的所有方法操作了。
java
public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {
protected Log log = LogFactory.getLog(this.getClass());
@Autowired
protected M baseMapper;
protected Class<T> entityClass = this.currentModelClass();
protected Class<M> mapperClass = this.currentMapperClass();
public ServiceImpl() {
}
public M getBaseMapper() {
return this.baseMapper;
}
public Class<T> getEntityClass() {
return this.entityClass;
}
/** @deprecated */
@Deprecated
protected boolean retBool(Integer result) {
return SqlHelper.retBool(result);
}
protected Class<M> currentMapperClass() {
return ReflectionKit.getSuperClassGenericType(this.getClass(), ServiceImpl.class, 0);
}
...
}
自定义的服务接口类,用来拓展自己特有的方法
java
public interface PayChannelService extends IService<PayChannelEntity> {}
自定义的服务类,把对应的mapper接口类型,和需要操作的实体类作为泛型传入
java
@Service
public class PayChannelServiceImpl extends ServiceImpl<PayChannelMapper, PayChannelEntity> implements PayChannelService {
}
模仿:
1.抽取出一个IService通用服务类
使用泛型T代指需要操作的实体类
java
public interface IService<T extends BaseEntity> {
/**
* 根据业务id查询数据
*
* @param bid 业务id
* @return 节点数据
*/
T queryByBid(Long bid);
/**
* 新增节点
*
* @param t 节点数据
* @return 新增的节点数据
*/
T create(T t);
/**
* 更新节点
*
* @param t 节点数据
* @return 更新的节点数据
*/
T update(T t);
/**
* 根据业务id删除数据
*
* @param bid 业务id
* @return 是否删除成功
*/
Boolean deleteByBid(Long bid);
}
2.创建ServiceImpl类实现IService接口
这里使用R泛型代指repostory接口类,这样一来就可以使用R类型的repostory接口来对方法进行实现。
java
/**
* 基础服务的实现
*/
public class ServiceImpl<R extends BaseRepository, T extends BaseEntity> implements IService<T> {
@Autowired
private R repository;
@Override
public T queryByBid(Long bid) {
return (T) this.repository.findByBid(bid).orElse(null);
}
@Override
public T create(T t) {
t.setId(null);//id由neo4j自动生成
return (T) this.repository.save(t);
}
@Override
public T update(T t) {
//先查询,再更新
T tData = this.queryByBid(t.getBid());
if (ObjectUtil.isEmpty(tData)) {
return null;
}
BeanUtil.copyProperties(t, tData, CopyOptions.create().ignoreNullValue().setIgnoreProperties("id", "bid"));
return (T) this.repository.save(tData);
}
@Override
public Boolean deleteByBid(Long bid) {
return this.repository.deleteByBid(bid) > 0;
}
}
3.自定义的服务接口
java
public interface AgencyService extends IService<AgencyEntity> {
}
public interface OLTService extends IService<OLTEntity> {
}
public interface TLTService extends IService<TLTEntity> {
}
4.创建自定义的服务类
创建实现类,并把这个类都交给spring管理
java
@Service
public class AgencyServiceImpl extends ServiceImpl<AgencyRepository, AgencyEntity> implements AgencyService {
}
@Service
public class TLTServiceImpl extends ServiceImpl<TLTRepository, TLTEntity>
implements TLTService {
}
@Service
public class OLTServiceImpl extends ServiceImpl<OLTRepository, OLTEntity>
implements OLTService {
}
工厂模式
我们可以使用工厂模式来动态的生成不同的服务实现类供我们使用
为什么可以使用工厂模式
因为所有的服务类都实现了IService这个通用的服务接口,这样一来就可以使用父类引用指向子类对象的多态思想
如何使用工厂模式
我们可以创建一个工厂类,还有一个枚举类,代替不同的机构类型,然后再工厂类的静态方法中根据不同的枚举类的code值来创建对应的服务类并返回。
枚举类
java
public enum OrganTypeEnum implements BaseEnum {
OLT(1, "一级转运中心"),
TLT(2, "二级转运中心"),
AGENCY(3, "网点");
/**
* 类型编码
*/
private final Integer code;
/**
* 类型值
*/
private final String value;
OrganTypeEnum(Integer code, String value) {
this.code = code;
this.value = value;
}
public Integer getCode() {
return code;
}
public String getValue() {
return value;
}
public static OrganTypeEnum codeOf(Integer code) {
return EnumUtil.getBy(OrganTypeEnum::getCode, code);
}
}
工厂类
第一种方式:使用switch方式来判断要返回哪一种服务类型
根据传入的Integer类型数据,使用枚举类的codeOf方法,创建对应的枚举类对象
然后使用hutool的SpringUtil获取对应服务类的实例bean对象并返回
java
/**
* 根据type选择对应的service返回
*
* @author zzj
* @version 1.0
*/
public class OrganServiceFactory {
public static IService getBean(Integer type) {
OrganTypeEnum organTypeEnum = OrganTypeEnum.codeOf(type);
if (null == organTypeEnum) {
throw new SLException(ExceptionEnum.ORGAN_TYPE_ERROR);
}
switch (organTypeEnum) {
case AGENCY: {
return SpringUtil.getBean(AgencyService.class);
}
case OLT: {
return SpringUtil.getBean(OLTService.class);
}
case TLT: {
return SpringUtil.getBean(TLTService.class);
}
}
return null;
}
}
第二种:通过反射来判断
以上方法我们要通过switch语句来判断是要返回哪一种类型的服务类,那如果枚举类型很多的话,这样是不是冗杂
定义一个接口,在接口里定义能返回对应机构枚举类型的方法
java
public interface OrganTypeService {
/**
* 获取对应的机构枚举类型
*/
OrganTypeEnum getOrganTypeEnum();
}
然后自定义的服务接口都继承这个接口
java
public interface AgencyService extends IService<AgencyEntity> ,OrganTypeService{
}
public interface OLTService extends IService<OLTEntity> ,OrganTypeService{
}
public interface TLTService extends IService<TLTEntity>,OrganTypeService{
}
服务类实现这个方法,分别返回对应的枚举类型
java
@Service
public class AgencyServiceImpl extends ServiceImpl<AgencyRepository, AgencyEntity> implements AgencyService {
@Override
public OrganTypeEnum getOrganTypeEnum() {
return OrganTypeEnum.AGENCY;
}
}
@Service
public class OLTServiceImpl extends ServiceImpl<OLTRepository, OLTEntity>
implements OLTService {
@Override
public OrganTypeEnum getOrganTypeEnum() {
return OrganTypeEnum.OLT;
}
}
@Service
public class TLTServiceImpl extends ServiceImpl<TLTRepository, TLTEntity>
implements TLTService {
@Override
public OrganTypeEnum getOrganTypeEnum() {
return OrganTypeEnum.TLT;
}
}
方法实现
java
public class OrganServiceFactory {
public static IService getBean(Integer type) {
OrganTypeEnum organTypeEnum = OrganTypeEnum.codeOf(type);
if (null == organTypeEnum) {
throw new SLException(ExceptionEnum.ORGAN_TYPE_ERROR);
}
//先从容器中获取所有的IService类型的bean实例对象
Map<String, IService> beans = SpringUtil.getBeansOfType(IService.class);
for (Map.Entry<String, IService> entry : beans.entrySet()) {
//通过反射执行getOrganTypeEnum()方法
Object invoke = ReflectUtil.invoke(entry.getValue(), "getOrganTypeEnum");
//如果类型一致就返回对应的服务类实例
if(ObjectUtil.equal(invoke,organTypeEnum)){
return entry.getValue();
}
}
return null;
}
}
对比:
第一种方式不用写额外的方法来返回对应的枚举类型,但是需要在工厂的静态方法里来判断枚举类型,如果枚举类型少而且不用经常改变代码,就可以使用这种方式
第二种方式需要写额外的方法来返回对应的枚举类型,但是在工厂方法里就可以使用反射直接执行获取枚举类型的方法来动态获取对应的服务类型。
使用工厂模式
java
@SpringBootTest
public class Test1 {
@Test
public void testo1(){
IService iService = OrganServiceFactory.getBean(1);
System.out.println(iService);
}
}
成功返回OLT类型的服务实现类

如果不使用工厂模式,我们就要提前注入这三种类型的服务bean,这样是不是很冗余呢,然后还需要根据需要操作的实体类的不同来判断使用哪一个服务类,十分麻烦
如
if(type==1) 使用oltService来操作
else if(type==2) ...
java
@Resource
OLTService oltService;
@Resource
TLTService tltService;
@Resource
AgencyService agencyService;