项目采用模块化开发,无法在实体上使用注解,所以Spring-data-mongodb无法使用因为实体上加了JPA的@Entity注解导致MongoPersistentEntityIndexCreator找不到类,只能使用原生的api,有了pojo的支持不再需要繁琐的代码
pom.xml
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<mongodb.version>3.5.0</mongodb.version>
</properties>
<dependencies>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver</artifactId>
<version>${mongodb.version}</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>${mongodb.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
实体类
/**
* mongodb 主键对象
* @author xiaofanku@live.cn
* @since 20170912
*/
public class SequenceId {
private String id;
private long seq;
//SET/GET
}
/**
* 实体
* @author xiaofanku@live.cn
* @since 20170912
*/
public class ForumMemberOnline implements Serializable{
private Long ID;
private int memberId=0;
private int uid=0;
private String sessionId;
private String nickname="guest";
//SET/GET
}
接口实现
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import static com.mongodb.client.model.Filters.and;
import static com.mongodb.client.model.Filters.eq;
import static com.mongodb.client.model.Filters.gt;
import static com.mongodb.client.model.Filters.in;
import static com.mongodb.client.model.Updates.combine;
import static com.mongodb.client.model.Updates.inc;
import static com.mongodb.client.model.Updates.set;
import com.mongodb.client.result.DeleteResult;
import java.util.ArrayList;
import java.util.List;
import net.htage.querylab.entity.ForumMemberOnline;
import net.htage.querylab.helper.SequenceId;
import net.htage.querylab.service.ForumMemberOnlineService;
import org.bson.codecs.configuration.CodecProvider;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.codecs.pojo.PojoCodecProvider;
import static org.bson.codecs.configuration.CodecRegistries.fromProviders;
import static org.bson.codecs.configuration.CodecRegistries.fromRegistries;
/**
* 接口实现
* @author xiaofanku@live.cn
* @since 20170912
*/
public class ForumMemberOnlineServiceImpl implements ForumMemberOnlineService{
private final MongoCollection<ForumMemberOnline> collection;
private final MongoDatabase database;
private static final String IDKEY="hosting";
public ForumMemberOnlineServiceImpl(){
CodecProvider pojoCodecProvider = PojoCodecProvider.builder().automatic(true).build();
CodecRegistry pojoCodecRegistry = fromRegistries(MongoClient.getDefaultCodecRegistry(), fromProviders(pojoCodecProvider));
MongoClient mongoClient = new MongoClient("10.0.0.4", 27017);
this.database = mongoClient.getDatabase("htage_ol").withCodecRegistry(pojoCodecRegistry);
this.collection = database.getCollection("apo_forum_online", ForumMemberOnline.class);
}
@Override
public boolean isOnline(int memberId) {
try{
ForumMemberOnline rs = collection.find(eq("memberId", memberId)).first();
if(rs.getID()>0){
return true;
}
}catch(Exception e){
throw new NullPointerException();
}
return false;
}
@Override
public boolean offline(String sessionId) {
DeleteResult dr = collection.deleteOne(eq("sessionId", sessionId));
return dr.getDeletedCount()==1;
}
@Override
public ForumMemberOnline get(String sessionId) {
return collection.find(eq("sessionId", sessionId)).first();
}
@Override
public ForumMemberOnline save(ForumMemberOnline entity) {
try{
if(entity.getID()==null){
entity.setID(getNextSequenceId());
}
}catch(NullPointerException e){
return null;
}
collection.insertOne(entity);
return entity;
}
@Override
public ForumMemberOnline findOne(Long primaryKey) {
return collection.find(eq("iD", primaryKey)).first();
}
@Override
public void edit(ForumMemberOnline entity) {
collection.updateOne(
eq("sessionId", entity.getSessionId()),
combine(
set("memberId", entity.getMemberId()),
set("uid", entity.getUid()),
set("nickname", entity.getNickname())));
}
@Override
public Long count() {
return collection.count();
}
@Override
public List<ForumMemberOnline> findAll() {
return collection.find().into(new ArrayList<>());
}
@Override
public List<ForumMemberOnline> isOnline(List<Integer> uids) {
return collection.find(and(gt("memberId", 0), in("uid", uids))).into(new ArrayList<>());
}
private long getNextSequenceId() throws NullPointerException {
//BasicDBObject find = new BasicDBObject();
//find.put("_id", IDKEY);
//BasicDBObject update = new BasicDBObject();
//update.put("$inc", new BasicDBObject("seq", 1));
MongoCollection<SequenceId> coll = database.getCollection("sequence", SequenceId.class);
SequenceId seqId = coll.findOneAndUpdate(eq("_id", IDKEY), inc("seq", 1));
if (seqId == null) {
throw new NullPointerException("Unable to get sequence id for key : " + IDKEY);
}
return seqId.getSeq();
}
}
如果实体上有其它注解
例如:如果实体上有javax.persistence.Entity注解,可能会有以下异常栈
org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class net.htage.forum.entity.ForumMemberOnline.
at org.bson.codecs.configuration.CodecCache.getOrThrow(CodecCache.java:46)
at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:63)
at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:37)
at com.mongodb.MongoCollectionImpl.getCodec(MongoCollectionImpl.java:591)
at com.mongodb.MongoCollectionImpl.insertOne(MongoCollectionImpl.java:314)
at com.mongodb.MongoCollectionImpl.insertOne(MongoCollectionImpl.java:307)
...
主要原因 是下面的代码没有将net.htage.forum.entity.ForumMemberOnline加入到CodecCache中
CodecProvider pojoCodecProvider = PojoCodecProvider.builder().automatic(true).build();
CodecCache.getOrThrow方法
@SuppressWarnings("unchecked")
public <T> Codec<T> getOrThrow(final Class<T> clazz) {
if (codecCache.containsKey(clazz)) {
Optional<? extends Codec<?>> optionalCodec = codecCache.get(clazz);
if (!optionalCodec.isEmpty()) {
return (Codec<T>) optionalCodec.get();
}
}
throw new CodecConfigurationException(format("Can't find a codec for %s.", clazz));
}
解决方案 自定义一个ForumMemberOnline的Codec
/**
* ForumMemberOnline编码和解码
* @author xiaofanku@live.cn
* @since 20170913
*/
public class ForumMemberOnlineCodec implements Codec<ForumMemberOnline>{
@Override
public void encode(BsonWriter writer, ForumMemberOnline t, EncoderContext ec) {
writer.writeStartDocument();
writer.writeInt64("_id", t.getID());
writer.writeInt32("uid", t.getUid());
writer.writeInt32("memberId", t.getMemberId());
writer.writeString("nickname", t.getNickname());
writer.writeString("sessionId", t.getSessionId());
writer.writeEndDocument();
}
@Override
public Class<ForumMemberOnline> getEncoderClass() {
return ForumMemberOnline.class;
}
@Override
public ForumMemberOnline decode(BsonReader reader, DecoderContext dc) {
ForumMemberOnline fmo=new ForumMemberOnline();
reader.readStartDocument();
fmo.setID(reader.readInt64("_id"));
fmo.setUid(reader.readInt32("uid"));
fmo.setMemberId(reader.readInt32("memberId"));
fmo.setNickname(reader.readString("nickname"));
fmo.setSessionId(reader.readString("sessionId"));
reader.readEndDocument();
return fmo;
}
}
ForumMemberOnlineServiceImpl的构造函数稍作更改
public ForumMemberOnlineDaoMongoImpl(){
//自定义
CodecRegistry customCodecRegistry = CodecRegistries.fromCodecs(new ForumMemberOnlineCodec(), new SequenceIdCodec());
CodecRegistry combineCodecRegistry = fromRegistries(MongoClient.getDefaultCodecRegistry(), customCodecRegistry);
MongoClient mongoClient = new MongoClient("10.0.0.4", 27017);
this.database = mongoClient.getDatabase("htage_ol").withCodecRegistry(combineCodecRegistry);
this.collection = database.getCollection("apo_forum_online", ForumMemberOnline.class);
}
MongoClient.getDefaultCodecRegistry()是一些基础对象的编码和解码.
public class MongoClient extends Mongo implements Closeable {
private static final CodecRegistry DEFAULT_CODEC_REGISTRY =
fromProviders(asList(new ValueCodecProvider(),
new BsonValueCodecProvider(),
new DBRefCodecProvider(),
new DBObjectCodecProvider(),
new DocumentCodecProvider(new DocumentToDBRefTransformer()),
new IterableCodecProvider(new DocumentToDBRefTransformer()),
new MapCodecProvider(new DocumentToDBRefTransformer()),
new GeoJsonCodecProvider(),
new GridFSFileCodecProvider()));
/**
* Gets the default codec registry. It includes the following providers:
*
* <ul>
* <li>{@link org.bson.codecs.ValueCodecProvider}</li>
* <li>{@link org.bson.codecs.BsonValueCodecProvider}</li>
* <li>{@link com.mongodb.DBRefCodecProvider}</li>
* <li>{@link com.mongodb.DBObjectCodecProvider}</li>
* <li>{@link org.bson.codecs.DocumentCodecProvider}</li>
* <li>{@link org.bson.codecs.IterableCodecProvider}</li>
* <li>{@link org.bson.codecs.MapCodecProvider}</li>
* <li>{@link com.mongodb.client.model.geojson.codecs.GeoJsonCodecProvider}</li>
* <li>{@link com.mongodb.client.gridfs.codecs.GridFSFileCodecProvider}</li>
* </ul>
*
* @return the default codec registry
* @see MongoClientOptions#getCodecRegistry()
* @since 3.0
*/
public static CodecRegistry getDefaultCodecRegistry() {
return DEFAULT_CODEC_REGISTRY;
}
...
如果不加上可能常出现一些基本对象的异常:
org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class java.lang.Long.
at org.bson.codecs.configuration.CodecCache.getOrThrow(CodecCache.java:46)
at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:63)
at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:37)
at com.mongodb.client.model.BuildersHelper.encodeValue(BuildersHelper.java:35)
at com.mongodb.client.model.Updates$SimpleUpdate.toBsonDocument(Updates.java:442)
at com.mongodb.MongoCollectionImpl.toBsonDocument(MongoCollectionImpl.java:599)
at com.mongodb.MongoCollectionImpl.findOneAndUpdate(MongoCollectionImpl.java:435)
at com.mongodb.MongoCollectionImpl.findOneAndUpdate(MongoCollectionImpl.java:430)
上面的异常是说Long,也有可能是其它基本对象的包装类.
最后
[A] 解码的顺序不对可产生的异常:
org.bson.BsonSerializationException: Expected element name to be 'memberId', not 'uid'.
at net.htage.forum.impl.test.OLRecordTest.edit(OLRecordTest.java:55)
只要将ForumMemberOnlineCodec.decode中的memberId,uid顺序倒一下即可出现,对着GUI写吧
[B] 缺少writeStartDocument和writeEndDocument方法调用可产生的异常:
org.bson.BsonInvalidOperationException: A Name value cannot be written to the root level of a BSON document.
at org.bson.AbstractBsonWriter.throwInvalidState(AbstractBsonWriter.java:736)
at org.bson.AbstractBsonWriter.writeName(AbstractBsonWriter.java:527)
at org.bson.AbstractBsonWriter.writeInt64(AbstractBsonWriter.java:438)
[C] 扫描方式
CodecProvider pojoCodecProvider = PojoCodecProvider.builder().automatic(true).build();
以上代码是自动扫描,同时还可以指定包名和类.class的一个可变长参数
//包名
CodecProvider pojoCodecProvider = PojoCodecProvider.builder().register("org.example.pojos").build();
//Class Model
ClassModel<ForumMemberOnline> fmoModel = ClassModel.builder(ForumMemberOnline.class).build();
ClassModel<SequenceId> sequenceModel = ClassModel.builder(SequenceId.class).build();
CodecProvider pojoCodecProvider = PojoCodecProvider.builder().register(fmoModel, sequenceModel).build();
[D] 参考网址: