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 自動封裝的特性來直接使用該物件。
在這裡沒有配置MongoTemplate,但是在後續的repository裡面我們卻可以注入進來,原因是繼承了AbstractMongoConfiguration ,該抽象類對其進行了實現。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是資料庫和程式碼之間的介面,對資料庫的操作都在它裡面。
注: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
專案工程目錄如下(部分配置適用於別的測試,可以無視):