Relationships

Pika resolves relationships at runtime from foreign key conventions, with no annotations or mapping files to write. You expose a relationship by adding an accessor to your bean that calls one of four helpers: loadMany, loadManyThrough, load, or loadReverse.

If you are not using beans, the same methods exist on the ORM instance (orm.loadMany(artist, Album.class)). See Plain Java Objects.

One-to-many (loadMany)

An Artist has many Albums. Pika expects the albums table to carry an artistId foreign key. Add an accessor that names the child type:

public class Artist extends PikaBean {
    Long id;
    String name;

    public PikaManyRelation<Album> getAlbums() {
        return loadMany(Album.class);
    }
}

getAlbums() returns a relation, not a list, and does not touch the database until you ask for results:

Artist artist = Artist.find().byId(1L);
PikaList<Album> albums = artist.getAlbums().toList();   // SELECT * FROM albums WHERE artist_id = 1

The relation also manages the foreign key for you, so you never set artistId by hand:

artist.getAlbums().addAndSave(existingAlbum);  // sets artist_id and saves the album

Album draft = artist.getAlbums().create();     // new Album with artist_id already set
draft.setTitle("Back in Black");
draft.save();

int count = artist.getAlbums().size();         // SELECT count(*)

To filter the children, toQuery() hands you a normal query already scoped to the foreign key:

PikaList<Album> recent = artist.getAlbums().toQuery()
        .where("year > :y", "y", 2000)
        .fetchList();

Many-to-many (loadManyThrough)

A Playlist has many Tracks through a PlaylistTrack join table. Name the join table and the target:

public class Playlist extends PikaBean {
    Long id;
    String name;

    public PikaManyThroughRelation<PlaylistTrack, Track> getTracks() {
        return loadManyThrough(PlaylistTrack.class, Track.class);
    }
}

Reading and editing the relationship go through the join table automatically:

Playlist p = Playlist.find().byId(1L);
PikaList<Track> tracks = p.getTracks().toList();   // joins through playlist_tracks

p.getTracks().addAndSave(track);   // inserts the join row linking playlist and track
p.getTracks().remove(track);       // deletes that join row (leaves both records alone)

Belongs-to (load)

The flip side of one-to-many: an Album holds the artistId and belongs to one Artist. load() reads the foreign key on the current object and fetches the parent.

public class Album extends PikaBean {
    Long id;
    String title;
    Long artistId;

    public Artist getArtist() {
        return load(Artist.class);   // reads this.artistId, returns that Artist
    }
}

One-to-one reverse (loadReverse)

Like belongs-to, but the foreign key lives on the other table and there is exactly one match. A User has one UserProfile, where profiles.user_id points back at the user:

public class User extends PikaBean {
    Long id;

    public UserProfile getProfile() {
        return loadReverse(UserProfile.class);   // finds the profile whose user_id = this.id
    }
}

Watch for N+1

Each accessor runs its query the moment you read it. Loop over 50 artists calling getAlbums().toList() and you have run 51 queries. N+1 Avoidance shows how to bulk-load instead.