虚拟机安装MongoDB请参看《CentOS7安装MongoDB4》正则表达式
我使用的IDE是STS4,你们按照本身的习惯选择便可。spring
关键是pom.xml要加入:mongodb
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency>
在application.properties中加入:数据库
spring.data.mongodb.uri=mongodb://用户名:密码@IP:PORT/数据库
实体类服务器
@Document(collection = "test_goods") publicclass GoodsEntity implements Serializable { privatestaticfinallongserialVersionUID = -805486477824888750L; @Id private String id; private String goodsName; privatelongcategoryId; privateintgoodsStatus; private String labels; //省略get、set方法 }
其余都不解释了,一看就懂,这里要注意id这个字段。app
如上配置,加上注解@Id的状况下,咱们新建一条Document的时候,不需额外设置,会自动生成一个以下图的主键:分布式
这是一个名为”_id”的ObjectId类型的主键,12个字节的BSON类型字符串。ide
4字节是UNIX时间戳,3字节表示MongoDB服务器,2字节是生成ID的进程,3字节是随机数。spring-boot
这么作的好处是对分布式友好。而且,由于id中包含时间戳,自然的就带上了建立时间。咱们能够经过测试
ObjectId id = new ObjectId(entity.getId()); System.out.println(id.getDate());
获取建立时间。
固然了,若是咱们使用MongoDB是对传统DB的一个补充,在系统中仍是但愿将DB中的ID存入MongoDB的话,那就去除id字段的注解,处理的时候对ID字段作好设置便可。
Dao
@Component publicclass GoodsDao { @Autowired private MongoTemplate mongoTemplate; /** * 新建 * * @param entity * @return */ public GoodsEntity add(GoodsEntity entity) { returnmongoTemplate.save(entity); } /** * 根据ID修改 * * @param entity * @return */ public UpdateResult upd(GoodsEntity entity) { Query query = new Query(Criteria.where("id").is(entity.getId())); Update update = new Update().set("goodsName", entity.getGoodsName()).set("categoryId", entity.getCategoryId()) .set("goodsStatus", entity.getGoodsStatus()).set("labels", entity.getLabels()); returnmongoTemplate.updateFirst(query, update, GoodsEntity.class); } /** * 根据ID删除 * * @param id * @return */ public DeleteResult delById(longid) { Query query = new Query(Criteria.where("id").is(id)); returnmongoTemplate.remove(query, GoodsEntity.class); } /** * 根据主键获取详情 * * @param id * @return */ public GoodsEntity getById(longid) { Query query = new Query(Criteria.where("id").is(id)); GoodsEntity entity = mongoTemplate.findOne(query, GoodsEntity.class); returnentity; } /** * 列出全部记录 * * @return */ public List<GoodsEntity> listAll() { List<GoodsEntity> entities = mongoTemplate.find(new Query(), GoodsEntity.class); returnentities; } /** * 根据某字段使用正则表达式模糊查询,且分页、ID倒序 * * @param label * @param pageNumber * @param pageSize * @return */ public List<GoodsEntity> queryPageByLabel(String label, intpageNumber, intpageSize) { // 彻底匹配 // Pattern pattern = Pattern.compile("^" + label + "$", // Pattern.CASE_INSENSITIVE); // 右匹配 // Pattern pattern = Pattern.compile("^.*\"+label+\"$", // Pattern.CASE_INSENSITIVE); // 左匹配 // Pattern pattern = Pattern.compile("^\"+label+\".*$", // Pattern.CASE_INSENSITIVE); // 模糊匹配 Pattern pattern = Pattern.compile("^.*" + MongoDBUtils.escapeExprSpecialWord(label) + ".*$", Pattern.CASE_INSENSITIVE); Query query = new Query(Criteria.where("labels").regex(pattern)); // ID倒序 query.with(new Sort(Sort.Direction.DESC, "id")); // 分页 PageRequest pageableRequest = PageRequest.of(pageNumber, pageSize); query.with(pageableRequest); returnmongoTemplate.find(query, GoodsEntity.class); } /** * 多查询条件,分页,ID倒序 * * @param entity * @param pageNumber * @param pageSize * @return */ public List<GoodsEntity> queryPage(GoodsEntity entity, intpageNumber, intpageSize) { Criteria criteria = new Criteria(); if (!StringUtils.isEmpty(entity.getGoodsName())) { Pattern pattern = Pattern.compile("^.*" + entity.getGoodsName() + ".*$", Pattern.CASE_INSENSITIVE); criteria.and("goodsName").regex(pattern); } if (!StringUtils.isEmpty(entity.getLabels())) { Pattern pattern = Pattern.compile("^.*" + entity.getLabels() + ".*$", Pattern.CASE_INSENSITIVE); criteria.and("labels").regex(pattern); } if (entity.getCategoryId() > 0) { criteria.and("categoryId").is(entity.getCategoryId()); } if (entity.getGoodsStatus() > 0) { criteria.and("goodsStatus").is(entity.getGoodsStatus()); } Query query = new Query(criteria); // 分页&ID倒序 PageRequest pageableRequest = PageRequest.of(pageNumber, pageSize, Sort.Direction.DESC, "id"); query.with(pageableRequest); returnmongoTemplate.find(query, GoodsEntity.class); } }
我的感受基本覆盖了大部分需求,再也不对代码详细解释了。
主要是注意一下,我此次没有使用ObjectId,而是用DB的ID,因此这里的Entity的ID是long。
测试
@RunWith(SpringRunner.class) @SpringBootTest publicclass GoodsDaoTest { @Autowired private GoodsDao goodsDao; @Test publicvoid add() { GoodsEntity entity = new GoodsEntity(); entity.setId(3); // 若是使用ObjectId,就不须要额外处理ID字段了。 entity.setCategoryId(5); entity.setGoodsName("测试商品E"); entity.setGoodsStatus(1); entity.setLabels("a,b,c,*,d"); GoodsEntity newEntity = goodsDao.add(entity); JsonFormaterUtil.printFromObj(newEntity); } @Test publicvoid upd() { GoodsEntity entity = goodsDao.getById(1); entity.setLabels("a,b,c,d"); JsonFormaterUtil.printFromObj(goodsDao.upd(entity)); } @Test publicvoid del() { JsonFormaterUtil.printFromObj(goodsDao.delById(3)); } @Test publicvoid getById() { JsonFormaterUtil.printFromObj(goodsDao.getById(1)); } @Test publicvoid listAll() { JsonFormaterUtil.printFromObj(goodsDao.listAll()); } @Test publicvoid queryByLabel() { JsonFormaterUtil.printFromObj(goodsDao.queryPageByLabel("*", 0, 2)); } @Test publicvoid queryPage() { GoodsEntity entity = new GoodsEntity(); // entity.setCategoryId(5); entity.setGoodsName("测试商品"); // entity.setGoodsStatus(1); // entity.setLabels("a,b,c"); JsonFormaterUtil.printFromObj(goodsDao.queryPage(entity, 0, 10)); } }
没什么好说的。
日志配置
由于我我的喜爱在控制台打印出对DB操做的语句,因此对log配置进行了修改。
Spring Boot使用的是logback,在resources目录下建立logback.xml文件,内容以下:
<?xml version="1.0" encoding="UTF-8"?> <configuration debug="false"> <!--定义日志文件的存储绝对路径--> <property name="LOG_HOME" value="d:/" /> <!-- 控制台输出 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{50} : %n%msg%n </pattern> </encoder> </appender> <!-- 按照天天生成日志文件 --> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!--日志文件输出的文件名 --> <FileNamePattern>${LOG_HOME}/mongodbdemo.log.%d{yyyy-MM-dd}.log </FileNamePattern> <!--日志文件保留天数 --> <MaxHistory>30</MaxHistory> </rollingPolicy> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} : %msg%n </pattern> </encoder> <!--日志文件最大的大小 --> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <MaxFileSize>10MB</MaxFileSize> </triggeringPolicy> </appender> <!-- 日志输出级别 --> <root level="ERROR"> <appender-ref ref="STDOUT" /> <appender-ref ref="FILE" /> </root> <!-- MongoDB日志输出 --> <logger name="org.springframework.data.mongodb.core.MongoTemplate" level="DEBUG" additivity="false"> <appender-ref ref="STDOUT" /> </logger> </configuration>
关键点是配置MongoDB日志输出,其中name是输出日志的类,level设置成debug,才会将执行的语句输出出来,相似:
find using query: { "goodsName" : { "$regex" : "^.*测试商品.*$", "$options" : "i" } } fields: Document{{}} for class: class org.leo.mongodb.demo.entity.GoodsEntity in collection: test_goods
而加上additivity="false"是由于若是不加,上面的日志会在控制台上打印两次。
其余
经过正则表达式查询的时候,会赶上一些特殊字符(*,?等),须要转义一下,代码以下:
publicclass MongoDBUtils { privatestaticfinal String[] fbsArr = { "\\", "$", "(", ")", "*", "+", ".", "[", "]", "?", "^", "{", "}", "|" }; /** * regex对输入特殊字符转义 * * @param keyword * @return */ publicstatic String escapeExprSpecialWord(String keyword) { if (!StringUtils.isEmpty(keyword)) { for (String key : fbsArr) { if (keyword.contains(key)) { keyword = keyword.replace(key, "\\" + key); } } } returnkeyword; } }
索引:
db.getCollection("test_goods").createIndex({ "categoryId": 1 }, { "name": "idx_goods_categoryid" })
为categoryId建立索引,其中{ "categoryId": 1 }的1,表明按升序建立,-1表明降序。
建立复合索引,以下:
db.getCollection("test_goods").createIndex({ "categoryId": 1, "goodsStatus": -1 })
设置惟一索引,以下:
db.getCollection("test_goods").createIndex({ "categoryId": 1}, { "unique": true })