spring data mongodb学习以及为repository提供可扩展的自定义方法
Spring Data 概述
Spring Data : Spring 的一个子项目。用于简化数据库访问,支持NoSQL 和 关系数据存储。其主要目标是使数据库的访问变得方便快捷。
SpringData 项目所支持 NoSQL 存储:
MongoDB (文档数据库)
Neo4j(图形数据库)
Redis(键/值存储)
Hbase(列族数据库)
SpringData 项目所支持的关系数据存储技术:
JDBC
JPA
Spring Data mongodb 概述
Spring Data mongodb : 致力于减少数据访问层 (DAO) 的开发量. 开发者唯一要做的,就只是声明持久层的接口,其他都交给 Spring Data mongodb 来帮你完成!
框架怎么可能代替开发者实现业务逻辑呢?比如:当有一个 customerRepository.findByNameAndAddressNumberAndAccountsAccountName(name, number,accountName) 这样一个方法声明,大致应该能判断出这是根据给定条件 查询出满足条件的 User 对象。Spring Data mongodb 做的便是规范方法的名字,根据符合规范的名字来确定方法需要实现什么样的逻辑。
使用 Spring Data JPA 进行持久层开发需要的四个步骤:
1、配置 Spring 整合 Mongodb
package com.dhb.springmvc.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
/**
* Created by ${denghb} on 2016/7/31.
*/
public class DhbWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { RootConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
// return new Class<?>[0];
return new Class [] { WebConfig.class, C3P0DataSourceBuilder.class, MongodbConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
2、让 Spring 为声明的接口创建代理对象。Spring 初始化容器时将会扫描 base-package 指定的包目录及其子目录,为继承 Repository 或其子接口的接口创建代理对象,并将代理对象注册为 Spring Bean,业务层便可以通过 Spring 自动封装的特性来直接使用该对象。
package com.dhb.springmvc.config;
import com.dhb.springmvc.base.support.CustomMongoRepositoryFactoryBean;
import com.mongodb.Mongo;
import com.mongodb.MongoClient;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
import org.springframework.data.mongodb.config.EnableMongoAuditing;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
/**
* Created by ${denghb} on 2016/8/5.
*/
@EnableMongoRepositories(
basePackages = {"com.dhb.springmvc"},
repositoryFactoryBeanClass = CustomMongoRepositoryFactoryBean.class
)
@EnableMongoAuditing
public class MongodbConfig extends AbstractMongoConfiguration {
@Override
protected String getDatabaseName() {
return "business";
}
@Override
public Mongo mongo() throws Exception {
return new MongoClient("127.0.0.1");
}
}
在这里没有配置MongoTemplate,但是在后续的repository里面我们却可以注入进来,原因是继承了AbstractMongoConfiguration ,该抽象类对其进行了实现。
MongoTemplate是数据库和代码之间的接口,对数据库的操作都在它里面。
注:MongoTemplate是线程安全的。
MongoTemplate实现了interface MongoOperations。
MongoDB documents和domain classes之间的映射关系是通过实现了MongoConverter这个interface的类来实现的。
MongoTemplate提供了非常多的操作MongoDB的方法。 它是线程安全的,可以在多线程的情况下使用。
MongoTemplate实现了MongoOperations接口, 此接口定义了众多的操作方法如”find”, “findAndModify”, “findOne”, “insert”, “remove”, “save”, “update” and “updateMulti”等。
它转换domain object为DBObject,并提供了Query, Criteria, and Update等流式API。
缺省转换类为MongoMappingConverter。
3、声明持久层的接口,该接口继承 Repository
Repository 是一个标记型接口,它不包含任何方法,如必要,Spring Data 可实现 Repository 其他子接口,其中定义了一些常用的增删改查,以及分页相关的方法。
在接口中声明需要的方法
package com.dhb.springmvc.base.repository;
import com.dhb.springmvc.base.entity.BaseEntity;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.repository.NoRepositoryBean;
import java.io.Serializable;
/**
* Created by ${denghb} on 2016/8/5.
*/
@NoRepositoryBean
public interface BaseRepository<T extends BaseEntity, ID extends Serializable>
extends MongoRepository<T, ID>, BaseRepositoryEnhance<T, ID> {
}
下面是可扩张的repository
package com.dhb.springmvc.base.repository;
import com.dhb.springmvc.base.entity.BaseEntity;
import java.io.Serializable;
/**
* Created by ${denghb} on 2016/8/5.
*/
public interface BaseRepositoryEnhance<T extends BaseEntity, ID extends Serializable> {
T softDelete(ID id);
}
package com.dhb.springmvc.base.repository.impl;
import com.dhb.springmvc.base.entity.BaseEntity;
import com.dhb.springmvc.base.repository.BaseRepositoryEnhance;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
import org.springframework.data.mongodb.repository.support.SimpleMongoRepository;
import java.io.Serializable;
/**
* Created by ${denghb} on 2016/8/5.
*/
public class BaseRepositoryImpl<T extends BaseEntity, ID extends Serializable>
extends SimpleMongoRepository<T, ID>
implements BaseRepositoryEnhance<T, ID> {
private final MongoOperations mongoOperations;
public BaseRepositoryImpl(MongoEntityInformation<T, ID> metadata, MongoOperations mongoOperations) {
super(metadata, mongoOperations);
this.mongoOperations = mongoOperations;
}
@Override
public T softDelete(ID id) {
return null;
}
}
public BaseRepositoryImpl(MongoEntityInformation<T, ID> metadata, MongoOperations mongoOperations) {
super(metadata, mongoOperations);
this.mongoOperations = mongoOperations;
}
这段代码必须实现,因为父类有一个有参的构造方法,没有无参的构造方法。
父类没有无参构造函数时,子类继承时,构造函数中必须显式调用父类构造方法,并且传递对应所需要的参数。 一个类如果显式的定义了带参构造函数,那么默认无参构造函数自动失效 。
4、Spring Data 将根据给定的策略(具体策略稍后讲解)来为其生成实现代码。
package com.dhb.springmvc.base.support;
import com.dhb.springmvc.base.entity.BaseEntity;
import com.dhb.springmvc.base.repository.impl.BaseRepositoryImpl;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
import org.springframework.data.mongodb.repository.support.MongoRepositoryFactory;
import org.springframework.data.mongodb.repository.support.MongoRepositoryFactoryBean;
import org.springframework.data.mongodb.repository.support.QueryDslMongoRepository;
import org.springframework.data.querydsl.QueryDslPredicateExecutor;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import java.io.Serializable;
import static org.springframework.data.querydsl.QueryDslUtils.QUERY_DSL_PRESENT;
/**
* 用于生成自扩展的Repository方法,比如softDelete
* Created by ${denghb} on 2016/8/5.
*/
public class CustomMongoRepositoryFactoryBean<T extends MongoRepository<S, ID>, S extends BaseEntity, ID extends Serializable>
extends MongoRepositoryFactoryBean<T, S, ID> {
@Override
protected RepositoryFactorySupport getFactoryInstance(MongoOperations operations) {
return new LCRRepositoryFactory(operations);
}
private static class LCRRepositoryFactory<S extends BaseEntity, ID extends Serializable> extends MongoRepositoryFactory {
private final MongoOperations mongoOperations;
public LCRRepositoryFactory(MongoOperations mongoOperations) {
super(mongoOperations);
this.mongoOperations = mongoOperations;
}
@Override
protected Object getTargetRepository(RepositoryMetadata metadata) {
Class<?> repositoryInterface = metadata.getRepositoryInterface();
MongoEntityInformation<?, Serializable> entityInformation = getEntityInformation(metadata.getDomainType());
if (isQueryDslRepository(repositoryInterface)) {
return new QueryDslMongoRepository(entityInformation, mongoOperations);
} else {
return new BaseRepositoryImpl<S, ID>((MongoEntityInformation<S, ID>) entityInformation, this.mongoOperations);
}
}
private static boolean isQueryDslRepository(Class<?> repositoryInterface) {
return QUERY_DSL_PRESENT && QueryDslPredicateExecutor.class.isAssignableFrom(repositoryInterface);
}
@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return isQueryDslRepository(metadata.getRepositoryInterface()) ? QueryDslMongoRepository.class
: BaseRepositoryImpl.class;
}
}
}
Repository 接口概述
Repository 接口是 Spring Data 的一个核心接口,它不提供任何方法,开发者需要在自己定义的接口中声明需要的方法
public interface Repository<T, ID extends Serializable> { }
Spring Data可以让我们只定义接口,只要遵循 Spring Data的规范,就无需写实现类。
与继承 Repository 等价的一种方式,就是在持久层接口上使用 @RepositoryDefinition 注解,并为其指定 domainClass 和 idClass 属性。如下两种方式是完全等价的
Repository 的子接口
基础的 Repository 提供了最基本的数据访问功能,其几个子接口则扩展了一些功能。它们的继承关系如下:
Repository: 仅仅是一个标识,表明任何继承它的均为仓库接口类
CrudRepository: 继承 Repository,实现了一组 CRUD 相关的方法
PagingAndSortingRepository: 继承 CrudRepository,实现了一组分页排序相关的方法
MongoRepository: 继承 PagingAndSortingRepository,实现一组 mongodb规范相关的方法
自定义的 XxxxRepository 需要继承 MongoRepository,这样的 XxxxRepository 接口就具备了通用的数据访问控制层的能力。
entity实体类:
package com.dhb.springmvc.entity;
import com.dhb.springmvc.base.entity.BaseEntity;
import com.dhb.springmvc.entity.support.Account;
import com.dhb.springmvc.entity.support.Address;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.List;
/**
* Created by ${denghb} on 2016/8/5.
*/
@Document
public class Customer extends BaseEntity {
private String name;
private List<Account> accounts;
private Address address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Account> getAccounts() {
return accounts;
}
public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
package com.dhb.springmvc.entity.support;
import com.dhb.springmvc.base.entity.BaseEntity;
/**
* Created by ${denghb} on 2016/8/5.
*/
public class Account extends BaseEntity {
private String accountName;
public String getAccountName() {
return accountName;
}
public void setAccountName(String accountName) {
this.accountName = accountName;
}
}
package com.dhb.springmvc.entity.support;
import com.dhb.springmvc.base.entity.BaseEntity;
/**
* Created by ${denghb} on 2016/8/5.
*/
public class Address extends BaseEntity {
private String number;
private String street;
private String town;
private String postcode;
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getTown() {
return town;
}
public void setTown(String town) {
this.town = town;
}
public String getPostcode() {
return postcode;
}
public void setPostcode(String postcode) {
this.postcode = postcode;
}
}
repository类:
package com.dhb.springmvc.repository;
import com.dhb.springmvc.entity.Customer;
import java.util.List;
/**
* Created by ${denghb} on 2016/8/8.
*/
public interface CustomerRepositoryEnhance {
public List<Customer> search(String keyword, String direction, String sort, int page, int size);
}
package com.dhb.springmvc.repository.impl;
import com.dhb.springmvc.entity.Customer;
import com.dhb.springmvc.repository.CustomerRepositoryEnhance;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import javax.annotation.Resource;
import java.util.List;
/**
* Created by ${denghb} on 2016/8/8.
*/
public class CustomerRepositoryImpl implements CustomerRepositoryEnhance {
@Resource
private MongoTemplate mongoTemplate;
@Override
public List<Customer> search(String keyword, String direction, String sort, int page, int size) {
Query query = new Query();
Criteria c = new Criteria();
query.addCriteria(Criteria.where("name").is(keyword));
query.with(new Sort(Sort.Direction.valueOf(direction), sort));
query.with(new PageRequest(page - 1, size));
return mongoTemplate.find(query, Customer.class);
}
}
package com.dhb.springmvc.repository;
import com.dhb.springmvc.base.repository.BaseRepository;
import com.dhb.springmvc.entity.Customer;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* Created by ${denghb} on 2016/8/5.
*/
@Repository
public interface CustomerRepository extends BaseRepository<Customer, String>, CustomerRepositoryEnhance {
List<Customer> findByNameAndAddressNumberAndAccountsAccountName(
String name, String number, String accountName);
}
service类:
package com.dhb.springmvc.service;
import com.dhb.springmvc.entity.Customer;
import com.dhb.springmvc.repository.CustomerRepository;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* Created by ${denghb} on 2016/8/5.
*/
@Service
public class CustomerService {
@Resource
private CustomerRepository customerRepository;
public void insertCustomer(Customer customer) {
customerRepository.save(customer);
}
public List<Customer> findAllCustomers() {
return customerRepository.findAll();
}
public void dropCustomerCollection() {
customerRepository.deleteAll();
}
public List<Customer> findByNameAndAddressNumberAndAccountsAccountName(String name, String number, String accountName) {
return customerRepository.findByNameAndAddressNumberAndAccountsAccountName(name, number,accountName);
}
public List<Customer> search(String keyword, String direction, String sort, int page, int size) {
return customerRepository.search(keyword, direction, sort, page, size);
}
}
restController类:
package com.dhb.springmvc.controller;
import com.dhb.springmvc.entity.Customer;
import com.dhb.springmvc.service.CustomerService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
* Created by ${denghb} on 2016/8/5.
*/
@RestController
@RequestMapping(value = "/v0.1/customer")
public class CustomerController {
@Resource
private CustomerService customerService;
@RequestMapping(value = "/get_all", method = RequestMethod.GET)
public Object findAllCustomerDetail() {
return customerService.findAllCustomers();
}
@RequestMapping(value = "/get_by/{name}/{number}/{accountName}", method = RequestMethod.GET)
public Object findByNameAndAddressNumberAndAccountsAccountName(@PathVariable String name, @PathVariable String number, @PathVariable String accountName) {
return customerService.findByNameAndAddressNumberAndAccountsAccountName(name, number, accountName);
}
@RequestMapping(value = "/search_by", method = RequestMethod.GET)
public List<Customer> search(@RequestParam(value= "query", defaultValue = "") String keyword,
@RequestParam(value= "direction", defaultValue = "DESC") String direction,
@RequestParam(value = "sort", defaultValue = "name") String sort,
@RequestParam(value = "page", defaultValue = "1") int page,
@RequestParam(value = "size", defaultValue = "30") int size) {
return customerService.search(keyword, direction, sort, page, size);
}
}
1、为某一个 Repository 上添加自定义方法
1)定义一个接口: 声明要添加的自实现的方法
2)提供该接口的实现类: 类名需在要声明的 Repository 后添加 Impl, 并实现方法
3)声明 Repository 接口, 并继承 1) 声明的接口
注意: 默认情况下, Spring Data 会在 base-package 中查找 “接口名Impl” 作为实现类. 也可以通过 repository-impl-postfix 声明后缀.
2、为所有的 Repository 都添加自实现的方法
1)声明一个接口, 在该接口中声明需要自定义的方法
2)提供 1) 所声明的接口的实现类. 且继承 SimpleJpaRepository, 并提供方法的实现
3)声明 Repository 接口, 并继承 1) 声明的接口, 且该接口需要继承 Spring Data 的 Repository.
4)定义 MongoRepositoryFactoryBean的实现类, 使其生成 1) 定义的接口实现类的对象
5)修改 mongodb repository节点的 factory-class 属性指向 3) 的全类名
注意: 全局的扩展实现类不要用 Imp 作为后缀名, 或为全局扩展接口添加 @NoRepositoryBean 注解告知 Spring Data: Spring Data 不把其作为 Repository
项目工程目录如下(部分配置适用于别的测试,可以无视):