we have a huge database application, which must get refactored (there are so many reasons for this. biggest one: security).
What we already have:
- MySQL Database
- JPA2 (Eclipselink) classes for over 100 tables
- Client application that accesses the database directly
What needs to be there:
- REST interface
- Login/Logout with roles via database
What I've done so far:
- Set up Spring MVC 3.2.1 with Spring Security 3.1.1
- Using a custom
UserDetailsService
(contains just static data for testing atm) - Created a few Controllers for testing (simply receiving/providing data)
Design Problems:
- We have maaaaany @OneToMany and @ManyToMany relations in our database
1.: (important)
If I'd send the whole object tree with all child objects as a response, I could probably send the whole database at once.
So I need a way to request for example 'all Articles'. But it should omit all the child objects. I've tried this yesterday and the objects I received were tons of megabytes:
@PersistenceContext
private EntityManager em;
@RequestMapping(method=RequestMethod.GET)
public @ResponseBody List<Article> index() {
List<Article> a = em.createQuery("SELECT a FROM Article a", Article.class).getResultList();
return a;
}
2.: (important)
If the client receives an Article, at the moment we can simply call article.getAuthor()
and JPA will do a SELECT a FROM Author a JOIN Article ar WHERE ar.author_id = ?
.
With REST we could make a request to /authors/{id}
. But: This way we can't use our old JPA models on the client side, because the model contains Author author
and not Long author_id
.
Do we have to rewrite every model or is there a simpler approach?
3.: (less important)
Authentication: Make it stateless or not? I've never worked with stateless auth so far, but Spring seems to have some kind of support for it. When I look at some sample implementations on the web I have security concerns: With every request they send username and password. This can't be the right way.
If someone knows a nice solution for that, please tell me. Else I'd just go with standard HTTP Sessions.
4.:
What's the best way to design the client side model?
public class Book {
int id;
List<Author> authors; //option1
List<Integer> authorIds; //option2
Map<Integer, Author> idAuthorMap; //option3
}
(This is a Book which has multiple authors). All three options have different pros and cons:
- I could directly access the corresponding
Author
model, but if I request aBook
model via REST, I maybe don't want the model now, but later. So option 2 would be better: - I could request a
Book
model directly via REST. And use theauthorIds
to afterwards fetch the corresponding author(s). But now I can't simply usemyBook.getAuthors()
. - This is a mixture of 1. and 2.: If I just request the
Book
s with only theAuthor
ids included, I could do something like:idAuthorMap.put(authorId, null)
.
But maybe there's a Java library that handles all the stuff for me?!
That's it for now. Thank you guys :)
The maybe solution(s):
Problem: Select only the data I need. This means more or less to ignore every @ManyToMany
, @OneToMany
, @ManyToOne
relations.
Solution: Use @JsonIgnore
and/or @JsonIgnoreProperties
.
Problem: Every ignored relation should get fetched easily without modifying the data model.
Solution: Example models:
class Book {
int bId;
Author author; // has @ManyToOne
}
class Author {
int aId;
List<Book> books; // has @OneToMany
}
Now I can fetch a book via REST: GET /books/4
and the result will look like that ('cause I ignore all relations via @JsonIgnore
): {"bId":4}
Then I have to create another route to receive the related author: GET /books/4/author
. Will return: {"aId":6}
.
Backwards: GET /authors/6/books
-> [{"bId":4},{"bId":42}]
.
There will be a route for every @ManyToMany
, @OneToMany
, @ManyToOne
, but nothing more. So this will not exist: GET /authors/6/books/42
. The client should use GET /books/42
.