cast mongodb query result to data model class in java - java

I want to create a data model of java class, so that I can automatically get some Properties of the data Model which retrieved from the dbcollection of mongodb by using method defined in the class. Let's say if I have a data structure stored in mongodb collection named "STUDENT" like:
{
"name":"Jone",
"id":"20140314201"
"courses":[
{
"CourseName":"math",
"teacher":"Prof Smith",
"Score":80
},
{
"CourseName":"literature",
"teacher":"Brown"
"Score":58
}
]
}
It's always convenient to define a student class like this:
class Student extends BasicDBObject{
private List<Course> courseList = new ArrayList();
private final String name;
private final String id;
public Student(String _name,String _id){
name = _name;
id = _id;
}
public List<Course> getFailedCourseList(){
List<Course> failedCouseList = blablabla...
return failedCouseList
}
public addCourse(Course _course){
couseList.add(_cousrse);
}
.....
}
The question is can I just do some job to make these happen:
1. when saving a STUDENT items into mongodb I can just do this:
Student studentItem = new Student("Jone","20140314201")
studentItem.addCourse(course1)
studentItem.addCourse(course2)
....
DBC.save(studentItem)
2. when retrieve data from db collection I can just cast BasicDBObject(which is the default object type dbcollection findOne returned) to Class Student I defined:
Student studentJone = (Student)DBC.findOne(new BasicDBObject("name":"Jone"));
so that I can just find Out what courses failed by just invoke a method of the Student class:
List<Course> failedCourseList = studentJone.getFailedCourseList();

Try this:
BasicDBObject query= new BasicDBObject();
query.put("any key","any value"); //THis add criteria
DBObject dbObjectResult = getMongoTemplate().getCollection(COLLECTION)
.findOne(query);
Foo foo = getMongoTemplate().getConverter().read(Foo.class, dbObjectResult);
Should be work

Of course you can
Please see this url : http://docs.mongodb.org/ecosystem/tutorial/use-java-dbobject-to-perform-saves/

Related

How to map multiple data rows to different fields

