I am new to mongo-db and i have few questions.
I have the following code:
public class User
{
private String id;
private String name;
private List<Order> orders;
}
public class Order
{
private String id;
private String orderName;
private Date orderDate;
}
What's the best persisting strategy for User object ?
should i create collection for both User and Order or just for User ?
should i save Order and then user ?
i am using spring data MongoRepository
Thank you.
I would consider how you're accessing the data when modeling. Some questions to ask yourself:
Do I need to get a user with his orders in one call?
How many orders are with a user on average? If it's a lot, maybe not best to denormalize user and orders.
How will my front end access this information? Will most calls to user even need orders? Will that information be too heavy/slow on the wire?
In general I would err on the side of denormalizing instead of the relational instinct to normalize. It's ok to have redundant data and to have inconsistent data.
Mongo doesn't do joins real time - at best you can do map/reduce.
The joining of data therefore either needs to be in the database (denormalized) or in the UI.
Related
Edit: I think it would be helpful to explain my goal here first. My goal is to reduce and avoid model/#Entity bloat when using stored procedures with Hibernate. You can get raw data back from the persistent EntityManager when using a stored procedure, but that data will not be mapped. If you send in a stripped down model to Hibernate, Hibernate will only send you back the columns which are annotated as #Column on the #Entity model (almost forcing you to create a new #Entity for every stored procedure!) You can attempt to map this data with a DTO that has more properties, but they won't map to anything because all the fields which were not included on the model will return null.
I've been struggling to find an answer to this in my research. We use an MSSQL database and Spring with JPA/javax/Hibernate persistence, but do not rely on Hibernate for its ORM. All CRUD operations are done using stored procedures. We have several models (Spring #Entity) which work well for retrieving and mapping data. For example, a basic user model.
import javax.persistence;
#Entity
public class User {
#Id
#Column
private int userID;
#Column
private String userName;
#Column
private Date userDOB;
public User(UserDTO userDTO){
userName = userDTO.getUserName();
userID = userDTO.getUserID();
// Extra column userDOB, and no way to map accountDetails from the DTO
}
public int getUserID(){...}
public String getUserName(){...}
public Date getUserDOB(){...}
}
This works well when the stored procedure selects columns in a way that matches up with the model, however in cases where we want to selectively query data using joins, the columns (names and number of columns) often don't match up with the models. In this case, it makes sense to have DTOs to actually map the receiving data from the database using a constructor in the related Model. However, Spring doesn't like injecting the DTO directly (because it complains it isn't an entity), and injecting the model directly into the stored procedure query will fail, since the columns (number of columns!) don't match up.
public class UserDTO {
private String accountDetails;
private int userID;
private String userName;
public int getUserID(){...}
public String getUserName(){...}
}
I've tried using ModelMapper, but the first argument is an Object (the data), which isn't obtainable, since the data can't be mapped. I can of course call StoredProcedureQuery without a Model hint, and receive the raw data, but it will not be mapped.
public class UserRepo {
import javax.persistence;
public List<UserDTO> getUserAccountInfo(){
StoredProcedureQuery query = entityManager.createQuery(storedProcedureSelectUserAccount, User.class);
query.execute(); // Will fail here with SQL Server error: Unknown column userDOB.
List<UserDTO> result = query.getResults();
return result;
}
}
Others have suggested using raw Selects or other String based mapping strategies, but I would really appreciate some advice on retrieving and mapping the returned data using a DTO with the already written Stored Procedures. Thank you!
I have a POJO called User which is also being used for inserting documents in MongoDb.
#Data
#Document(collection = Constants.COLLECTION_USERS)
public class User {
public ObjectId _id;
public String userID;
public String email;
public String name;
public String sex;
public String dob;
public String photo;
//have more variables
}
I have a simple application where a user registers by giving in a subset of data listed in the User class. The signature of the register method in my controller is as follows.
public GenericResponse registerUser(#RequestBody User userRegistrationRequest)
It can be noticed that I am using the same POJO for the registration request. Until now everything is fine. I can persist the data user object just fine.
This registration API is just used to persist a small set of a user's data. There would be other information as well in the MongoDb document, which would be accessed/persisted from some other APIs.
Suppose a user has registered with the basic information and also has persisted other information via APIs other than the registration one.
How would I make an API which can just get me some selective data from the User document again using the same User Pojo? If I call the repository to give data for a specific userID, it will give me the whole document mapped to the User class. I don't want my API to give all the information stored in the document.
One approach is to make another POJO with the details I want, and map the information selectively using a Converter. But, I want to avoid this approach, as I want to use the same class.
Second approach: Modify the Mongo query to return data selectively as given in the docs. But here I would have to specify all the fields I want in the result set. This would again be a length query.
Is there a better way to filter out data from the object?
How would I make an API which can just get me some selective data from the User document again using the same User Pojo?
How would I go off-road with a car I would like to take me girl to the restaurant at the evening? I would not - if I would have the same car for everything I would look stupid next to the restaurant, coming out in a suite or I would stuck in a swamp.
The biggest Java advantage is object creation time - you should not be afraid of it. Just create another model for registration, another as DTO for saving data, another for front-end presentation etc
Never mix responsibility of objects. You will finish with something like
#Entity
class ThePileOfShit {
#Id
private Long id;
#my.audit.framework.Id
private String anotherId;
#JsonIgnore
// just a front-end flag ignore
private boolean flag;
#Column
// not a field but getter because of any-weird-issue-you-want-to-put-here
public String getWeirdStuff() { ... }
// Useless converters
public ModelA getAsModelA() { ... }
public ModelB getAsModelB() { ... }
// etc
// etc
}
Four frameworks, five technologies - nobody knows what's going on.
If you are afraid of converting stuff use ModelMapper or another tool but keep your POJOs as simple as possible
You can use Gson's #Expose annotation only on the fields you want to return in the API.
To serialize the data, use:
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
String json = gson.toJson(userData);
I have a mongodb with two model classes say User and UserInfo. The criteria is in User class I have to retrieve a multiple fields around 10 fields like "firstName","lastName", etc and in UserInfo Model class I like to retrieve only one field say "age".
At this moment I referenced the UserInfo class's object to the User class like stated below in the Structure and its stores in the DB as {"firstName","John"},{"lastName","Nash"},{userInfo: userInfoID} but if I make an Embedded Relation then it would store all the userInfo's fields and I think to retrieve one ("age") field it is Unwanted to Embed all the userInfo's fields which inturn will make the application slow I think.
Which scenario should I use whether #Reference or #Embedded, I think Embedded will slow down my response to DB but in the websites its given as reference annotation only slows down querying time and needs some sort of Lazy Loading an all, my structure is like below:
class User extends Model{
public String firstName;
public String lastName;
public String loginTime;
public String logoutTime;
public String emailId; etc,etc......
Some more 10 fields like this+userInfo reference object
#Reference
public UserInfo userInfo;
}
class UserInfo extends Model{
public String emailId;
public String age;
public String sex;
public String address;
public String bank; etc,etc......
Some more 10 fields like this
}
As I stated above I want only age field from UserInfo and all fields of User, so which Annotation is best and #Reference or #Embedded. It will be more helpful if I get a single query for User class in which I can retrieve all fields of User and only "age" field of UserInfo. In short I need a query like this when I go for #Reference relationship
field("userInfo.age") for userInfo.emailId = (MorphiaQuery q = User.createMorphiaQuery;
q.field("firstName").equal("John"); q.field("lastName").equal("Nash"); q.field("loginTime").greaterthan("sometime"))//the complex part where I need age of particular userInfo but I have only the ID of the userInfo since I am using Reference and that Id too got from a **subQuery**....
Please don't write two queries I need a single query or maybe a query with subquery. To be more clear I can tell in SQL language:
SELECT age FROM UserInfo where emailId = u.emailId
(SELECT * FROM User WHERE firstName='John' AND lastName='Nash' AND
logintime='someTime') AS u;
I need this exact same query without writing two morphia queries which consumes more time by referring two tables.
Mongo does not support query across tables / collections. And such page would satisfy you:
MongoDB and "joins"
As in sql, the join query is also build intermediate result set and make query again:
Understanding how JOIN works when 3 or more tables are involved. [SQL]
When you build your model, you should not consider a lot about what single query but structural modeling:
http://docs.mongodb.org/manual/core/data-modeling/
For your case, if you are using embeded, you can make this in one query and specify the fields you need by using queries like:
db.User.find({"some_field":"some_query"},{"firstName":1,....,"userInfo.age":1})
Check projections here:
http://docs.mongodb.org/manual/reference/method/db.collection.find/
If you are using reference or even soft link like using Morphia Key<> to lazy load the UserInfo, it requires two queries.
If it's not real-time application, you can also try mongo map-reduce to merge collection to handle big data, though the map-reduce is too bad for mongo though.
I'm reasonably sure you can't with just one query.
I am bit confused about what data should a DTO contain.
For example let's assume that we have two tables: User, and Orders.
Orders table contains id_users, which is foreign key to user table.
Obviously I have two DAOs, MysqlUserDao and MysqlOrdersDao, with crud operations, and two transfer objects User, and Order, in which I store jdbc rowset.
If I want to get the list of users and for each user all his orders how should I do:
1) In my MysqlUserDao create a function: getUsersAndOrders(select users.,orders. from users join orders)
And my User DTO should have a OrderList property in where i put orders ?
2) In my MysqlUserDao i create a function getAllUsers(select * from users),
and foreach user I use MysqlOrdersDao function getOrder(id_user);
And some clarifications:
1) For each table in database I need to create a DAO object? or just for complex ones?
For example products and images, should be 2 dao or just one?
2) a DTO object should have only properties and setter getter, or it is possible to have other methods like convertEuroToUsd etc.
thanks
In your scenario #1 is the best option because #2 generates too much overhead.
1) In my MysqlUserDao create a function: getUsersAndOrders(select users.,orders. from users join orders) And my User DTO should have a OrderList property in where i put orders ?
Clarifications:
1: If your database has a good Design, then a DAO for each table is a good approach. There some cases where you can merge DAOs together (e.g: inheritance).
2: Yes. It should be a plain bean (or POJO if you want). I suggest creating another layer where you can define your workflow. I've seem people calling this extra layer as model, sometimes DataManager, sometimes just Manager.
For instance: When creating a order you should insert a record in Order table and also insert a record in the Notification table (because end users will be notified via email every time a order is created)
class OrderManager {
private OrderDAO oDao;
private NotificationDao nDao;
public saveOrder(OrderDTO o) {
Long orderId = oDao.save(o);
NotificationDTO n = new NotificationDTO();
n.setType(NotificationType.ORDER_CREATED);
n.setEntityId(orderId);
nDao.save(n);
}
}
UPDATE:
In most cases we can say that:
"Managers" may handle many DAOs;
DAOs should not contain other DAOs and are tied to a DTO;
DTOs can contain other DTOs
There is an important idea of LAZY or EAGER load when it comes to handling collections. But this is another subject :D
Disclaimer:
+ The following assumes that these DTOs are used mainly for persistence, i.e., for use with DAOs.
+ this approach is very oriented towards a relational database persistence
+ it is assumed a user can have placed orders, but that an order can have at most one user
+ also, that you want to query/process separatedly orders and users
I would have done the following:
a DTO for User (UserDTO + UserDAO)
a DTO for Orders (OrderDTO + OrderDAO)
a DTO to connect both (UserOrderDTO + UserOrderDAO)
I would not have references in the UserDTO to any OrderDTO
I may have a reference in the OrderDTO to the UserDTO as an attribute having a string id (being the string id the user id), but also I may not. I assume the later.
a Service Application to manage the different DAOs associated to the Order (OrderSA)
The resulting code would be as follows:
class OrderManagerServiceApplication {
private OrderDAO oDao;
private UserDao uDao;
private UserOrderDao uoDao;
public saveOrder(OrderDTO o, String userId) {
// Save the order
Long orderId = oDao.save(o);
// Save the association to the user who ordered
UserOrderDTO uodto=new UserOrderDTO(orderId,userId);
uoDao.save(uodto);
}
public List<OrderDTO> getOrdersForUser(String userId) {
// get the orders associated to the user
List<String> orderIds=uoDao.getAllForUser(userId);
// retrieve the order DTOs
ArrayList<OrderDTO> result=new ArrayList<OrderDTO>();
for (String orderId:orderIds){
result.add(oDAO.getOrder(orderId));
}
return result;
}
public UserDTO getUserForOrder(Stirng orderId) {
// get the user associated with the order
String userId=uoao.getUserForOrder(orderId);
// retrieve the user DTO
return uDAO.getUser(userId);
}
}
I was wondering: Imagine a scenario where e.g.
//POJO
public class User {
private String userName;
private String name;
private String surname;
private List<Blog> blogList;
//All getters and setters are found here and assume they're generated.
}
public class Blog {
private String title;
private String content;
private User author;
private Date datePublished;
private Date dateLastModified;
//All getters and setters have been generated (by Eclipse or NetBeans)
}
Imagine that these objects have been correctly mapped into their respective Hibernate configuration files.
My question:
How would I retrieve my user with the list of all the user blogs on code level? (i.e., not allow hibernate to populate blogList automatically for me.
I want to add paging (i.e. from list 5, retrieve 20 list) and also, if you think carefully, this might be an infinite loop as a Blog has a User entity which has a List<Blog> entity.
How do I prevent this?
PS Just out of curiousity, how would I let Hibernate populate my blogList on the configuration side?
Thanks in advance.
Hibernate detects such loops and doesn't let them happen
You can mark your collection with fetch type=lazy (fetchType=FetchType.LAZY) so that the collection elements are not fetched when the owning object is
you can used a Query with setFirstResult(..) and setMaxResults(..) in order to achieve paging. (and get rid of the collection then)