Mongo runtime throws following error when trying to insert a new document in the collection. Note that the database and collection does NOT exist yet (and my assumption is that mongo runtime will create the database, followed by collection and then insert my first document by converting my POJO to BSON using a default codec). Any suggestions?
Error: 2015-12-13 18:46:46,384 - application-akka.actor.default-dispatcher-3 - [error] - application - Can't find a codec for class models.User.
/* Model Class */
package models;
import javax.persistence.Id;
import org.mongojack.ObjectId;
public class User {
#ObjectId #Id public String _id;
public String firstname;
public String lastname;
public String email;
public String phone;
public String address;
}
/* Controller Class */
public class Users extends Controller {
#BodyParser.Of(BodyParser.Json.class)
public Result create() {
Logger.info("Enter - Users::create()");
try {
Form<User> user = Form.form(User.class).bindFromRequest();
if (user.hasErrors()) {
Logger.info("User: " + user.toString());
Logger.info(user.errorsAsJson().toString());
return badRequest(user.errorsAsJson());
}
else {
User oneUser = user.get();
MongoClient mongoClient= new MongoClient();
MongoDatabase db = mongoClient.getDatabase("marketplace");
MongoCollection<User> col = db.getCollection("users", User.class);
col.insertOne(oneUser);
mongoClient.close();
return ok();
}
}
catch (Exception e) {
Logger.error(e.getLocalizedMessage());
return internalServerError(e.getMessage());
}
finally {
Logger.info("Exit - Users::create()");
}
}
}
You will have to wrap the MongoCollection with a JacksonDBCollection to enable all the MongoJack features.
Something like this:
JacksonDBCollection<User, String> userColl = JacksonDBCollection.wrap(col, User.class, String.class);
And then use userColl to insert your new object.
It seems that this the only way supported at this time. This requires using the getDB() method, which is deprecated since the mongo driver 3.0.
See the relevant issue on github here:
https://github.com/mongojack/mongojack/issues/105
Yup - this works. However I had to use deprecated getDB since it has a getter that returns collection of type DBCollection which "wrap" function expects. I was hoping to use getDatabase instead but gets me MongoCollection which Jackson's "Wrap" won't accept. Any suggestions?
User oneUser = user.get();
MongoClient mongoClient= new MongoClient();
DB db = mongoClient.getDB("marketplace");
DBCollection col = db.getCollection("marketplace");
JacksonDBCollection<User, String> userCol = JacksonDBCollection.wrap(col,User.class, String.class);
userCol.insert(oneUser);
Related
I have to store in Mongo some documents which must be inherited from class from external library (jar):
public class ExternalLibraryClass {
private EventContainer eventContainer;
}
public class MyDocument extends ExternalLibraryClass {
private String firstName;
private String lastName;
}
Spring Data serializes eventContainer field from ExternalLibraryClass as expected. How can I force to ignore this field?
You should be able to do it using custom converters. Something like this
public class MyDocumentConverter implements Converter<MyDocument, DBObject> {
public DBObject convert(MyDocument source) {
DBObject dbo = new BasicDBObject();
dbo.put("firstName", source.getFirstName());
dbo.put("lastName", source.getLastName());
return dbo;
}
}
EDIT
Can't guarantee this will work, but better approach would be to create a converter for ExternalLibraryClass, and ignore the eventContainer
public class ExternalLibraryClassConverter implements Converter<ExternalLibraryClass, DBObject> {
public DBObject convert(ExternalLibraryClass source) {
DBObject dbo = new BasicDBObject();
return dbo;
}
}
Again, this is just an idea, haven't tried it, wouldn't be surprised if it doesn't work.
I have a class as follows:
package mongo;
import com.mongodb.BasicDBObject;
public class tweet extends BasicDBObject{
private String name;
private Integer id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
Now I am trying to insert the object of this class into mongodb:
public void connect() throws UnknownHostException
{
MongoClient mongoClient = new MongoClient( "localhost" , 27017 );
DB db = mongoClient.getDB( "test" );
tweet t=new tweet();
t.setId(100);
t.setName("Gggggg");
DBCollection Collection = null ;
Collection = db.getCollection("test");
DBObject doc = new BasicDBObject();
doc.put("first", t);
Collection.save( doc);
System.err.println(Collection.findOne());
}
But when I run this code, the object t is not inserted to the db and the system.err.println return the following:
{ "_id" : { "$oid" : "546c00efbadcd42088c8fee3"}}
How can I add the object into mongodb? Is it possible to do that?
BasicDBObject is actually a LinkedHashMap. So when you extend it, you need to put and retrieve values as you do it in a map.
So simply setting the values as attributes doesn't make sense, those attributes need to be put inside the Tweet map.
You need to modify your Tweet Class as follows:
class Tweet extends BasicDBObject{
public String getName() {
return (String)this.get("name");
}
public void setName(String name) {
this.put("name", name);
}
public Integer getId() {
return (Integer)this.get("_id");
}
public void setId(Integer id) {
this.put("_id", id);
}
}
and your main method as:
MongoClient mongoClient = new MongoClient( "localhost" , 27017 );
DB db = mongoClient.getDB( "test" );
Tweet t=new Tweet();
t.setId(100);
t.setName("Ghorbani");
DBCollection collection = null ;
collection = db.getCollection("test");
collection.save(t);
System.err.println(collection.findOne());
If you notice,collection.save(t);, the Tweet object is being directly saved. That is how it should work.
o/p:
{ "_id" : 100 , "name" : "Ghorbani"}
You seem to be trying to set an ID for your object. Usually that is something that is done automatically from Mongo. Try removing t.setId(100); and then run your code again.
Tip - try using Mongoose to manage your connection to Mongo.
so I have a POJO object that I am creating and saving to a MongoDB collection using Jongo:
import java.util.Map;
public class MyObject {
private String name;
private Map<String, String> mappings;
public MyObject() {
}
public MyObject(String name, Map mappings) {
this.name = name;
this.mappings = mappings;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Map<String, String> getMappings() {
return mappings;
}
public void setMappings(Map<String, String> mappings) {
this.mappings = mappings;
}
}
Test class for saving objects to mongo:
import com.mongodb.DB;
import com.mongodb.MongoClient;
import org.jongo.Jongo;
import org.jongo.MongoCollection;
import java.net.UnknownHostException;
import java.util.HashMap;
public class NullFieldTest {
public static void main(String[] args) throws UnknownHostException {
DB db = new MongoClient("localhost", 27017).getDB("testDB") ;
Jongo jongo = new Jongo(db);
MongoCollection testCollection = jongo.getCollection("testCollection");
MyObject objectA = new MyObject("objectA", new HashMap());
MyObject objectB = new MyObject("objectB", null);
testCollection.save(objectA);
testCollection.save(objectB);
}
}
This test saves the objects fine:
{
"_id" : ObjectId("543cf1a6cd8936deafcf66cf"),
"name" : "objectA",
"mappings" : {}
}
{
"_id" : ObjectId("543cf1a6cd8936deafcf66d0"),
"name" : "objectB"
}
but what I really want is for the null mappings in objectB to appear as
"mappings" : null
I know that a field within a collection can have a null value, but I dont know how to do this with the jongo driver, any ideas?
FYI, I'm using jongo V1.1
problem solved, my issue was in the serialization of the objects, added an annotation from Jackson to include even null fields:
#JsonInclude(JsonInclude.Include.ALWAYS)
public class MyObject {
Found more details on this post:
How to tell Jackson to ignore a field during serialization if its value is null?
Jongo comes with a pre-configured Jackson which ignore null fields : https://github.com/bguerout/jongo/blob/443b461e47acdcffb9f51efafb291b7e0c805c26/src/main/java/org/jongo/marshall/jackson/configuration/VisibilityModifier.java
You can change this configuration by creating a custom Mapper using JacksonMapper.Builder :
Mapper mapper = new JacksonMapper.Builder().addModifier(new MapperModifier() {
public void modify(ObjectMapper mapper) {
mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
}
}).build();
Jongo jongo = new Jongo(db, mapper);
I have a simple User model whose fields are annotated with play validation annotations and morphia annotations like below.
import play.data.validation.*;
import play.modules.morphia.Model;
import com.google.code.morphia.annotations.*;
#Entity
public class User extends Model{
#Id
#Indexed(name="USERID", unique=true)
public ObjectId userId;
#Required public String userName;
#Email
#Indexed(name="USEREMAIL", unique=true)
#Required public String userEmail;
}
Now I have a service which has a CreateNewUser method responsible for persisting the data. I have used Morphia plugin for the dao support. But the problem is that User Document gets persisted in mongo-db even if userName or userEmail is NULL. Also #Email validation does not happen
// Below code is in app/controllers/Application.java
User a = new User();
a.userName = "user1";
// calling bean to create user, userService is in app/service/UserService
userService.createNewUser(a);
It does not work even after adding #valid and validation.hasErrors() check.Below code is in app/service/UserService
public void createNewUser(#Valid User user) {
if (Validation.hasErrors()) {
System.out.println("has errors");
} else {
// TODO Auto-generated method stub
userDao.save(user);
}
}
Now I understand, createNewUser is not an action.
So you can enforce object validation:
public void createNewUser(User user) {
final Validation.ValidationResult validationResult = validation.valid(user);
if (validationResult.ok) {
userDao.save(user);
} else {
System.out.println("has errors");
}
}
api: http://www.playframework.org/documentation/api/1.2.5/play/data/validation/Validation.html
Old answer
You forgot an annotation to validate the object and you must check if the form has errors.
public void createNewUser(#Valid User user) {
if(validation.hasErrors()) ...
source: http://www.playframework.org/documentation/1.2.5/validation#objects
Just getting into the NoSQL stuff so forgive me if this is a simple question. I am trying to somewhat implement a repository type pattern using a generic repository for the more common operations.
One thing that I have run into that is killing this idea is that in order to get the collection you plan to work with you have to pass a string value for the name of the collection.
var collection = database.GetCollection<Entity>("entities");
This means that I have to hard code my collection names or code up a dictionary somewhere to act as a lookup so that i can map the object type to a collection name.
How is everyone else handling this?
What you can do is "semi-hardcode." You can put the name of the collection in a class name and refere to it:
public class Entity {
public static readonly string Name = "entities";
}
var collection = database.GetCollection<Entity>(Entity.Name);
I wrote a class to manage DB transactions
First you need a base class for all entities:
public abstract class Entity
{
public ObjectId Id { set; get; }
}
then an static class to manage all:
public static class MongoDB
{
private static string connectionString = "mongodb://localhost";
public static string DatabaseName { get { return "test"; } }
private static MongoServer _server;
private static MongoDatabase _database;
public static MongoServer Server
{
get
{
if (_server == null)
{
var client = new MongoClient(connectionString);
_server = client.GetServer();
}
return _server;
}
}
public static MongoDatabase DB
{
get
{
if(_database == null)
_database = Server.GetDatabase(MongoDB.DatabaseName);
return _database;
}
}
public static MongoCollection<T> GetCollection<T>() where T : Entity
{
return DB.GetCollection<T>(typeof(T).FullName);
}
public static List<T> GetEntityList<T>() where T : Entity
{
var collection = MongoDB.DB.GetCollection<T>(typeof(T).FullName);
return collection.FindAll().ToList<T>();
}
public static void InsertEntity<T>(T entity) where T : Entity
{
GetCollection<T>().Save(entity);
}
}
then use it like this:
public class SomeEntity : Entity { public string Name {set;get;} }
MongoDB.InsertEntity<SomeEntity>(new SomeEntity(){ Name = "ashkan" });
List<SomeEntity> someEntities = MongoDB.GetEntityList<SomeEntity>();
I finally found an approach very usefull for me as all my mongo collections follow a camel case underscore naming convention, so I made a simple string extension to translate the POCO naming convention to my mongo convention.
private readonly IMongoDatabase _db;
public IMongoCollection<TCollection> GetCollection<TCollection>() =>
_db.GetCollection<TCollection>(typeof(TCollection).ToString().MongifyToCollection());
This method is inside a class made for handling mongo using dependency injection and it also wraps the default GetCollection to make it a bit more OO
public class MongoContext : IMongoContext
{
private readonly IMongoDatabase _db;
public MongoContext()
{
var connectionString = MongoUrl.Create(ConfigurationManager.ConnectionStrings["mongo"].ConnectionString);
var client = new MongoClient(connectionString);
_db = client.GetDatabase(connectionString.DatabaseName);
RegisterConventions();
}
public IMongoCollection<TCollection> GetCollection<TCollection>() =>
_db.GetCollection<TCollection>(typeof(TCollection).Name.MongifyToCollection());
...
And the extension:
// It may require some improvements, but enough simple for my needs at the moment
public static string MongifyToCollection(this string source)
{
var result = source.Mongify().Pluralize(); //simple extension to replace upper letters to lower, etc
return result;
}