SpringBoot结合mongodb

SpringBoot结合mongodb

一、添加mongodb的依赖

打开pom.xml,在<dependencies></dependencies>下加入相关依赖

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

二、在配置文件中加入mongodb的连接地址

打开resources中的application.properties文件,加入以下代码:

spring.data.mongodb.uri= mongodb://<用户名>:<密码>@<ip地址>/<数据库名称>

例如

spring.data.mongodb.uri= mongodb://hyq:123456f@123.23.44.11/school

上面连接了一个ip地址为123.23.44.11,用户名为hyq,密码为123456,名为school的数据库

三、加入关于Mongo的java配置文件

AbstractMongoConfiguration 是Spring Data MongoDB 基于java配置的抽象类,所以要继承它

@Configuration
@EnableMongoAuditing  
public class SpringMongoConfig extends AbstractMongoConfiguration {  
    @Autowired  
    private ApplicationContext appContext;  
    @Autowired
    private Environment environment;
    @Override  
    protected String getDatabaseName() {  
        String mongoUrl = environment.getProperty("spring.data.mongodb.uri");
        String arr[] = mongoUrl.split("/");
        String name = arr[arr.length-1];
        return name;  
    }  
    @Override  
    @Bean  
    public Mongo mongo() throws Exception {  
        String mongoUrl = environment.getProperty("spring.data.mongodb.uri");
        MongoClientURI mongoClientURI = new MongoClientURI(mongoUrl);  
        return new MongoClient(mongoClientURI);  
    }  
    // 重写mongoTemplate方法(上层抽象类已经默认实现了这个方法)
    @Override  
    @Bean  
    public MongoTemplate mongoTemplate() throws Exception {  
        MongoDbFactory factory = mongoDbFactory();  
        // 以下操作是配置mongodb的记录不保存_class这个字段
        MongoMappingContext mongoMappingContext = new MongoMappingContext();  
        mongoMappingContext.setApplicationContext(appContext);  
        MappingMongoConverter converter = new MappingMongoConverter(new DefaultDbRefResolver(factory), mongoMappingContext); 
        converter.setTypeMapper(new DefaultMongoTypeMapper(null));  
        return new MongoTemplate(factory, converter);  
    }  
    @Bean  
    public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {  
        return new PropertySourcesPlaceholderConfigurer();  
    } 
}

将数据持久化操作时会遇到多出一个_class列,我们可以用上述重写对mongoTemplate的获取的方法来避免

四、关于MongoDB的java实体

几个常用的注解

@Document,将一个类声明为一个mongodb中的文档,如果@Document(collection=“xx”),则指定对应xx文档
@Id,将一个字段声明为mongodb文档中的ObjectId
@Indexed,将一个字段声明为文档中的索引,如果@Indexed(unique=true),则声明为唯一索引
@Field(”xxx”),声明当前字段对应文档中的哪一个列
@Transient,映射忽略的字段,不会保存到文档中去

@Document(collection="securityUser")
public class User {
    @Id
    private String id;
    @Field("username")
    private String name;
    private String pass;
    @Transient
    private String unuseField;
    ...setter、getter...
}

听说spring-data-mongo的映射是通过MappingMongoConverter类实现的,这也是为什么上面为了去除_class列要重新设置MappingMongoConverter里面的属性

五、关于对MongoDB的操作的接口

我习惯的有两种方式:用原生的mongoTemplate 或者 声明持久化接口

1、先说后者,声明接口的方式,我们只需新建一个接口,继承 MongoRepository
@NoRepositoryBean
public interface MongoRepository<T, ID extends Serializable>
        extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
        ...

很显然这是一个泛型接口,因为接口也不知道实现自己的或者需要自己进行CURD的是什么类,所以泛型的第一个参数T就应该是我们要进行CURD的实体类,但是小弟不才,第二个参数不知道干嘛的了,我对Serializable类不太了解。于是我就一路看它继承了什么,直到看到了这个接口:

/**
 * Central repository marker interface. Captures the domain type to manage as well as the domain type's id type. General
 * purpose is to hold type information as well as being able to discover interfaces that extend this one during
 * classpath scanning for easy Spring bean creation.
 * <p>
 * Domain repositories extending this interface can selectively expose CRUD methods by simply declaring methods of the
 * same signature as those declared in {@link CrudRepository}.
 * 
 * @see CrudRepository
 * @param <T> the domain type the repository manages
 * @param <ID> the type of the id of the entity the repository manages
 * @author Oliver Gierke
 */
