In db4o you manage relations by storing references to other objects. In db4o navigational access is usually a lot faster than queries. Therefore think about how you navigate to the right information. This topic gives an overview how to manage different relations.
As example we use shopping cards which hold items. In most use cases you need to know which items are on a card. For that you navigate from the card (the 1-part) to the items (the N-part). In this case you simply can use a collection on the shopping card which references all items.
Keep in mind that a contains-query on collections is very slow. Finding out which items are on a certain shopping cards is very slow. If you want to know that, take a look at the suggestions below.
Set<Item> items = new HashSet<Item>(); public void add(Item terrain) { items.add(terrain); } public void remove(Item o) { items.remove(o); }
Imagine that you store people and in which country a person is born. Here you usually navigate from the person (the N-part) to the country (the 1-part). Therefore you can have a field which refers to the country.
In the rare case where you want to know all people born in a certain country you can do a query. When the country reference is indexed, then that query is fast.
// Optionally we can index this field, when we want to find all people for a certain country private Country bornIn; public Country getBornIn() { return bornIn; }
Getting all people of a country is not that hard and fast when the ‘bornIn’ field is indexed.
final Country country = loadCountry(container,"USA"); final ObjectSet<Person> peopleBurnInTheUs = container.query(new Predicate<Person>() { @Override public boolean match(Person p) { return p.getBornIn() == country; } });
When you want to navigate a 1-N relationship bidirectional you can use the method above, because the query if fast enough. Just ensure that you index the field holding the reference.
Alternatively you also can add an additional collection which holds the items. However in that case you need to manually manage the consistency of the relation.
For example the relationship between dogs and their owners. The dog has a field with its owner, while the person has a collection of his dogs. The setters then manage the relationship and ensure that it is always consistent.
private Person owner; // This setter ensures that the model is always consistent public void setOwner(Person owner) { if(null!=this.owner){ Person oldOwner = this.owner; this.owner = null; oldOwner.removeOwnerShipOf(this); } if(null!=owner && !owner.ownedDogs().contains(this)) { owner.addOwnerShipOf(this); } this.owner = owner; } public Person getOwner() { return owner; }
private Set<Dog> owns = new HashSet<Dog>(); // The add and remove method ensure that the relations is always consistent public void addOwnerShipOf(Dog dog) { owns.add(dog); dog.setOwner(this); } public void removeOwnerShipOf(Dog dog) { owns.remove(dog); dog.setOwner(null); } public Collection<Dog> ownedDogs() { return Collections.unmodifiableCollection(owns); }
Like 1-N relations N-N relations also can be one directional. For example a person can have multiple citizenships in different countries. Let's suppose that you only want to know the citizenship of a person and not the citizens of a country. Then you navigate from people to countries. You can store that in a simple collection.
Keep in mind that a contains-query on a collection is very slow. Finding the people of a certain country will be very slow.
private Set<Country> citizenIn = new HashSet<Country>(); public void removeCitizenship(Country o) { citizenIn.remove(o); } public void addCitizenship(Country country) { citizenIn.add(country); }
For managing bidirectional N-N relations you can use collections, but maintain a collection on both sides. For example a club has a member-list and each member has a list of clubs where he is a member.
Keep in mind that a contains-query on a collection is very slow. That's why you maintain two collections, so that you can navigate to the club-members or club-membership.
private Set<Person> members = new HashSet<Person>(); public void addMember(Person person) { if (!members.contains(person)) { members.add(person); person.join(this); } } public void removeMember(Person person) { if (members.contains(person)) { members.remove(person); person.leave(this); } }
private Set<Club> memberIn = new HashSet<Club>(); public void join(Club club) { if (!memberIn.contains(club)) { memberIn.add(club); club.addMember(this); } } public void leave(Club club) { if (memberIn.contains(club)) { memberIn.remove(club); club.removeMember(this); } } public Collection<Club> memberOf() { return Collections.unmodifiableCollection(memberIn); }