I have following classes:
public class Note extends RealmObject {
#PrimaryKey
private String id;
private Template template;
// other primitive fields, getters & setters
}
public class Template extends RealmObject {
private String name;
private String color;
// other primitive fields, getters & setters
}
I get my data from backend via Retrofit & Gson, so I have ready-to-use java objects in response.
Let's imagine that backend returns me same three Notes each time I call it.
When I get the list of Note objects, I do the following:
private void fetchNotesAndSave() {
List<Notes> notes = getNotesViaRetrofit();
Realm realm = Realm.getInstance(mContext);
realm.beginTransaction();
realm.copyToRealmOrUpdate(notes);
realm.commitTransaction();
realm.close();
}
After that I call these lines to check count of stored objects:
int notesCount = mRealm.where(Note.class).findAll().size();
int templatesCount = mRealm.where(Template.class).findAll().size();
For the first time:
notesCount == 3;
templatesCount == 3;
That's right. But, if I call the server again, get same notes (same primaryKey ids), and call fetchNotesAndSave() again, I'll get these results:
notesCount == 3;
templatesCount == 6;
Each time I call copyToRealmOrUpdate(), nested objects, that are inside of objects with primaryKey are duplicated - not updated.
Is there any way to change this behaviour?
Please let me know if you need more information. Thanks in advance!
It is because your Template class doesn't have any primary key. In that case these objects are inserted again as there is no guarantee that the referenced template objects safely can be updated, even if they are part of another object that has a primary key.
If you add a #PrimaryKey to your template class it should work as you expect it to.
If you can't provide a PK as suggested, you might want to use the following work around to avoid duplicates.
for (Note note: notes) {
realm.where(Note.class)
.equalTo("id", note.getId())
.findFirst()
.getTemplate()
.deleteFromRealm();
}
realm.copyToRealmOrUpdate(notes);
Related
I have two classes (entity and DTO)
public class Deliver {
private Long id;
private String uri;
private Instant moment;
private DeliverStatus status; // enum PENDING,ACCEPTED,REJECTED
private String feedback; // feedback about received task
private Integer correctCount; // nr of correct questions
private Enrollment enrollment;
private Lesson lesson;
// constructors, getters and setters..
public class DeliverRevisionDto {
private DeliverStatus status;
private String feedback;
private Integer correctCount;
// constructors, getters and setters..
The goal is pretty simple, update the entity fields conveyed by Dto class I have the following code at Service layer (Spring Boot version 2.4.4):
#Service
public class DeliverService {
#Autowired
private DeliverRepository deliverRepository;
#Autowired
private ModelMapper modelMapper;
#Transactional
public void saveRevision(Long id, DeliverRevisionDto dto) {
Deliver deliver = deliverRepository.getOne(id);
System.out.println("BEFORE MAPPING: " + deliver.toString()); // # debug purpose
deliver = modelMapper.map(dto, Deliver.class);
// # debug purpose
TypeMap<DeliverRevisionDto, Deliver> tm = modelMapper.getTypeMap(DeliverRevisionDto.class, Deliver.class);
List<Mapping> list = tm.getMappings();
for (Mapping m : list)
{
System.out.println(m);
}
System.out.println("AFTER MAPPING: " + deliver.toString()); // # debug purpose
deliverRepository.save(deliver);
}
}
The console output is:
BEFORE MAPPING: Deliver [id=1, uri=``https://github/someone.com``, moment=2020-12-10T10:00:00Z, status=PENDING, feedback=null, correctCount=null, enrollment=com.devsuperior.dslearnbds.entities.Enrollment#7e0, lesson=com.devsuperior.dslearnbds.entities.Task#23]`
`PropertyMapping[DeliverRevisionDto.correctCount -> Deliver.correctCount]`
`PropertyMapping[DeliverRevisionDto.feedback -> Deliver.feedback]`
`PropertyMapping[DeliverRevisionDto.status -> Deliver.status]`
`AFTER MAPPING: Deliver [id=null, uri=null, moment=null, status=ACCEPTED, feedback=Muito bem cabra, tarefa aceita., correctCount=5, enrollment=null, lesson=null]
The mapping of the 3 fields in DTO is done correctly, BUT all the other fields of my entity are set to null. I know that I can skip fields according http://modelmapper.org/user-manual/property-mapping/
The problem is that I don´t want to couple the code with specific field names/getters/setters, that´s the reason I´m using ModelMapper. I wonder if there is any configuration that, upon mapping the modelmapper object says "Hey, the TARGET class have way more fields than the SOURCE class, I will left them untouched unconditionally (meaning I don´t need to say what fields are).
I'm trying to map fields between 2 classes with different set of fields (some are the same), and when I map the class with smaller set of fields to the one with bigger set of fields, the mapper set fields that don´t match with "null", I want these fields untouched (with original values) without I telling which one they are, after all, the mapper knows which ones match.
ModelMapper documentation is not the best part of that framework. Let us see what happens in your code.
Here you fetch the entity to be updated from the repo:
Deliver deliver = deliverRepository.getOne(id);
and log it having all the fields as should be. However this line:
deliver = modelMapper.map(dto, Deliver.class);
does a re-assignment to your variable deliver. This method creates a new instance of Deliver class and assigns it to variable deliver so discarding the entity fetched from repo.
This new instance will have all the fields that are not existing or not set in DTO null.
This is the API doc that my IDE provides, fotr these two different methods:
String org.modelmapper.ModelMapper.map(Object source, Class destinationType)
Maps source to an instance of destinationType. Mapping is performed according to the corresponding TypeMap. If no TypeMap exists for source.getClass() and destinationType then one is created.
Versus
void org.modelmapper.ModelMapper.map(Object source, Object destination)
Maps source to destination. Mapping is performed according to the corresponding TypeMap. If no TypeMap exists for source.getClass() and destination.getClass() then one is created.
It might not be clearly stated that the first method actually creates a new instance based on the type (Class) passed but it should be clear that ModelMapper cannot alter some arbitrary variable just by knowing the type. You need to pass the variable to alter as method parameter.
I want to write a generic function that accepts two objects of same entity class and compares the fields that are different and returns List of all the changes made to particular fields along with time.
One among the many entity classes would be say Member as follows
public class Member {
String firstName;
String lastName;
String driverLicenseNumber;
Integer age;
LocalDateTime timestamp;
}
In the DB, I have a table called member_audit that gets populated with old data whenever there is a change in member table using triggers (Similarly for other entities).
The List of resource for each of the entity I would be returning is something like
public class MemberAuditsResource {
private String field;
private LocalDateTime on;
private String changeType;
private String oldValue;
private String newValue;
}
I can only think of writing a function for each entity separately like this
private List<MembeAuditsResource> memberCompare(Member obj1, Member obj2) {
//Compare every field in both the objects using if else and populate the resource.
}
And then calling the above function to compare every pair of record in the entity_audit table.
The code would be very large to compare every field and multiplied by different entities.
Is there a better and efficient way?
If you extend the ideas to compare the object graph , it is not a trivial problem. So, the efficient way is not to re-inventing the wheel but use an existing library such as JaVers :
Member oldMember = new Member("foo" ,"chan" ,"AB12" , 21 ,LocalDateTime.now());
Member newMember = new Member("bar" ,"chan" ,"AB12" , 22 ,LocalDateTime.now());
Diff diff = javers.compare(oldMember, newMember);
for(Change change: diff.getChanges()) {
System.out.println(change);
}
Then , you can get something like:
ValueChange{ 'firstName' changed from 'foo' to 'bar' }
ValueChange{ 'age' changed from '21' to '22' }
Convert both object to a Map using JSON objectMapper.convertValue method. Then you can easily compare the keys/values of the two maps and create a list of differences.
I have a class which is used to get transfer data from the one application to another and then also to update if changes were made.
public class Data {
private String name;
private String number;
private String info;
... getters/setters...
}
Let's say name and number will be updated if you change them but e.g. info is not. What's the best way to tell programmers in the future that this is intended so they can recognize it immediately?
Update:
It's encoded as a JSON file and when I get it back I don't care about the info field anymore. It could be empty
You can create your custom annotation, specific to your application. If you are using any framework like Hibernate you can use #transient.
Probably not the correct way, but if you are just talking about "informing" other programmers, you could simply put the transient keyword on your info field.
But of course, that would be really "informal"; as it would probably not at all affect how your framework is dealing with your fields.
I would use serialisation combined with the transient keyword
What is object serialization?
import java.io.*;
import java.util.*;
// This class implements "Serializable" to let the system know
// it's ok to do it. You as programmer are aware of that.
public class SerializationSample implements Serializable {
// These attributes conform the "value" of the object.
// These two will be serialized;
private String aString = "The value of that string";
private int someInteger = 0;
// But this won't since it is marked as transient.
private transient List<File> unInterestingLongLongList;
There's no indication in your file that name or number are being persisted.
If you are going to put behavior into the file in some durable way, this isn't just a file, it's a representation of an object, where data and the related behavior live as one. Write a method clarifying the intent.
public boolean isStorable() {
boolean isOk = true;
isOk &= (name != null && name.length() > 0);
isOk &= (number > 0);
return isOk;
}
Makes it clear that not every one of these items contribute to being able to store the object, and that not every value within these items contribute to a valid storage state.
It also makes it clear that this object permits invalid states within its private data. That's a code smell that could indicate a design flaw. Perhaps you should look into whether that is a design flaw, and if it is, then fix it.
Start here https://docs.oracle.com/javaee/6/tutorial/doc/bnbpz.html
Almost any programmer seing a POJO like this will know that behaviour is what you explained....
#Table(name = "data")
public class Data {
#Id
#Column(name = "name")
private String name;
#Column(name = "number")
private String number;
private String info;
... getters/setters...
}
UPDATE: It's encoded as a JSON file and when I get it back I don't care about the info field anymore. It could be empty
So I have a class with three fields that maps to a table using hibernate
Class Widget
{
String field1;
String field2;
String field3;
}
On application startup a number of instances these widgets will be added to the database from an external files, but when I exit the application I need to know which (if any) of these fields have been changed by the user since the application was started, so the changes can be saved back to the files. I also need to store the original value for logging purposes.
I can't work whether I need a status field in the table or whether there is already a way of doing this using Hibernate/Database.
EDIT:A good solution to the program was given below . however the main reason I am using Hibernate is to reduce memory consumption so storing the original values when changed is not a good solution for me , I want everthing stored in the database. So I have create this new question How do I store a copy of each entity I add to database in Hibernate
Given an entity like the following you can track changes on one of it's field (while preserving its original value too).
#Entity
#Table(schema = "test", name = "test")
public final class Test {
private static final int ORIGINAL = 0;
private static final int CURRENT = 1;
private Integer id;
// holds the original and current state of the field
private final AtomicReferenceArray<String> field = new AtomicReferenceArray<>(2);
#Id
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#Transient
public String getOriginalField() {
return field.get(ORIGINAL);
}
#Basic
public String getField() {
return field.get(CURRENT);
}
public void setField(String field) {
this.field.compareAndSet(ORIGINAL, null, field);
this.field.set(CURRENT, field);
}
#PreUpdate
public void preUpdate() {
System.out.format("Original: %s, New: %s\n", getOriginalField(), getField());
}
...
}
If there is a single row in a database like this:
id: 1
field: a
version: 2011-12-02 11:24:00
before the field gets updated (say, from a to b) you'll get the following output.
Original: d, New: b
The original value gets preserved even if the the entity is updated multiple times and both state can be accessed through the corresponding getters (getField and getOriginalField—you can get more creative than me in the naming :).
This way, you can spare yourself from creating version columns in your database and also can hide the implementation details from clients.
Instead of an AtomicReferenceArray you could use arrays, lists, etc, to track all changes like this way.
The #PreUpdate isn't necessary of course, but this way you can be notified of changes in the entity's state and atomically save the updated fields into file. There more annotations like these: see the documentation for javax.persistence for other annotation types.
If you are using MySql then you can get table's last update time from information_schema database like
SELECT UPDATE_TIME FROM `information_schema`.`tables`
WHERE TABLE_SCHEMA = 'dbName' AND TABLE_NAME = 'tableName'
Or else simple solution will be to add a column for update time stamp. By this you can even monitor which particular row has been updated.
If you need to synchronize with files as soon as you save into database, You can use the Hibernate event mechanism to intercept any save to database and save it to file, here's a sample doing that.
I'm doing a school project in Java and I the following question have arisen:
I have an entity with attributes - id, name, phone.. with id as the unique primary key. I want to store them in a data structure(such as list..). Then in the application I obtain the data for creating a new instance (name, phone..) and I want to create a new instance of the entity and store it in my data structure with a new unique id. The id shouldn't be random, it would be best if the id rised continuously with the size of the list. Also I dont want to reuse ids.
The first implementation that comes to my mind is to use ArrayList and simply set id as indexes. But ArrayList.remove(int index) after removal shifts all following elements to left. I assume that ArrayList.remove(Object o) works the same, but i would be gratefull i I'm proven wrong. Determining ids from last element would not work either. I could go through all of them but that seems inefiicient.
Thanks in advance for any help :)
You want to keep a counter for them. You could use a static value in the class (you may need to synchronize it for multi-threaded classes.)
import java.util.concurrent.atomic.AtomicInteger;
class MyClass {
// thread safe
private static final AtomicInteger safeCounter = new AtomicInteger();
private final int uniqueId; // can never change uniqueId
private String name; // the data of the class
public MyClass(String name) {
this.name = name;
this.uniqueId = MyClass.safeCounter.getAndIncrement();
}
public boolean equals(Object o) {
if(o instanceof MyClass) { // instanceof also does null check :-)
MyClass mc = (MyClass)o;
return mc.uniqueId == this.uniqueId;
}
return false;
}
public int hashCode() {
return uniqueId;
}
}
If this is for homework, or if threadsafety isn't a concern, you can use a simple static int
class MyClass {
private static int nextUniqueId() {
int result = counter;
counter++;
return result;
}
// not thread safe
private static int counter;
private final int uniqueId; // can never change uniqueId
private String name; // the data of the class
public MyClass(String name) {
this.name = name;
this.uniqueId = nextUniqueId();
}
public boolean equals(Object o) {
if(o instanceof MyClass) { // instanceof also does null check :-)
MyClass mc = (MyClass)o;
return mc.uniqueId == this.uniqueId;
}
return false;
}
public int hashCode() {
return uniqueId;
}
}
How about using a Factory that users a Strategy for generating your identifiers?
Edited to answer question about factories
A Factory is a design pattern that is used to encapsulate the creation of different types of Objects. A Strategy is another design pattern that is used to encapsulate the behavior of specific business logic that might have different rules or that might change over time.
In your case you clearly require a new Identifier for each object that needs to be unique. You also stated in your question comments above that eventually you will be storing your objects in a database, which also would most likely require you to get your identifier from your database in the long run.
Here is a smallish example of using a Factory to create your User Objects instead of just using new(). Please kindly disregard any spelling or compile mistakes, I wrote the following code with out the assistance of a compiler or IDE.
public interface UserFactory {
User createUser();
}
public interface IdentifierStrategy {
// I just picked Long for ease of use.
Long getIdentifier();
}
public class UserFactoryImpl {
private final IdentifierStrategy identifierStrategy;
public UserFactoryImpl(final IdentifierStrategy identifierStrategy) {
this.identifierStrategy = identifierStrategy;
}
public User createUser() {
Long identifier = this.identifierStrategy.getIdentifier();
User user = new User(identifier);
return user;
}
}
public class LongIdentifierStrategy implements IdentifierStrategy {
public Long getIdentifier() {
// Do something here that will return a unique long.
Long long = new Long(1);
return long;
}
}
// In the long term, you would most likely use this IdentiferStrategy
// to get your identifiers from the database.
public class JDBCIdentifierStrategy implements IdentifierStrategy {
public Long getIdentifer() {
// Get a jdbc connection from a jdbc connection pool.
// Get the next identifier from the databsae.
Long long = new Long(1);
return long;
}
}
Now, in the long run, if your requirement change for how you need to identifier your User objects, you would only need to write a new IdentifierStrategy and update your UserFactoryImpl with that new Strategy.
One important question: what's the scope of the uniqueness?
Just for the duration of a run of the application? Do you have a single thread or multiple threads, so unique across those threads? Or could there be several copies of the app running at the same time, so unique across all instances, even across many machines? Will you save the data somewhere and so need uniqueness across future runs of the program too?
Two fundamental schemes:
a). use a database, they usually offer some kind of auto-generated primary key: you insert the record, it gives you a unique key.
b). generate the key yourself, in this case: first isolate the key generation to it's own class, then you can make the generation as clever as you wish. Sketch:
some initialisation, generate an initial value, simple case it's zero, or it derives from the current date/time, or MAC address of your machine, or whatever
provide a getNextId() function, which probably needs to be synchronized if threads are involved.
A very simple scheme, which will be OK for low volume systems, just use
new Date().getTime();
You can also look for GUID generators, which produce something unique, but rather bigger than an int.
My suggestion is to have an Object Pooling for ID generation. When the entity is "deleted", the ID should be returned to the pool, and when needing a new ID, the pool should either
Give you a new ID (if old ID doesn't exists in pool) or
Create a new ID for an entity.
The problem is that you will have to create an entity management system that caters for returning the "used" ID to the pool if entity is "deleted" (bear in mind the multithreading environment, which you will need to manage).
Alternatively, use a database system which provides primary key generation (most uses AUTO_INCREMENT).