jaylong 2019-10-19
转自:https://blog.csdn.net/weixin_41792559/article/details/79575524
1.Decimal128的了解
由于mongodb4.3以上新加了Decimal128类型。Decimal128类型对小数给了最好的支持,而double类型对小数存在精度的问题。个人觉得Decimal128还是不错的。但是我测试发现spring-data-mongodb 1.*和现在的spring-data-mongodb2.0.5目前不支持Decimal128自动转换为java的BigDecimal类型。
异常: No converter found capable of converting from type [org.bson.types.Decimal128] to type [java.math.BigDecimal]
大概就是描述:没有Decimal128转换成BigDecimal的转换器。
2.访问mongodb方式:spring-data-mongodb的MongoTemplate。
目前先以spring-data-mongodb的MongoTemplate方式为案例。之后我会增加mongodb原始的dom处理方式,以及spring-boot-starter-data-mongodb下开启了对Repository的支持方式进行测试一下。(进行了测试)
代码:https://github.com/topsnowwolf/mongodbit
3.如何解决Decimal128,BigDecimal的类型转换问题呢?
思路:没有转换器我们新加一个转换。如何新加呢?增加之后如何配置呢?这就是重点了。
首先spring增加类型转换器有 三种方式。
1.实现GenericConverter接口
2.实现ConverterFactory接口
3.实现Converter接口
这三种方式分别如何实现类型转换器,我就不多说了。网上很多例子。
下面我就以实现Converter接口方式的来实现。
自定义类型转换器:
package com.wolf.mongodbit.converter; import org.bson.types.Decimal128; import org.springframework.core.convert.converter.Converter; import org.springframework.data.convert.ReadingConverter; import org.springframework.data.convert.WritingConverter; import java.math.BigDecimal; @ReadingConverter @WritingConverter public class BigDecimalToDecimal128Converter implements Converter<BigDecimal, Decimal128> { public Decimal128 convert(BigDecimal bigDecimal) { return new Decimal128(bigDecimal); } }
package com.wolf.mongodbit.converter; import org.bson.types.Decimal128; import org.springframework.core.convert.converter.Converter; import org.springframework.data.convert.ReadingConverter; import org.springframework.data.convert.WritingConverter; import java.math.BigDecimal; @ReadingConverter @WritingConverter public class Decimal128ToBigDecimalConverter implements Converter<Decimal128, BigDecimal> { public BigDecimal convert(Decimal128 decimal128) { return decimal128.bigDecimalValue(); } }
下面我就分析一下MongoTemplate的源码。
我们先带着问题去看。我们增加了转换器,这个转换器是给访问mongodb用的,那肯定是给MongoTemplate设置新的转换器了。
打开MongoTemplate这个类,果然如此。MongoTemplate的构造方法中就有一个参数是设置类型转换器的。看到希望了。哈哈!
public MongoTemplate(MongoDbFactory mongoDbFactory, MongoConverter mongoConverter) { this.writeConcernResolver = DefaultWriteConcernResolver.INSTANCE; this.writeResultChecking = WriteResultChecking.NONE; Assert.notNull(mongoDbFactory, "MongoDbFactory must not be null!"); this.mongoDbFactory = mongoDbFactory; this.exceptionTranslator = mongoDbFactory.getExceptionTranslator(); this.mongoConverter = mongoConverter == null ? getDefaultMongoConverter(mongoDbFactory) : mongoConverter; this.queryMapper = new QueryMapper(this.mongoConverter); this.updateMapper = new UpdateMapper(this.mongoConverter); this.mappingContext = this.mongoConverter.getMappingContext(); if (null != this.mappingContext && this.mappingContext instanceof MongoMappingContext) { this.indexCreator = new MongoPersistentEntityIndexCreator((MongoMappingContext)this.mappingContext, mongoDbFactory); this.eventPublisher = new MongoMappingEventPublisher(this.indexCreator); if (this.mappingContext instanceof ApplicationEventPublisherAware) { ((ApplicationEventPublisherAware)this.mappingContext).setApplicationEventPublisher(this.eventPublisher); } } }
最终发现MongoConverter是一个接口。那一定有实现它的类。查一下api发现,抽象的类AbstractMongoConverter,最后是MappingMongoConverter类。此时就会发现AbstractMongoConverter类中有setCustomConversions设置自定义类型转换器的set方法。现在兴奋了吧!!!
现在大概的思路就是通过创建一个MappingMongoConverter对象,将定义的类型转换器调用setCustomConversions方法进行注册。最后调用MongoTemplate的构造方法得到MongoTemplate对象。
废话不多说上代码:
自定义一个配置类:
package com.wolf.mongodbit.config; import com.mongodb.MongoClient; import com.mongodb.MongoClientURI; import com.wolf.mongodbit.converter.BigDecimalToDecimal128Converter; import com.wolf.mongodbit.converter.Decimal128ToBigDecimalConverter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.mongodb.MongoDbFactory; import org.springframework.data.mongodb.config.AbstractMongoConfiguration; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.SimpleMongoDbFactory; import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; import org.springframework.data.mongodb.core.convert.MongoCustomConversions; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import java.util.ArrayList; import java.util.List; @Configuration public class MongodbConfig extends AbstractMongoConfiguration{ private String dbName = "wolf"; @Override public MongoClient mongoClient() { MongoClient mongoClient = new MongoClient(); return mongoClient; } @Override protected String getDatabaseName() { return dbName; } @Bean public MappingMongoConverter mappingMongoConverter() throws Exception { DefaultDbRefResolver dbRefResolver = new DefaultDbRefResolver(this.dbFactory()); MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, this.mongoMappingContext()); List<Object> list = new ArrayList<>(); list.add(new BigDecimalToDecimal128Converter());//自定义的类型转换器 list.add(new Decimal128ToBigDecimalConverter());//自定义的类型转换器 converter.setCustomConversions(new MongoCustomConversions(list)); return converter; } @Bean public MongoDbFactory dbFactory() throws Exception { return new SimpleMongoDbFactory(new MongoClientURI("mongodb://localhost:27017/wolf")); } @Bean public MongoMappingContext mongoMappingContext() { MongoMappingContext mappingContext = new MongoMappingContext(); return mappingContext; } @Bean public MongoTemplate mongoTemplate() throws Exception { return new MongoTemplate(this.dbFactory(), this.mappingMongoConverter()); } }
实体类:
package com.wolf.mongodbit.entity.mongodb; import lombok.Data; @Data public class Address { private String aCode; private String add; }
package com.wolf.mongodbit.entity.mongodb; import lombok.Data; import org.bson.types.ObjectId; import org.springframework.data.mongodb.core.mapping.Document; import java.util.Date; @Document(collection="blacklist") @Data public class Blacklist { private ObjectId objectId; private String username; private String userid; private String blacktype; private String status; private Date update; private Date indate; private Address address; private Comments comments; }
package com.wolf.mongodbit.entity.mongodb; import lombok.Data; import java.math.BigDecimal; @Data public class Comments { private String cause; private String desc; private BigDecimal money; }
测试的controller:
package com.wolf.mongodbit.controller; import com.wolf.mongodbit.entity.mongodb.Blacklist; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Query; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; import static org.springframework.data.mongodb.core.query.Criteria.where; import static org.springframework.data.mongodb.core.query.Query.query; @RestController @RequestMapping("/mongodb") public class BlackListController { private final static Logger logger = LoggerFactory.getLogger(BlackListController.class); @Autowired private MongoTemplate mongoTemplate; @PostMapping("/check") private Blacklist checkBlack(Blacklist blackList){ List<Blacklist> list = mongoTemplate.find(query(where("userid").is(blackList.getUserid())), Blacklist.class); logger.info("springboot+mongodb:size={}",list.size()); if(list.size()>0) return list.get(0); return null; } }
pom配置文件:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.wolf</groupId> <artifactId>mongodbit</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>mongodbit</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--lombok自动生成实体类get/set方法 start--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--lombok自动生成实体类get/set方法 end--> <!--mongodb引入 start --> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-mongodb</artifactId> </dependency> <!-- <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency> --> <!--mongodb引入 end --> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
yml配置文件:
#访问项目的url前缀 server: servlet: context-path: /mongodb
mongodb的测试数据:
var blacklist1 = { "username" : "snowwolf", "userid" : "2018001014344", "address" : { "aCode" : "0020", "add" : "广州" }, "certificate":{ "certificateid":"20018554111134", "certificatetype":"01", "desc":"学生证" }, "blacktype" : "01", "comments":{ "cause":"01", "desc":"逾期欠费", "money":NumberDecimal("18889.09") }, "status":"01", "update" : ISODate("2017-12-06T04:26:18.354Z"), "indate" : ISODate("2017-12-06T04:26:18.354Z") }; var blacklist2 = { "username" : "lison", "userid" : "2018001014345", "address" : { "aCode" : "0075", "add" : "深圳" }, "certificate":{ "certificateid":"20018554111134", "certificatetype":"02", "desc":"护照" }, "blacktype" : "01", "comments":{ "cause":"02", "desc":"恶意欠费", "money":NumberDecimal("188890.00") }, "status":"01", "update" : ISODate("2016-01-06T04:26:18.354Z"), "indate" : ISODate("2015-12-06T04:26:18.354Z") }; var blacklist3 = { "username":"tom", "userid":"2018001014346", "address":{ "aCode" : "0020", "add" : "广州" }, "certificate":{ "certificateid":"20018554111136", "certificatetype":"01", "desc":"学生证" }, "blacktype":"01", "comments":{ "cause":"03", "desc":"公安机关确定的涉嫌短信欺诈、诈骗等犯罪行为的用户" }, "status":"01", "update":ISODate("2017-12-06T04:26:18.354Z"), "indate":ISODate("2017-12-06T04:26:18.354Z") }; db.blacklist.insert(blacklist1); db.blacklist.insert(blacklist2); db.blacklist.insert(blacklist3);
在postman中测试:
结果完美!
时间有限,有些地方可能不是很完美!不足之处请大牛指出,谢谢!
有需要代码的我之后将会发布到git上!
https://github.com/topsnowwolf/mongodbit