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.
Related
(SAVING DATA WITH RELATION USING BACKENDLESS 4.0)
I would like to save data with one to one relation.
example: -person_table have a relation with adress_table
the data is save in person_table and address_table without relation. how can make this? #backendless4.0
Adress newAdress= new Adress();
newAdress.setAdress("43 Street");
newAdress.setPersonId(Person);
Backendless.Persistence.save(newAdress, new AsyncCallback< Adress >() {
#Override
public void handleResponse(Adress response) {
}
#Override
public void handleFault(BackendlessFault fault) {
fault.getMessage();
}
});
I find the result:
for save with relation in backendless 4.0 you need to save the object first and save the relation after.
ref link 1 : ADD RELATION WITH OBJECTS
ref link 2 : save object with relation using REST API
Person personObject = // personObject retrieval is out of scope in this example
Adress objectAdress = // addressObject retrieval is out of scope in this example
ArrayList< Person > personCollection = new ArrayList< Person >();
personCollection.add( personObject );
Backendless.Data.of( Adress.class ).addRelation( objectAdress, "personId:Adress:n", personCollection,
new AsyncCallback<Integer>()
{
#Override
public void handleResponse( Integer response )
{
progressDialog.dismiss();
Log.i( "MYAPP", "related objects have been added");
}
#Override
public void handleFault( BackendlessFault fault )
{
}
} );
I've to implement caching with EhCache. Basic requirement is, I have to keep that cached object for fixed interval ( for now 1 hours in code below). So, I implemented the code as below:
Sample domain object:
import lombok.*;
#Getter
#Setter
#ToString
#AllArgsConstructor
public class City implements Serializable {
public String name;
public String country;
public int population;
}
Cache manager class:
import net.sf.ehcache.*;
public class JsonObjCacheManager {
private static final Logger logger = LoggerFactory.getLogger(JsonObjCacheManager.class);
private CacheManager manager;
private Cache objectCache;
public JsonObjCacheManager(){
manager = CacheManager.create();
objectCache = manager.getCache("jsonDocCache");
if( objectCache == null){
objectCache = new Cache(
new CacheConfiguration("jsonDocCache", 1000)
.memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.LRU)
.eternal(false)
.timeToLiveSeconds(60 * 60)
.timeToIdleSeconds(0)
.diskExpiryThreadIntervalSeconds(0)
.persistence(new PersistenceConfiguration().strategy(PersistenceConfiguration.Strategy.LOCALTEMPSWAP)));
objectCache.disableDynamicFeatures();
manager.addCache(objectCache);
}
}
public List<String> getKeys() { return objectCache.getKeys();}
public void clearCache(){
manager.removeAllCaches();
}
public void putInCache(String key, Object value){
try{
objectCache.put(new Element(key, value));
}catch (CacheException e){
logger.error(String.format( "Problem occurred while putting data into cache: %s", e.getMessage()));
}
}
public Object retrieveFromCache(String key){
try {
Element element = objectCache.get(key);
if(element != null)
return element.getObjectValue();
}catch (CacheException ce){
logger.error(String.format("Problem occurred while trying to retrieveSpecific from cache: %s", ce.getMessage()));
}
return null;
}
}
It caches and retrieves the values very properly. But my requirement is, I must modify the object that I retrieve from cache for given key. What I'm getting is, if I modify the object that I retrieved from cache, then cached object for that key is also getting modified.
Below is the example:
public class Application {
public static void main(String[] args) {
JsonObjCacheManager manager = new JsonObjCacheManager();
final City city1 = new City("ATL","USA",12100);
final City city2 = new City("FL","USA",12000);
manager.putInCache(city1.getName(), city1);
manager.putInCache(city2.getName(), city2);
System.out.println(manager.getKeys());
for(String key: manager.getKeys()){
System.out.println(key + ": "+ manager.retrieveFromCache(key));
}
City cityFromCache = (City) manager.retrieveFromCache(city1.getName());
cityFromCache.setName("KTM");
cityFromCache.setCountry("NPL");
System.out.println(manager.getKeys());
for(String key: manager.getKeys()){
System.out.println(key + ": "+ manager.retrieveFromCache(key));
}
}
}
The output that I'm getting is:
[ATL, FL]
ATL: City(name=ATL, country=USA, population=12100)
FL: City(name=FL, country=USA, population=12000)
[ATL, FL]
ATL: City(name=KTM, country=NPL, population=12100)
FL: City(name=FL, country=USA, population=12000)
This means, whenever I'm retrieving and modifying the object for given key, it also being reflected in cached value.
What my requirement is, the cached object for given key should not be modified. Is there any way to achieve this? Or is it not correct way to implement EhCache? Or I'm missing some fundamental principle?
I'm using EhCache V2.10.3
Thank you!
When you use a cache that is storing its data on the heap and with direct object references, you need to copy the object before using it.
In general it is good practice not to mutate a value after handing over the object reference to the cache (or anybody else beyond your control).
Some caches do have a copy mechanism to protect the cached values from modification. E.g. in EHCache3 you can add copiers, see Serializers and Copiers.
Alternatively, change your design: When you have the need to mutate the value, maybe you can split the values into two objects, one that is caches, one that contains the data that needs mutating and make the latter containing the first.
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();
}
}
I am using Swagger version 2 with Java Spring. I have declared a property and it works fine and it generates a drop down list of value I assigned.
#ApiParam(value = "Pass any one Shuttle provider ID from the list", allowableValues = "1,2,3,4,10")
private Long hotelId;
Now, I need a way to populate this list which is passed in allowableValues from my database as it could be random list as well as huge data. How can I assign list of values dynamically from database in this allowableValues?
This question is bit old, I too faced the same problem so thought of adding here which may help some one.
//For ApiModelProperty
#ApiModelProperty(required = true, allowableValues = "dynamicEnum(AddressType)")
#JsonProperty("type")
private String type;
Created a component which implements ModelPropertyBuilderPlugin
#Component
#Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1)
public class ApiModelPropertyPropertyBuilderCustom implements ModelPropertyBuilderPlugin {
private final DescriptionResolver descriptions;
#Autowired
public ApiModelPropertyPropertyBuilderCustom(DescriptionResolver descriptions) {
this.descriptions = descriptions;
}
public void apply(ModelPropertyContext context) {
try {
AllowableListValues allowableListValues = (AllowableListValues) FieldUtils.readField(context.getBuilder(),
"allowableValues", true);
if(allowableListValues!=null) {
String allowableValuesString = allowableListValues.getValues().get(0);
if (allowableValuesString.contains("dynamicEnum")) {
String yourOwnStringOrDatabaseTable = allowableValuesString.substring(allowableValuesString.indexOf("(")+1, allowableValuesString.indexOf(")"));
//Logic to Generate dynamic values and create a list out of it and then create AllowableListValues object
context.getBuilder().allowableValues(allowableValues);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public boolean supports(DocumentationType delimiter) {
return SwaggerPluginSupport.pluginDoesApply(delimiter);
}
}
Similary for ApiParam we can create component which will implement ParameterBuilderPlugin
#Override
public void apply(ParameterContext context) {
#SuppressWarnings("Guava") final Optional<ApiParam> apiParam =
context.resolvedMethodParameter().findAnnotation(ApiParam.class);
if (apiParam.isPresent()) {
final String allowableValuesString = apiParam.get().allowableValues();
//Your logic here
context.parameterBuilder().allowableValues(allowableValues);
}
}
You need to create constructor in SwaggerConfiguration class.
#Autowire service and withdraw data you need from database
assign this to final variable
assign this final variable to allowableValues in annotation
enjoy not efficient api
private final String allowableValues;
public SwaggerConfiguration() {
List<YourEntitiy> list = someService.findAll();
//code to get every value you need and add create comma separated String
StringJoiner stringJoiner = new StringJoiner(",");
stringJoiner.add(list.get(0).getValue());
this.allowableValues = stringJoiner.toString();
}
#ApiParam(allowableValues = allowableValues)
But I think it's bad idea getting all ids from database just to create allowable values. Just validate in api method if that id exist and/or Create new api to get ids from database, use pagination from Spring Data project, like PageImpl<> javadocs
I've created tables to store information from parsing xml files that are very nested. Therefore the tables in my database are nested too. The top level entity is called SpecimenTb.
#Entity
#Table(name="SPECIMEN_TB")
public class SpecimenTb implements Serializable {
private String mrn;
#Column(name="SPECIMEN_NO", unique = true)
private String specimenNo;
#OneToMany(mappedBy="specimenTb", cascade=CascadeType.ALL)
private List<FmReportTb> fmReportTbs;
}
SpecimenTb has a child entity called FmReportTb which has its own onetomany relationship to FmReportGeneTb
#Entity
#Table(name="FM_REPORT_TB")
public class FmReportTb implements Serializable {
//bi-directional many-to-one association to SpecimenTb
#ManyToOne
#JoinColumn(name="SPECIMEN_ID")
private SpecimenTb specimenTb;
#OneToMany(mappedBy="fmReportTb", cascade=CascadeType.ALL)
private List<FmReportGeneTb> fmReportGeneTbs;
}
It is possible a newer version of file will come at a later time, so I need to implement update scheme in my code. When I persist, I look for record in SpecimenTb by specimenNo. If it does not exist, insert new record. Otherwise, update the same records, father, children and grandkids.
SpecimenDao specimenDao = new SpecimenDao(em);
SpecimenTb specimenTb = specimenDao.findSpecimenBySpecno(blockId);
FmReportTb report = null;
if (specimenTb != null) { // update
report = specimenTb.getFmReportTbs().get(0);
} else { // insert
report = new FmReportTb();
}
if (report.getFmReportGeneTbs() != null) { // update
geneList = report.getFmReportGeneTbs();
geneList.clear();
} else { //insert
geneList = new ArrayList<FmReportGeneTb>();
}
// parse "Genes" section of xml and store in geneList
for (Node gene : genes) {
FmReportGeneTb geneTb = new FmReportGeneTb();
<< set a bunch of stuff for geneTb>>
geneTb.setFmReportTb(report);
}
geneList.add(geneTb);
report.setFmReportGeneTb(geneList);
if (specimenTb == null) { // insert new record
specimenTb = new SpecimenTb();
specimenTb.setSpecimenNo(blockId);
specimenTb.setMrn(mrn);
report.setSpecimenTb(specimenTb);
reports.add(report);
specimenTb.setFmReportTbs(reports);
specimenDao.persistSpecimen(specimenTb);
} else { // update
report.setSpecimenTb(specimenTb);
reports.add(report);
specimenDao.updateSpecimen(specimenTb, mrn, reports);
}
In the DAO class, persist and update methods are as follows:
// insert
public void persistSpecimen(SpecimenTb specimen) {
EntityTransaction transaction = entityManager.getTransaction();
try {
transaction.begin();
entityManager.persist(specimen);
transaction.commit();
} catch (Exception e) {
transaction.rollback();
}
}
// update
public void updateSpecimen(SpecimenTb specimen, String mrn, List<FmReportTb> reports) {
EntityTransaction transaction = entityManager.getTransaction();
try {
transaction.begin();
specimen.setSpecimenNo(blockId);
specimen.setMrn(mrn);
specimen.setFmReportTbs(reports);
transaction.commit();
} catch (Exception e) {
transaction.rollback();
}
}
Insert works exactly what I wanted, it persists father entity and all entities underneath thanks to cascade=CascadeType.ALL. When I update though, it does update the old record in SpecimenTb and FmReportTb but tends to insert a new series of records in FmReportGeneTb. So I ended up appending a new set of records in FmReportGeneTb that is linked to the same FmReportTb instead of updating (GeneList in old and new versions may differ in length). There are actually child entities and grandchild entities of FmReportGeneTb that I did not show. How do I update all related tables from top down?
I don't know what other fields do you set on FmReportTb, but seen your code, on the first line there is a
FmReportTb report = new FmReportTb();
That explain why you ended having two records of FmReportTb in your specimen.
If you want to update the existing FmReportTb you should set its id before update it.