Restrict documents returned by spring-data-rest - java

A class Dog is using a private field owner to keep the owner username kept in class User (which implements UserDetails):
#Document
public class Dog {
#Id
private ObjectId id;
private String owner;
}
#Document
public class User implements UserDetails {
#Id
private ObjectId id;
private String username;
}
These documents are persisted using spring-data-mongodb and exposed using spring-data-rest. User is authenticated using Spring Security, so it is available as #AuthenticationPrincipal.
It is requested that REST can only access to the dog the user owns. Is it possible to tweak spring-data-rest to return only a subset of the documents from collection dogs, the ones with 'owner' field is the same as the username in the User instance returned by the authentication principal ?

What I did in such a scenario was implementing custom RepositoryInvoker.
The invoker was a proxy for the one created by Spring Data Rest. The idea was to intercept invokeFindAll (or other method that should filter by the ownership) and delegate to the the query method that implements the filtering.
Note the I was using Spring Data JPA and Specfications to implement the filtering predicate. I believe similar approach should by feasible with Mongo.

Related

Ignore fields from being passed on in Json Object

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);

JPA Design of User and Roles

I have a User class and a Role class. Both of these classes are JPA entities, and hence stored in a Person table and a Role table, as well as a corresponding link table Person_Role used for joins, since the association is many to many. A user may have many roles, and a role may be assigned to many users.
#Entity
#Table(name="role")
public class Role implements Comparable<Role>
{
// data members
#Id #GeneratedValue
private int id; // primary key
private String name; // name of the role
private String description; // description of the role
...
}
#Entity
#Table(name="person")
public class Person implements Comparable<Person>
{
// data members
#Id #GeneratedValue
protected int id; // the primary key
protected String username; // the user's unique user name
protected String firstName; // the user's first name
protected String lastName; // the user's last name
protected String email; // the user's work e-mail address
#Transient
protected String history; // chronological list of changes to the person
// don't want to load this unless an explicit call to getHistory() is made
#Transient
protected Set<Role> roles; // list of roles assigned to the user
// don't want to load this unless an explicit call to getRoles() is made
...
}
The User entity is used extensively throughout the application, as it is a shared reference for many objects, and is used in many, many searches. 99.99% of the time, the user's roles and history are not needed. I'm new to JPA, and have been reading the "Java Persistence with Hibernate" book in order to learn. As I understand lazy fetching, it will load all the corresponding User data from the database when any getXXX() method is called.
Ex: user.getFirstName() would cause a database hit and load all the data, including roles and history, for the user.
I want to avoid this at all costs. Its just needless in 99.99% of the use cases. So, what's the best way to handle this?
My initial thought is to mark the Set<Role> roles and Set<String> history in the User class as #Transient and manually query for the roles and history only when the user.getRoles() or user.getHistory() method is called.
Thanks for any suggestions.
As I understand lazy fetching, it will load all the corresponding User
data from the database when any getXXX() method is called.
You can force JPA to be eager or lazy while fetching data from the database but first and foremost it depends on JPA provider. As described in JPA 2.1 specification, chapter 11.1.6:
The FetchType enum defines strategies for fetching data from the
database:
public enum FetchType { LAZY, EAGER };
The EAGER strategy is a requirement on the persistence provider
runtime that data must be eagerly fetched. The LAZY strategy is a
hint to the persistence provider runtime that data should be fetched lazily when it is first accessed. The implementation is permitted to
eagerly fetch data for which the LAZY strategy hint has been
specified. In particular, lazy fetching might only be available for
Basic mappings for which property-based access is used.
A nice presentation on how fetching strategies work and how performant they are in real-life scenarios you can find here.
Ex: user.getFirstName() would cause a database hit and load all the
data, including roles and history, for the user.
Data are retrieved either directly from the persistence context (usually it has a short lifespan) or indirectly from the underlying database (when it's not found in the transactional/shared caches). If entity manager is requested to get your entity object and it does not exist in the persistence context it needs to go deeper - into the database in the worst scenario.
I want to avoid this at all costs. Its just needless in 99.99% of the
use cases. So, what's the best way to handle this?
An example approach:
#Entity
#NamedQuery(name="Person.getNameById",
query="SELECT p.name FROM Person p WHERE p.id = :id")
public class Person
{
#Id #GeneratedValue
protected int id;
private String name; //the sole attribute to be requested
#ManyToMany //fetch type is lazy by default
#JoinTable
protected Set<Role> roles; //not loaded until referenced or accessed
...
}
Usually the best way to go is the find method. It's perfect when you want to retrieve all non-relationship attributes at once:
Person p = em.find(Person.class, id)
An alternative for you would be to use named query. It's useful when you need a single attribute or a small subset of attributes:
String name = em.createNamedQuery("Person.getNameById", String.class)
.setParameter("id", id)
.getSingleResult()
My initial thought is to mark the Set roles and Set history in the
User class as #Transient and manually query for the roles and history
only when the user.getRoles() or user.getHistory() method is called.
Transient attributes are not persisted in a database. Whatever you will set to these attributes it will stay in memory only. I would prefer JPA doing it lazily.
It will not Load all the data just the the relative to
Person entity = (Person) this.em.find(Person.class, id);
in lazy fetching it will issue a select statement from only the table person, as for protected Set<Role> roles;it will not be loaded but replaced with a proxy object
Hibernate uses a proxy object to implement lazy loading. When we request to load the Object from the database, and the fetched Object has a reference to another concrete object, Hibernate returns a proxy instead of the concrete associated object.
Hibernate creates a proxy object using bytecode instrumentation (provided by javassist). Hibernate creates a subclass of our entity class at runtime using the code generation library and replaces the actual object with the newly created proxy.

