Add a property to the entity that is not a column and that can contain an HTTP request value - spring

I have a User entity that has as many properties as there are columns in the database and a transient one next to that. The user can be part of an Organisation, which is a onetoone relationship. In that entity the data type is an Organisation object.
The model is also used in the controller to check and contain the request data values, but this cannot be achieved with organisation, because the user passes the name as a string and in the entity, the organisation is an object as already mentioned.
So I would like to add another property that can contain the organisation name, so then I can convert it to an Organisation object in the Service. But I can't add a regular property because Spring JPA will think it should be a column and it can't be a transient property either, because it's ignored on the controller side for the request.
Currently I'm achieving this by adding another parameter with #RequestParam(name="organisationname"), and passing it together with the User object to the Service, but I have the feeling that this is not the proper way.
How can I approach this differently and in the User entity itself?

You can have your JSON serializer NOT ignore #Transient fields (I like this approach as transient is a DB term so it shouldn't be affecting your JSON serialization) - you can then use #JsonIgnore to skip JSON serialization properties.
So #Transient to ignore when interacting with the DB, #JsonIgnore to ignore when serializing/deserializing.
If using the Jackson ObjectMapper you can do the following:
#Bean
public ObjectMapper objectMapper() {
final ObjectMapper mapper = new ObjectMapper();
final Hibernate5Module hibernate5Module = new Hibernate5Module();
hibernate5Module.disable(Hibernate5Module.Feature.USE_TRANSIENT_ANNOTATION);
mapper.registerModule(hibernate5Module);
return mapper;
}

Related

How can I map the fields return in JSON with my entity provided the key in JSON has different name than fields in my entity

I am trying to Map a JSON response to a Java POJO which has a different field name from different API.
I need an efficient way to do this reducing boilerplate codes.
I have tried mapping the JSON property field in Java POJO.
However, the problem is I am fetching data from different sources.
Let's say I have below user class
Class User{
String name;
String contact;
}
The JSON I may receive from different sources can be
{"name": "ABC" , "contact": "123456"}
or
{"userName": "XYZ" , "mobileNo":"4354665"}
There may be more variations as we go on integrating more API's
Is there a way I can archive this?
above is just a simple example
there could be more complex JSON object I may need to read.
like List of User etc.
You can use the #JsonAlias() to give the variable more than one JSON key binding.
#JsonAlias is introduced in Jackson 2.9 release. #JsonAlias defines one or more alternative names for a property to be accepted during deserialization i.e. setting JSON data to Java object. But at the time of serialization i.e. while getting JSON from Java object, only actual logical property name is used and not alias. #JsonAlias is defined as follows.
#Entity
Class User{
#JsonProperty()
#JsonAlias({"name", "userName"})
String name;
#JsonProperty()
#JsonAlias({"contact", "mobileNo"})
String contact;
}
You could use the #JsonSetter annotation like :
public class User{
public String contact;
public String name;
#JsonSetter("name")
public void setName(String name) {
this.name = name;
}
#JsonSetter("userName")
public void setName(String name) {
this.name = name;
}
}
Instead of directly mapping to an entity class , you should have a DTO object or model in between to map the json response. Then, you can convert that into any entity you may choose.If you are fetching the data from different sources , it means you are calling different endpoints, why don't you create different DTO 's for that.In that way even if one of the endpoints introduce a change , it won't affect the rest of the endpoint calls.
Vice-versa you could have different DTO objects being returned from the two endpoints instead of returning the same Entity class as well, that way you can have control over which attributes should be there in the response.
To reduce the boiler plate code, you could use library such as MAP STRUCT to enable conversion between entity and DTO objects easily
Read here about the advantages of using a DTO .

Spring Data MongoDB document expiry time

