前言
本文主要总结于如下的官方文档的部分内容,如有翻译不当理解不当的地方,欢迎留言指正
Spring官方文档
Spring JDBC官方文档
Spring MongoDB官方文档
JPA
JAVA Persistence API,java持久化的api
JPA的主要目标之一就是提供更加简单的编程模型:在JPA框架下创建实体和创建Java 类一样简单,没有任何的约束和限制,只需要使用 javax.persistence.Entity进行注释,JPA的框架和接口也都非常简单,没有太多特别的规则和设计模式的要求,开发者可以很容易地掌握。JPA基于非侵入式原则设计,因此可以很容易地和其它框架或者容器集成。
MongoDB与JDBC
mongoDB和JDBC(JAVA DATABASE CONNECTIVITY)最顶级的抽象类CrudRepository有很多相似的地方,下面就来看看CrudRepository和MongoRepository的对比,根据源码来看看两者之间的关系
MongoRepository
@NoRepositoryBean
public interface MongoRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
@Override
<S extends T> List<S> saveAll(Iterable<S> entites);
@Override
List<T> findAll();
@Override
List<T> findAll(Sort sort);
<S extends T> S insert(S entity);
<S extends T> List<S> insert(Iterable<S> entities);
@Override
<S extends T> List<S> findAll(Example<S> example);
@Override
<S extends T> List<S> findAll(Example<S> example, Sort sort);
}
CrudRepository
public interface CrudRepository<T, ID extends Serializable>
extends Repository<T, ID> {
<S extends T> S save(S entity);
Optional<T> findById(ID primaryKey);
Iterable<T> findAll();
long count();
void delete(T entity);
boolean existsById(ID primaryKey);
// … more functionality omitted.
}
两者的接口函数真的很相似
如果继续往上追根溯源,我们会发现:
public interface MongoRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T>
MongoRepository继承了PagingAndSortingRepository这个接口,这个接口主要是用来实现查询的分页的功能,点进去再仔细一看,就会发现:
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID>
这个时候,就会发现MongoRepository的最上层,就是CrudRepository,由此可见,CrudRepository就是MongoRepository的最上层的抽象类,因此两者之间有这么多相似的地方也就没有什么奇怪了。但是因为MongoDb是一种非关系型的数据库,因此实现的方式才会有所区别。
CrudRepository派生的查询(Derive的 Query)
在CrudRepository中有派生的count以及派生的delete等其他的查询,这样可以通过自定义的驼峰的形式来进行的CRUD等操作。
Derived Count Query
interface UserRepository extends CrudRepository<User, Long> {
long countByLastname(String lastname);
}
Derived Delete Query
interface UserRepository extends CrudRepository<User, Long> {
long deleteByLastname(String lastname);
List<User> removeByLastname(String lastname);
}
MongoRepository中派生的查询
既然MongoRepository是CrudRepository的派生类,那么应该也可以实现相应的派生的自定义的CRUD操作
这个Repository proxy有两种方式来派生出来查询的query
使用Name来生成Query
By deriving the query from the method name directly.
例子
public interface CustomProjectRepo extends MongoRepository<CustomProject,Integer> {
List<CustomProject> findByIdInAndNameRegex(List<Integer> ids, String name);
List<CustomProject> findByIdIn(List<Integer> ids);
List<CustomProject> findByName(String name);
Optional<CustomProject> findByPathWithNamespace(String pathWithNamespace);
List<CustomProject> findByNamespaceIdIn(List<Integer> groupIds);
}
无需自己另外实现,函数会自行的解析,重上到下的依次实现的功能:
- id在ids中,并且名字符合name的正则的的CustomProject
- id在ids中的CustomProject
- 找到对应name的CustomProject
- 找到指定pathWithNamespace的CustomProject
- 找到namespaceId匹配groupIds中的CustomProject
基本用法
这种机制来创造MongoDB Query,很方便,只需要相应的前缀加上相应的名字,使用java标准的命名方式(驼峰的方式),该机制就会自动的开始解析:
- find…By
- read…By
- query…By
- get…By
除此之外,这些语句还能够包含其他更加深层次的表达式,比如:
- Distinct 去重
- IgnoreCase 无视大小写
- OrderBy…Asc 按照某个属性升序
- OrderBy…Desc 按照某个属性降序
- Between 在…之间
- LessThan 少于
- GreaterThan 多于
- Like 模糊查询
第一个By是当做最基础的查询语句的基础,除此之外,还可以使用And和Or来连接表达式
歧义处理
List<Person> findByAddressZipCode(ZipCode zipCode);
可能会被解析成AddressZipCode当成一个字段,AddressZip Code,也有可能Address ZipCode.为了解决这种歧义,MongoRepository使用了下划线来手动设置
List<Person> findByAddress_ZipCode(ZipCode zipCode);
这也是为什么下划线被设置成了保留字,而推荐用户使用标准java命名方式的原因
特殊参数处理
可以传入特殊的参数,比如Pageable和sort,可以动态的设置分页和排序,例子:
Page<User> findByLastname(String lastname, Pageable pageable);
Slice<User> findByLastname(String lastname, Pageable pageable);
List<User> findByLastname(String lastname, Sort sort);
List<User> findByLastname(String lastname, Pageable pageable);
第一种返回page的方式在查询的结果集比较庞大的时候,代价也就会特别大,因此可以选择返回Slice,Slice可以知道下一个slice是否是有用的,能够在大的数量级下产生作用。
限制结果集的数量
就像在数据查询的时候,可以查第一条或者若干条和最上面的一条或者若干条,MongoDBRepository也可以实现这个功能,同时也可以实现分页和排序的功能
User findFirstByOrderByLastnameAsc();
User findTopByOrderByAgeDesc();
Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);
Slice<User> findTop3ByLastname(String lastname, Pageable pageable);
List<User> findFirst10ByLastname(String lastname, Sort sort);
List<User> findTop10ByLastname(String lastname, Pageable pageable);
手动定义来生成Query
This section covers repository customization and how fragments form a composite repository.
当需要的方法不能直接通过派生的机制直接派生的时候,就需要自己手动定义了,步骤如下
定义自定义接口
interface CustomizedUserRepository {
void someCustomMethod(User user);
}
实现自定义接口中的函数
class CustomizedUserRepositoryImpl implements CustomizedUserRepository {
public void someCustomMethod(User user) {
// Your custom implementation
}
}
原始库的修改
集成自定义的接口即可
interface UserRepository extends CrudRepository<User, Long>, CustomizedUserRepository {
// Declare query methods here
}
总结
还有MongoTemplate的方式来实现各种Query,但是由于工作中遇到过了一次bug,mongo中的其中一个库的数据突然不见了,由于mongo没有什么特定的sql语句可以查询,而且使用的是mongoTemplate,出现的地方很多,排查bug的时候耗费了很大的精力。从那之后,前辈就推荐使用repo的方式来进行对mongoDB的Crud操作了。