MongoDB Java - update a field in a record - mongodb

I would like to update a field of a record which has been already been stored in the database. How can I achieve that? All of the examples I've come across so far are either in Python/Php or an older version of MongoDB (Java) and the api is not longer current.
Basically I would like to load an object with a particular userId in memory and change its username value. (I'd like to refer you to the code below)
Thank you so much
EDIT
So far I've got the following code:
public class UserDAO {
MongoOperations mongoDb;
public UserDAO(){
mongoDb = MongoDBInstanceFactory.getMongoDBinstance();
}
public User getUserByUsername(String username){
Query searchUserQuery = new Query(Criteria.where("username").is(username));
return mongoDb.findOne(searchUserQuery, User.class);
}
public User getUserById(String id){
Query searchUserQuery = new Query(Criteria.where("id").is(id));
return mongoDb.findOne(searchUserQuery, User.class);
}
public User addUser(String username){
mongoDb.save(new User(username));
return getUserByUsername(username);
}
}
#Document(collection = "users")
public class User {
public User(String username) {
this.username = username;
}
public User() {
}
#Id
private String id;
private String username;
//getter, setter, toString, Constructors
/**
* #return the id
*/
public String getId() {
return id;
}
/**
* #return the username
*/
public String getUsername() {
return username;
}
}
#RequestMapping(value = "/registerUser")
public #ResponseBody
String registerUser(#RequestParam(value = "username") String username, HttpServletResponse response) throws IOException {
sb = new StringBuilder();
user = userDao.getUserByUsername(username);
if (user == null) {
user = userDao.addUser(username);
userQueryPhaseDao.addUserQueryPhase(user.getId(), null, "0");
sb.append("1|").append(user.getId());
response.addCookie(new Cookie("userId", user.getId()));
} else {
sb.append("0|User with that handle already exists!");
}
return sb.toString();
}
Thank you for your time

To change an existing document, use the method DBCollection.update.
The method takes two parameters.
The first parameter tells MongoDB which document to update. It works exactly like find or findOne.
The second is the document that document will be replaced with. When you only want to update a single field and not replace the whole document, you need to use the $set operator.

Related

Exception when selecting specific columns using Hibernate and Spring Data JPA

I have a table that has a bytea column (named 'pdf') and I don't want to always select it, specially when I'm returning a list from the database, due to performance issues.
I use native queries with spring data inside the repository to solve these types of situations before (when I used eclipselink), but with Hibernate, if I don't write all the columns in the query, it throws an exception.
For test purposes, I'm trying to select only the id from the User and I still get the exception.
Example: "SELET user.id FROM user WHERE user.id = '1'"
It throws an exception saying that it did not find name in the ResultSet, if I put name in the SQL, it then says age was not found and so on, until I have to write all the columns in the SQL.
Thanks in advance for any help.
What I have tried already:
Updating/Downgrading Hibernate and Spring Data with no luck.
Creating a new entity with only the columns I need, works, but it's a messy solution for me.
Maybe the problem is the combination of the frameworks I use and the way I use them, if someone wants, I could try to upload my whole project structure.
My code:
Entity
#Entity
#Table(name = "user", schema = "portal")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Column(name = "pdf")
private byte[] pdf;
#Column(name = "name")
private String name;
#Column(name = "age")
private Integer age;
public User() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public byte[] getPdf() {
return pdf;
}
public void setPdf(byte[] pdf) {
this.pdf = pdf;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
#Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Anexo)) {
return false;
}
Anexo other = (Anexo) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
#Override
public String toString() {
return "br.gov.to.secad.portal.domain.User[ id=" + id + " ]";
}
}
Service
#Service
#Transactional(readOnly = true)
public class UserService implements Serializable {
private static final long serialVersionUID = 1L;
#Autowired
private IUserRepository userRepository;
public UserService() {
}
public User findOne() {
return userRepository.findOneSQL();
}
}
Repository
public interface IUserRepository extends JpaRepository<User, Serializable>, JpaSpecificationExecutor {
#Query(value = "SELECT user.id FROM user WHERE user.id = '1'", nativeQuery = true)
public User findOneSQL();
}
The exception:
org.postgresql.util.PSQLException: The column name name was not found in this ResultSet.
Solution
The solution is using an array of Object when I want to select anything less than what I've mapped on my Entity class, thats the limitation of Hibernate that I now understand.
So basically, the method will return Object[] and then I can iterate each position and instantiate a new entity of User with these values.
Example:
#Query(value = "SELECT user.id FROM user WHERE user.id = '1'", nativeQuery = true)
public Object[] findOneSQL();
I have faced the same problem, I know it is late but well there is a solution that I found elegant.
By the Spring documentation you can declare an interface and from here take the fields you want, in my case it has been something similar to this.
The interface to minimize the fields:
public interface CountryMinify {
String getName();
String getNameTranslation();
}
And my JpaRepository
public interface PlanetRepository extends JpaRepository<Planet, Long> {
#Query(value = "select p.name_country as name, p.name_country_translation as nameTranslation from vm_planet p where gid = ?1", nativeQuery = true)
CountryMinify findByCode(String codeCountry);
}
Keep in mind that the columns should be called the same as gos getter. For example: column name_country -> AS name and the getter of the interface is getName()
Try this
#Query(value = "SELECT user.id FROM user WHERE user.id = '1'", nativeQuery = true)
Integer findOneSQL();
Call the method like so
Integer user = userRepository.findOneSQL();
Edit 1 :
Since you are using native query you wont be able to use Projections which is a great way of accessing only certain entity fields. There is a JIRA ticket which is still under investigation.
Solution
Return List from your repository like so
#Query(value = "SELECT user.id, user.name FROM user WHERE user.id = '1'", nativeQuery = true)
List<Object[]> findOneSQL();
Iterate over the list of Objects and get your specific columns.
List<Object[]> userNative = userRepository.findOneSQL();
for (Object[] obj : userNative) {
System.out.println("User id : " + obj[0]);
System.out.println("User Name : " + obj[1]);
}

