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 ,该抽象类对其进行了实现。

spring data mongodb学习以及为repository提供可扩展的自定义方法_第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
        extends MongoRepository, BaseRepositoryEnhance {
}

下面是可扩张的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 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 metadata, MongoOperations mongoOperations) {
        super(metadata, mongoOperations);
        this.mongoOperations = mongoOperations;
    }

这段代码必须实现,因为父类有一个有参的构造方法,没有无参的构造方法。

spring data mongodb学习以及为repository提供可扩展的自定义方法_第2张图片

父类没有无参构造函数时,子类继承时,构造函数中必须显式调用父类构造方法,并且传递对应所需要的参数。 一个类如果显式的定义了带参构造函数,那么默认无参构造函数自动失效 。

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;
        }
    }
}

Repository 接口概述
Repository 接口是 Spring Data 的一个核心接口,它不提供任何方法,开发者需要在自己定义的接口中声明需要的方法 
    public interface Repository { } 
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 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;
    }
}

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 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);
}

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 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);
    }
}

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 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

项目工程目录如下(部分配置适用于别的测试,可以无视):

spring data mongodb学习以及为repository提供可扩展的自定义方法_第3张图片


你可能感兴趣的:(Spring)