Spring对mongoDB分布式数据库整合

MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似的格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。
它的特点是高性能、易部署、易使用,存储数据非常方便。主要功能特性有:

  1. 面向集合存储,易存储对象类型的数据。
  2. 模式自由。
  3. 支持动态查询。
  4. 支持完全索引,包含内部对象。
  5. 支持查询。
  6. 支持复制和故障恢复。
  7. 使用高效的二进制数据存储,包括大型对象(如视频等)。
  8. 自动处理碎片,以支持云计算层次的扩展性。
  9. 支持RUBY,PYTHON,JAVA,C++,PHP,C#等多种语言。
  10. 文件存储格式为BSON(一种JSON的扩展)。
  11. 可通过网络访问。

所谓“面向集合”(Collection-Oriented),意思是数据被分组存储在数据集中,被称为一个集合(Collection)。每个集合在数据库中都有一个唯一的标识名,并且可以包含无限数目的文档。集合的概念类似关系型数据库(RDBMS)里的表(table),不同的是它不需要定义任何模式(schema)。Nytro MegaRAID技术中的闪存高速缓存算法,能够快速识别数据库内大数据集中的热数据,提供一致的性能改进。
模式自由(schema-free),意味着对于存储在mongodb数据库中的文件,我们不需要知道它的任何结构定义。如果需要的话,你完全可以把不同结构的文件存储在同一个数据库里。
存储在集合中的文档,被存储为键-值对的形式。键用于唯一标识一个文档,为字符串类型,而值则可以是各种复杂的文件类型。我们称这种存储形式为BSON(Binary Serialized Document Format)。
MongoDB已经在多个站点部署,其主要场景如下:

  • 网站实时数据处理。它非常适合实时的插入、更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性。
  • 缓存。由于性能很高,它适合作为信息基础设施的缓存层。在系统重启之后,由它搭建的持久化缓存层可以避免下层的数据源过载。
  • 高伸缩性的场景。非常适合由数十或数百台服务器组成的数据库,它的路线图中已经包含对MapReduce引擎的内置支持。

不适用的场景如下:

  • 要求高度事务性的系统。
  • 传统的商业智能应用。
  • 复杂的跨文档(表)级联查询。

Spring中手续需要定义maven:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

这里我使用的是Spring boot进行测试,其实Spring MVC也是一样的。

接着就是配置文件:

@Configuration
@EnableMongoRepositories(value = "com.example.demo.repository.mongo")
public class MongoConfig extends AbstractMongoConfiguration {

    private String host = "127.0.0.1";
    private int port = 27017;

    @Override
    public MongoClient mongoClient() {
        return new MongoClient(host,port);
    }

    @Override
    protected String getDatabaseName() {
        return "demo";
    }
}
配置文件的EnableMongoRepositories跟JPA的有点像,value制定的是mongo的仓库代码,如果不指定,
也可以在仓库代码中添加@Repository.

配置文件处理完毕,现在需要的是一个仓库实现类,Spring-data项目提供了很大帮助,接下来我是继承了MongoRepository跟JPA一样,提供了一个公共实现类,我们只需要继承它,就可以使用公共的方法,比如:

public interface MongoRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
    <S extends T> List<S> saveAll(Iterable<S> var1);

    List<T> findAll();

    List<T> findAll(Sort var1);

    <S extends T> S insert(S var1);

    <S extends T> List<S> insert(Iterable<S> var1);

    <S extends T> List<S> findAll(Example<S> var1);

    <S extends T> List<S> findAll(Example<S> var1, Sort var2);
}
这里的T是数据模型类,ID,是mongoDB需要设置的@Id主键,这个主键是不会像mysql那样自增的,所以在保存数据的时候需要我们设置好这个id。

我们先创建一个模型类,已提供给仓库使用,如Token:

/**
 * Created by alan on 2018/6/18.
 */
@Document
public class MTokenModel extends BasicModel {

    @Id
    private Long uid;

    private String token;

    public MTokenModel() {

    }

    public MTokenModel(Long id, String t) {
        this.uid = id;
        this.token = t;
    }

    public static MTokenModel newInstance(Long id, String s) {
        return new MTokenModel(id, s);
    }

    public Long getUid() {
        return uid;
    }

    public void setUid(Long uid) {
        this.uid = uid;
    }

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }
}
模型类必须使用@Document,主键也必须使用@Id申明,不然会出错。

接着就是仓库类,仓库类我们大部分是不需要实现的,只需要提供一个接口,并且Spring-data做了很多补充,比如根据特定的命名规则,我们只需要申明接口的方法跟返回值就可以了,剩下的Spring会帮我们去实现。例如:find(查找)By(通过)Token(模型类的属性),结果就是 MTokenModel findByToken(String token);只需要这样子,但是如果你不是一个条件怎么办?你可以这样子:MTokenModel findByTokenAndUid(String token,Long uid);真的很简单。

