In the model layer of an application I'm working on, I have an Organization class, that has a one-to-many relationship with a Person class. Person objects can have a number of different roles, based on their one-to-many relationships to another set of objects of the superclass Certificate. To determine if a Person is a "signatory", I call
public boolean isSignatory() {
return this.certificatesAsSignatory.size() > 0;
}
I want to return a list of signatories for an organization, so I'm going to loop through the set of related persons, checking for signatories. What I'd like to do is cache the result, so I don't have to regenerate the data from the database each time. I've added a private field to Organization that looks like
private List<Person> signatories;
and the basic method looks like this
public List<Person> getSignatories() {
for ( final Person person : this.people ) {
if ( person.isSignatory() ) {
this.signatories.add( person );
}
}
return this.signatories;
}
Now, when I call the method to return the list, I'll store the result in signatories and return it. When it's called again, I can check if signatories already contains a list, and return that instead of doing the calculations again.
My question is this: how do I keep the cache of the list of signatories up to date?
So the list contains all persons who are signatories. It would seem a good idea to update that list every time Person changes from being a signatory to not being one, or vice versa. Im guessing you have some method that sets if a Person is a signatory or not? In that function you could make a call to clear the cache so it would refill the list next time it is needed. Im also guessing it can be done when a new Person is inserted into the db and should be a signatory.
Its hard to be more specific since I dont know how your code is sturctured. But the idea of a cache is to reset it when the data thats in it changes.
Related
As the documentation regarding these topics seems to be limited (and I was searching a lot - either wrong or the documentation is really limited), I would like to place the question here.
So far I could only find documentation that shows how to implement the basic CRUD operations (insert, delete, update, deleteAll, getAll) in the Android Architecture Components, but never queries which only return a single item. In general, the question is: Is the idea to hold all information in the repository by holding LiveData of all table contents and returning single objects from the repository?
Let me precise the question in two cases:
One table / entity
Two tables / entities with a 1:many relationship
One table
I fully get the concept of using LiveData for a single table, which I can use in a recycler view to list all table rows. But in many cases, I only need one row/object to work with. For example when editing one item.
Question 1: Is it common to implement a method on the repository to get the needed item out of the LiveData<List> allObjects like below? Of course, I could also pass all information from the last activity to my editActivity through my intent, but I find it easier to implement if I just pass the ID of an object and load it in my editActivity.
private LiveData<List<object>> allObjects;
public void getObjectById(int id){
for (Object o : allObjects) {
if(o.getid() == id){
return o;
}
}
Two tables
I also get the idea of having two entities and defining their relation in a separate class. Let's use a common example from the documentation: school with students (1:m).
Question 2: Is it common to hold LiveData<List> in my repository?
In my recycler view, I could use this list to display (for example) all schools with their number of students. Therefore I might not need the LiveData allSchools anymore.
Question 3: What is the best way to implement a query which returns me a student and the school he is visiting? I could implement a (relationship) class StudentWithSchool and keep LiveData of it in my repository.
private LiveData<List<StudentWithSchool>> allStudentsWithSchool;
public void getStudentWithSchoolByStudentId(int id){
for (StudentWithSchool s : allStudentsWithSchool) {
if(s.getid() == id){
return s;
}
}
It would be really helpful if somebody can explain to me how to implement the above examples correctly. Thank you, guys!
I'm sure there is an answer to this already, I just don't know how to describe what I need to do in good search terms.
Let's say I am trying to make a program that handles the sale of unique pieces of wood (each has an ID) as well as tables made of those unique pieces of wood. I have two classes: WoodPiece and Table.
How should I create the Table class so that it can have instances of Table with unique wood pieces listed out so that you can add and remove the pieces up until the point the table is actually made and sold? Right now I'm thinking I make Table a subclass WoodPiece and it just has an ArrayList of woodPieces.
I'm not great at Java so I want to make sure I have the right idea before I waste a bunch of hours going backwards. Any tutorials you know of about this would be great. I'm using JavaFx for my program.
I would construct the Table class using composition. A Table is not a WoodPiece, but rather it contains WoodPieces. In the future, a Table may have a tablecloth or people, etc.
class Table
{
private Map<String, WoodPiece> woodPieces = new HashMap<>();
public Table ()
{
// constructor
}
public void addWoodPiece (WoodPiece wp)
{
woodPieces.put(wp.getID(), wp);
}
public WoodPiece removeWoodPiece (String id)
{
return woodPieces.remove(id);
}
}
Note: I went with a map instead of a List since your WoodPiece object has a unique ID associated with it. Furthermore, I assume you don't care about the order of the woodPieces since a Table really doesn't have a natural ordering of the wood that composes it.
Further reading: https://en.wikipedia.org/wiki/Composition_over_inheritance
I understand with your description that I table is only made with unique pieces and is not possible to made a table with other tables. So you only have to add a List of WoodPiece to your table class and the add and remove methods.
In case you want to be able to make a table with other tables you should first create an interface (let's call it TablePiece) and both Table and WoodPiece must implement it and Table must have a List of TablePiece. In the following graph Component would be TablePiece, Leaf WoodPiece and Composite Table.
The project has 4 classes. A Person, an Employee that extends Person and an Academic that extends Employee.
I also have a Store, which is a user-made alternative to an Array. One of the functions of Store is ‘elementAt()’, which returns to us an object in Store.
The problem I have is that elementAt() always returns as type Person. This is a huge problem because, in my Controller class, before I let the user perform an action only applicable to an Academic I NEED to check whether the user has actually chosen an employee or not.
public Person elementAt(int index)
{
// Returns element at position index
return list[index];
}
There is one big problem; according to the project specification I cannot alter the Person, Employee, Academic or Store class any further. Meaning, I have to determine the type of the Store index somewhere, somehow within my controller class.
I wanted to run this by people with more experience so thank you for having a look.
Instanceof seems to me the only option you have; and I don't think that its evil to use it in this case. But you are right, it is not the nicest thing in thinking in objects ;o)
At least you might encapsulate that like
public boolean isAcademic(Person p) {
return p instanceof Academic;
}
to concentrate the "code smell" in one position, and make it easier to later refactor it.
I don't know if i got it right, but if you just want to check the returned object type, do this:
Person person = store.elementAt(0);
if (person instanceof Academic) {
//do stuff
}
Say that the design of my application is such that there are two classes: Person and Company. Also suppose I have defined a UML diagram that defines a one-to-many relationship between Person and Company: every Person object must belong to exactly one Company, and a Company can contain many Persons.
What are some best practices for ensuring that this simple constraint always holds, i.e., that there is never a point in time where a Person object is contained in more than one Company? I've thought of three approaches:
Have perfect, bug-free code that would never violate any of my constaints. However, being a realist, I know that this is difficult or impossible, especially as the number of constraints grows.
Implement an ensureCorrectness() method that manually checks each and every constraint. In this example, it would loop through all Persons, and make sure that exactly one Company contained that Person. If not, it would print an error. I would call this method every so often.
Use, e.g., Hibernate to define a database schema and store the data into a database. In the schema, I can define a constraint to match the constrains in my UML diagram. Then, whenever I persists data into the database, Hibernate will check all the constraints and print an error if something's gone wrong.
But I can't help wondering if there is another, cleaner way to do this in Java, that doesn't rely on (1) perfection, (2) manual validation, or (3) a relational database. Is there a library or framework that would allow me to specify annotations in the code similar to:
#OneToMany(Person, Company)
Or perhaps more specifically:
#OneToMany(Person, Company.persons)
assuming the Company class had a List<Person> persons member variable.
And if I wanted to ensure that a person's social security number is unique across all Persons, I could say:
#Unique(Person.SSN)
Does anything like this exist for Java?
The relation between a Company to Person is not difficult. Make e.g. a list with Person elements contained in Company. The one thing you have to ensure manually is that the list does not contain multiple instances of a single Person.
The other way around is more difficult. You have to manually check this (that each list does not contain multiple instances of the same Person). But you can combine it with the previous restriction:
The length of all lists of persons should be equal as the length of all lists with unique persons.
I do not have much knowledge about databases, but I would go for the second option (ensureCorrectNess) to check the manual constriction above.
I'd go for #1, in combination with encapsulation. This way, the amount of code that has to be 100% bug free is very small
public class Company {
private List<Person> persons = new ArrayList<>();
private List<Person> publicPerson = Collections.unmodifiableList(persons);
public List<Person> getPersons { return publicPersons; }
public void addPerson(Person p) {
... ensure p is removed from old company
p.setCompany(this);
persons.add(p);
}
}
public class Person {
private Company company;
/* pkg-private */ setCompany(Company company) {
this.company = company;
}
public Company getCompany() {
return company;
}
}
Careful, this code is not threadsafe!
Notice that hibernate will only check the constraints if you save to the database. Due to caching, inconsistencies may appear.
I would implement it like that in Java:
public class Company {
Set<Person> persons;
...
public void addPerson(Person person) {
if (person.getCompany() != this) { // Avoiding an infinite loop between addPerson() and setCompany()
person.setCompany(this);
persons.add(person);
}
}
public boolean removePerson(Person person) {
return persons.remove(person);
}
...
}
public class Person {
Company company;
public Person(Company company) {
this.company = company;
}
public void setCompany(Company company) {
if (this.company != null) {
this.company.remove(this);
}
this.company = company;
company.addPerson(this);
}
...
}
In code, Person cannot have more than one Company, and Company has a list of Persons. With the Person(Company) constructor, Person has at least one company assigned.
EDIT: To modify the Set (no duplicates), you have to pass through the addPerson() method that call the Person.setCompany() method. This method will remove the Person from previous Company list and add him to the new Company list.
In setCompany(), you have to call to addPerson(), because programmers can assign directly a Person to a Company without calling first to addPerson().
first you can configure all this in the DB very easily by set the col in the db not null and the customer id unique.
you can use triggers before and after to be sure that the data is remove or added to the DB.
second option you can set all the DB on java classes by using Hash Maps to contain the data.
in my opinion the first option is more easy..
I've written a java based web application, simple for now since I'm a beginner in this space. What it does is looks up the phone number of a person, when the user enters the name of the person in the jsp page. I'm using a hashmap for now where I've created two objects the person and the number and basically the map is the association of the person to the number. The reason I have person as an object is later on I plan to add more information to the person object. I also plan to eventually move the association to the database. However, in this specific situation where I have a hash map how do I handle the use case of a "person with the same name" can be two different users because according to my hashmap the key is the person object. Specifically, the user can look up the person by name. I also thought of adding a system generated id to the person object to make it unique but it doesn't solve the use case of two people having the same name as when the user is querying for the phone number he will only enter the person's name.
The simplest solution would be to have your value type be a list of people, so:
Map<String, List<Person>> map = new HashMap...
It sounds like your design could do with a bit of work though. While you're prototyping (pre database), for example, is there any reason you need to use a HashMap? Why not just store a List<Person>, and iterate the list each time you want to search? That would let you easily search on other properties of a person too.
Edit: In response to Phoenix's comment.
The approach is to store a list of all people with the given name in lists within the map.
To add a new person, you need to check if a person already exists with their name, and create an empty list to put them into if there isn't already a person. [Sorry, I don't have a compiler handy, so haven't compiled or tested this, but the idea should be right]
void add(Map<String, List<Person>> map, Person p) {
if (!map.containsKey(p.name()) {
map.put(p.name(), new ArrayList<Person>());
}
// The map will always have a (possibly empty) list of people with a given name now
map.get(p.name()).add(p);
}
Looking people up is easy. To print all the people with a given name:
void printPeople(BufferedWriter out, Map<String, List<Person>> map, String name) {
for (Person p : map.get(name)) {
out.println(p.toString());
}
}
What about this?
Map<String, List<Person>> map = new HashMap<String, List<Person>>();
if(!map.contains(personName)){
map.put(personName, new ArrayList<Person>());
}
// Construct Person person = new Person(personName, phoneNumber);
map.get(personName).add(person);
You keep a list of person as Martin suggested and put the values as list of person instead of single person.
While iterating...
for(Person person : map.get(personName)){
// Process person.getPhoneNumber();
}
I'm adding a second answer here. My other answer shows how to implement it with a Map, but I don't think it's worth the effort.
If you're using a HashMap because you're worried about performance, then you'll fix that once you put the database backend in that you've mentioned. As a general rule, don't spend effort optimising code you know you're going to throw away anyway, or don't know for sure even needs optimising.
My solution (pending the real backend), would be to just use a List. You don't need to worry about getting your hashCode algorithm right, and I doubt it's going to come out significantly slower unless you have many thousands of entries.
public class People {
private List<Person> people = new ArrayList<Person>();
public void add(Person p) {
people.add(p);
}
public List<Person> findByName(String name) {
List<Person> result = new ArrayList<Person>();
for (Person p : people) {
if (p.getName().equals(name)) {
result.add(p);
}
}
return result;
}
It's just so much simpler and clearer than the Map solution.