You are here: Best Practices > 1-N Relation: Navigating from 1 to N

Managing Relations

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.

1-N Relation: Navigating from 1 to N.

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);
}
ShoppingCard.java: Simple 1-n relation. Navigating from the card to the items

1-N Relation: Navigating from N to 1

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;
}
Person.java: Simple 1-n relation. Navigating from the person to the countries

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;
    }
});
RelationManagementExamples.java: Query for people burn in a country

1-N Relation: Bidirectional

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;
}
Dog.java: Bidirectional 1-N relations. The dog has a 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);
}
Person.java: Bidirectional 1-N relations. The person has dogs

N-N Relations: One Way Navigation

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);
}
Person.java: One directional N-N relation

N-N Relations: Bidirectional Navigation

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);
    }
}
Club.java: Bidirectional N-N relation
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);
}
Person.java: Bidirectional N-N relation