I've set up a simple document model (below) along with a Spring Data repository
#Document(collection = "users")
public class UserDocument {
#Id
private String userId;
#Indexed(expireAfterSeconds=3600)
private LocalDateTime registeredDate;
}
This seems to work fine and deletes the user documents after whatever time I set expireAfterSeconds to. However, rather than typing in a value of 3600 I'd like to pull that number from a config .yml. The usual way of adding #Value("${config.file.path.of.expiry}") won't work because #Indexedrequires the value to be a runtime constant, which #Value isn't.
Is there any other way to set up document expiry dates in Spring/Mongodb that doesn't use #Indexed(expireAfterSeconds=x)?
Instead of using the Indexed annotation to declare an index which Spring Data Mongo will create on your behalf ... you could create the index explicitly using a MongoTemplate instance.
#Value("${config.file.path.of.expiry}")
long expireAfterSeconds;
...
mongoTemplate.indexOps("users").ensureIndex(
new Index().on("registeredDate", Order.ASCENDING)
.expire(expireAfterSeconds)
);
This would allow you to source the value for expireAfterSeconds from a configuration file at runtime rather than hardcoding it in an annotation.

Spring Hibernate - Does it support nested objects?

I recently asked this question : Spring Mongodb - Insert Nested document?
And found out that Spring-Data-MongoDB does not support such behavior - so now I need a working alternative.
Now - to avoid having you look at the code on another page, I am going to paste it here from the other question... Here are my two POJOs :
#Document
public class PersonWrapper {
#Id
private ObjectId _Id;
#DBRef
private Person leader;
#DBRef
List<Person> delegates;
// Getters and setters removed for brevity.
}
public class Person
{
#Id
private ObjectId _Id;
private String name;
// Getters and setters removed for brevity.
}
Now, what I want to be able to do here - is send up a JSON object in my POST request as follows :
{
"personWrapper":
{
"_Id":"<ID HERE (MIGHT WANT SQL TO GENERATE THIS DURING CREATE>",
"leader":{
"_Id":"<ID HERE (MIGHT WANT SQL TO GENERATE THIS DURING CREATE>",
"name":"Leader McLeaderFace"
},
delegates:[{...},{...},{...}]
}
}
At this point - I would like the SQL side of this to create the individual records needed - and then insert the PersonWrapper record, with all of the right foreign keys to the desired records, in the most efficient way possible.
To be honest, if one of you thinks I am wrong about the Spring-Data-MongoDB approach to this, I would still be interested in the answer - because it would save me the hassle of migrating my database setup. So I will still tag the spring-data-mongodb community here, too.
If I understand well you want to cascade the save of your objects ?
ex : you save a PersonWrapper with some Person in the delegates property and spring data will save PersonneWrapper in a collection and save also the list of Person in another Collection.
It is possible to do that with Spring DATA JPA if you annotate your POJO with the JPA annotation #OneToMany and setup cascade property of this annotation. See this post
However the cascade feature is not available for Spring DATA mongoDB. See documentation .First you have to save the list of Person and then you save PersonWrapper.

Avoid N+1 with DTO mapping on Hibernate entities