Get the same java object on saving that object in mongodb

MY POJO looks like this:
#Document(collection = "users")
public class User {
#Id
private String id;
String username;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
I am able to save the object's values in the mongodb by calling mongoDBClient.save(userObj); as bellow:
public void save(Object paramObject) {
mongoOperations.save(paramObject);
}
The problem with this save api is that it's returning void. I want to get the same object which was saved. How can I achieve that?
paramObject is viable for introspection after save. It represents it's persistent state at that stage.
For example, the driver is responsible to assign the id on your behalf.
In addition, all properties which you don't set yourself and are designated to be populated by spring data - such as properties annotated with #CreatedDate - are also available after the save() method (assuming auditing is configured).
In any case, the Id is set. Later you can query for the same object by calling findById(paramObject.getId(), User.class)

update mongodb document using java object

I have one User class like this:
#Document(collection = "users")
public class User {
#Id
private String id;
String username;
String password;
String description;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
#Override
public String toString() {
return "User[id=" + id + ", username=" + username + ", password=" + password + ", description"
+ description + "]";
}
}
I am able to perform limited update. Like:
Query searchQuery = new Query(Criteria.where("id").is("shashi"));
mongoDBClient.updateFirst(searchQuery, Update.update("password", "newpassword"), User.class);
Now if I want to update rest other fields(username and description) of User class, I need to call updateFirst method so many times.
I want to avoid this and pass the entire object to updateFirst method. Something like:
mongoDBClient.updateFirst(searchQuery, Update.update(userObject), User.class);
Basically, I want to edit all/multiple fields in one call using java POJO object. How I can achieve this?
Edit/All multiple fields in one call using java POJO object, can be done as shown below
1) Query the document which need to be updated --> we get the java object
2) Do all modifications in the java object
3) Save the object
Code:
Query query = new Query();
query.addCriteria(Criteria.where("id").is("shashi"));
User user = mongoOperation.findOne(query, User.class);
//modify the user object with the properties need to be updated
//Modify password and other fields
user.setPassword("newpassword");
user.setDescription("new description");
user.setUsername("NewUserName");
//save the modified object
mongoOperation.save(user);

how to get inserted object id in jongo with mongodb