public interface Repository<T, ID extends Serializable> {
}

通过这个可以看出,第一个参数的确是要进行管理的域(也就是我们所说的类)第二个参数是这个域的id的类型

下面展示我是如何使用的:

public interface UserRepository extends MongoRepository<User, String> {
}
简单的查询逻辑

声明完这个接口,基本的CURD api 都已经包含了(划掉了继承Object的方法)
这里写图片描述
除了这些之外,我们还可以自己来声明方法来丰富操作,例如findByXXX或者deleteByXXX。
举个栗子:

public interface UserRepository extends MongoRepository<User, String> {
    User findByUser(String userName);
}

还是刚才的User类,find by User,这里的user是我们User类里的一个私有的属性,对应文档中的username列,意思就是根据username来查询一条User记录
如果是多个条件,就findByUserAndPass(String userName,String pass)
要注意by后面的所有查询条件的字符串,一定要在相对应的类里面有这个字段才行

除了ByXXX还有一些别的查询条件,(我们在User类里加入身高,height,单位为cm,类型是int)

大于(GreaterThan)
对应方法:List<User> findByHeightGreaterThan(int height)
对应原生查询语句:{height:{$gt: heightValue}}

小于(LessThan)
对应方法:List<User> findByHeightLessThan(int height)
对应原生查询语句:{height:{$lt: heightValue}}

在…之间(Between)
对应方法:List<User> findByHeightBetween(int height1, int height2)
对应原生查询语句:{height:{$gt: height2, $lt: height1}}

除此之外还有:是否为空(IsNull),是否非空(IsNotNull),模糊查询(Like, findByUserLike(String userName))等

  • 如果加入分页,请在接口方法后面加入一个Pageable pageable参数
/*
Pageable 是一个接口,我们需要实现,除了分页还能排序。
*/
List<User> findByHeightLessThan(int height,Pageable pageable)
复杂的查询逻辑(@Query)

如果查询条件过多或者因为更多的需求控制,我们往往不能只是简单地使用声明方法来实现。spring框架为我们提供了一个@Query注解(org.springframework.data.mongodb.repository.Query),来丰富声明的接口方法所能达到的需求

使用方法:

@Query()
List<T> methodName(params);

有了这个注解,在执行查询语句时就不会根据方法名来映射查询条件了,而是根据@Query里的条件来查询

看看注解里可以添加什么属性
这里写图片描述

我们从下往上来看:

  • value:查询条件,接受字符串类型
@Query(value="{username:{$regex:?0},age:{$gt:?1}")
List<User> findByUsernameLikeAndAge(String username,int age);

上面是根据用户名模糊检索和age字段进行匹配,?num 是占位符,表示在方法里第几个参数(从0开始)

  • fields:指定返回字段 (默认包括id),接受字符串类型
@Query(value="{username:{$regex:?0},age:{$gt:?1}",fields="{username:1,age:1}")
List<User> findByUsernameLikeAndAge(String username,int age);

上面指定了只返回username和age这两个属性,这样我们会发现User里面其余字段都为null,fields所接受的字符串也是以JSON的形式{fieldname:0或1},0代表不想取出这个字段,1代表只想取出这个字段
当我们取文档数据时,某个字段内容比较大而且我们又不需要,就可以使用此方法

  • exists:表示当前语句是否以判断查询内容是否存在来执行,接受布尔值
@Query(value="{username:{$regex:?0},age:{$gt:?1}",fields="{username:1,age:1}",exists=true)
boolean isExistsByUsernameLikeAndAge(String username,int age);

方法可以返回布尔值,就是看所查内容是否存在

  • delete:表示当前语句是否以删除查询内容来执行,接受布尔值
@Query(value="{username:{$regex:?0},age:{$gt:?1}",fields="{username:1,age:1}",delete=true)
List<User> deleteByUsernameLikeAndAge(String username,int age);

小心使用,因为返回的信息是你最后一次看到了(会删除查询到的内容)

  • count:表示当前语句是否以计算查询内容的记录个数来执行,接受布尔值
@Query(value="{username:{$regex:?0},age:{$gt:?1}",fields="{username:1,age:1}",count=true)
int countByUsernameLikeAndAge(String username,int age);

可以返回整数

2、再说前者,使用原生mongoTemlate

