Javers compare entity to DTO - javers

i'm new to Javers, and i`m currently using it to create a patch update for my entity. But i'm having some struggle with one specific scenario
I want to compare a Entity against a EntityDTO/VO and get only the changes in values, the current comparison only returns that is a NewObject and ObjectRemoved changes.
Example:
public class Entity{
private ObjectId id;
private String name;
private String description;
}
public class EntityDTO{
private String name;
}
//
Entity oldState = new Entity(new ObjectId(), "oldName" , "oldDescription);
EntityDTO newState = new EntityDTO( "newName" );
JaversBuilder.javers().build().compare(oldState, newState).getChanges();
//This is returning only NewObject/ObjectRemoved changes, and the intended is to be ValueChange/ReferenceChange
The questions is, is there a way to compare only the similar attributes between the objects?

No, in JaVers, you can compare only objects of the same type.

Related

Advantage of assigning the returned savedEntity in Spring Data

I see in most of the coders save data(using spring data) as:
savedEntity = repo.save(savedEntity);
Long id = savedEntity.getId();
I am confused about why most of them assign back the returned value to the saved Entity while the following code also works exact(I have tested myself):
repo.save(savedEntity);
Long id = savedEntity.getId();
Did I miss some benefit of assigning back?
for example, let the entity be:
#Entity
public class SavedEntity {
#Id
private int id;
private String name;
//getter, setters, all arg-constructor, and no-arg constructor
}
Consider the object of SavedEntity is
SavedEntity entity = new SavedEntity(1,"abcd");
now for your first question,
SavedUser entity1 = repo.save(entity);
Long id = entity1.getId();
this entity1 object is the return object getting from the database, which means the above entity is saved in the database succesfully.
for the Second Question,
repo.save(entity);
Long id = entity.getId();//which you got it from SavedEntity entity = new SavedEntity(1,"abcd");
here the value of id is the integer you mentioned in place of id(the raw value).
Most of the time the id (primary key) is generated automatically while storing the entity to the database using strategies like AUTO, Sequence etc. So as to fetch those id's or autogenerated primary key values we assign back the saved entity.
For example:
#Entity
public class Customer {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
}
In this case you'll not pass the id externally but it will create a value for it automatically while storing the data to DB.

Dozer mapper field don`t exists

I trying to user Dozer in my app, so I have 2 classes:
public MyEntity{
private String name;
private Stirng age;
private String user;
private Integer day;
}
public class MyVO{
private String name;
private String age;
}
So, first I read the Entity from my db(with all fields filled) then I call the dozer to copy the values from VO to Entity
MyEntity entity = myRepo.findById(1);
entity = mapper.map(myVo, MyEntity.class);
but dozzer first sets null to all props in myEntity and then copy the values from myVo,
It`s possible to keep that props (who does not exist in both object) and copy only that fields that exists(or are mapped in my .xml) file
mapper.map(myVo, MyEntity.class);
This call to Dozer tells it to create a new MyEntity instance and then map the values from myVo. This is why some of your fields are null in the resulting entity.
If you want to update an existing instance using Dozer, call Dozer with the instance instead of the class name, i.e.
mapper.map(myVo, entity);
Note, this does not return the entity back to you since it modifies it in place.

Spring entity dynamically calculated field with parameter from native query

I have a somewhat complex entity like following (Notice the super-class with many more fields):
public class Question extends Entry {
#OneToMany(orphanRemoval = true, mappedBy = "question")
#JsonManagedReference
private List<Answer> answers = new ArrayList<>();
private Long viewCount = 0L;
private Category category;
#OneToMany(mappedBy = "question", fetch = FetchType.LAZY,
cascade = CascadeType.ALL, orphanRemoval = true)
private List<QuestionTranslation> translations = new ArrayList<>();
#Transient
private double distance;
}
distance should be calculated from the DB when retrieving the result set from a native query.
E.g.
SELECT q.*, ST_Distance_Sphere(cast(q.location as geometry), ST_MakePoint(cast(?1 as double precision), cast(?2 as double precision))) as distance from question q
I cannot use #Formula to annotate my field distance since the query has to take parameters.
How can I map the field distance from the SQL query result to my entity field distance while leaving all the other mappings to be done by Hibernate?
Edit
Based on #gmotux suggestion I created a wrapper entity.
#Entity
#SqlResultSetMapping(
name="MappingQ",
entities={
#EntityResult(
entityClass = QuestionWithDistance.class,
fields={
#FieldResult(name="distance",column="distance"),
#FieldResult(name="question",column="question")})})
public class QuestionWithDistance{
#Id
#GeneratedValue
private String id;
#OneToOne
private Question question;
private double distance;
}
Query
Query query = entityManager.createNativeQuery("SELECT q.*, 222.22 as distance from question q", "MappingQ");
But it always fails with
org.postgresql.util.PSQLException: The column name id1_15_0_ was not found in this ResultSet.
Since you need extra parameters to calculate your field, you indeed cannot use #Formula, or even a getter to calculate the field.
Unfortunately for your case the only thing that comes to mind, assuming you are using an EntityManager based configuration for Hibernate, is leveraging its #PostLoad event listener, which you can use for calculating field values upon entity loading, like :
public class Question extends Entry {
#PostLoad
private void postLoad() {
this.distance = DistanceCalculator.calculateDistance(Double param1,Double param2);
//other calculations
}
}
That of-course is only a workaround and it means that you must have a static method somewhere execute native queries.
I would suggest detaching the "distance" notion from your Question entity, if possible in your requirements and calculate it, when required, with either a native SQL function call or a service method.

Add data to database from Controller, different methods but same row

I have an entity model, for simplification purposes let's say it looks like this :
public class Results {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private Long firstUser;
private Long secondUser;
private Double average;
private Double median;
private Double score;
}
This is my ResultsService Implementation:
public class ResultsServiceImpl implements ResultsService{
#Autowired
private CalculateDataRepository calculateDataRepository;;
#Autowired
private ResultsService resultsService;
Results results=new Results();
public void Average(Long id1, Long id2){
UserData firstClient = calculateDataRepository.findOne(id1);
userData secondClient = calculateDataRepository.findOne(id2);
clientId = firstClient.getClient().getId();
secondId = secondClient.getClient().getId();
Double average=(firstClient.getA()+secondClient.getA())/2;
results.setAverage(average);
}
public void Score(Long id1, Long id2){
SurveyData firstClient = surveyDataRepository.findOne(id1);
SurveyData secondClient = surveyDataRepository.findOne(id2);
clientId = firstClient.getClient().getId();
secondId = secondClient.getClient().getId();
Double average=(firstClient.getB()+secondClient.getB());
results.setScore(average);
results.setFirstUser(clientId );
results.setSecondUser(secondId );
resultsService.save(results);
}
....
I tried declaring Results results=new Results(); inside every method, but when I save them they get saved in different rows, instead of the same one.
How do I hold the reference so that when I call the setter of a field in one function, it's in the same row as the setter of a field in the other function.
To keep the problem focused, I tried to avoid showing the implementation of calculateDataRepository which is just the repository of an entity where some results are saved for different users.
The Results Method has no foreign field reference nor a reference from somewhere else, as there are fields firstUser and secondUser which I set from one of the methods;
Thank you.
Edit:
Results results=resultsService.findByFirstUserAndSecondUser(clientId, secondId);
if(results==null) {
results= new Results();
// Store to db ?
}
results.setAverage();
resultsService.save(results);
Actually you need a method in ResultsRepository
Results findByFirstAndSecond(Long first, Long second);
In the each Average and Score methods (BTW Java naming convention requires to have method names start from lowercase letter) call the findByFirstAndSecond(id1, id2)
If the method returns null (no such result) create a new instance and save in the DB (INSERT). If some Results is returned store the info there and save changes in DB (UPDATE).

Spring Data Rest save record with relation (foreign-key) in Java (repo.save())

I don't know how to save a record in SDR with a link to an existing table.
For example:
There is a lookup-table Flag and another table Account with name and n:1 relation to Flag-ID.
The IDs in Flag-table are already created.
#Entity
public class Account{
public Account(String name, Flag flag){
this.name = name;
this.flag = flag;
}
#Id
private int id;
#Column
private String name;
#ManyToOne
private Flag flag;
// Getter & Setter
}
#Entity
public class Flag{
public Flag(String title){
this.title = title;
}
#Id
private int id;
#Column
private String title;
// Getter & Setter
}
Now I want to add an account and link it to the flag-id like this:
AccountRepo accountRepo;
accountRepo.save(new Account("Name", 0));
But I declared an object in my account-function and if I want to execute the save-function, I have to add a flag-object like this:
accountRepo.save(new Account("Name", new Flag("title")));
However in this case, the framework will add a new Flag-record, what I don't want. I only want to link it.
So I need help for solving my problem.
Thanks!
Edit:
The two answers from #piotr-sołtysiak and #upesh-m helped and worked for me. Thanks for your help!
You can use 'merge' of hibernate, ie. entityManager.merge(new Account("Name", new Flag("title"))). But the Id of the Flag should be an existing Id , so that it just adds an entry to Account.
ie. If you already have a flag record existing in db with id = 1, and you want to add an account linked to this flag, then use entityManager.merge(new Account("Name", existingFlagObject)
Find desired flag entity using dedicated repository, e.g
Flag flag = flagRespository.findByTitle("title");
Set it in Account entity and save:
accountRepo.save(new Account("Name", flag));

Resources