菜鳥入坑》springboot整合mongodb問題1-Decimal128和BigDecimal的轉換
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