How to model immutable entities with static factory method - spring

Hello there i have a question concerning the right way of modelling immutable entities:
Consider this entity (edited as of the suggestion by Jens Schauder):
#Getter
#RequiredArgsConstructor(staticName = "of", access = AccessLevel.PACKAGE)
public final class Student {
private final #Id #Wither
long studentId;
#NotNull
#Size(min = 4, max = 20)
private final String userId;
#NotNull
#Min(0)
private final int matriculationNumber;
#NotNull
#Email
private final String eMail;
}
So this entity should be immutable and offers a static of creation method. Also the RequiredArgsConstructor builds a private constructor although it should create a package visible one for all final/non null fields per definition. In short i did an AllArgsConstructor so to speak.
This document over here https://docs.spring.io/spring-data/jdbc/docs/current/reference/html/#mapping.fundamentals in detail the section about "Object creation internals" states 4 aspects for improved handling - "the constructor to be used by Spring Data must not be private" amongst others which are fulfilled in my opinion.
So my question:
Is this pictured entity done right in both ways concerning immutabillity and spring data jdbc internals optimum mapping?
EDIT:
There seems to be a bug with lombok plugin in intellij, hindering the access = AccessLevel.PACKAGE doing the right stuff. See here:
https://github.com/mplushnikov/lombok-intellij-plugin/issues/584
Although the issue is already closed a new version of the plugin is not available ...

This depends on your definition of "optimum mapping".
It should work, so this is already something.
But the optimization described in the docs cannot be applied because your constructor is private.
Therefore you lose the 10% performance boost of that which it probably does not make it "optimal".
But the 10% boost is about the object instantiation.
It is not about the roundtrip to the database which involves:
extraction of data from your entities
construction (or lookup) of SQL to use
sending both to the database
performing the query in the database
returning the result
This makes it very likely that the gain from that optimization is well below 10% and in most cases nothing to worry about.
Of course, you will never really know until you made your own benchmarks with real data.
For this, you would need to create an all args constructor which has at least package scope.

Related

How to handle mongodb relation modeling with webflux?

I have an old-ish (3 years) service I need to maintain and just joined the team. It is a spring-webflux app with mongodb (4). All models are defined with lombok and where there are relations between them, #DBRef annotation was used. It works fine, but now I have to bump up spring (and the rest of the dependencies) and I just realized #DBRef is no longer supported.
Somehow, I understand the decision, but I couldn't find any straight forward alternative other than doing myself all the cascade operations.
So, is there any easier way to approach this?
Thanks.
#DBRef has been replaced by #DocumentReference some time ago.
#DocumentReference: Applied at the field to indicate it is to be stored as a pointer to another document. This can be a single value (the id by default), or a Document provided via a converter.
This very simple example shows how it works:
public class Account {
private String id;
private Float total;
}
public class Person {
private String id;
#DocumentReference
private List<Account> accounts;
}
For more details, have a look at the official docu:
https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#mapping-usage-annotations

In REST, how do we deal with multiple ways to return a resource collection?

