MongoDB通用持久类MongoBaseDao

MongoDB通用持久类MongoBaseDao

MongoTemplate已经给我们提供了丰富的api,为什么还需要通用持久层类?
第一,比较复杂的持久化方法(如:批量更新、批量删除),MongoTemplate没有开箱即用的api,是需要我们自定义的。
第二,定义通用持久层类支持泛型操作,可以简化整个持久层的代码量,对于简单的业务场景来说,下列持久类MongoBaseDao就可以满足要求。

public class MongoBaseDao<T extends Serializable & BaseEntity<ID>, ID> {
    private static final String _id = "_id";
    private static final String id = "id";

    @Autowired
    private MongoClient mongoClient;
	
	// 获取MongoDB操作模板对象
    public MongoTemplate getTemplate(String database) {
        return new MongoTemplate(mongoClient, database);
    }
	
	// 批量更新操作(如果id存在执行更新,如果id不存在执行插入)
    public void upsert(List<T> list, Class<T> tClass, String database){
        BulkOperations operations = getTemplate(database).bulkOps(BulkOperations.BulkMode.UNORDERED, tClass);
        operations.upsert(getPairList(list));
        operations.execute();
    }

    private List<Pair<Query, Update>> getPairList(List<T> list) {
        List<Pair<Query, Update>> pairList = new LinkedList<>();
        for (T t : list) {
            Pair<Query, Update> pair = getPair(t);
            pairList.add(pair);
        }
        return pairList;
    }

    @SneakyThrows
    private Pair<Query, Update> getPair(T t) {
        Query query = new Query();
        Update update = new Update();
        ObjectMapper objectMapper = new ObjectMapper();
        String json = objectMapper.writeValueAsString(t);
        Document document = objectMapper.readValue(json, Document.class);
        // 这里_id和id的使用注意区分,实体类定义的主键为id,Mongodb的主键为_id
        query.addCriteria(Criteria.where(_id).is(document.remove(id)));
        // 这里更新了除_id之外所有的字段,适合于先查询后更新的应用场景
        document.forEach(update::set);
        // 如果是未查询、仅更新某些字段,应排除你未设置值(为null)的其他字段,这时候需使用下列forEach函数
        /*document.forEach((key, value)->{
            if(ObjectUtils.isNotEmpty(value)){
                update.set(key, value);
            }
        });*/
        return Pair.of(query, update);
    }
	
	// 批量删除,适用于先查询后删除的使用场景
    public void remove(List<T> list, Class<T> tClass, String database){
        BulkOperations operations = getTemplate(database).bulkOps(BulkOperations.BulkMode.UNORDERED, tClass);
        List<Query> queryList = new LinkedList<>();
        for (T t : list) {
            queryList.add(Query.query(Criteria.where(_id).is(t.getId())));
        }
        operations.remove(queryList);
        operations.execute();
    }
	
	// 根据Id单个删除
    public void removeById(ID id, Class<T> tClass, String database){
        getTemplate(database).remove(Query.query(Criteria.where(_id).is(id)), tClass);
    }
	
	// 根据Id批量删除,适用于未查询就删除的使用场景
    public void removeById(List<ID> list, Class<T> tClass, String database){
        BulkOperations operations = getTemplate(database).bulkOps(BulkOperations.BulkMode.UNORDERED, tClass);
        List<Query> queryList = new LinkedList<>();
        for (ID id : list) {
            queryList.add(Query.query(Criteria.where(_id).is(id)));
        }
        operations.remove(queryList);
        operations.execute();
    }
	
	// 根据Id 批量查询
    public List<T> findById(List<ID> list, Class<T> tClass, String database){
        return getTemplate(database).find(Query.query(Criteria.where(_id).in(list)), tClass);
    }
}

以上api是MongoTemplate没有提供的,定义为持久层通用类后,其他持久类只需继承它即可。列如:

@Repository
public class UserDao extends MongoBaseDao<User, String> {
}

你没看错,其他持久类一般不用定义任何方法,因为MongoTemplate提供的api与MongoBaseDao提供的api已基本够用。
如果要使用MongoTemplate原生的api,使用userDao.getTemplate(database).remove(Object)等。
如果要使用MongoBaseDao自定义api,使用userDao.remove(list, Object.class, database)等。

你可能会说,要是我的业务非常复杂,需要一些其他的Dao方法呢?这个时候的确可以在诸如UserDao的持久类中定义一些其他的方法。但个人建议,既然是业务的复杂度导致的问题,新的方法应该定义在Service层而不是Dao层,Dao层只负责执行增删改查,不负责处理复杂业务,持久类可以继续保持空类状态,业务处理逻辑交给业务类。

类似的,实体类也可以定义一个通用接口。你也许已经注意到MongoBaseDao调用了BaseEntity,它用于统一约定实体类主键字段,从而支持泛型对象获取主键值,即支持批量删除方法中调用t.getId()的实现。
如下,通用实体接口BaseEntity,以及它的实现类User。

public interface BaseEntity<ID> {
    ID getId();
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable, BaseEntity<String> {
    @Id
    private String id;
    private String name;
    private Integer age;
}

最后,给出MongoClient的配置。你肯定已经注意到MongoBaseDao支持database数据库名传参获取对应的MongoTemplate对象。使用下面的配置类,SpringBoot中央配置文件便可以不用配置任何参数,实现数据库切换操作。当然,前提是你的Mongo数据库提前做好了权限设置。最简单的方式便是禁用MongoDB权限认证,具体设置方式因MongoDB安装方式的不同而不同,此处不再赘述。

@Configuration
public class MongoConfig {
    @Bean
    public MongoClient mongoClient() {
        return MongoClients.create("mongodb://localhost:27017");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值