I'm trying to figure out how to use Android's Room library for implementing a prepopulated sqlite database in my app and I came across this Android tutorial. One of the lines (the one in the title) confuses me though, because in another tutorial (also by Android), this line isn't present. Why is this line of code present in the first tutorial but not the second? What is its purpose?
I ask this because my code (which I'm basing off the second tutorial) doesn't include this line and yet this post by a different user attempting to do something similar with a prepopulated database does include it.
Here is some of the code I have (each of the fields has a getter method which just returns this.thatfield'sname):
#Entity (tableName = "words")
public class Words {
#PrimaryKey
#NonNull
#ColumnInfo (name = "word_id")
private int wordId;
#ColumnInfo(name = "a_words")
private String aWords;
#ColumnInfo(name = "b_words")
private String bWords;
#ColumnInfo(name = "c_words")
private String cWords;
This code gives me a "Cannot find setter for field" but just changing the fields from public to private seems to solve that (not sure if this is the best way to solve this error, though).
Why is this line of code present in the first tutorial but not the second?
That line is an additional class constructor that takes 1 non-null String and sets the mWord member/variable to the provided String.
Without then you can only use myWord = new Word(); to instantiate a Word object and the value would be either the default value if provided or null.
With the additional constructor then you could use both
myWord = new Word();
or
myOtherWord = new Word("A Word");
So, in short it's provided an alternative way of constructing/instantiating a new Object of that Class.
Using your code then you could have, for example :-
#Entity(tableName = "words")
class Words {
#ColumnInfo(name = "word_id")
#PrimaryKey
private int wordId;
#ColumnInfo(name = "a_words")
String aWords;
#ColumnInfo(name = "b_words")
String bWords;
#ColumnInfo(name = "c_words")
String cWords;
public void setWordId(int wordId, String aWord, String bWords, String c) {
this.wordId = wordId;
this.aWords = aWord;
this.bWords = bWords;
this.cWords = c;
}
}
Note for demonstration the parameter names use 3 different standards, ideally you would stick to a single standard/convention for naming the parameters.
So now you could use the one constructor that expects 4 parameters e.g.
myWord = new Words(1,"Apple","Banana","Cherry");
which equates to
myWord = new Words();
myWord.wordId = 1;
myWord.aWords = "Apple;
myWord.bWords = "Banana";
myWord.cWords = "Cherry";
As you have specified a constructor, the default constructor is no longer usable.
What is its purpose?
As can be seen, additional constructors, can reduce the amount of coding, there use will also prompt for the values (hence the use of useful parameter names improves i.e. c as above is not very meaningful at all (although in conjunction with the other parameters if would be better than x))
I need to compare one PatientDTO dto object with other one PatientModel model object.
Both classes are quite similar:
class PatientDTO {
private String name;
private List<AddressDTO> address;
// Constructors, getters and setters
}
class PatientModel {
private String id;
private String nameElement;
private List<AddressModel> addressElement;
// Constructors, getters and setters
}
class AddressDTO {
private String city;
private String country;
private List<String> linesElement;
// Constructors, getters and setters
}
class AddressModel {
private String city;
private String countryElement;
private List<String> linesElement;
// Constructors, getters and setters
}
Main differences are:
Some fields are not present on DTOs: PatientDTO.id doesn't exist.
Some field names contains suffixes on Model classes: PatientDTO.name <> PatientModel.nameElement.
Other issue I like to solve, is that:
Address related assertion should be shared. I mean, Address-like classes are present on other classes, for exemple, Organization, Practitioner...
I'd like to build an assertion like this:
PatientDTO patientDTO;
PatientModel patientModel;
assertThat(patientDTO).isEqual(patientModel);
Shortly:
Should I build a custom assertion?
Should I have an assertion for Address an other one for Patient containing previous Address assertion? How could I get this?
What aboud Address assertion for Patient, Organization
What I want to avoid is code like this:
assertThat(patientDTO).anySatisfy(p->{
assertThat(p.getName()).withFailMessage("expected name: "+ p.getAddress().getCity()).isEqualTo(patientModel.getNameElement());
assertThat(p.getAddress().getCity()).withFailMessage("expected city: "+ p.getAddress().getCity()).isEqualTo(patientModel.getCityElement());
assertThat(p.getAddress().getCountry()).withFailMessage("expected country: "+ p.getAddress().getCountry()).isEqualTo(patientModel.getCountryElement());
...
}
);
I want to avoid above code since Patient classes are really large. Here I've shorted them for clarity purpouses.
Any ideas?
The field-by-field recursive comparison could help for this purpose:
PatientDTO patientDTO = new PatientDTO(...);
PatientModel patientModel = new PatientModel(...);
assertThat(patientDTO).usingRecursiveComparison()
.isEqualTo(patientModel);
Some fields are not present on DTOs: PatientDTO.id doesn't exist.
There are a few methods that can be used to tune the comparison and ignore fields:
Directly with ignoringFields(String… fieldsToIgnore)
By regexes with ignoringFieldsMatchingRegexes(String… regexes)
By types with ignoringFieldsOfTypes(Class… typesToIgnore)
Some field names contains suffixes on Model classes: PatientDTO.name <> PatientModel.nameElement.
This is currently not supported and was also asked in https://stackoverflow.com/a/70381488/9714611. We plan to raise a feature request about it and I will update the answer once the issue link is ready.
Address related assertion should be shared. I mean, Address-like classes are present on other classes, for exemple, Organization, Practitioner...
If the target is always isEqualTo, probably a custom assertion implementation is not needed as long as the limitation of the recursive comparison about not being able to compare fields with different names is not a show-stopper. These fields would require ad-hoc comparison until a better solution is available.
If the target is to provide assertions in a domain-specific language, like:
assertThat(patientDTO).hasAddress(addressDTO);
then a custom assertion implementation can be added.
Also, there is an assertions generator with plugins for Maven and Gradle that can be used to generate assertions based on the class attributes.
I have created a wrapper class to create an Object and send it as a request to a third party system. It was working well. But after I added a two new arguments of the Datatype Date, I am getting the below error.
Constructor not defined: [SFDC_DataObject.CustomerAccountObject].<Constructor>(Id, String, Id, String, Id, String, Integer, NULL, String, String, Id, String, NULL, String, String, String, String)
The request that I am creating and sending is as below.
SFDC_DataObject.CustomerAccountObject cusAccObj = new SFDC_DataObject.CustomerAccountObject(o.AccountId, o.Customer_Name__c, o.Agency_Name__r.Id,o.Agency_Name_OB__c, o.Opportunity.OwnerId, o.Opportunity.Owner.FederationIdentifier, PrimarySalesSplitPercent, null, secSOSalesforceId.get(o.OpportunityId), secSOSalesforceEmail.get(o.OpportunityId), o.Opportunity.Customer_Success_Manage__r.Id, o.Opportunity.Customer_Success_Manage__r.FederationIdentifier, null, o.Billing_Email__c, o.Billing_Phone__c, o.Bill_To_Name__c, o.Billing_Notes__c);
My wrapper class for the same object is as below.
public class CustomerAccountObject {
public String sfCustomerId;
public String customerName;
public String sfAgencyId;
public String agencyName;
public String sfPrimarySalesOwnerId;
public String primarySalesOwnerEmail;
public Integer primarySalesOwnerPercentage;
public Date primarySalesOwnerEffectiveFrom;
public String sfSecondarySalesOwnerId;
public String secondarySalesOwnerEmail;
public Date secondarySalesOwnerEffectiveFrom;
public String sfAccountManagerId;
public String accountManagerEmail;
public String billingEmail;
public String billingPhone;
public String billingName;
public String billingNotes;
public CustomerAccountObject() {}
public CustomerAccountObject(String sfCustomerId, String customerName, String sfAgencyId, String agencyName, String sfPrimarySalesOwnerId, String primarySalesOwnerEmail, Integer primarySalesOwnerPercentage, Date primarySalesOwnerEffectiveFrom, String sfSecondarySalesOwnerId, String secondarySalesOwnerEmail, Date secondarySalesOwnerEffectiveFrom, String sfAccountManagerId, String accountManagerEmail, String billingEmail, String billingPhone, String billingName, String billingNotes) {
this.sfCustomerId = sfCustomerId;
this.customerName = customerName;
this.sfAgencyId = sfAgencyId;
this.agencyName = agencyName;
this.sfPrimarySalesOwnerId = sfPrimarySalesOwnerId;
this.primarySalesOwnerEmail = primarySalesOwnerEmail;
this.primarySalesOwnerPercentage = primarySalesOwnerPercentage;
this.primarySalesOwnerEffectiveFrom = primarySalesOwnerEffectiveFrom;
this.sfSecondarySalesOwnerId = sfSecondarySalesOwnerId;
this.secondarySalesOwnerEmail = secondarySalesOwnerEmail;
this.secondarySalesOwnerEffectiveFrom = secondarySalesOwnerEffectiveFrom;
this.sfAccountManagerId = sfAccountManagerId;
this.accountManagerEmail = accountManagerEmail;
this.billingEmail = billingEmail;
this.billingPhone = billingPhone;
this.billingName = billingName;
this.billingNotes = billingNotes;
}
}
I began getting the error after I added the null for the Date arguments I.e primarySalesOwnerEffectiveFrom and secondarySalesOwnerEffectiveFrom during the Object creation.
Can anyone please let me know what am I doing wrong here.
The order is wrong.
In c-tor definition you have
String sfCustomerId, String customerName, String sfAgencyId, String
agencyName, String sfPrimarySalesOwnerId, String
primarySalesOwnerEmail, Integer primarySalesOwnerPercentage, Date
primarySalesOwnerEffectiveFrom, String sfSecondarySalesOwnerId, String
secondarySalesOwnerEmail, Date secondarySalesOwnerEffectiveFrom + 6 more Strings
So
... Integer, Date, String, String, Date, ...
But the code that calls it goes
o.AccountId, o.Customer_Name__c,
o.Agency_Name__r.Id,o.Agency_Name_OB__c, o.Opportunity.OwnerId,
o.Opportunity.Owner.FederationIdentifier, PrimarySalesSplitPercent,
null, secSOSalesforceId.get(o.OpportunityId),
secSOSalesforceEmail.get(o.OpportunityId),
o.Opportunity.Customer_Success_Manage__r.Id,
o.Opportunity.Customer_Success_Manage__r.FederationIdentifier, null, +
4 strings
There are extra 2 strings before 2nd null. And only 4 strings after it. You need to inject that null just after secSOSalesforceEmail?
This will get only worse to maintain as time goes on. Consider making a simple constructor and making the properties public. You could then set them after constructor in normal call. And if you don't need dates you just don't write line that sets date fields instead of injecting null at right position.
Follow-up edit
Not sure if there's an official guide to that technique or a blog post. Tools like Apex-PMD complain when you make methods with too many arguments, rules like "Avoid long parameter lists".
One way would be to do something like this:
SFDC_DataObject.CustomerAccountObject cusAccObj = new SFDC_DataObject.CustomerAccountObject();
cusAccObj.sfCustomerId = o.AccountId;
cusAccObj.customerName = o.Customer_Name__c;
cusAccObj.sfAgencyId = o.Agency_Name__c;
cusAccObj.agencyName = o.Agency_Name_OB__c;
cusAccObj.sfPrimarySalesOwnerId = o.Opportunity.OwnerId;
cusAccObj.primarySalesOwnerEmail = o.Opportunity.Owner?.FederationIdentifier;
cusAccObj.primarySalesOwnerPercentage = PrimarySalesSplitPercent;
// cusAccObj.primarySalesOwnerEffectiveFrom = null; // just don't bother with the line?
cusAccObj.sfSecondarySalesOwnerId = secSOSalesforceId.get(o.OpportunityId);
// ..
That's not very object oriented, not very elegant but caller has full control on the mapping. Problem will be if you need to map new field and this has been copy-pasted into 10 places. You'll have to update them all (which will be easier than adding N-th parameter to long call but still)
Another way would be to create a baseline constructor that takes whole Order object (it's an Order, right?), it'd map the fields internally. Then if needed - you specify some extra fields after constructor. Or maybe make few constructors?
public CustomerAccountObject(){
// I'm parameterless, I'm doing nothing! I'm just here if somebody needs a really custom field mapping or JSON deserialisations need a parameterless one
}
public CustomerAccountObject(Order o){
// I can map all fields from Order! Want to map new field? Just chuck it in here!
sfCustomerId = o.AccountId;
// ...
}
public CustomerAccountObject(Order o, Map<Id, String> secSOSalesforceId, Map<Id, String> secSOSalesforceEmail){
// I can do everything above and few more fields too!
this(o);
sfSecondarySalesOwnerId = secSOSalesforceId.get(o.OpportunityId);
secondarySalesOwnerEmail = secSOSalesforceEmail.get(o.OpportunityId);
}
You have bit of code reuse, the Order fields mapping is defined in just 1 place, just 1 line to change in future. You don't have an orgy of this everywhere anymore. And then your call if you really need the last constructor or you'll call the one that just takes Order o and then set the 2 extra fields after it finishes.
I have a model in play framework
public class XYZ extends Model
{
#Id
public int a;
public String field1;
public String field2;
}
In my index.scala.html I need to generate field1 and field2 dynamically.
I have an object xyz of XYZ class.
I need to get the value of xyz.field1.
I generate the string field1 dynamically in my code using "field".concat("1") and now I need to convert this string to a field so as to call xyz.field1.
I am not able to figure out how to do this conversion in my scala.html file.
You can use reflections to get a field by its name, even in a template.
#classof[XYZ].getField("field" + fieldNum).get(xyz)
If you have only a two fields, a simple if/else would probably a better way to get the fields values. If it's more complex create a method in your model and use some switch statement or a map, like Mikesname suggested.
I have the following scenario: A Doctor can have multiple Companions. A Companion can also have multiple Doctors. Here are my classses (minus the context):
Public Class Doctor
Public Property DoctorId As Integer
Public Property Regeration As Integer
Public Property Name As String
Public Property Actor As String
Public Property Companions As List(Of Companion)
End Class
Public Class Companion
Public Property CompanionId As Integer
Public Property Name As String
Public Property Actor As String
Public Property Doctors As List(Of Doctor)
End Class
Public Class DoctorViewModel
Public Property DoctorId As Integer
Public Property Actor As String
Public Property Companions As List(Of CompanionViewModel)
End Class
Public Class CompanionViewModel
Public Property Actor As String
End Class
I'm trying to fetch a singular Doctor with a list of companions who have travelled with him. This I can do quite simply, but I'm trying to shape the query to get only a few columns and not the entire entity. He is my bungled query - the X's are what I can't figure out.
Dim query = From d In context.Doctors
From c In context.Companions
Select New DoctorViewModel With {
.Actor = d.Actor,
.DoctorId = d.DoctorId,
.Companions = XXXXXXX}
EDIT: If I query:
(From d In Tardis.Doctors
Where d.Actor = "Tom Baker"
Select New DoctorViewModel With {.Actor = d.Actor, .Companions = d.Companions.Select(Function(c) New CompanionViewModel With {.Actor = c.Actor})}).SingleOrDefault
I get:
"Unable to cast the type 'System.Collections.Generic.IEnumerable1' to
type 'System.Collections.Generic.List1'. LINQ to Entities only
supports casting Entity Data Model primitive types."
Would it be considered nasty to ditch the ViewModel classes in the query and just get the stuff as an anonymous type, then pass this to a constuctor in a ViewModel (my models have a whack of functions that are needed) and fill the class like this?
It probably would be not only okay but much more readable (aka maintainable) as well. Especially since the query wouldn't return an anonymous type, it would return an IQueryable
Solved! I've spent days on this! Trick was to change the List inside the Doctor ViewModel to IEnumerable(Of CompanionViewModel)