Entity basics, selecting and modifying.

package org.jminor.framework.demos.chinook.tutorial;

import org.jminor.common.db.ConditionType;
import org.jminor.common.db.Database;
import org.jminor.common.db.Databases;
import org.jminor.common.db.exception.DatabaseException;
import org.jminor.common.user.Users;
import org.jminor.framework.db.EntityConnection;
import org.jminor.framework.db.EntityConnectionProvider;
import org.jminor.framework.db.condition.EntitySelectCondition;
import org.jminor.framework.db.local.LocalEntityConnectionProvider;
import org.jminor.framework.domain.Domain;
import org.jminor.framework.domain.entity.Entity;
import org.jminor.framework.domain.entity.StringProvider;
import org.jminor.framework.domain.property.Property;

import java.sql.Types;
import java.util.List;

import static java.util.Arrays.asList;
import static org.jminor.framework.db.condition.Conditions.entitySelectCondition;
import static org.jminor.framework.demos.chinook.tutorial.EntitiesTutorial.Chinook.*;
import static org.jminor.framework.domain.entity.Entities.getKeys;
import static org.jminor.framework.domain.entity.KeyGenerators.automatic;
import static org.jminor.framework.domain.entity.OrderBy.orderBy;
import static org.jminor.framework.domain.property.Properties.*;

 * When running this make sure the chinook demo module directory is the
 * working directory, due to a relative path to a db init script
public final class EntitiesTutorial {

  /** The domain class, which contains the domain model definition */
  public static final class Chinook extends Domain {

    //string constants for the table entityId ('T_' prefix)
    //and a propertyId for each column
    public static final String T_ARTIST = "chinook.artist";
    public static final String ARTIST_ID = "artistid";
    public static final String ARTIST_NAME = "name";

    //string constants for the table entityId ('T_' prefix),
    //and a propertyId for each column and one for the foreign key relation
    public static final String T_ALBUM = "chinook.album";
    public static final String ALBUM_ALBUMID = "albumid";
    public static final String ALBUM_TITLE = "title";
    public static final String ALBUM_ARTISTID = "artistid";
    public static final String ALBUM_ARTIST_FK = "artist_fk";

    public Chinook() {
      //create properties for the columns in the table 'chinook.artist'
      Property.Builder artistId = primaryKeyProperty(ARTIST_ID);
      Property.Builder artistName = columnProperty(ARTIST_NAME, Types.VARCHAR, "Name");

      //define an entity based on the table 'chinook.artist',
      //with the above properties
      define(T_ARTIST, artistId, artistName)
              .stringProvider(new StringProvider(ARTIST_NAME))

      //create properties for the columns in the table 'chinook.album'
      Property.Builder albumId = primaryKeyProperty(ALBUM_ALBUMID);
      Property.Builder albumTitle = columnProperty(ALBUM_TITLE, Types.VARCHAR, "Title");
      //we wrap the actual 'artistid' column property in a foreign key
      //referencing the entity identified by T_ARTIST
      Property.Builder albumArtist =
              foreignKeyProperty(ALBUM_ARTIST_FK, "Artist", T_ARTIST,

      //define an entity based on the table 'chinook.album',
      //with the above properties
      define(T_ALBUM, albumId, albumTitle, albumArtist)
              .stringProvider(new StringProvider()
                      .addText(" - ")

   * Demonstrates how to use a {@link EntityConnection} to select Entity instances.
   * @throws DatabaseException in case of an exception
  private static void selectingEntities(EntityConnectionProvider connectionProvider) throws DatabaseException {
    //fetch the connection from the provider, note that the provider always
    //returns the same connection or a new one if the previous one has been
    //disconnected or has become invalid for some reason
    EntityConnection connection = connectionProvider.getConnection();

    //select the artist Metallica by name, the selectSingle() method
    //throws a RecordNotFoundException if no record is found and a
    //MultipleRecordsFoundException if more than one are found
    Entity metallica =
            connection.selectSingle(T_ARTIST, ARTIST_NAME, "Metallica");

    //select all albums by Metallica, by using select() with the
    //Metallica Entity as condition value, basically asking for the
    //records where the given foreign key references that specific Entity
    //select() returns an empty list if none are found
    List<Entity> albums =
            connection.select(T_ALBUM, ALBUM_ARTIST_FK, metallica);


    //for more complex queries we use a EntitySelectCondition, provided
    //by the Conditions factory class.
    //we create a select condition, where we specify the id of the entity
    //we're selecting, the id of the property we're searching by, the type
    //of condition and the value.
    EntitySelectCondition artistsCondition =
                    ARTIST_NAME, ConditionType.LIKE, "An%");
    //and we set the order by clause

    List<Entity> artistsStartingWithAn =


    //create a select condition
    EntitySelectCondition albumsCondition =
                    ALBUM_ARTIST_FK, ConditionType.LIKE, artistsStartingWithAn);

    List<Entity> albumsByArtistsStartingWithAn =


   * Demonstrates how to use a {@link EntityConnection} to modify Entity instances.
   * @throws DatabaseException in case of an exception
  private static void modifyingEntities(EntityConnectionProvider connectionProvider) throws DatabaseException {
    EntityConnection connection = connectionProvider.getConnection();

    //this Domain object serves as a factory for Entity instances
    Domain domain = connectionProvider.getDomain();

    //lets create a new band
    Entity myBand = domain.entity(T_ARTIST);
    //and give the band a name
    myBand.put(ARTIST_NAME, "My band name");

    //we start a transaction

    //we insert the Entity, the insert() method returns the primary key
    //of the inserted record, but we don't need it right now so we ignore it.
    //Note that the primary key of the entity instance is populated
    //during insert, that's because we're running with a local connection,
    //with a remote connections you have to select the entity
    //after insert to get an instance containing the generated key value
    //or use the key received via the return value

    //now for our first album
    Entity album = domain.entity(T_ALBUM);
    //set the album artist by setting the artist foreign key to my band
    album.put(ALBUM_ARTIST_FK, myBand);
    //and set the title
    album.put(ALBUM_TITLE, "My first album");

    //and insert the album

    //and finally we commit

    //lets rename our album and our band as well
    myBand.put(ARTIST_NAME, "A proper name");
    album.put(ALBUM_TITLE, "A proper title");

    //and perform the update, note that we only have to use transactions
    //when we're performing multiple insert/update or delete calls,
    //here we're just doing one so this call automatically happens within
    //a single transaction
    connection.update(asList(myBand, album));

    //finally we clean up after ourselves by deleting our imaginary band and album,
    //note that the order of the entities matters, since we can't delete
    //the artist before the album, this method deletes records in the
    //same order as the are received
    connection.delete(getKeys(asList(album, myBand)));

  public static void main(final String[] args) throws DatabaseException {
    // Configure the database
    //initialize a connection provider, this class is responsible
    //for supplying a valid connection or throwing an exception
    //in case a connection can not be established
    EntityConnectionProvider connectionProvider =
            new LocalEntityConnectionProvider(Databases.getInstance())