轉自: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