I was just wondering if what I'm doing to update a entire table in realm is the correct safe approach. I receive a list of conversations from the server and update my db like this:
#Override
public void saveConversations(final List<Conversation> conversations) {
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
// Remove all conversations and replace with the one passed in
// Realm does not support cascade deletes. Remove messages as well
// https://github.com/realm/realm-java/issues/1104
realm.delete(Conversation.class);
realm.delete(Message.class);
realm.copyToRealmOrUpdate(conversations);
}
});
}
Conversation.java has a RealmList of messages inside:
public class Conversation extends RealmObject {
private RealmList<Message> messages = new RealmList<>();
This works, I couldn't find any bugs with it but it does not look particularly elegant. What if realm.copyToRealmOrUpdate(conversations); goes wrong? I would lose all my data.
Anyways, I know this is not very probable, but I was wondering if there was a better way of doing things.
PS: bear in mind that I delete everything from the db because I don't want conversations in my db that don't exist in the server anymore.
Don't forget that you're executing a transaction, so if copyToRealmOrUpdate() fails, then the transaction is cancelled, which means you wouldn't lose all your data.
Personally, I used to go with the "delete all" approach, and if you can clear out all tables then it won't cause issues, but if you have a third table where you're referring to Conversation and Message (for example User.class), you'd be invalidating all relations. So I personally prefer to merge like this.
merging data and removing all data that's not in the list you've saving
.
public class Contact {
#PrimaryKey
private long id;
#Index
private String name;
#Index
private String phoneNumber;
#Index
private boolean isBeingSaved; // this line is important
//getters, setters
}
Then merge:
// background thread
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
final List<Contact> contacts = getContacts();
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.insertOrUpdate(contacts);
realm.where(Contact.class)
.equalTo(ContactFields.IS_BEING_SAVED, false) // compile 'dk.ilios:realmfieldnameshelper:1.1.0'
.findAll()
.deleteAllFromRealm(); // delete all non-saved data
// in your case, this is where you'd handle the cascading too though manually
for(Contact realmContact : realm.where(Contact.class).findAll()) { // realm 0.89.0+
realmContact.setIsBeingSaved(false); // reset all save state
}
}
});
} finally {
if(realm != null) {
realm.close();
}
}
Related
I have 2 entity. One of them User, and the other one is Followers. I'm trying to make a follower system like Twitter. User can follow another user but, i want to check if user followed, don't do it again.
This is my Follower Entity :
#Entity
public class Followers {
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private long id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name="from_user_fk")
#JsonIgnore
private User from;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name="to_user_fk")
#JsonIgnoreProperties({ "password", "balance","id","mail" })
private User to;
public Followers() {};
public Followers(User from, User to) {
this.from = from;
this.to = to;
}
public User getFrom() {
return from;
}
public void setFrom(User from) {
this.from = from;
}
public User getTo() {
return to;
}
public void setTo(User to) {
this.to = to;
}
}
And this is the Service class :
public ResponseEntity<?> followUser(String username, User user) {
User byUsername = getByUsername(username);
List<Followers> followers1 = byUsername.getFollowers();
List<Followers> collect = followers1.stream().filter(p -> p.getTo().getId().equals(user.getId())).collect(Collectors.toList());
if(followers1.size()>0){
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("e");
}
Followers followers = new Followers();
followers.setFrom(user);
followers.setTo(byUsername);
followersRepository.save(followers);
return ResponseEntity.ok(new GenericResponse("Followed"));
}
public List<Followers> getUserFollowers(String username) {
User byUsername = getByUsername(username);
List<Followers> followers = byUsername.getFollowers();
return followers;
}
As you can see, I got the followers of the user I want to follow, and try to check if its following or not. But I couldn't.
Any help would be appreciated
What you've built is incredibly inefficient:
Go to the DB and fetch all followers for a given user.
Then check through these if the person you'd like to add already exists.
If no, add it.
If yes, don't do anything or show an error.
The fail whale is in your near future with this kind of inefficiency.
There's a much, much simpler way. Just.. add it! Don't do any check at all.
Your DB should be configured to disallow having the same user/follower pair, so if you attempt to pull that stunt when that user already has that follower, the DB will refuse and throw an exception that indicates that there's a DB constraint violation. That's your cue to render whatever error you please.
Note that 'check if X is allowed, if yes, do X' is fundamentally broken when talking about multicore architecture. What you're forgetting: What if the user 'double clicks' the 'add follower' link? Then 2 requests start, simultaneously. They both check if X is already a follower (they are not), then they both add X as follower (and now X is a double-follower which you did not want).
Generally if a DB is involved, it is better at data consistency and transactions support, so use the best tool for the job: A DB constraint.
I have an aggregate Organization which can have several addresses. So we have modeled this OrganizationDeliveryAddress as an Aggregate Member. On the OrganizationDeliveryAddress we command and event sourcing handlers for the entity itself.
Here's my current implementation:
#Aggregate
public class Organization {
private #AggregateIdentifier
#NonNull UUID organizationId;
#AggregateMember
private final List<OrganizationDeliveryAddress> deliveryAddresses = new ArrayList<>();
#CommandHandler
public UUID on(AddOrganizationDeliveryAddressCommand command) {
val addressId = UUID.randomUUID();
val event = new OrganizationDeliveryAddressAddedEvent(command.getOrganizationId(), addressId, command.getAddress());
AggregateLifecycle.apply(event);
return addressId;
}
#EventSourcingHandler
public void on(OrganizationDeliveryAddressAddedEvent event) {
val address = new OrganizationDeliveryAddress(event.getOrganizationDeliveryAddressId(), false);
deliveryAddresses.add(address);
}
}
public class OrganizationDeliveryAddress {
private #EntityId
#NonNull UUID organizationDeliveryAddressId;
#CommandHandler
public void on(RemoveOrganizationDeliveryAddressCommand command) {
AggregateLifecycle.apply(new OrganizationDeliveryAddressRemovedEvent(command.getOrganizationId(),
command.getOrganizationDeliveryAddressId()));
}
#EventSourcingHandler
public void on(#SuppressWarnings("unused") OrganizationDeliveryAddressRemovedEvent event) {
if (organizationDeliveryAddressId.equals(event.getOrganizationDeliveryAddressId())) {
AggregateLifecycle.markDeleted();
}
}
}
We want to remove one of the addresses, but it looks like not just the address, but the entire aggregate is deleted.
So here's my question: How can I instruct Axon Framework to remove the OrganizationDeliveryAddress Aggregate Member?
The AggregateMember is not an Aggregate per se but just a member of another Aggregate. This is why if you call AggregateLifecycle.markDeleted(); it will mark the Aggregate itself as deleted.
To 'delete' an AggregateMember you should do the opposite as adding it, which means you can have an #EventSourcingHandler method on your Aggregate listening to the OrganizationDeliveryAddressRemovedEvent. This method would be responsible to find the right DeliveryAddress on your AggregateMember (deliveryAddresses) or even better a Map as you will see below and simply remove it from that. A pseudo-code could be something like this:
// Organization.java
...
#AggregateMember
private final Map<UUID, OrganizationDeliveryAddress> deliveryAddressItToDeliveryAddress = new HashMap<>();
...
#EventSourcingHandler
public void on(#SuppressWarnings("unused") OrganizationDeliveryAddressRemovedEvent event) {
Assert.isTrue(deliveryAddressItToDeliveryAddress.containsKey(event.getOrganizationDeliveryAddressId()), "We do not know about this address");
deliveryAddressItToDeliveryAddress.remove(event.getOrganizationDeliveryAddressId());
}
I created a room database following this guide from code labs It makes use of a repository to:
A Repository manages query threads and allows you to use multiple backends. In the most common example, the Repository implements the logic for deciding whether to fetch data from a network or use results cached in a local database.
I followed the guide and i'm now able to create the entity's & retrieve the data. I even went further and created another whole entity outside the scope of the guide.
However I can't find many resources that use this MVVM(?) style so am struggling as to really under stand the repository. For now I want to update a field. Just one, as if I am able to manage that the rest should be similar.
I want to update a field called dartshit and I have the dao method created for this:
#Query("UPDATE AtcUserStats SET dartsHit = :amount WHERE userName = :userName")
void UpdateHitAmount(int amount, String userName);
I have one repository which I assumed I use for all entities:
public class UsersRepository {
private UsersDao mUsersDao;
private AtcDao mAtcDao;
private LiveData<List<Users>> mAllUsers;
private LiveData<List<AtcUserStats>> mAllAtc;
private AtcUserStats mAtcUser;
UsersRepository(Application application) {
AppDatabase db = AppDatabase.getDatabase(application);
mUsersDao = db.usersDao();
mAtcDao = db.atcDao();
mAllUsers = mUsersDao.fetchAllUsers();
mAllAtc = mAtcDao.getAllAtcStats();
}
LiveData<List<Users>> getAllUsers() {
return mAllUsers;
}
LiveData<List<AtcUserStats>> getAllAtcStats() {
return mAllAtc;
}
LiveData<AtcUserStats> getAtcUser(String username) {
return mAtcDao.findByName(username);
}
public void insert (Users user) {
new insertAsyncTask(mUsersDao).execute(user);
}
public void insertAtc (AtcUserStats atc) {
new insertAsyncAtcTask(mAtcDao).execute(atc);
}
private static class insertAsyncTask extends AsyncTask<Users, Void, Void> {
private UsersDao mAsyncTaskDao;
insertAsyncTask(UsersDao dao) {
mAsyncTaskDao = dao;
}
#Override
protected Void doInBackground(final Users... params) {
mAsyncTaskDao.insertNewUser(params[0]);
return null;
}
}
private static class insertAsyncAtcTask extends AsyncTask<AtcUserStats, Void, Void> {
private AtcDao mAsyncTaskDao;
insertAsyncAtcTask(AtcDao dao) {
mAsyncTaskDao = dao;
}
#Override
protected Void doInBackground(final AtcUserStats... params) {
mAsyncTaskDao.insertNewAtcUser(params[0]);
return null;
}
}
}
My question is how do I create a AsyncTask for the update query I am trying to run in this repository?
Here is what I have so far by broadly copying the insert repository methods:
private class updateHitAsyncTask {
private AtcDao mAsyncTaskDao;
public updateHitAsyncTask(AtcDao mAtcDao) {
mAsyncTaskDao = mAtcDao;
}
protected Void doInBackground(int amount, String name) {
mAsyncTaskDao.UpdateHitAmount(amount, name);
return null;
}
}
Which is incorrect is that I'm getting a llegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time. error. But i thought this AsyncTask is suppose to take care of this?
Here is my update method in my view model, which is reporting 0 errors:
void updateHitAmount (int amount, String name) {
mRepository.updateAtcHits(amount, name);
}
and here is the UI code where im actually trying to tie all these together, I suspect there must be a better way that using onChanged for simply updating a field but again I am struggling to come across any advice on google with the repository approach:
private void callOnChanged() {
mAtcViewModel = ViewModelProviders.of(this).get(AtcViewModel.class);
mAtcViewModel.getAllUsers().observe(this, new Observer<List<AtcUserStats>>() {
#Override
public void onChanged(#Nullable final List<AtcUserStats> atc) {
// Update the cached copy of the users in the adapter.
for (int i = 0; i < atc.size(); i++) {
if (atc.get(i).getUserName().equals(mUser)) {
mAtcViewModel.updateHitAmount(55, mUser);
//atc.get(i).setDartsHit(55);
Log.d("id", String.valueOf(userSelected.getId()));
}
}
}
});
How can I update fields using this approach on the background thread?
Figured it out due to this answer here. It was mostly because of my lack of understanding of AsyncTask. Essentially I needed to create an object and pass the data that way and then execute in the background:
private static class MyTaskParams {
int amount;
String name;
MyTaskParams(int amount, String name) {
this.amount = amount;
this.name = name;
}
}
public void updateAtcHits (int amount, String name) {
MyTaskParams params = new MyTaskParams(amount,name);
new updateHitAsyncTask(mAtcDao).execute(params);
}
private class updateHitAsyncTask extends AsyncTask<MyTaskParams,Void,Void>{
private AtcDao mAsyncTaskDao;
public updateHitAsyncTask(AtcDao mAtcDao) {
mAsyncTaskDao = mAtcDao;
}
#Override
protected Void doInBackground(MyTaskParams... myTaskParams) {
int amount =myTaskParams[0].amount;
String name = myTaskParams[0].name;
mAsyncTaskDao.UpdateHitAmount(amount, name);
return null;
}
}
I am fetching data from json api I have set PrimaryKey as state. This is the Json data I am fetching:
records[
{
"id":"192693681",
"timestamp":"1500204608",
"state":"Rajasthan",
"district":"Rajasamand",
"market":"Rajasamand",
"commodity":"Sugar",
"variety":"Shakkar",
"arrival_date":"16/07/2017",
"min_price":"4000",
"max_price":"4100",
"modal_price":"4050"
},
{
"id":"192693701",
"timestamp":"1500204608",
"state":"Rajasthan",
"district":"Rajasamand",
"market":"Rajasamand",
"commodity":"Wheat",
"variety":"Farmi",
"arrival_date":"16/07/2017",
"min_price":"1600",
"max_price":"1650",
"modal_price":"1625"
},
{
"id":"192693721",
"timestamp":"1500204608",
"state":"Rajasthan",
"district":"Rajasamand",
"market":"Rajasamand",
"commodity":"Wheat",
"variety":"Lokwan",
"arrival_date":"16/07/2017",
"min_price":"1550",
"max_price":"1600",
"modal_price":"1575"
}
]
This is my query to return the market data for every state:
private void populateMarketData(String state){
cityAdapter = new CityAdapter(mRealm.where(Records.class).equalTo(Constants.STATE, state).findAll(), this);
cityRecyclerView.setAdapter(cityAdapter);
Log.d(TAG, "Total Cities" + String.valueOf(cityAdapter.getData().size()));
cityAdapter.notifyDataSetChanged();
}
This is my query to return all commodities for the market with the states:
#Override
public void onMarketClicked(String cityName) {
tradeAdapter = new TradeAdapter(mRealm.where(Records.class)
.equalTo(Constants.MARKET, cityName).findAll(), this);
tradeRecyclerView.setAdapter(tradeAdapter);
}
This is GcmTaskService to update data in background service:
Realm.init(mContext);
List<Records> records = new MandiDataHandler().getMandiQuotes(url);
SaveMandi saveMandi = new SaveMandi(state, records);
Realm realm = new RealmController(getApplication()).getRealm();
realm.beginTransaction();
realm.copyToRealmOrUpdate(saveMandi);
realm.commitTransaction();
realm.close();
This is DataHelper to save the data from Json API
#PrimaryKey
private String state;
private RealmList<Records> recordsRealmList = new RealmList<>();
public SaveMandi(){}
public SaveMandi(String state, List<Records> recordsList) {
try {
this.state = state;
for (Records records: recordsList ) {
records = new Records(records.getId(), DateUtils.getRelativeTimeSpanString(
Long.parseLong(records.getTimestamp())).toString(), records.getState(), records.getMarket(),
records.getCommodity(), records.getVariety(), records.getMin_price(), records.getMax_price());
this.recordsRealmList.add(records);
}
}catch (Exception e){
e.printStackTrace();
}
}
The problem in my RealmRecyclerView it either returns single item when I set the PrimaryKey to state, else when I set the PrimaryKey to id it returns multiple duplicate data. I am not sure about where I might be wrong.
You want to choose your PrimaryKey to be something that will only match when they are (what you deem as) "equal", much like an equals() method. If you put two RealmObjects with the same PrimaryKey into Realm only one of them will show up in Realm since there can only be one object for each distinct PrimaryKey.
For example if I have a RealmObject a with a PrimaryKey of 1 in realm and I put another RealmObject b with a PrimaryKey of 1 into Realm, a will be deleted with b since there can only be one object for every PrimaryKey.
A good tool to look through your Realm databases is Stetho, I highly recommend it for debugging applications using Realm.
I'm trying to upgrade from Spring Data Neo4J 3 to 4 - I'm using Neo4J 2.2.2.
I use a GraphRepository instance to query the database, fetching back an object.
This object has several relationships, which are not fetched (deliberately, to avoid reading in the entire graph).
In the SDN3 code, simply used the Neo4JTemplate class to perform a fetch call for each relationship I needed to fetch. This worked extremely well.
However, in SDN4 this facility has been removed, and replaced by various implementations of the load() method. It's not clear from the documentation how to achieve what I did in SDN3.
To be clear: if I have a Set of objects in the first class I retrieve, governed by a relationship, I want to retrieve only the objects in that Set, not the entire collection of those objects in the database.
Have I missed something crucial in the upgrade process, or is there a simple way of doing what I'm trying to do?
Adding code:
My entity class:
#NodeEntity
public class File implements MetroNode {
private Long id;
private String fileName;
private SourceState sourceState;
private Set<State> states;
#GraphId
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
#Relationship(type = "HAS_FILE", direction = Relationship.INCOMING)
public SourceState getSourceState() {
return sourceState;
}
public void setSourceState(SourceState sourceState) {
this.sourceState = sourceState;
}
public State addState(MetroNode otherNode, StateStatus status) {
if (this.states == null) {
this.states = new HashSet<State>();
}
State state = new State(this, otherNode, status.toString());
this.states.add(state);
return state;
}
#Relationship(type = "HAS_STATE", direction = Relationship.OUTGOING)
public Set<State> getStates() {
return states;
}
public State getActiveState() {
if (this.states != null) {
for (State state : this.states) {
if (state.isActive()) {
return state;
}
}
}
return null;
}
}
My repository class:
public interface FileRepository extends GraphRepository<File> {
File findByFileName(String fileName);
}
When executing the getActiveState() method I get a null return, because the states Set is empty (hasn't been fetched).
Looking again at my code, I wonder if it's because I'm not using a "native" load method from the repository, but the overloaded version?
SDN 4 allows you to control loading of related entities with the persistence horizon.
Loading an entity with depth 0 will fetch properties of the entity and no related entities.
Depth 1 will fetch the first level of related entities, but not their relations and so on.
Controlling the depth by relationship type is not supported.