In our Restful application we decided to use DTO's to shield the Hibernate domain model for several reasons.
We map Hibernate entities to DTO and vice versa manually using DTOMappers in the Service Layer.
Example in Service Layer:
#Transactional(readOnly=true)
public PersonDTO findPersonWithInvoicesById(Long id) {
Person person = personRepository.findById(id);
return PersonMapperDTOFactory.getInstance().toDTO(person);
}
The main concept could be explained like this:
JSON (Jackson parser) <-> Controller <-> Service Layer (uses Mapping Layer) <-> Repository
We agreed that we retrieve associations by performing a HQL (or Criteria) using a left join.
This is mostly a performant way to retrieve relations and avoids the N+1 select issue.
However, it's still possible to have the N+1 select issue when a developer mistakenly forgets to do a left join. The relations will still be fetched because the PersonDTOMapper will iterate over the Invoices of a Person for converting to InvoiceDTOs. So the data is still fetched because the DTOMapper is executed where a Hibernate Session is active (managed by Spring)
Is there some way to make the Hibernate Session 'not active' in our DTOMappers? We would face a LazyInitializationException that should trigger the developer that he didn't fetch some data like it should.
I've read about #Transactional(propagation = Propagation.NOT_SUPPORTED) that suspends the transaction. However, I don't know that it was intended for such purposes.
What is a clean solution to achieve this? Alternatives are also very welcome!
Usually I use the mapper in the controller layer. From my prspective, the service layer manages the application business logic, dtos are very useful if you want to rapresent data to the external world in a different way. In this way you may get the lazy inizitalization excpetion you are looking for.
I have one more reason to prefer this solution: just image you need to invoke a public method inside a public method in the service class: in this case you might need to call the mapper several times.
If you are using Hibernate, then there are specific ways that you can determine if an associated object has been lazy-loaded.
For example, let's say you have an entity class Foo that contains a #ManyToOne 'foreign' association to entity class Bar which is represented by a field in Foo called bar.
In you DTO mapping code you can check if the associated bar has been lazy-loaded using the following code:
if (!(bar instanceof HibernateProxy) ||
!((HibernateProxy)bar).getHibernateLazyInitializer().isUninitialized()) {
// bar has already been lazy-loaded, so we can
// recursively load a BarDTO for the associated Bar object
}
The simplest solution to achieve what you desire is to clear the entity manager after querying and before invoking the DTO mapper. That way, the object will be detached and access to uninitialized assocations will trigger a LazyInitializationException instead.
I felt your pain as well which drove me to developing Blaze-Persistence Entity Views which allows you to define DTOs as interfaces and map to the entity model, using the attribute name as default mapping, which allows very simple looking mappings.
Here a little example
#Entity
class Person {
#Id Long id;
String name;
String lastName;
String address;
String city;
String zipCode;
}
#EntityView(Person.class)
interface PersonDTO {
#IdMapping Long getId();
String getName();
}
Querying would be as simple as
#Transactional(readOnly=true)
public PersonDTO findPersonWithInvoicesById(Long id) {
return personRepository.findById(id);
}
interface PersonRepository extends EntityViewRepository<PersonDTO, Long> {
PersonDTO findById(Long id);
}
Since you seem to be using Spring data, you will enjoy the spring data integration.

Entity Objects vs Value Objects - Hibernate and Spring

Okay. I am getting a little confused here...
Lets say I have a class called User.
class User {
// all variables
// all getters and setters
}
Now, I use JSR 303 validation and put #NotNull, #Range, etc here for the variables.
I use this as Form / Command object. Meaning, when a form a submitted, the values are validated and BindingResult gives me errors.
Should this be used as in Entity Object for Hibernate as well? (If so, I need to add other Hibernate annotations like #Entity, #Id, #Column, etc on top of Validation annotations)
When we load the data from Database, do these validations kick in as well? (If yes, what if the data is already existing, and do not confirm to the validations?)
Where do we normally write business validations, like for example, country exists or not in the database, xyz value exists in a different table, etc?
Questions arise here as well:
- User form may not have all the fields that exist in the User class
- Database table User may have more fields or less fields than User class
- Form may have fields from different objects as well, say User and and Order.
How do we handle these?
Trying to wrap my mind around it
No you shouldn't mix entities objects and values objects. Entities objects are for DB mapping and values objects are used in the presentation layer.
To validate an object annoted, you need to use a Validator (commonly used with a DataBinder. Spring validation)
Is it DB constraints or backend validation?
For your last question, that's one of the reason to have 2 differentes objects for your presentation layer and your persistence layer. This way values objects can match what is displayed or input by the user (Form).
Entity object is an object of our plain old java class(POJO) Model/Business class, which needs to be persisted in a database using Hibernate, while Value Type object is an object of another class but it is stored as a part of Entity object within a database table.
#Embeddable
#Data
#AllArgsConstructor
public class Address{
private String country;
private String city;
private String street1;
private String street2;
private postalCode;
}
#Entity
#Data
#AllArgsConstructor
public class Person{
#Id
private Long id;
private String firstName;
private String lastName;
private ing age;
private Address address;
}
}
So after running this code in spring and hibernate project you will see Person table in database is created with it's attributes and Address class attributes.
For more information, I suggest reading this:
[https://www.decodejava.com/hibernate-value-type-object.htm][1]

Resources