I'm trying to use querydsl and projection to map the results of a query onto an object.
I have something like the following:
public record DataTransferObject(MyTable obj1, MyTable obj2) {
public static final ConstructorExpression<DataTransferObject> PROJECTION =
Projections.constructor(obj1, obj2);
}
#Service
public class QueryService {
private final JPQLQueryFactory queryFactory;
public DataTransferObject getData(String id1, String id2) {
return queryFactory.select(DataTransferObject.PROJECTION)
.from(QMyTable.myTable)
.where(QMyTable.myTable.id.eq(id1)
.or(QMyTable.myTable.id.eq(id2))
.fetchOne();
}
}
But this doesn't work as I run into com.querydsl.core.NonUniqueResultException.
Using joins like in the following results in obj1 and obj2 being the same object (even though the 2 ids map to 2 unique rows):
.leftJoin(QMyTable.myTable)
.on(QMyTable.myTable.id.eq(id1))
.leftJoin(QMyTable.myTable)
.on(QMyTable.myTable.id.eq(id2))
I want to match DB row corresponding to String id1 to the MyTable obj1 field in the DataTransferObject object. Similarly, I want to match DB row corresponding to String id2 to the MyTable obj2 field in the DataTransferObject object.
What's a preferable/best way to accomplish this?
Try changing fetchOne() to fetchFirst() or fetchAll()
Looks like I preemptively defining the objects worked. It's working with something like:
#Service
public class QueryService {
private static final QMyTable OBJ1 = new QMyTable("obj1");
private static final QMyTable OBJ2 = new QMyTable("obj2");
public static final ConstructorExpression<DataTransferObject> PROJECTION = Projections.constructor(
MyTable.projection(OBJ1),
MyTable.projection(OBJ2)
);
private final JPQLQueryFactory queryFactory;
public DataTransferObject getData(String id1, String id2) {
return queryFactory.select(PROJECTION)
.from(QMyTable.myTable)
.leftJoin(OBJ1)
.on(OBJ1.id.eq(id1))
.leftJoin(OBJ2)
.on(OBJ2.id.eq(id2))
.fetchOne();
}
}
Left some stuff out, but that's the gist.

JSON by Jackson: hide field name but keep the value

I am creating a JSON using Jackson library.
For example I have an Employee with fields: name, surname, and a list of pair of info as showed below
#Data
#NoArgsConstructors
public class EmployeeJSON {
private String name;
private String surname;
private List<CoupleInfo> addresses = new ArrayList<>();
// this class continue with regular implementation to fill the fields and creating an object for CoupleInfo
A class is implemented for CoupleInfo:
#Data
#NoArgsConstructors
public class CoupleInfo {
private List<String> list = new ArrayList<>();
public CoupleInfo(String place, String address){
list.add(place);
list.add(address);
}
}
I would like to get in the JSON, of course with the fields name and surname, the field addresses populated as list of the couple of values (as array in the JSON), but without, inside the array, the field list.
How can do that?
I tried with #JsonIgnore but all the field and the value disappear.
To get this at the end:
{"name":"giulio", "surname":"marri", "addresses":[["roma","piazza liberta'"],["torino","via torre"]]}
instead to be:
{"name":"giulio", "surname":"marri", "addresses":[{"list":["roma","piazza liberta'"]},{"list":["torino","via torre"]}}
you can use #JsonValue to get the addresses as list of lists with no field name:
#Data
#NoArgsConstructor
public class CoupleInfo {
public List<String> list = new ArrayList<>();
public CoupleInfo(String place, String address){
list.add(place);
list.add(address);
}
#JsonValue
public List<String> getList() {
return list;
}
}
the result will be:
{"name":"giulio","surname":"marri","addresses":[["roma","piazza liberta"],["torino","via torre"]]}

Type mismatch: cannot convert from List<Map<String,Object>> to List<Object>

We have
String sql = "SELECT * FROM user WHERE id = '" +userId +"'";
List<Object> userList = template.queryForList(sql);
The query shall return the different users with the given id.
The template is an object of JdbcTemplate class.
Here the query returns a List of Map<String,Object> and the left hand side is List<Object>. Is there any way to do the conversion. This is part of the spring transaction management example.
You can always use only the values which you are interested in the first place like:
List<Object> userList = new ArrayList<Object>(template.queryForList(sql).values());
In case it returns List<Map<String, Object>> instead of Map<String,Object>:
List < Object > userList = new ArrayList < Object > ();
for (Map < String, Object > obj: template.queryForList(sql)) {
userList.addAll(obj.values());
}
This should give you a list of Object as you need. If you need to explore why Map.values() cannot be directly casted to list and we need to create a new ArrayList for that purpose can be found here: why HashMap Values are not cast in List
I think you should take another approach. Spring can not build objects from the data returned from the database, but you can use a RowMapper to construct user objects. Additionally you should not build the query on your own but use PreparedStatements. Otherwise you might be vulnerable to SQL injection.
Here is a example on how to do it:
template.query("SELECT * FROM user WHERE id = ?", new Object[] { userId }, new RowMapper<User>() {
public User mapRow(ResultSet rs, int rowNum) {
// Build a user from the current row and return it
}
});
I think you have confused it a lot. The best way to do it is indeed what is suggested by Steffen Kreutz. Consider the following example:
User Table in DB has following fields:
1. UserID -> Type: Int
2. User_Name -> Type: Varchar
3. User_Contact -> Type: Varchar
...
Now, you can simple write a RowMapper to map all these fields to your custom POJO Object like follows:
POJO Class:
public class User{
private int userId;
private String userName;
private String userContact;
public int getUserId() {
return this.userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
/* Other Getter-Setter(s) */
}
Query String:
private static final String SELECT_QUERY = "SELECT * FROM user WHERE id = ?";
JdbcTemplate Call:
Here you are passing userId for ?. JdbcTemplate will automatically take care of it.
List<User> = (List<User>) jdbcTemplate.query(SELECT_QUERY, new Object[] { userId }, new UserRowMapper());
Finally the UserRowMapper Class:
class UserRowMapper implements RowMapper {
#Override
public Object mapRow(ResultSet resultSet, int row) throws SQLException {
User user = new User();
user.setUserId(resultSet.getInt("UserID"));
user.setUserName(resultSet.getString("User_Name"));
user.setUserContact(resultSet.getString("User_Contact"));
return user;
}
}
This is indeed the best and recommended way of using JdbcTemplate.

How to add two different objects into a single object

Here is my function which is adding two different objects.
#GET
#Path("/getApplicationEnv")
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public List<Object> getApplicationEnv(){
List<ApplicationDTO> allApplication = applicationService.getAllApplication();
List<Application> Applist = new ArrayList<Application>();
for(ApplicationDTO d: allApplication)
{
Application a = new Application();
a.setApplicationId(d.getApplicationId());
a.setApplicationName(d.getApplicationName());
a.setCreateTime(d.getCreateTime());
a.setOwner(d.getOwner());
Applist.add(a);
}
List<EnvironmentDTO> allEnvironments = environmentService.getAllEnvironments();
List<Environment> Envlist = new ArrayList<Environment>();
for(EnvironmentDTO d: allEnvironments)
{
Environment e = new Environment();
e.setEnvironmentId(d.getEnvironmentId());
e.setEnvironmentName(d.getEnvironmentName());
e.setOwner(d.getOwner());
e.setCreateTime(d.getCreateTime());
Envlist.add(e);
}
ArrayList<Object> obj= new ArrayList<Object>();
obj.addAll(Applist);
obj.addAll(Envlist);
return obj;
}
Currently I am using ArrayList of Object for adding two objects, but I am getting following error:
*SEVERE: A message body writer for Java class java.util.ArrayList, and Java type java.util.List, and MIME media type application/xml was not found
*
I have tried making a common parent class but I am having some attributes of both the class common so it is not possible to have a common parent class.
Can anyone please suggest me a way to achieve this ?
you can try changing
ArrayList<Object> obj= new ArrayList<Object>();
obj.addAll(Applist);
obj.addAll(Envlist);
to
ArrayList<MyModel> obj= new ArrayList<MyModel>();
MyModel mm = new MyModel();
mm.setVal1(Applist);
mm.setVal2(Envlist);
obj.add(mm);
UPDATE
class MyModel
{
private ApplicationDTO appDTO;
private EnvironmentDTO enDTO;
//getters settters here
}
here MyModel is just a model class with two fields as per your requirement..
hope this helps
The problem is that object is not considered as an xml element. You can create an object like this
#XmlRootElement
public class MyObject{
private List<Application> somename;
private List<Environment> somename2;
//Getters
//setters
}
and do like this
public List<MyObject> getApplicationEnv(){
/*Some code*/
}

Is there a best way to clone a model for changing just one entry?

I have a model with some fields, and I'd like to add a new entry in
the database of this model, but with changing only one field. Is there
a best way to do so, without having to create a new instance and
setting one by one each field ?
Case :
public class MyModel extends Model {
public String firstname;
public String lastname;
public String city;
public String country;
public Integer age;
}
And the code I actually have
MyModel user1 = MyModel.findById(1);
MyModel user2 = new MyModel();
// is there a way to do it with clone or user1.id = null ? and then create()?
// Actually, I do that :
user2.firstname = "John";
user2.lastname = user1.lastname;
user2.city = user1.city;
user2.country = user1.country;
user2.age = user1.age;
user2.create();
What I am lookig for would to do something like :
MyModel user1 = MyModel.findById(1);
MyModel user2 = clone user1;
user2.firstname = "John";
user2.create();
or
MyModel user = MyModel.findById(1);
user.id = null;
user.firstname = "John";
user.create();
But I don't know if it's correct to do it like that.
Implement the Cloneable interface for the entity, & than calling clone() method will return a shallow copy of the original object. To obtain a deep copy, override it, where you can set id as null & copy non-primitive fields.
#Override
protected Object clone() throws CloneNotSupportedException {
MyModel model = (MyModel) super.clone();
model.setId(null);
//-- Other fields to be altered, copying composite objects if any
return model.
}
Persisting the cloned object :
MyModel user = MyModel.findById(1);
detachedUser = user.clone(); //-- cloning
user.firstname = "John"; //-- modifying
user.create(); //-- persisting

Categories

Resources