Jax-RS - java rest api and hibernate

I'm trying to do my first JEE application with rest api and hibernate. I dont know how to do it in proper way. I mean that I have entity User
#Entity
#XmlRootElement
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String username;
private String password;
private String email;
private LocalDateTime lastDateOfLogin;
#OneToMany(fetch=FetchType.LAZY)
private List<Post> posts = new ArrayList<>();
#OneToMany(fetch=FetchType.LAZY)
private List<Comment> comments = new ArrayList<>();
#OneToMany(fetch=FetchType.LAZY)
private List<Like> likes = new ArrayList<>();
#OneToMany(fetch=FetchType.LAZY)
private List<User> followers = new ArrayList<>();
#OneToMany(fetch=FetchType.LAZY)
private List<User> following = new ArrayList<>();
and I have UserResource method to get one user
#GET
#Path("{id}")
public User getUser(#PathParam("id") long id)
{
return userService.getOne(id);
}
Now my problem is that when I'm trying to get one user I'm getting exception org.hibernate.LazyInitializationException - of course I know why but question is how to do this get it in proper way. In this #GET I don't need this OneToMany collections because when I want E.g user posts I will call user/1/posts url and I will receive all user posts.
How to develop this kind of applications? Should I delete relations from user entity and just search in database posts of user when I need them? Or maybe there is another solution for it?
If that particular endpoint is only interested in the basic details about the user and none of the associations your database model has to other objects in your system, you need to somehow prevent the serialization process from looking at those attributes.
If it is a field that you want to always be ignored, you could annotate it with #XmlTransient, but this is a decision that is determined at build time and cannot be modified at runtime. For situations where you need to dynamically influence the serialization step, you can either look at these two articles:
How to Conditionally serialize with JAXB or Jackson
Jacksons JsonView or MOXy's external mapping-files
Another alternative would be to modify your service to return a class instance for that specific view that only contains the attributes you want to be marshalled into the output XML. This can easily be accomplished with a JPA select new query like:
SELECT new com.company.app.BasicUser(u.userName, other attributes) FROM User u
WHERE u.id = :id
Now your XML marshalling would be based on BasicUser and not your domain entity User, where that BasicUser doesn't have any of the associations or attributes that you don't wish to have serialized for that specific view.

ORM - Use whole object or only identifier as JoinColumn?

I have a table/class User and Message. One User can have many messages. How do I model the Java class for Message; should it have a property of type User, or just a String for the username which is the key in this relationship?
When using Hibernate to generate the classes, it gave the Message class a User property. Does that mean it's the right way?
I find it problematic when creating messages to also give it a User instance, because I can't seem to do that in a Spring webflow that I'm using (I'm only passing the username from the view).
you can design your class with a OneToMany relation and Message class does not need to have an user field.
For exemple :
public class User {
#OneToMany
private List<Message> messages;
}
public class Message {
#Basic
private String body;
}
In this case, the database table for Message will have a column for the user id. You can specify another columnn by using JoinColumn :
public class User {
#OneToMany
#JoinColumn(name="my_user_id_column")
private List<Message> messages;
}
Otherwise, you can use #JoinTable like in this answer

What is the best approach to write a data access object (DAO)?

I was trying to write a user authentication system in Java. So I wrote some DAO class. First I did write a class named Persistence which is abstract. It is responsible for holding some common attributes. And wrote a class named User extending Persistence class. Those classes are –
public abstract class Persistance {
private Date createdDate;
private Date lastUpdatedDate;
private long version;
private boolean isDeleted;
//getter and setters
}
and the user class
public class User extends Persistance{
private String username;
private String password;
private String passwordConfired;
// getters and setters
}
My questions are- what is the best way to write variable name, which one is good, createdDate or dateCreated, deleted or isDeleted etc.
And is this approach is okay or is there more good approach ?
And how to implement data versioning?
To write a DAO, typically you create an interface that defines the behavior of the DAO.
interface MyObjDao {
public int save(MyObj myObj);
public void delete (MyObj myObj);
// as many methods as you need for data acess
}
and then you create the actual implementation
class MyObjDaoImpl implements MyObjDao {
// implement methods here
}
The advantages of this are:
1) Because you define an interface, mocking DAOs is easy for any testing framework
2) The behavior is not tied to an implementation -- your DAOImpl could use jdbc, hibernate, whatever
Your Persistance class is really a base class for all entities -- i.e. all classes instances of which get saved, where you want to represent some common fields in one place. This is a good practice -- I wouldn't call the class Persistance, something like BaseEntity is better (IMHO). Be sure to have javadocs that explain the purpose of the class.
With respect to variable names, as long as they make sense and describe what they are for, its good.
so dateCreated or createdDate are both fine; they both get the idea across.
You are mixing a DAO (data access object) and a VO (value object) - also known as a DTO (data transfer object) - in the same class.
Example using an interface for DAO behavior (blammy and kpow might be webservice, oracle database, mysql database, hibernate, or anything meaningful):
public interface UserDTO
{
boolean deleteUser(String userId);
UserVO readUser(String userId);
void updateUser(String userId, UserVO newValues);
}
package blah.blammy;
public class UserDTOImpl implements UserDTO
{
... implement it based on blammy.
}
package blah.kpow;
public class UserDTOImpl implements UserDTO
{
... implement it based on kpow.
}
Example VO:
public class UserVO
{
String firstName;
String lastName;
String middleInitial;
... getters and setters.
}
I prefer to identify the target of the delete using an ID instead of a VO object. Also, it is possible that an update will change the target identified by user ID "smackdown" to have user ID "smackup", so I generally pass an id and a VO.
A good approach would be to use JPA with all of its features, this tutorial was really helpful.
It explains how to use the #PrePersist and #PreUpdate annotations for setting create and update timestamps. Optimistic locking is supported by the #Version annotation.
My questions are- what is the best way to write variable name, which
one is good, createdDate or dateCreated, deleted or isDeleted etc.
createdDate or dateCreated is very subjective. In databases, I have mostly seen createdDate though. Between deleted and isDeleted, I prefer (again subjective) deleted. I think the getter method can be named isDeleted().

Categories