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
extends MongoRepository, BaseRepositoryEnhance {
}
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 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
extends SimpleMongoRepository
implements BaseRepositoryEnhance {
private final MongoOperations mongoOperations;
public BaseRepositoryImpl(MongoEntityInformation metadata, MongoOperations mongoOperations) {
super(metadata, mongoOperations);
this.mongoOperations = mongoOperations;
}
@Override
public T softDelete(ID id) {
return null;
}
}
public BaseRepositoryImpl(MongoEntityInformation
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, S extends BaseEntity, ID extends Serializable>
extends MongoRepositoryFactoryBean {
@Override
protected RepositoryFactorySupport getFactoryInstance(MongoOperations operations) {
return new LCRRepositoryFactory(operations);
}
private static class LCRRepositoryFactory 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 entityInformation = getEntityInformation(metadata.getDomainType());
if (isQueryDslRepository(repositoryInterface)) {
return new QueryDslMongoRepository(entityInformation, mongoOperations);
} else {
return new BaseRepositoryImpl((MongoEntityInformation) 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;
}
}
}
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 accounts;
private Address address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List getAccounts() {
return accounts;
}
public void setAccounts(List 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;
}
}
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 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 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, CustomerRepositoryEnhance {
List findByNameAndAddressNumberAndAccountsAccountName(
String name, String number, String accountName);
}
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 findAllCustomers() {
return customerRepository.findAll();
}
public void dropCustomerCollection() {
customerRepository.deleteAll();
}
public List findByNameAndAddressNumberAndAccountsAccountName(String name, String number, String accountName) {
return customerRepository.findByNameAndAddressNumberAndAccountsAccountName(name, number,accountName);
}
public List search(String keyword, String direction, String sort, int page, int size) {
return customerRepository.search(keyword, direction, sort, page, size);
}
}
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 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
项目工程目录如下(部分配置适用于别的测试,可以无视):