We have a resource called messages. We want to have two ways of listing its collection. One would return only messages that are mandatory and have been viewed; the other, all messages. Each one has fields that are not necessary for the other, thus we would like to not return them. E.g.
One response should look like this:
public class MessageListingResponse {
private Long messageId;
private String title;
private String imageUrl;
private LocalDateTime createdAt;
private Boolean isViewed;
}
The other one is like this:
public class MandatoryMessageListingResponse {
private Long messageId;
private String title;
private String imageUrl;
private LocalDateTime createdAt;
private String description;
}
I could not find a common rule for this scenario. So, which option follows REST?
/messages/mandatories
/messages?view=mandatories
/messages?mandatoryListing=true
/mandatory-messages
I could not find a common rule for this scenario. So, which option follows REST?
REST doesn't care what spelling conventions you use for your resource identifiers.
In particular, the machines don't care whether or not the spelling of the identifier matches the semantics of the resource (reminder: URL shorteners work)
/messages/mandatories
/messages?view=mandatories
/messages?mandatoryListing=true
/mandatory-messages
Those are all fine.
There are some purely mechanical differences; a query with key value pairs is convenient when using HTML forms as URI templates. A path hierarchy is convenient when using dot-segments and relative resolution to describe other identifiers in the same hierarchy.
Identifiers as text show up in a number of places - we paste URI into messages, they are tracked in browser histories, they appear in your access logs. So we have a lot of different humans looking at the identifiers in different contexts. Try to choose a spelling that's tolerable for all of them; but optimize as best you can which ever experience is most important.
This is borderline opinion-based but this is how I would do it.
The most RESTful way would be using a query parameter, so something like /messages?view=mandatories. The problem with this (might not be a problem for your use case, you need to think about it) is that the response model will need to be the same as for /messages, which means that you would need to have a model as follows (merging MessageListingResponse and MandatoryMessageListingResponse):
public class MessageListingResponse {
private Long messageId;
private String title;
private String imageUrl;
private LocalDateTime createdAt;
private Boolean isViewed;
private String description;
}
If this is not wanted (I would also avoid it), then I would go with a not-so-RESTful approach, such as /messages/mandatories. It is not so RESTful because messages are your resource and the following path information should be an ID so that you can get only one message. Another possibility would be something like /messages/view:mandatories which I understand might seem weird to most people. Whatever structure you use, with this approach you have the benefit of having a specific model for this and thus you can have very specific model for each endpoint, avoiding properties that would be null in some cases and not null in another.
First of all, if it's messages, then /messages should be there because the resource is message.
After that, messages/mandatories means that there a listing of mandatories, that it's a subset of messages. Do you intend to add or update a mandatory message with put, post or patch, messages/mandatories is the right way.
But if it's only a filter for messages that are mandatory, the best way to do it is with a GET like this: /messages?status=mandatories
Where status is the field that indicate if the message is mandatory

How come Spring Data Rest only allows updates from the owner side of ManyToOne/ManyToMany?