但是有时候通用的命名规则并不能满足我们,那么我可以像JPA一样使用@Query的标注去查询需要的数据,注意的是,这里的@Query使用的是json信息,不是SQL语句,比如:@Query(“{token:?0,uid:?1}”);这里的?0是第一个参数,如果第二个那么就是?1,以此类推。

下面直接贴上仓库代码:

public interface TokenMongoRepository extends MongoRepository<MTokenModel, Long> {

    MTokenModel findByToken(String token);

    MTokenModel findByTokenAndUid(String token, long uid);

    @Query("{token:?0,uid:?1}")
    MTokenModel findByTokenAndUidss(String token, long uid);

}

这样最基本的增删改查全部都实现了,但是如果你真的还是不满足,那么你可以实现接口的方法,添加自定义的查询,使用的方法跟JPA基本类似,只需要注入MongoOperations进行自定义查询即可。

下面贴上逻辑类代码的实现:

@Service
public class TokenMongoServiceImpl extends CompactService implements ITokenMongoService {

    @Autowired
    private MongoOperations mongo;

    @Resource
    private TokenMongoRepository mongoRepository;

    public TokenMongoServiceImpl(){
        super(TokenMongoServiceImpl.class);
    }

    @Override
    public int insert(MTokenModel record) throws IllegalServiceException {
        record = mongoRepository.insert(record);
        return record.getUid().intValue();
    }

    @Override
    public MTokenModel getRowById(int id) throws IllegalServiceException {
        Optional<MTokenModel> optional = mongoRepository.findById((long) id);
        return optional.isPresent() ? optional.get() : null;
    }

    @Override
    public int updateById(MTokenModel record) throws IllegalServiceException {
        if (record.getUid() == null || record.getUid().intValue() <= 0){
            throw new IllegalServiceException("The uid must be than 0.");
        }
        mongoRepository.save(record);
        return record.getUid().intValue();
    }

    @Override
    public int deleteById(int id) throws IllegalServiceException {
        mongoRepository.deleteById(Long.valueOf(id));
        return id;
    }

    @Override
    public List<MTokenModel> getAll() throws IllegalServiceException {
        return mongoRepository.findAll();
    }

    @Override
    public MTokenModel parser(MTokenModel obj) throws NullPointerException {
        return null;
    }

    @Override
    public MTokenModel getRowByToken(String token) {
        return mongoRepository.findByToken(token);
    }

    @Override
    public MTokenModel getRowByTokenAndUid(String token, int uid) {
//        return mongoRepository.findByTokenAndUid(token,uid);
        return mongoRepository.findByTokenAndUidss(token,uid);
    }

    @Override
    public long getLastId() {
        Sort sort = new Sort(Sort.Direction.DESC,"uid");
        List<MTokenModel> list = mongoRepository.findAll(sort);
        return list != null && list.size() > 0 ? list.get(0).getUid() : 1L;
    }
}

大功告成,现在该有的都有了,接下来就是测试:

@RequestMapping(value = "/testMongo")
    public Map<String, Object> testMongo(HttpServletRequest request) {
        Map<String, Object> hash = new HashMap<>();

        try {
//            out(tokenMongoService.insert(MTokenModel.newInstance(tokenMongoService.getLastId()+1,"token00_auto"))+"");
//            out(tokenMongoService.updateById(MTokenModel.newInstance(tokenMongoService.getLastId(),"token007"))+"");
//
//            hash.put("list_0",tokenMongoService.getAll());
//            hash.put("byId7",tokenMongoService.getRowById(7));
//            hash.put("byId8",tokenMongoService.getRowById(8));
//            hash.put("delId",tokenMongoService.deleteById(1));
//            hash.put("list_1",tokenMongoService.getAll());
//            hash.put("byToken1",tokenMongoService.getRowByToken("token001"));
//            hash.put("byToken2",tokenMongoService.getRowByToken("token002"));
//            hash.put("byTokenAndId",tokenMongoService.getRowByTokenAndUid("token003",3));

            long t = System.currentTimeMillis();
            for (int i = 100; i < 10000; i++) {
                userMongoService.updateById(MUserModel.newInstance(i, "user_a000" + i));
            }
            out("insert use time is " + (System.currentTimeMillis() - t) + "ms");

            t = System.currentTimeMillis();
            for (int i = 100; i < 10000; i++) {
                hash.put("tokenuid=" + i, userMongoService.getRowById(i));
                hash.put("userid=" + i, userMongoService.getRowById(i));
            }
            out("find use time is " + (System.currentTimeMillis() - t) + "ms");

        } catch (IllegalServiceException e) {
            e.printStackTrace();
        }


        return hash;
    }

连续测试了3次,分别每次插入或更新10000条数据,并且读取出来,测试的时间效果:

insert use time is 2075ms
find use time is 2439ms

insert use time is 2137ms
find use time is 1895ms

insert use time is 1575ms
find use time is 1687ms

声明下测试数据,如果是服务器肯定是更快的,本人是用笔记本测试的,硬盘是机械硬盘,所以资源有效。

 

Leave a Comment

 
Copyright © 2008-2021 lanxinbase.com Rights Reserved. | 粤ICP备14086738号-3 |