mongoTemlate我们在前面提到的继承于AbstractMongoConfiguration的java配置文件中提到了,以下是AbstractMongoConfiguration中实现好的

/**
* Creates a {@link MongoTemplate}.
 * 
 * @return
 * @throws Exception
 */
@Bean
public MongoTemplate mongoTemplate() throws Exception {
    return new MongoTemplate(mongoDbFactory(), mappingMongoConverter());
}

我们在实现类SpringMongoConfig中重写了这个方法,是为了去掉_class
mongoTemplate的用法实在太多了,我这里只列举我怎么用的:

我最常用的方法某过于下面这个:
mongoTemplate.find(query, className)

public <T> List<T> find(Query query, Class<T> entityClass) {
    return find(query, entityClass, determineCollectionName(entityClass));
}

Query对象很无解,我们只要把我们的查询语句内容全部灌输进去即可

但是我常用的是BasicQuery(继承了Query,用起来简单)和BasicDBObject(继承了DBObject,类似于Map)这两个类
下面是BasicQuery的构造函数源码(看一下方便理解)

// queryObject/query是查询条件, fieldsObject/fields是指定字段内容
public BasicQuery(String query) {
    this((DBObject) JSON.parse(query));
}
public BasicQuery(DBObject queryObject) {
    this(queryObject, null);
}
public BasicQuery(String query, String fields) {
    this.queryObject = (DBObject) JSON.parse(query);
    this.fieldsObject = (DBObject) JSON.parse(fields);
}
public BasicQuery(DBObject queryObject, DBObject fieldsObject) {
    this.queryObject = queryObject;
    this.fieldsObject = fieldsObject;
}

下面是一个集排序、分页、指定条件、指定映射字段于一身的查询代码
(请注意不能直接拿来用,有些自定义类需要自己创建,不创建也可以用别的方式实现)

/**
     * 取数据列表并制定某些字段
     * 
     * @param conditions 查询条件
     * @param fieldNames 指定字段
     * @param objectCls 映射类类名
     * @param dbOrder 排序方式(因为我自己大多数情况都是只按一个字段排序,
     * 同学们如果有按多个字段排序的情况可以将参数改为List<DBOrder>)
     * @param dbPage 分页方式
     * @return
     */
    public <T> List<T> getDBObjectForListWithAppointField(Map<String, Object> conditions, List<String> fieldNames,
            Class<T> objectCls, DBOrder dbOrder, DBPage dbPage) {
    /* 
     * MongoDataPageable 是自定义的类 实现以下接口即可
     *  MongoDataPageable implements Pageable, Serializable
     *  Integer pageNumber = 1;
        Integer pageSize = 10;
        Sort sort;
     */
    MongoDataPageable mongoDataPageable = null;
    if (dbPage != null) {
        mongoDataPageable = new MongoDataPageable();
        /*  
         * DBPage是自定义类,无继承无实现,只有两个字段
         * int pageNumber;
         * int pageSize;
         */
        mongoDataPageable.setPageNumber(dbPage.getPageNumber());
        mongoDataPageable.setPageSize(dbPage.getPageSize());
    }
    /*
     * Sort 是Spring框架的类,构造参数接受可接受一个Order对象、Order数组、Order列表
     */
    Sort sort = null;
    /*  
     * DBOrder是自定义类,无继承无实现,只有两个字段
     *  String fieldName; // 要排序的字段名称
        Direction direction; // 排序方向
     */
    if (dbOrder != null) {
        sort = new Sort(new Order(dbOrder.getDirection(), dbOrder.getFieldName()));
        if (mongoDataPageable != null) {
            mongoDataPageable.setSort(sort);
        }
    }
    BasicDBObject queryObject = new BasicDBObject(conditions);
    BasicDBObject fieldsObject = new BasicDBObject();
    for (String fieldName : fieldNames) {
        fieldsObject.put(fieldName, true);
    }
    BasicQuery basicQuery = new BasicQuery(queryObject, fieldsObject);// 查询条件,指定映射字段
    // 如果分页和排序的话需要用到with方法,所以这里进行了一些可能不会用到分页或排序的判断
    if (mongoDataPageable == null && sort != null) {
        return mongoTemplate.find(basicQuery.with(sort), objectCls);
    } else if (mongoDataPageable == null && sort == null) {
        return mongoTemplate.find(basicQuery, objectCls);
    }
    return mongoTemplate.find(basicQuery.with(mongoDataPageable), objectCls);
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值