I downloaded a sample project from here https://www.baeldung.com/spring-data-rest-relationships
I then ran it and did some test REST calls. As far as I can tell you can only update association from the owner side using SDR. What I mean is
public class Book {
#ManyToOne
#JoinColumn(name = "library_id")
private Library library;
}
and
public class Library {
#OneToMany(mappedBy = "library")
private List<Book> books;
}
You can't actually make post/put calls to /libraries/1/books. Server return 204 but no effect on the db whatsoever.
You can however, make post/put calls to /books/1/library and everything works as intended including keeping the other entity in sync.
Is this normal? It's the same behaviour for #ManyToMany as well. Is there a way to allow for updates from both sides? If I write my own API I can certainly make this the case. Why does SDR not do this?
Is this normal?
In a sense, yes. That's exactly how pure JPA would behave when you added a Book to the collection of Library.books with your current mapping - it would make no changes whatsoever.
My guess is that Spring Data Rest doesn't know (or care) which side of the association is the owner side, and just doesn't go the extra mile to make sure updating the inverse side works as well.
Is there a way to allow for updates from both sides?
A workaround could be to simply pretend both sides of the associations were the owning side, i.e.:
public class Library {
#OneToMany
#JoinColumn(name = "library_id")
private List<Book> books;
}
Be advised that this makes Hibernate treat Library.books and Book.library as two completely separate associations. In some corner cases, your entities may not behave the way you would expect. You have been warned.
Updating bidirectional relations from both sides is very tricky and will potentially cause side effects considering Springs endpoint authorization or Spring Data RESTs BeforeLinkSaveEvent and AfterLinkSaveEvent (https://docs.spring.io/spring-data/rest/docs/current/reference/html/#events).
There has to be a strict parent child relation. I don't think that you can configure the desired behaviour.

Spring Rest, JsonManagedReferences and cyclic references

I'm facing an issue where using #JsonManagedReferences and #JsonBackReference won't break the infinite recursion loop when marshaling the Objects to Json.
I know that I could try to avoid bidirectional relationships but this would not serve very well. Another not desirable solution would be to drop the #RestResource(exported=false) annotation and follow the link provided just once.
One example would be:
#JsonManagedReference
#ManyToOne(cascade=CascadeType.ALL)
#RestResource(exported=false)
#JoinColumn(name="organisation_id")
private Organisation organisation;
with it's counterpart in another class:
#JsonBackReference
#OneToMany(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
#JoinColumn(name ="organisation_id")
#RestResource(exported=false)
#Fetch(value = FetchMode.SUBSELECT)
private Set<OrganisationUnit> organisationUnits;
Bot classes have a #RepositoryRestResource with nothing special in it.
#RepositoryRestResource
public interface OrganisationRepository extends JpaRepository<Organisation, Long> {
public final static String ID_QUERY = "SELECT u FROM Organisation u where u.id=:objectId";
#Query(ID_QUERY)
public Organisation findObjectById(#Param("objectId") Long objectId);}
I know that Jackson 2 should be able to handle to kinds of situations but in this case it does not resolve the issue. Is this known behavior that I'm not aware of?
Please let me know of any obvious flaws as I'm not very experienced using JPA, Hibernate or Spring.
The error message which is provided is:
There was an unexpected error (type=Internal Server Error, status=500). Could not write content: Infinite recursion (StackOverflowError) (through reference chain: org.springframework.hateoas.PagedResources["_embedded"]);
nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion
I'd be happy about any pointers.
Which Jackson version are you using? Jackson 2.2.3 does not have this issue. Its taken care automatically

Neo4j Spring data POC for social RESTful layer

Starting to work on a new project... RESTful layer providing services for social network platform.
Neo4j was my obvious choice for main data store, I had the chance to work with Neo before but without exploiting Spring Data abilities to map POJO to node which seems very convenient.
Goals:
The layer should provide support resemble to Facebook Graph API, which defines for each entity/object related properties & connections which can be refer from the URL. FB Graph API
If possible I want to avoid transfer objects which will be serialized to/from domain entities and use my domain pojo's as the JSON's transferred to/from the client.
Examples:
HTTP GET /profile/{id}/?fields=...&connections=... the response will be Profile object contains the requested in the URL.
HTTP GET /profile/{id}/stories/?fields=..&connections=...&page=..&sort=... the response will be list of Story objects according to the requested.
Relevant Versions:
Spring Framework 3.1.2
Spring Data Neo4j 2.1.0.RC3
Spring Data Mongodb 1.1.0.RC1
AspectJ 1.6.12
Jackson 1.8.5
To make it simple we have Profile,Story nodes and Role relationship between them.
public abstract class GraphEntity {
#GraphId
protected Long id;
}
Profile Node
#NodeEntity
#Configurable
public class Profile extends GraphEntity {
// Profile fields
private String firstName;
private String lastName;
// Profile connections
#RelatedTo(type = "FOLLOW", direction = Direction.OUTGOING)
private Set<Profile> followThem;
#RelatedTo(type = "BOOKMARK", direction = Direction.OUTGOING)
private Set<Story> bookmarks;
#Query("START profile=node({self}) match profile-[r:ROLE]->story where r.role = FOUNDER and story.status = PUBLIC")
private Iterable<Story> published;
}
Story Node
#NodeEntity
#Configurable
public class Story extends GraphEntity {
// Story fields
private String title;
private StoryStatusEnum status = StoryStatusEnum.PRIVATE;
// Story connections
#RelatedToVia(type = "ROLE", elementClass = Role.class, direction = Direction.INCOMING)
private Set<Role> roles;
}
Role Relationship
#RelationshipEntity(type = "ROLE")
public class Role extends GraphEntity {
#StartNode
private Profile profile;
#EndNode
private Story story;
private StoryRoleEnum role;
}
At first I didn't use AspectJ support, but I find it very useful for my use-case cause it is generating a divider between the POJO to the actual node therefore I can request easily properties/connections according to the requests and the Domain Driven Design Approach seems very nice.
Question 1 - AspectJ:
Let's say I want to define default fields for an object, these fields will be returned to the client whether if requested in the URL or not...so I have tried #FETCH annotation on these fields but it seems it is not working when using AspectJ.
At the moment I do it that way..
public Profile(Node n) {
setPersistentState(n);
this.id = getId();
this.firstName = getFirstName();
this.lastName = getLastName();
}
Is it the right approach to achieve that? does the #FETCH annotation should be supported even when using AspectJ? I will be happy to get examples/blogs talking about AspectJ + Neo4j didn't find almost anything....
Question 2 - Pagination:
I would like to support pagination when requesting for specific connection for example
/profile/{id}/stories/ , if stories related as below
// inside profile node
#RelatedTo(type = "BOOKMARK", direction = Direction.OUTGOING)
private Set<Story> bookmarks;
/profile/{id}/stories/ ,if stories related as below
// inside profile node
#Query("START profile=node({self}) match profile-[r:ROLE]->story where r.role = FOUNDER and story.status = PUBLIC")
private Iterable<Story> published;
Is pagination is supported out of the box with either #Query || #RelatedTo || #RelatedToVia using Pageable interface to retrieve Page instead of Set/List/Iterable? the limit and the sorting should be dynamic depending on the request from the client... I can achieve that using Cypher Query DSL but prefer to use the basic.. other approaches will be accepted happily.
Question 3 - #Query with {self}:
Kind of silly question but I can't help it :), it seems that when using #Query inside the node entity ( using {self} parameter } the return type must be Iterable which make sense..
lets take the example of...
// inside profile node
#Query("START profile=node({self}) match profile-[r:ROLE]->story where r.role = FOUNDER and story.status = PUBLIC")
private Iterable<Story> published;
When published connection is requested:
// retrieving the context profile
Profile profile = profileRepo.findOne(id);
// getting the publishe stories using AspectJ - will redirect to the backed node
Iterable<Story> published = profile.getPublished();
// set the result into the domain object - will throw exception of read only because the type is Iterable
profile.setPublished(published);
Is there a workaround for that? which is not creating another property which will be #Transiant inside Profile..
Question 4 - Recursive relations:
I am having some problems with transitive / recursive relations, when assigning new Profile Role in Story the relation entity role contain #EndNode story , which contain roles connection...and one of them is the context role above and it is never end :)...
Is there a way to configure the spring data engine not to create these never ending relations?
Question 5 - Transactions:
Maybe I should have mentioned it before but I am using the REST server for the Neo4j DB, from previous reading I understand that there is not support out-of-the-box in transactions? like when using the Embedded server
I have the following code...
Profile newProfile = new Profile();
newProfile.getFollowThem().add(otherProfile);
newProfile.getBookmarks().add(otherStory);
newProfile.persist(); // or profileRepo.save(newProfile)
will this run in transaction when using REST server? there are few operations here, if one fail all fail?
Question 6 - Mongo + Neo4j:
I need to store data which don't have relational nature.. like Feeds, Comments , Massages. I thought about an integration with MongoDB to store these.. can I split domain pojo fields/connections to both mongo/neo4j with cross-store support? will it support AspectJ?
That is it for now.... any comments regarding any approach I presented above will be welcome.. thank you.
Starting to answer, by no means complete:
Perhaps upgrade to the the .RELEASE versions?
Question 1
If you want to serialize AspectJ entities to JSON you have to exclude the internal fields generated by the advanced mapping (see this forum discussion).
When you use the Advanced Mapping #Fetch is not necessary as the data is read-through from the database anyway.
Question 2
For the pagination for fields, you can try to use a cypher-query with #Query and LIMIT 100 SKIP 10 as a fixed parameter. Otherwise you could employ a repository/template to actually fill a Collection in a field of your entity with the paged information.
Question 3
I don't think that the return-type of an #Query has to be an Iterable it should also work with other types (Collections or concrete types). What is the issue you run into?
For creating recursive relationships - try to store the relationship-objects themselves first and only then the node-entities. Or use template.createRelationshipBetween(start, end, type, allowDuplicates) for creating the relationships.
Question 5
As you are using SDN over REST it might not perform very well, as right now the underlying implementation uses the RestGraphDatabase for fine-grained operations and the advanced mapping uses very fine grained calls. Is there any reason why you don't want to use the embedded mode? Against a REST server I would most certainly use the simple-mapping and try to handle read operations mostly with cypher.
With the REST APi there is only one tx per http-call the only option of having larger transactions is to use the rest-batch-api.
There is a pseudo-transaction support in the underlying rest-graph-database which batches calls issued within a "transaction" to be executed in one batch-rest-request. But those calls must not rely on read-results during the tx, those will only be populated after the tx has finished. There were also some issues using this approach with SDN so I disabled it for that (it is a config-option/system-property for the rest-graphdb).
Question 6
Right now cross-store support for both MongoDB and Neo4j is just used against a JPA / relational store. We discussed having cross-store references between the spring-data projects once but didn't follow up on this.

Resources