Account entity
public class Account
{
#MongoObjectId
private String _id;
private String name;
public String get_id() {
return _id;
}
public void set_id(String _id) {
this._id = _id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class JongoExample {<br>
MongoClient mongoClient = new MongoClient(); // code to connect with database
DB db = mongoClient.getDB("kommboxj");
Jongo jongo = new Jongo(db);<br>
MongoCollection accountCollection = jongo.getCollection("account");
Account account = new Account();<br>
account.setName("rks");<br>
String accountIdMongo = (String) accountCollection.insert(account).getUpsertedId();}
Here I found acountIdMongo is null, don't know how to get it, here I want inserted account ObjectId.
For me this helped:
Jongo Starter Project
Based on the above the _id in Color.java would be:
#MongoObjectId
private String _id;
and then in PersistenceHandler.java in the insert method the Object returned would contain the inserted object in the Mongo collection with _id populated.
public static Object insert(MongoCollection mongoCollection, Method method, Object[] args) throws Throwable {
final Iterable<Parameter> params = Reflection.params(method, args);
final Parameter parameter = params.iterator().next();
Object value = parameter.getValue();
if (value == null)
throw new IllegalArgumentException(parameter.getType().getSimpleName() + " object is null");
mongoCollection.insert(value);
return value;
}

Update multiple - but a subset of - object fields at once without replacing entire document (Spring 3.1 and MongoTemplate for MongoDB)

I'm trying to update a number of fields at the same time in my "User" document. However, I only want to update some of the fields and not replace the entire document and it's the latter that I cannot seem to avoid. The method I have for doing this looks like so:
public void mergeUser(User user) {
Update mergeUserUpdate = new Update();
mergeUserUpdate.set("firstName", user.getFirstName());
mergeUserUpdate.set("lastName", user.getLastName());
mergeUserUpdate.set("username", user.getUsername());
mongoTemplate.updateFirst(new Query(Criteria.where("_id").is(user.getId())), mergeUserUpdate, User.class);
}
My user object does contain other fields - a password field being one of them - but if this was set to a value before it is promptly replaced with an empty string or removed entirely. So in the database, this:
{
"_id" : ObjectId("4fc34563c3276c69248271d8"),
"_class" : "com.test.User",
"password" : "d26b7f5c0ed888e46889dd1e3d217816d070510596f495e156e9efe4b035fec5a1fe1be643955359",
"username" : "john#gmail.com",
"alias" : "john"
}
gets replaced by this after I call the mergeUser method:
{
"_id" : ObjectId("4fc34563c3276c69248271d8"),
"_class" : "com.test.User",
"username" : "john#gmail.com",
"firstName" : "John",
"lastName" : "Doe",
"address" : {
"addressLine1" : ""
}
}
If I look at the Update object I see it contains the following:
{$set={firstName=John, lastName=Doe, username=john#gmail.com}}
This looks correct to me and from my understanding of the MongoDB $set function, this should only set the values that are specified. I was therefore expecting the password field to remain unchanged and the other fields added or altered accordingly.
As a general discussion point, I'm ultimately trying to achieve some kind of "merge" functionality whereby Spring will auto-magically check which fields are present in the supplied User object and only update the database with the values that are filled in, not all the fields. That should be theoretically possible I would have thought. Anyone know of a nice way to do this?
Here's my user object just in case:
/**
* Represents an application user.
*/
#Document(collection = "users")
public class User {
#Id
private String id;
#NotEmpty( groups={ChangePasswordValidationGroup.class} )
private String password;
#Indexed
#NotEmpty
#Email
private String username;
private String firstName;
private String lastName;
private Date dob;
private Gender gender;
private Address address;
public enum Gender {
MALE, FEMALE
}
// /////// GETTERS AND SETTERS ///////////
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Date getDob() {
return dob;
}
public void setDob(Date dob) {
this.dob = dob;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Gender getGender() {
return gender;
}
public void setGender(Gender gender) {
this.gender = gender;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getAlias() {
return alias;
}
public void setAlias(String alias) {
this.alias = alias;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
The code above does work just fine. I made a silly mistake whereby after updating correctly I proceed to save the user object again, which replaces it with a new document.
Having another solution to update an Entity without some fields using Spring MongoTemplate:
DBObject userDBObject = (DBObject) mongoTemplate.getConverter().convertToMongoType(user);
//remove unnecessary fields
userDBObject.removeField("_id");
userDBObject.removeField("password");
//Create setUpdate & query
Update setUpdate = Update.fromDBObject(new BasicDBObject("$set", userDBObject));
mongoTemplate.updateFirst(new Query(Criteria.where("_id").is(user.getId())), setUpdate , User.class);
//Or use native mongo
//mongoTemplate.getDb().getCollection("user").update(new BasicDBObject("_id",user.getId())
, new BasicDBObject("$set", userDBObject), false, false);
Because it uses auto converter so is is very helpful when your entity has many fields.