JMinor Application Framework

As simple as possible but not simpler

User Tools

Site Tools


documentation:tutorials:chinook

Chinook: Application tutorial

Run the application

Web Start

Chinook (Web Start)

You'll need to add http://jminor.no-ip.org to the Java exception site list.

Getdown

chinook-getdown.zip

Zip file containing the necessary files for running with Getdown.

Local JAR file

chinook.zip

Zip file containing a jar file along with an assortment of launch files for running the application without WebStart. Note that the remote configurations all assume the server is running on localhost.

Data

Domain model

File: org/jminor/framework/demos/chinook/domain/Chinook.java -

/*
 * Copyright (c) 2004 - 2017, Björn Darri Sigurðsson. All Rights Reserved.
 */
package org.jminor.framework.demos.chinook.domain;
 
import org.jminor.common.db.Databases;
import org.jminor.framework.domain.Entities;
import org.jminor.framework.domain.Entity;
import org.jminor.framework.domain.Properties;
import org.jminor.framework.domain.Property;
 
import java.sql.Types;
import java.text.NumberFormat;
 
public final class Chinook extends Entities {
 
  public Chinook() {
    artist();
    album();
    employee();
    customer();
    genre();
    mediaType();
    track();
    invoice();
    invoiceLine();
    playlist();
    playlistTrack();
    processAnnotations(Chinook.class);
  }
 
  public static final String ARTIST_ARTISTID = "artistid";
  public static final String ARTIST_NAME = "name";
  @Entity.Table(tableName = "chinook.artist",
          orderByClause = ARTIST_NAME,
          keyGenerator = Entity.KeyGenerator.Type.AUTOMATIC,
          keyGeneratorSource = "chinook.artist")
  public static final String T_ARTIST = "artist@chinook";
 
  void artist() {
    define(T_ARTIST,
            Properties.primaryKeyProperty(ARTIST_ARTISTID, Types.BIGINT),
            Properties.columnProperty(ARTIST_NAME, Types.VARCHAR, "Name")
                    .setNullable(false)
                    .setMaxLength(120)
                    .setPreferredColumnWidth(160))
            .setKeyGeneratorType(Entity.KeyGenerator.Type.AUTOMATIC)
            .setStringProvider(new Entities.StringProvider(ARTIST_NAME))
            .setSearchPropertyIds(ARTIST_NAME)
            .setCaption("Artists");
  }
 
  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_ARTISTID_FK = "artistid_fk";
  @Entity.Table(tableName = "chinook.album",
          orderByClause = ALBUM_ARTISTID + ", " + ALBUM_TITLE,
          keyGenerator = Entity.KeyGenerator.Type.AUTOMATIC,
          keyGeneratorSource = "chinook.album")
  public static final String T_ALBUM = "album@chinook";
 
  void album() {
    define(T_ALBUM,
            Properties.primaryKeyProperty(ALBUM_ALBUMID, Types.BIGINT),
            Properties.foreignKeyProperty(ALBUM_ARTISTID_FK, "Artist", T_ARTIST,
                    Properties.columnProperty(ALBUM_ARTISTID, Types.BIGINT))
                    .setNullable(false)
                    .setPreferredColumnWidth(160),
            Properties.columnProperty(ALBUM_TITLE, Types.VARCHAR, "Title")
                    .setNullable(false)
                    .setMaxLength(160)
                    .setPreferredColumnWidth(160))
            .setKeyGeneratorType(Entity.KeyGenerator.Type.AUTOMATIC)
            .setStringProvider(new Entities.StringProvider(ALBUM_TITLE))
            .setSearchPropertyIds(ALBUM_TITLE)
            .setCaption("Albums");
  }
 
  public static final String EMPLOYEE_EMPLOYEEID = "employeeid";
  public static final String EMPLOYEE_LASTNAME = "lastname";
  public static final String EMPLOYEE_FIRSTNAME = "firstname";
  public static final String EMPLOYEE_TITLE = "title";
  public static final String EMPLOYEE_REPORTSTO = "reportsto";
  public static final String EMPLOYEE_REPORTSTO_FK = "reportsto_fk";
  public static final String EMPLOYEE_BIRTHDATE = "birthdate";
  public static final String EMPLOYEE_HIREDATE = "hiredate";
  public static final String EMPLOYEE_ADDRESS = "address";
  public static final String EMPLOYEE_CITY = "city";
  public static final String EMPLOYEE_STATE = "state";
  public static final String EMPLOYEE_COUNTRY = "country";
  public static final String EMPLOYEE_POSTALCODE = "postalcode";
  public static final String EMPLOYEE_PHONE = "phone";
  public static final String EMPLOYEE_FAX = "fax";
  public static final String EMPLOYEE_EMAIL = "email";
  @Entity.Table(tableName = "chinook.employee",
          orderByClause = EMPLOYEE_LASTNAME + ", " + EMPLOYEE_FIRSTNAME,
          keyGenerator = Entity.KeyGenerator.Type.AUTOMATIC,
          keyGeneratorSource = "chinook.employee")
  public static final String T_EMPLOYEE = "employee@chinook";
 
  void employee() {
    define(T_EMPLOYEE,
            Properties.primaryKeyProperty(EMPLOYEE_EMPLOYEEID, Types.BIGINT),
            Properties.columnProperty(EMPLOYEE_LASTNAME, Types.VARCHAR, "Last name")
                    .setNullable(false)
                    .setMaxLength(20),
            Properties.columnProperty(EMPLOYEE_FIRSTNAME, Types.VARCHAR, "First name")
                    .setNullable(false)
                    .setMaxLength(20),
            Properties.columnProperty(EMPLOYEE_TITLE, Types.VARCHAR, "Title")
                    .setMaxLength(30),
            Properties.foreignKeyProperty(EMPLOYEE_REPORTSTO_FK, "Reports to", T_EMPLOYEE,
                    Properties.columnProperty(EMPLOYEE_REPORTSTO, Types.BIGINT)),
            Properties.columnProperty(EMPLOYEE_BIRTHDATE, Types.DATE, "Birthdate"),
            Properties.columnProperty(EMPLOYEE_HIREDATE, Types.DATE, "Hiredate"),
            Properties.columnProperty(EMPLOYEE_ADDRESS, Types.VARCHAR, "Address")
                    .setMaxLength(70),
            Properties.columnProperty(EMPLOYEE_CITY, Types.VARCHAR, "City")
                    .setMaxLength(40),
            Properties.columnProperty(EMPLOYEE_STATE, Types.VARCHAR, "State")
                    .setMaxLength(40),
            Properties.columnProperty(EMPLOYEE_COUNTRY, Types.VARCHAR, "Country")
                    .setMaxLength(40),
            Properties.columnProperty(EMPLOYEE_POSTALCODE, Types.VARCHAR, "Postal code")
                    .setMaxLength(10),
            Properties.columnProperty(EMPLOYEE_PHONE, Types.VARCHAR, "Phone")
                    .setMaxLength(24),
            Properties.columnProperty(EMPLOYEE_FAX, Types.VARCHAR, "Fax")
                    .setMaxLength(24),
            Properties.columnProperty(EMPLOYEE_EMAIL, Types.VARCHAR, "Email")
                    .setMaxLength(60))
            .setKeyGeneratorType(Entity.KeyGenerator.Type.AUTOMATIC)
            .setStringProvider(new Entities.StringProvider(EMPLOYEE_LASTNAME)
                    .addText(", ").addValue(EMPLOYEE_FIRSTNAME))
            .setSearchPropertyIds(EMPLOYEE_FIRSTNAME, EMPLOYEE_LASTNAME, EMPLOYEE_EMAIL)
            .setCaption("Employees");
  }
 
  public static final String CUSTOMER_CUSTOMERID = "customerid";
  public static final String CUSTOMER_FIRSTNAME = "firstname";
  public static final String CUSTOMER_LASTNAME = "lastname";
  public static final String CUSTOMER_COMPANY = "company";
  public static final String CUSTOMER_ADDRESS = "address";
  public static final String CUSTOMER_CITY = "city";
  public static final String CUSTOMER_STATE = "state";
  public static final String CUSTOMER_COUNTRY = "country";
  public static final String CUSTOMER_POSTALCODE = "postalcode";
  public static final String CUSTOMER_PHONE = "phone";
  public static final String CUSTOMER_FAX = "fax";
  public static final String CUSTOMER_EMAIL = "email";
  public static final String CUSTOMER_SUPPORTREPID = "supportrepid";
  public static final String CUSTOMER_SUPPORTREPID_FK = "supportrepid_fk";
  @Entity.Table(tableName = "chinook.customer",
          orderByClause = CUSTOMER_LASTNAME + ", " + CUSTOMER_FIRSTNAME,
          keyGenerator = Entity.KeyGenerator.Type.AUTOMATIC,
          keyGeneratorSource = "chinook.customer")
  public static final String T_CUSTOMER = "customer@chinook";
 
  void customer() {
    define(T_CUSTOMER,
            Properties.primaryKeyProperty(CUSTOMER_CUSTOMERID, Types.BIGINT),
            Properties.columnProperty(CUSTOMER_LASTNAME, Types.VARCHAR, "Last name")
                    .setNullable(false)
                    .setMaxLength(20),
            Properties.columnProperty(CUSTOMER_FIRSTNAME, Types.VARCHAR, "First name")
                    .setNullable(false)
                    .setMaxLength(40),
            Properties.columnProperty(CUSTOMER_COMPANY, Types.VARCHAR, "Company")
                    .setMaxLength(80),
            Properties.columnProperty(CUSTOMER_ADDRESS, Types.VARCHAR, "Address")
                    .setMaxLength(70),
            Properties.columnProperty(CUSTOMER_CITY, Types.VARCHAR, "City")
                    .setMaxLength(40),
            Properties.columnProperty(CUSTOMER_STATE, Types.VARCHAR, "State")
                    .setMaxLength(40),
            Properties.columnProperty(CUSTOMER_COUNTRY, Types.VARCHAR, "Country")
                    .setMaxLength(40),
            Properties.columnProperty(CUSTOMER_POSTALCODE, Types.VARCHAR, "Postal code")
                    .setMaxLength(10),
            Properties.columnProperty(CUSTOMER_PHONE, Types.VARCHAR, "Phone")
                    .setMaxLength(24),
            Properties.columnProperty(CUSTOMER_FAX, Types.VARCHAR, "Fax")
                    .setMaxLength(24),
            Properties.columnProperty(CUSTOMER_EMAIL, Types.VARCHAR, "Email")
                    .setNullable(false)
                    .setMaxLength(60),
            Properties.foreignKeyProperty(CUSTOMER_SUPPORTREPID_FK, "Support rep", T_EMPLOYEE,
                    Properties.columnProperty(CUSTOMER_SUPPORTREPID, Types.BIGINT)))
            .setKeyGeneratorType(Entity.KeyGenerator.Type.AUTOMATIC)
            .setStringProvider(new Entities.StringProvider(CUSTOMER_LASTNAME)
                    .addText(", ").addValue(CUSTOMER_FIRSTNAME))
            .setSearchPropertyIds(CUSTOMER_FIRSTNAME, CUSTOMER_LASTNAME, CUSTOMER_EMAIL)
            .setCaption("Customers");
  }
 
  public static final String GENRE_GENREID = "genreid";
  public static final String GENRE_NAME = "name";
  @Entity.Table(tableName = "chinook.genre",
          orderByClause = GENRE_NAME,
          keyGenerator = Entity.KeyGenerator.Type.AUTOMATIC,
          keyGeneratorSource = "chinook.genre")
  public static final String T_GENRE = "genre@chinook";
 
  void genre() {
    define(T_GENRE,
            Properties.primaryKeyProperty(GENRE_GENREID, Types.BIGINT),
            Properties.columnProperty(GENRE_NAME, Types.VARCHAR, "Name")
                    .setNullable(false)
                    .setMaxLength(120)
                    .setPreferredColumnWidth(160))
            .setKeyGeneratorType(Entity.KeyGenerator.Type.AUTOMATIC)
            .setStringProvider(new Entities.StringProvider(GENRE_NAME))
            .setSearchPropertyIds(GENRE_NAME)
            .setSmallDataset(true)
            .setCaption("Genres");
  }
 
  public static final String MEDIATYPE_MEDIATYPEID = "mediatypeid";
  public static final String MEDIATYPE_NAME = "name";
  @Entity.Table(tableName = "chinook.mediatype",
          orderByClause = MEDIATYPE_NAME,
          keyGenerator = Entity.KeyGenerator.Type.AUTOMATIC,
          keyGeneratorSource = "chinook.mediatype")
  public static final String T_MEDIATYPE = "mediatype@chinook";
 
  void mediaType() {
    define(T_MEDIATYPE,
            Properties.primaryKeyProperty(MEDIATYPE_MEDIATYPEID, Types.BIGINT),
            Properties.columnProperty(MEDIATYPE_NAME, Types.VARCHAR, "Name")
                    .setNullable(false)
                    .setMaxLength(120)
                    .setPreferredColumnWidth(160))
            .setKeyGeneratorType(Entity.KeyGenerator.Type.AUTOMATIC)
            .setStringProvider(new Entities.StringProvider(MEDIATYPE_NAME))
            .setSmallDataset(true)
            .setCaption("Media types");
  }
 
  public static final String TRACK_TRACKID = "trackid";
  public static final String TRACK_NAME = "name";
  public static final String TRACK_ARTIST_DENORM = "artist_denorm";
  public static final String TRACK_ALBUMID = "albumid";
  public static final String TRACK_ALBUMID_FK = "albumid_fk";
  public static final String TRACK_MEDIATYPEID = "mediatypeid";
  public static final String TRACK_MEDIATYPEID_FK = "mediatypeid_fk";
  public static final String TRACK_GENREID = "genreid";
  public static final String TRACK_GENREID_FK = "genreid_fk";
  public static final String TRACK_COMPOSER = "composer";
  public static final String TRACK_MILLISECONDS = "milliseconds";
  public static final String TRACK_MINUTES_SECONDS_DERIVED = "minutes_seconds_transient";
  public static final String TRACK_BYTES = "bytes";
  public static final String TRACK_UNITPRICE = "unitprice";
  @Entity.Table(tableName = "chinook.track",
          orderByClause = TRACK_NAME,
          keyGenerator = Entity.KeyGenerator.Type.AUTOMATIC,
          keyGeneratorSource = "chinook.track")
  public static final String T_TRACK = "track@chinook";
 
  public static final Property.DerivedProperty.Provider TRACK_MIN_SEC_PROVIDER =
          linkedValues -> {
            final Integer milliseconds = (Integer) linkedValues.get(TRACK_MILLISECONDS);
            if (milliseconds == null || milliseconds <= 0) {
              return "";
            }
 
            final int seconds = ((milliseconds / 1000) % 60);
            final int minutes = ((milliseconds / 1000) / 60);
 
            return minutes + " min " + seconds + " sec";
          };
 
  void track() {
    define(T_TRACK,
            Properties.primaryKeyProperty(TRACK_TRACKID, Types.BIGINT),
            Properties.denormalizedViewProperty(TRACK_ARTIST_DENORM, TRACK_ALBUMID_FK,
                    getProperty(T_ALBUM, ALBUM_ARTISTID_FK), "Artist")
                    .setPreferredColumnWidth(160),
            Properties.foreignKeyProperty(TRACK_ALBUMID_FK, "Album", T_ALBUM,
                    Properties.columnProperty(TRACK_ALBUMID, Types.BIGINT))
                    .setFetchDepth(2)
                    .setPreferredColumnWidth(160),
            Properties.columnProperty(TRACK_NAME, Types.VARCHAR, "Name")
                    .setNullable(false)
                    .setMaxLength(200)
                    .setPreferredColumnWidth(160),
            Properties.foreignKeyProperty(TRACK_GENREID_FK, "Genre", T_GENRE,
                    Properties.columnProperty(TRACK_GENREID, Types.BIGINT)),
            Properties.columnProperty(TRACK_COMPOSER, Types.VARCHAR, "Composer")
                    .setMaxLength(220)
                    .setPreferredColumnWidth(160),
            Properties.foreignKeyProperty(TRACK_MEDIATYPEID_FK, "Media type", T_MEDIATYPE,
                    Properties.columnProperty(TRACK_MEDIATYPEID, Types.BIGINT))
                    .setNullable(false),
            Properties.columnProperty(TRACK_MILLISECONDS, Types.INTEGER, "Duration (ms)")
                    .setNullable(false)
                    .setFormat(NumberFormat.getIntegerInstance()),
            Properties.derivedProperty(TRACK_MINUTES_SECONDS_DERIVED, Types.VARCHAR, "Duration (min/sec)",
                    TRACK_MIN_SEC_PROVIDER, TRACK_MILLISECONDS),
            Properties.columnProperty(TRACK_BYTES, Types.INTEGER, "Bytes")
                    .setFormat(NumberFormat.getIntegerInstance()),
            Properties.columnProperty(TRACK_UNITPRICE, Types.DOUBLE, "Price")
                    .setNullable(false))
            .setKeyGeneratorType(Entity.KeyGenerator.Type.AUTOMATIC)
            .setStringProvider(new Entities.StringProvider(TRACK_NAME))
            .setSearchPropertyIds(TRACK_NAME)
            .setCaption("Tracks");
  }
 
  public static final String INVOICE_INVOICEID = "invoiceid";
  public static final String INVOICE_INVOICEID_AS_STRING = "invoiceid || ''";
  public static final String INVOICE_CUSTOMERID = "customerid";
  public static final String INVOICE_CUSTOMERID_FK = "customerid_fk";
  public static final String INVOICE_INVOICEDATE = "invoicedate";
  public static final String INVOICE_BILLINGADDRESS = "billingaddress";
  public static final String INVOICE_BILLINGCITY = "billingcity";
  public static final String INVOICE_BILLINGSTATE = "billingstate";
  public static final String INVOICE_BILLINGCOUNTRY = "billingcountry";
  public static final String INVOICE_BILLINGPOSTALCODE = "billingpostalcode";
  public static final String INVOICE_TOTAL = "total";
  public static final String INVOICE_TOTAL_SUB = "total_sub";
  public static final String INVOICE_TOTAL_SUBQUERY = "select sum(unitprice * quantity) from chinook.invoiceline where invoiceid = invoice.invoiceid";
  @Entity.Table(tableName = "chinook.invoice",
          orderByClause = INVOICE_CUSTOMERID + ", " + INVOICE_INVOICEDATE + " desc",
          keyGenerator = Entity.KeyGenerator.Type.AUTOMATIC,
          keyGeneratorSource = "chinook.invoice")
  public static final String T_INVOICE = "invoice@chinook";
 
  void invoice() {
    define(T_INVOICE,
            Properties.primaryKeyProperty(INVOICE_INVOICEID, Types.BIGINT, "Invoice no."),
            Properties.columnProperty(INVOICE_INVOICEID_AS_STRING, Types.VARCHAR, "Invoice no.")
                    .setReadOnly(true)
                    .setHidden(true),
            Properties.foreignKeyProperty(INVOICE_CUSTOMERID_FK, "Customer", T_CUSTOMER,
                    Properties.columnProperty(INVOICE_CUSTOMERID, Types.BIGINT))
                    .setNullable(false),
            Properties.columnProperty(INVOICE_INVOICEDATE, Types.DATE, "Date")
                    .setNullable(false),
            Properties.columnProperty(INVOICE_BILLINGADDRESS, Types.VARCHAR, "Billing address")
                    .setMaxLength(70),
            Properties.columnProperty(INVOICE_BILLINGCITY, Types.VARCHAR, "Billing city")
                    .setMaxLength(40),
            Properties.columnProperty(INVOICE_BILLINGSTATE, Types.VARCHAR, "Billing state")
                    .setMaxLength(40),
            Properties.columnProperty(INVOICE_BILLINGCOUNTRY, Types.VARCHAR, "Billing country")
                    .setMaxLength(40),
            Properties.columnProperty(INVOICE_BILLINGPOSTALCODE, Types.VARCHAR, "Billing postal code")
                    .setMaxLength(10),
            Properties.columnProperty(INVOICE_TOTAL, Types.DOUBLE, "Total")
                    .setMaximumFractionDigits(2)
                    .setHidden(true),
            Properties.subqueryProperty(INVOICE_TOTAL_SUB, Types.DOUBLE, "Calculated total", INVOICE_TOTAL_SUBQUERY)
                    .setMaximumFractionDigits(2))
            .setKeyGeneratorType(Entity.KeyGenerator.Type.AUTOMATIC)
            .setStringProvider(new Entities.StringProvider(INVOICE_INVOICEID))
            .setSearchPropertyIds(INVOICE_INVOICEID_AS_STRING)
            .setCaption("Invoices");
  }
 
  public static final String INVOICELINE_INVOICELINEID = "invoicelineid";
  public static final String INVOICELINE_INVOICEID = "invoiceid";
  public static final String INVOICELINE_INVOICEID_FK = "invoiceid_fk";
  public static final String INVOICELINE_TRACKID = "trackid";
  public static final String INVOICELINE_TRACKID_FK = "trackid_fk";
  public static final String INVOICELINE_UNITPRICE = "unitprice";
  public static final String INVOICELINE_QUANTITY = "quantity";
  public static final String INVOICELINE_TOTAL = "total";
  @Entity.Table(tableName = "chinook.invoiceline",
          keyGenerator = Entity.KeyGenerator.Type.AUTOMATIC,
          keyGeneratorSource = "chinook.invoiceline")
  public static final String T_INVOICELINE = "invoiceline@chinook";
 
  public static final Property.DerivedProperty.Provider INVOICELINE_TOTAL_PROVIDER =
          linkedValues -> {
            final Integer quantity = (Integer) linkedValues.get(INVOICELINE_QUANTITY);
            final Double unitPrice = (Double) linkedValues.get(INVOICELINE_UNITPRICE);
            if (unitPrice == null || quantity == null) {
              return null;
            }
 
            return quantity * unitPrice;
          };
 
  void invoiceLine() {
    define(T_INVOICELINE,
            Properties.primaryKeyProperty(INVOICELINE_INVOICELINEID, Types.BIGINT),
            Properties.foreignKeyProperty(INVOICELINE_INVOICEID_FK, "Invoice", T_INVOICE,
                    Properties.columnProperty(INVOICELINE_INVOICEID, Types.BIGINT))
                    .setFetchDepth(0)
                    .setNullable(false),
            Properties.foreignKeyProperty(INVOICELINE_TRACKID_FK, "Track", T_TRACK,
                    Properties.columnProperty(INVOICELINE_TRACKID, Types.BIGINT))
                    .setNullable(false)
                    .setPreferredColumnWidth(100),
            Properties.denormalizedProperty(INVOICELINE_UNITPRICE, INVOICELINE_TRACKID_FK,
                    getProperty(T_TRACK, TRACK_UNITPRICE), "Unit price")
                    .setNullable(false),
            Properties.columnProperty(INVOICELINE_QUANTITY, Types.INTEGER, "Quantity")
                    .setNullable(false),
            Properties.derivedProperty(INVOICELINE_TOTAL, Types.DOUBLE, "Total", INVOICELINE_TOTAL_PROVIDER,
                    INVOICELINE_QUANTITY, INVOICELINE_UNITPRICE))
            .setKeyGeneratorType(Entity.KeyGenerator.Type.AUTOMATIC)
            .setCaption("Invoice lines");
  }
 
  public static final String PLAYLIST_PLAYLISTID = "playlistid";
  public static final String PLAYLIST_NAME = "name";
  @Entity.Table(tableName = "chinook.playlist",
          orderByClause = PLAYLIST_NAME,
          keyGenerator = Entity.KeyGenerator.Type.AUTOMATIC,
          keyGeneratorSource = "chinook.playlist")
  public static final String T_PLAYLIST = "playlist@chinook";
 
  void playlist() {
    define(T_PLAYLIST,
            Properties.primaryKeyProperty(PLAYLIST_PLAYLISTID, Types.BIGINT),
            Properties.columnProperty(PLAYLIST_NAME, Types.VARCHAR, "Name")
                    .setNullable(false)
                    .setMaxLength(120)
                    .setPreferredColumnWidth(160))
            .setKeyGeneratorType(Entity.KeyGenerator.Type.AUTOMATIC)
            .setStringProvider(new Entities.StringProvider(PLAYLIST_NAME))
            .setSearchPropertyIds(PLAYLIST_NAME)
            .setCaption("Playlists");
  }
 
  public static final String PLAYLISTTRACK_PLAYLISTID = "playlistid";
  public static final String PLAYLISTTRACK_PLAYLISTID_FK = "playlistid_fk";
  public static final String PLAYLISTTRACK_TRACKID = "trackid";
  public static final String PLAYLISTTRACK_TRACKID_FK = "trackid_fk";
  public static final String PLAYLISTTRACK_ALBUM_DENORM = "album_denorm";
  public static final String PLAYLISTTRACK_ARTIST_DENORM = "artist_denorm";
  @Entity.Table(tableName = "chinook.playlisttrack")
  public static final String T_PLAYLISTTRACK = "playlisttrack@chinook";
 
  void playlistTrack() {
    define(T_PLAYLISTTRACK,
            Properties.foreignKeyProperty(PLAYLISTTRACK_PLAYLISTID_FK, "Playlist", T_PLAYLIST,
                    Properties.primaryKeyProperty(PLAYLISTTRACK_PLAYLISTID, Types.BIGINT)
                            .setUpdatable(true))
                    .setNullable(false)
                    .setPreferredColumnWidth(120),
            Properties.denormalizedViewProperty(PLAYLISTTRACK_ARTIST_DENORM, PLAYLISTTRACK_ALBUM_DENORM,
                    getProperty(T_ALBUM, ALBUM_ARTISTID_FK), "Artist")
                    .setPreferredColumnWidth(160),
            Properties.foreignKeyProperty(PLAYLISTTRACK_TRACKID_FK, "Track", T_TRACK,
                    Properties.primaryKeyProperty(PLAYLISTTRACK_TRACKID, Types.BIGINT)
                            .setPrimaryKeyIndex(1)
                            .setUpdatable(true))
                    .setFetchDepth(3)
                    .setNullable(false)
                    .setPreferredColumnWidth(160),
            Properties.denormalizedViewProperty(PLAYLISTTRACK_ALBUM_DENORM, PLAYLISTTRACK_TRACKID_FK,
                    getProperty(T_TRACK, TRACK_ALBUMID_FK), "Album")
                    .setPreferredColumnWidth(160))
            .setStringProvider(new Entities.StringProvider(PLAYLISTTRACK_PLAYLISTID_FK)
                    .addText(" - ").addValue(PLAYLISTTRACK_TRACKID_FK))
            .setCaption("Playlist tracks");
  }
 
  @Databases.Operation(className="org.jminor.framework.demos.chinook.server.UpdateTotalsProcedure")
  public static final String P_UPDATE_TOTALS = "chinook.update_totals_procedure";
}

File: org/jminor/framework/demos/chinook/server/UpdateTotalsProcedure.java -

/*
 * Copyright (c) 2004 - 2017, Björn Darri Sigurðsson. All Rights Reserved.
 */
package org.jminor.framework.demos.chinook.server;
 
import org.jminor.common.db.AbstractProcedure;
import org.jminor.common.db.exception.DatabaseException;
import org.jminor.framework.db.condition.EntityConditions;
import org.jminor.framework.db.condition.EntitySelectCondition;
import org.jminor.framework.db.local.LocalEntityConnection;
import org.jminor.framework.demos.chinook.domain.Chinook;
import org.jminor.framework.domain.Entities;
import org.jminor.framework.domain.Entity;
 
import java.util.List;
 
public final class UpdateTotalsProcedure extends AbstractProcedure<LocalEntityConnection> {
 
  public UpdateTotalsProcedure(final String id) {
    super(id, "Update invoice totals");
  }
 
  @Override
  public void execute(final LocalEntityConnection entityConnection, final Object... arguments) throws DatabaseException {
    try {
      entityConnection.beginTransaction();
      final EntitySelectCondition selectCondition = new EntityConditions(entityConnection.getDomain()).selectCondition(Chinook.T_INVOICE);
      selectCondition.setForUpdate(true);
      selectCondition.setForeignKeyFetchDepthLimit(0);
      final List<Entity> invoices = entityConnection.selectMany(selectCondition);
      for (final Entity invoice : invoices) {
        invoice.put(Chinook.INVOICE_TOTAL, invoice.get(Chinook.INVOICE_TOTAL_SUB));
      }
      final List<Entity> modifiedInvoices = Entities.getModifiedEntities(invoices);
      if (!modifiedInvoices.isEmpty()) {
        entityConnection.update(modifiedInvoices);
      }
      entityConnection.commitTransaction();
    }
    catch (final DatabaseException dbException) {
      if (entityConnection.isTransactionOpen()) {
        entityConnection.rollbackTransaction();
      }
      throw dbException;
    }
  }
}

Domain model unit test

File: org/jminor/framework/demos/chinook/domain/ChinookTest.java -

/*
 * Copyright (c) 2004 - 2017, Björn Darri Sigurðsson. All Rights Reserved.
 */
package org.jminor.framework.demos.chinook.domain;
 
import org.jminor.common.User;
import org.jminor.common.model.CancelException;
import org.jminor.framework.domain.Entities;
import org.jminor.framework.domain.testing.EntityTestUnit;
 
import org.junit.Test;
 
import static org.jminor.framework.demos.chinook.domain.Chinook.*;
 
public class ChinookTest extends EntityTestUnit {
 
  private static final Entities ENTITIES = new Chinook();
 
  private static final User UNIT_TEST_USER = new User(
          System.getProperty("jminor.unittest.username", "scott"),
          System.getProperty("jminor.unittest.password", "tiger"));
 
  public ChinookTest() {
    super(ENTITIES);
  }
 
  @Test
  public void album() throws Exception {
    testEntity(T_ALBUM);
  }
 
  @Test
  public void artist() throws Exception {
    testEntity(T_ARTIST);
  }
 
  @Test
  public void customer() throws Exception {
    testEntity(T_CUSTOMER);
  }
 
  @Test
  public void employee() throws Exception {
    testEntity(T_EMPLOYEE);
  }
 
  @Test
  public void genre() throws Exception {
    testEntity(T_GENRE);
  }
 
  @Test
  public void invoce() throws Exception {
    testEntity(T_INVOICE);
  }
 
  @Test
  public void invoiceLine() throws Exception {
    testEntity(T_INVOICELINE);
  }
 
  @Test
  public void mediaType() throws Exception {
    testEntity(T_MEDIATYPE);
  }
 
  @Test
  public void playlist() throws Exception {
    testEntity(T_PLAYLIST);
  }
 
  @Test
  public void playlistTrack() throws Exception {
    testEntity(T_PLAYLISTTRACK);
  }
 
  @Test
  public void track() throws Exception {
    testEntity(T_TRACK);
  }
 
  @Override
  protected User getTestUser() throws CancelException {
    return UNIT_TEST_USER;
  }
}
 

Application UI layer

File: org/jminor/framework/demos/chinook/beans/ui/AlbumEditPanel.java -

/*
 * Copyright (c) 2004 - 2017, Björn Darri Sigurðsson. All Rights Reserved.
 */
package org.jminor.framework.demos.chinook.beans.ui;
 
import org.jminor.swing.framework.model.SwingEntityEditModel;
import org.jminor.swing.framework.ui.EntityEditPanel;
 
import javax.swing.JTextField;
import java.awt.GridLayout;
 
import static org.jminor.framework.demos.chinook.domain.Chinook.ALBUM_ARTISTID_FK;
import static org.jminor.framework.demos.chinook.domain.Chinook.ALBUM_TITLE;
 
public class AlbumEditPanel extends EntityEditPanel {
 
  public AlbumEditPanel(final SwingEntityEditModel editModel) {
    super(editModel);
  }
 
  @Override
  protected void initializeUI() {
    setInitialFocusProperty(ALBUM_ARTISTID_FK);
    final JTextField txtArtist = createForeignKeyLookupField(ALBUM_ARTISTID_FK);
    txtArtist.setColumns(18);
    final JTextField txtTitle = createTextField(ALBUM_TITLE);
    txtTitle.setColumns(18);
 
    setLayout(new GridLayout(2, 1, 5, 5));
    addPropertyPanel(ALBUM_ARTISTID_FK);
    addPropertyPanel(ALBUM_TITLE);
  }
}
 

File: org/jminor/framework/demos/chinook/beans/ui/ArtistEditPanel.java -

/*
 * Copyright (c) 2004 - 2017, Björn Darri Sigurðsson. All Rights Reserved.
 */
package org.jminor.framework.demos.chinook.beans.ui;
 
import org.jminor.swing.framework.model.SwingEntityEditModel;
import org.jminor.swing.framework.ui.EntityEditPanel;
 
import javax.swing.JTextField;
import java.awt.GridLayout;
 
import static org.jminor.framework.demos.chinook.domain.Chinook.ARTIST_NAME;
 
public class ArtistEditPanel extends EntityEditPanel {
 
  public ArtistEditPanel(final SwingEntityEditModel editModel) {
    super(editModel);
  }
 
  @Override
  protected void initializeUI() {
    setInitialFocusProperty(ARTIST_NAME);
    final JTextField txtName = createTextField(ARTIST_NAME);
    txtName.setColumns(18);
 
    setLayout(new GridLayout(1, 1, 5, 5));
    addPropertyPanel(ARTIST_NAME);
  }
}

File: org/jminor/framework/demos/chinook/beans/ui/CustomerEditPanel.java -

/*
 * Copyright (c) 2004 - 2017, Björn Darri Sigurðsson. All Rights Reserved.
 */
package org.jminor.framework.demos.chinook.beans.ui;
 
import org.jminor.swing.common.ui.UiUtil;
import org.jminor.swing.common.ui.layout.FlexibleGridLayout;
import org.jminor.swing.framework.model.SwingEntityEditModel;
import org.jminor.swing.framework.ui.EntityEditPanel;
 
import javax.swing.JTextField;
 
import static org.jminor.framework.demos.chinook.domain.Chinook.*;
 
public class CustomerEditPanel extends EntityEditPanel {
 
  public CustomerEditPanel(final SwingEntityEditModel editModel) {
    super(editModel);
  }
 
  @Override
  protected void initializeUI() {
    setInitialFocusProperty(CUSTOMER_FIRSTNAME);
    final JTextField txtFirstName = createTextField(CUSTOMER_FIRSTNAME);
    txtFirstName.setColumns(16);
    final JTextField txtLastName = createTextField(CUSTOMER_LASTNAME);
    txtLastName.setColumns(16);
    final JTextField txtCompany = createTextField(CUSTOMER_COMPANY);
    txtCompany.setColumns(16);
    final JTextField txtAddress = createTextField(CUSTOMER_ADDRESS);
    txtAddress.setColumns(16);
    final JTextField txtCity = createTextField(CUSTOMER_CITY);
    txtCity.setColumns(16);
    final JTextField txtState = (JTextField) UiUtil.makeUpperCase(createTextField(CUSTOMER_STATE));
    txtState.setColumns(16);
    final JTextField txtCountry = createTextField(CUSTOMER_COUNTRY);
    txtCountry.setColumns(16);
    final JTextField txtPostalcode = createTextField(CUSTOMER_POSTALCODE);
    txtPostalcode.setColumns(16);
    final JTextField txtPhone = createTextField(CUSTOMER_PHONE);
    txtPhone.setColumns(16);
    final JTextField txtFax = createTextField(CUSTOMER_FAX);
    txtFax.setColumns(16);
    final JTextField txtEmail = createTextField(CUSTOMER_EMAIL);
    txtEmail.setColumns(16);
    createForeignKeyComboBox(CUSTOMER_SUPPORTREPID_FK);
 
    setLayout(new FlexibleGridLayout(3, 4, 5, 5));
    addPropertyPanel(CUSTOMER_FIRSTNAME);
    addPropertyPanel(CUSTOMER_LASTNAME);
    addPropertyPanel(CUSTOMER_COMPANY);
    addPropertyPanel(CUSTOMER_ADDRESS);
    addPropertyPanel(CUSTOMER_CITY);
    addPropertyPanel(CUSTOMER_STATE);
    addPropertyPanel(CUSTOMER_COUNTRY);
    addPropertyPanel(CUSTOMER_POSTALCODE);
    addPropertyPanel(CUSTOMER_PHONE);
    addPropertyPanel(CUSTOMER_FAX);
    addPropertyPanel(CUSTOMER_EMAIL);
    addPropertyPanel(CUSTOMER_SUPPORTREPID_FK);
  }
}

File: org/jminor/framework/demos/chinook/beans/ui/EmployeeEditPanel.java -

/*
 * Copyright (c) 2004 - 2017, Björn Darri Sigurðsson. All Rights Reserved.
 */
package org.jminor.framework.demos.chinook.beans.ui;
 
import org.jminor.swing.common.ui.DateInputPanel;
import org.jminor.swing.common.ui.UiUtil;
import org.jminor.swing.common.ui.layout.FlexibleGridLayout;
import org.jminor.swing.framework.model.SwingEntityEditModel;
import org.jminor.swing.framework.ui.EntityEditPanel;
 
import javax.swing.JTextField;
 
import static org.jminor.framework.demos.chinook.domain.Chinook.*;
 
public class EmployeeEditPanel extends EntityEditPanel {
 
  public EmployeeEditPanel(final SwingEntityEditModel editModel) {
    super(editModel);
  }
 
  @Override
  protected void initializeUI() {
    setInitialFocusProperty(EMPLOYEE_FIRSTNAME);
    final JTextField txtFirstName = createTextField(EMPLOYEE_FIRSTNAME);
    txtFirstName.setColumns(16);
    final JTextField txtLastName = createTextField(EMPLOYEE_LASTNAME);
    txtLastName.setColumns(16);
    final DateInputPanel birthdateInputPanel = createDateInputPanel(EMPLOYEE_BIRTHDATE);
    birthdateInputPanel.getInputField().setColumns(16);
    final JTextField txtAddress = createTextField(EMPLOYEE_ADDRESS);
    txtAddress.setColumns(16);
    final JTextField txtCity = createTextField(EMPLOYEE_CITY);
    txtCity.setColumns(16);
    final JTextField txtState = (JTextField) UiUtil.makeUpperCase(createTextField(EMPLOYEE_STATE));
    txtState.setColumns(16);
    final JTextField txtCountry = createTextField(EMPLOYEE_COUNTRY);
    txtCountry.setColumns(16);
    final JTextField txtPostalcode = createTextField(EMPLOYEE_POSTALCODE);
    txtPostalcode.setColumns(16);
    final JTextField txtPhone = createTextField(EMPLOYEE_PHONE);
    txtPhone.setColumns(16);
    final JTextField txtFax = createTextField(EMPLOYEE_FAX);
    txtFax.setColumns(16);
    final JTextField txtEmail = createTextField(EMPLOYEE_EMAIL);
    txtEmail.setColumns(16);
    createForeignKeyComboBox(EMPLOYEE_REPORTSTO_FK);
    final DateInputPanel hiredateInputPanel = createDateInputPanel(EMPLOYEE_HIREDATE);
    hiredateInputPanel.getInputField().setColumns(16);
    final JTextField txtTitle = createTextField(EMPLOYEE_TITLE);
    txtTitle.setColumns(16);
 
    setLayout(new FlexibleGridLayout(4, 4, 5, 5));
    addPropertyPanel(EMPLOYEE_FIRSTNAME);
    addPropertyPanel(EMPLOYEE_LASTNAME);
    addPropertyPanel(EMPLOYEE_BIRTHDATE);
    addPropertyPanel(EMPLOYEE_ADDRESS);
    addPropertyPanel(EMPLOYEE_CITY);
    addPropertyPanel(EMPLOYEE_STATE);
    addPropertyPanel(EMPLOYEE_COUNTRY);
    addPropertyPanel(EMPLOYEE_POSTALCODE);
    addPropertyPanel(EMPLOYEE_PHONE);
    addPropertyPanel(EMPLOYEE_FAX);
    addPropertyPanel(EMPLOYEE_EMAIL);
    addPropertyPanel(EMPLOYEE_REPORTSTO_FK);
    addPropertyPanel(EMPLOYEE_HIREDATE);
    addPropertyPanel(EMPLOYEE_TITLE);
  }
}

File: org/jminor/framework/demos/chinook/beans/ui/GenreEditPanel.java -

/*
 * Copyright (c) 2004 - 2017, Björn Darri Sigurðsson. All Rights Reserved.
 */
package org.jminor.framework.demos.chinook.beans.ui;
 
import org.jminor.swing.framework.model.SwingEntityEditModel;
import org.jminor.swing.framework.ui.EntityEditPanel;
 
import javax.swing.JTextField;
import java.awt.GridLayout;
 
import static org.jminor.framework.demos.chinook.domain.Chinook.GENRE_NAME;
 
public class GenreEditPanel extends EntityEditPanel {
 
  public GenreEditPanel(final SwingEntityEditModel editModel) {
    super(editModel);
  }
 
  @Override
  protected void initializeUI() {
    setInitialFocusProperty(GENRE_NAME);
    final JTextField txtName = createTextField(GENRE_NAME);
    txtName.setColumns(12);
 
    setLayout(new GridLayout(1, 1, 5, 5));
    addPropertyPanel(GENRE_NAME);
  }
}

File: org/jminor/framework/demos/chinook/beans/ui/InvoiceLineEditPanel.java -

/*
 * Copyright (c) 2004 - 2017, Björn Darri Sigurðsson. All Rights Reserved.
 */
package org.jminor.framework.demos.chinook.beans.ui;
 
import org.jminor.swing.common.ui.UiUtil;
import org.jminor.swing.framework.model.SwingEntityEditModel;
import org.jminor.swing.framework.ui.EntityEditPanel;
 
import javax.swing.JLabel;
import javax.swing.JTextField;
import java.awt.BorderLayout;
 
import static org.jminor.framework.demos.chinook.domain.Chinook.INVOICELINE_QUANTITY;
import static org.jminor.framework.demos.chinook.domain.Chinook.INVOICELINE_TRACKID_FK;
 
public class InvoiceLineEditPanel extends EntityEditPanel {
 
  private JTextField tableSearchField;
 
  public InvoiceLineEditPanel(final SwingEntityEditModel editModel) {
    super(editModel);
    editModel.setValuePersistent(INVOICELINE_TRACKID_FK, false);
  }
 
  public void setTableSearchFeld(final JTextField tableSearchField) {
    this.tableSearchField = tableSearchField;
  }
 
  @Override
  protected void initializeUI() {
    setInitialFocusProperty(INVOICELINE_TRACKID_FK);
    final JTextField txtTrack = createForeignKeyLookupField(INVOICELINE_TRACKID_FK);
    txtTrack.setColumns(25);
    final JTextField txtQuantity = createTextField(INVOICELINE_QUANTITY);
    UiUtil.removeTransferFocusOnEnter(txtQuantity);//otherwise the action added below wont work
    txtQuantity.addActionListener(getSaveControl());
 
    setLayout(new BorderLayout(5, 5));
    add(createPropertyPanel(INVOICELINE_TRACKID_FK), BorderLayout.WEST);
    add(createPropertyPanel(INVOICELINE_QUANTITY), BorderLayout.CENTER);
    add(createPropertyPanel(new JLabel(" "), tableSearchField, true), BorderLayout.EAST);
  }
}

File: org/jminor/framework/demos/chinook/beans/ui/InvoiceEditPanel.java -

/*
 * Copyright (c) 2004 - 2017, Björn Darri Sigurðsson. All Rights Reserved.
 */
package org.jminor.framework.demos.chinook.beans.ui;
 
import org.jminor.swing.common.ui.DateInputPanel;
import org.jminor.swing.framework.model.SwingEntityEditModel;
import org.jminor.swing.framework.ui.EntityEditPanel;
import org.jminor.swing.framework.ui.EntityPanel;
 
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.JTextField;
import java.awt.BorderLayout;
import java.awt.GridLayout;
 
import static org.jminor.framework.demos.chinook.domain.Chinook.*;
 
public class InvoiceEditPanel extends EntityEditPanel {
 
  private EntityPanel invoiceLinePanel;
 
  public InvoiceEditPanel(final SwingEntityEditModel editModel) {
    super(editModel);
  }
 
  public void setInvoiceLinePanel(final EntityPanel invoiceLinePanel) {
    this.invoiceLinePanel = invoiceLinePanel;
  }
 
  @Override
  protected void initializeUI() {
    setInitialFocusProperty(INVOICE_CUSTOMERID_FK);
    final JTextField txtCustomer = createForeignKeyLookupField(INVOICE_CUSTOMERID_FK);
    txtCustomer.setColumns(16);
    final DateInputPanel datePanel = createDateInputPanel(INVOICE_INVOICEDATE);
    datePanel.getInputField().setColumns(16);
    final JTextField txtAddress = createTextField(INVOICE_BILLINGADDRESS);
    txtAddress.setColumns(16);
    final JTextField txtCity = createTextField(INVOICE_BILLINGCITY);
    txtCity.setColumns(16);
    final JTextField txtState = createTextField(INVOICE_BILLINGSTATE);
    txtState.setColumns(16);
    final JTextField txtCountry = createTextField(INVOICE_BILLINGCOUNTRY);
    txtCountry.setColumns(16);
    final JTextField txtPostalcode = createTextField(INVOICE_BILLINGPOSTALCODE);
    txtPostalcode.setColumns(16);
    final JTextField txtTotal = createTextField(INVOICE_TOTAL_SUB);
    txtTotal.setColumns(16);
 
    final JPanel propertyBase = new JPanel(new GridLayout(4, 2, 5, 5));
    propertyBase.add(createPropertyPanel(INVOICE_CUSTOMERID_FK));
    propertyBase.add(createPropertyPanel(INVOICE_INVOICEDATE));
    propertyBase.add(createPropertyPanel(INVOICE_BILLINGADDRESS));
    propertyBase.add(createPropertyPanel(INVOICE_BILLINGCITY));
    propertyBase.add(createPropertyPanel(INVOICE_BILLINGSTATE));
    propertyBase.add(createPropertyPanel(INVOICE_BILLINGCOUNTRY));
    propertyBase.add(createPropertyPanel(INVOICE_BILLINGPOSTALCODE));
    propertyBase.add(createPropertyPanel(INVOICE_TOTAL_SUB));
 
    final JPanel centerBase = new JPanel(new BorderLayout(5, 5));
    centerBase.add(propertyBase, BorderLayout.CENTER);
 
    invoiceLinePanel.setBorder(BorderFactory.createTitledBorder("Invoice lines"));
 
    setLayout(new BorderLayout(5, 5));
    add(centerBase, BorderLayout.CENTER);
    add(invoiceLinePanel, BorderLayout.EAST);
  }
}

File: org/jminor/framework/demos/chinook/beans/ui/MediaTypeEditPanel.java -

/*
 * Copyright (c) 2004 - 2017, Björn Darri Sigurðsson. All Rights Reserved.
 */
package org.jminor.framework.demos.chinook.beans.ui;
 
import org.jminor.swing.framework.model.SwingEntityEditModel;
import org.jminor.swing.framework.ui.EntityEditPanel;
 
import javax.swing.JTextField;
import java.awt.GridLayout;
 
import static org.jminor.framework.demos.chinook.domain.Chinook.MEDIATYPE_NAME;
 
public class MediaTypeEditPanel extends EntityEditPanel {
 
  public MediaTypeEditPanel(final SwingEntityEditModel editModel) {
    super(editModel);
  }
 
  @Override
  protected void initializeUI() {
    setInitialFocusProperty(MEDIATYPE_NAME);
    final JTextField txtName = createTextField(MEDIATYPE_NAME);
    txtName.setColumns(12);
 
    setLayout(new GridLayout(1, 1, 5, 5));
    addPropertyPanel(MEDIATYPE_NAME);
  }
}

File: org/jminor/framework/demos/chinook/beans/ui/PlaylistEditPanel.java -

/*
 * Copyright (c) 2004 - 2017, Björn Darri Sigurðsson. All Rights Reserved.
 */
package org.jminor.framework.demos.chinook.beans.ui;
 
import org.jminor.swing.framework.model.SwingEntityEditModel;
import org.jminor.swing.framework.ui.EntityEditPanel;
 
import javax.swing.JTextField;
import java.awt.GridLayout;
 
import static org.jminor.framework.demos.chinook.domain.Chinook.PLAYLIST_NAME;
 
public class PlaylistEditPanel extends EntityEditPanel {
 
  public PlaylistEditPanel(final SwingEntityEditModel editModel) {
    super(editModel);
  }
 
  @Override
  protected void initializeUI() {
    setInitialFocusProperty(PLAYLIST_NAME);
    final JTextField txtName = createTextField(PLAYLIST_NAME);
    txtName.setColumns(12);
 
    setLayout(new GridLayout(1, 1, 5, 5));
    addPropertyPanel(PLAYLIST_NAME);
  }
}

File: org/jminor/framework/demos/chinook/beans/ui/PlaylistTrackEditPanel.java -

/*
 * Copyright (c) 2004 - 2017, Björn Darri Sigurðsson. All Rights Reserved.
 */
package org.jminor.framework.demos.chinook.beans.ui;
 
import org.jminor.swing.framework.model.SwingEntityEditModel;
import org.jminor.swing.framework.ui.EntityEditPanel;
 
import javax.swing.JTextField;
import java.awt.GridLayout;
 
import static org.jminor.framework.demos.chinook.domain.Chinook.PLAYLISTTRACK_PLAYLISTID_FK;
import static org.jminor.framework.demos.chinook.domain.Chinook.PLAYLISTTRACK_TRACKID_FK;
 
public class PlaylistTrackEditPanel extends EntityEditPanel {
 
  public PlaylistTrackEditPanel(final SwingEntityEditModel editModel) {
    super(editModel);
  }
 
  @Override
  protected void initializeUI() {
    setInitialFocusProperty(PLAYLISTTRACK_PLAYLISTID_FK);
    createForeignKeyComboBox(PLAYLISTTRACK_PLAYLISTID_FK);
    final JTextField txtTrack = createForeignKeyLookupField(PLAYLISTTRACK_TRACKID_FK);
    txtTrack.setColumns(30);
 
    setLayout(new GridLayout(2, 1, 5, 5));
    addPropertyPanel(PLAYLISTTRACK_PLAYLISTID_FK);
    addPropertyPanel(PLAYLISTTRACK_TRACKID_FK);
  }
}

File: org/jminor/framework/demos/chinook/beans/ui/TrackEditPanel.java -

/*
 * Copyright (c) 2004 - 2017, Björn Darri Sigurðsson. All Rights Reserved.
 */
package org.jminor.framework.demos.chinook.beans.ui;
 
import org.jminor.swing.common.ui.TextInputPanel;
import org.jminor.swing.common.ui.layout.FlexibleGridLayout;
import org.jminor.swing.common.ui.textfield.IntegerField;
import org.jminor.swing.framework.model.SwingEntityEditModel;
import org.jminor.swing.framework.ui.EntityComboBox;
import org.jminor.swing.framework.ui.EntityEditPanel;
import org.jminor.swing.framework.ui.EntityPanelProvider;
import org.jminor.swing.framework.ui.EntityUiUtil;
 
import javax.swing.Action;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import java.awt.GridLayout;
 
import static org.jminor.framework.demos.chinook.domain.Chinook.*;
 
public class TrackEditPanel extends EntityEditPanel {
 
  public TrackEditPanel(final SwingEntityEditModel editModel) {
    super(editModel);
  }
 
  @Override
  protected void initializeUI() {
    setInitialFocusProperty(TRACK_ALBUMID_FK);
    final JTextField txtAlbum = createForeignKeyLookupField(TRACK_ALBUMID_FK);
    txtAlbum.setColumns(18);
    final JTextField txtName = createTextField(TRACK_NAME);
    txtName.setColumns(18);
    final EntityComboBox mediaTypeBox = createForeignKeyComboBox(TRACK_MEDIATYPEID_FK);
    final Action newMediaTypeAction = EntityEditPanel.createEditPanelAction(mediaTypeBox,
            new EntityPanelProvider(T_MEDIATYPE, getEditModel().getEntities().getCaption(T_MEDIATYPE))
                    .setEditPanelClass(MediaTypeEditPanel.class));
    final JPanel mediaTypePanel = EntityUiUtil.createEastButtonPanel(mediaTypeBox, newMediaTypeAction, false);
    final EntityComboBox genreBox = createForeignKeyComboBox(TRACK_GENREID_FK);
    final Action newGenreAction = EntityEditPanel.createEditPanelAction(genreBox,
            new EntityPanelProvider(T_GENRE, getEditModel().getEntities().getCaption(T_GENRE))
                    .setEditPanelClass(GenreEditPanel.class));
    final JPanel genrePanel = EntityUiUtil.createEastButtonPanel(genreBox, newGenreAction, false);
    final TextInputPanel txtComposer = createTextInputPanel(TRACK_COMPOSER);
    txtComposer.getTextField().setColumns(18);
    final IntegerField txtMilliseconds = (IntegerField) createTextField(TRACK_MILLISECONDS);
    txtMilliseconds.setGroupingUsed(true);
    final IntegerField txtBytes = (IntegerField) createTextField(TRACK_BYTES);
    txtBytes.setGroupingUsed(true);
    txtBytes.setColumns(18);
    final JTextField txtUnitPrice = createTextField(TRACK_UNITPRICE);
    txtUnitPrice.setColumns(18);
    final JTextField txtDuration = createTextField(TRACK_MINUTES_SECONDS_DERIVED, true);
    final JPanel durationPanel = new JPanel(new GridLayout(1, 2, 5, 5));
    durationPanel.add(createPropertyPanel(TRACK_MILLISECONDS, txtMilliseconds));
    durationPanel.add(createPropertyPanel(new JLabel("(min/sec)"), txtDuration, true));
 
    setLayout(new FlexibleGridLayout(4, 2, 5, 5, true, false));
    addPropertyPanel(TRACK_ALBUMID_FK);
    addPropertyPanel(TRACK_NAME);
    add(createPropertyPanel(TRACK_GENREID_FK, genrePanel));
    addPropertyPanel(TRACK_COMPOSER);
    add(createPropertyPanel(TRACK_MEDIATYPEID_FK, mediaTypePanel));
    addPropertyPanel(TRACK_BYTES);
    addPropertyPanel(TRACK_UNITPRICE);
    add(durationPanel);
  }
}

Main application panel

File: org/jminor/framework/demos/chinook/client/ui/ChinookAppPanel.java -

/*
 * Copyright (c) 2004 - 2017, Björn Darri Sigurðsson. All Rights Reserved.
 */
package org.jminor.framework.demos.chinook.client.ui;
 
import org.jminor.common.User;
import org.jminor.common.Version;
import org.jminor.common.db.exception.DatabaseException;
import org.jminor.common.model.CancelException;
import org.jminor.framework.db.EntityConnectionProvider;
import org.jminor.framework.demos.chinook.beans.ui.AlbumEditPanel;
import org.jminor.framework.demos.chinook.beans.ui.ArtistEditPanel;
import org.jminor.framework.demos.chinook.beans.ui.CustomerEditPanel;
import org.jminor.framework.demos.chinook.beans.ui.CustomerTablePanel;
import org.jminor.framework.demos.chinook.beans.ui.EmployeeEditPanel;
import org.jminor.framework.demos.chinook.beans.ui.GenreEditPanel;
import org.jminor.framework.demos.chinook.beans.ui.InvoiceEditPanel;
import org.jminor.framework.demos.chinook.beans.ui.InvoiceLineEditPanel;
import org.jminor.framework.demos.chinook.beans.ui.MediaTypeEditPanel;
import org.jminor.framework.demos.chinook.beans.ui.PlaylistEditPanel;
import org.jminor.framework.demos.chinook.beans.ui.PlaylistTrackEditPanel;
import org.jminor.framework.demos.chinook.beans.ui.TrackEditPanel;
import org.jminor.framework.demos.chinook.domain.Chinook;
import org.jminor.framework.domain.Entities;
import org.jminor.swing.common.ui.UiUtil;
import org.jminor.swing.common.ui.control.ControlSet;
import org.jminor.swing.common.ui.control.Controls;
import org.jminor.swing.framework.model.SwingEntityApplicationModel;
import org.jminor.swing.framework.model.SwingEntityModel;
import org.jminor.swing.framework.ui.EntityApplicationPanel;
import org.jminor.swing.framework.ui.EntityPanel;
import org.jminor.swing.framework.ui.EntityPanelProvider;
import org.jminor.swing.framework.ui.EntityTablePanel;
 
import javax.swing.JTable;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
 
import static org.jminor.framework.demos.chinook.domain.Chinook.*;
 
public final class ChinookAppPanel extends EntityApplicationPanel<ChinookAppPanel.ChinookApplicationModel> {
 
  @Override
  protected Entities initializeEntities() {
    return new Chinook();
  }
 
  /* ARTIST
    *   ALBUM
    *     TRACK
    * PLAYLIST
    *   PLAYLISTTRACK
    * CUSTOMER
    *   INVOICE
    *     INVOICELINE
    */
  @Override
  protected void setupEntityPanelProviders() {
    final Entities entities = getModel().getEntities();
    final EntityPanelProvider trackProvider = new EntityPanelProvider(T_TRACK, entities.getCaption(T_TRACK));
    trackProvider.setEditPanelClass(TrackEditPanel.class);
 
    final EntityPanelProvider customerProvider = new EntityPanelProvider(T_CUSTOMER, entities.getCaption(T_CUSTOMER));
    customerProvider.setEditPanelClass(CustomerEditPanel.class);
    customerProvider.setTablePanelClass(CustomerTablePanel.class);
 
    final EntityPanelProvider genreProvider = new EntityPanelProvider(T_GENRE, entities.getCaption(T_GENRE));
    genreProvider.setEditPanelClass(GenreEditPanel.class);
    genreProvider.addDetailPanelProvider(trackProvider).setDetailPanelState(EntityPanel.PanelState.HIDDEN);
 
    final EntityPanelProvider mediaTypeProvider = new EntityPanelProvider(T_MEDIATYPE, entities.getCaption(T_MEDIATYPE));
    mediaTypeProvider.setEditPanelClass(MediaTypeEditPanel.class);
    mediaTypeProvider.addDetailPanelProvider(trackProvider).setDetailPanelState(EntityPanel.PanelState.HIDDEN);
 
    final EntityPanelProvider employeeProvider = new EntityPanelProvider(T_EMPLOYEE, entities.getCaption(T_EMPLOYEE));
    employeeProvider.setEditPanelClass(EmployeeEditPanel.class);
    employeeProvider.addDetailPanelProvider(customerProvider).setDetailPanelState(EntityPanel.PanelState.HIDDEN);
 
    addSupportPanelProviders(genreProvider, mediaTypeProvider, employeeProvider);
  }
 
  @Override
  protected List<EntityPanel> initializeEntityPanels(final ChinookApplicationModel applicationModel) {
    final List<EntityPanel> panels = new ArrayList<>();
 
    final SwingEntityModel artistModel = applicationModel.getEntityModel(Chinook.T_ARTIST);
    final EntityPanel artistPanel = new EntityPanel(artistModel, new ArtistEditPanel(artistModel.getEditModel()));
    final SwingEntityModel albumModel = artistModel.getDetailModel(Chinook.T_ALBUM);
    final EntityPanel albumPanel = new EntityPanel(albumModel, new AlbumEditPanel(albumModel.getEditModel()));
    final SwingEntityModel trackModel = albumModel.getDetailModel(Chinook.T_TRACK);
    final EntityPanel trackPanel = new EntityPanel(trackModel, new TrackEditPanel(trackModel.getEditModel()));
 
    albumPanel.addDetailPanel(trackPanel);
    artistPanel.addDetailPanel(albumPanel);
    panels.add(artistPanel);
 
    final SwingEntityModel playlistModel = applicationModel.getEntityModel(Chinook.T_PLAYLIST);
    final EntityPanel playlistPanel = new EntityPanel(playlistModel, new PlaylistEditPanel(playlistModel.getEditModel()));
    final SwingEntityModel playlistTrackModel = playlistModel.getDetailModel(Chinook.T_PLAYLISTTRACK);
    final EntityPanel playlistTrackPanel = new EntityPanel(playlistTrackModel, new PlaylistTrackEditPanel(playlistTrackModel.getEditModel()));
 
    playlistPanel.addDetailPanel(playlistTrackPanel);
    panels.add(playlistPanel);
 
    final SwingEntityModel customerModel = applicationModel.getEntityModel(Chinook.T_CUSTOMER);
    final EntityPanel customerPanel = new EntityPanel(customerModel, new CustomerEditPanel(customerModel.getEditModel()),
            new CustomerTablePanel(customerModel.getTableModel()));
    final SwingEntityModel invoiceModel = customerModel.getDetailModel(Chinook.T_INVOICE);
    final EntityPanel invoicePanel = new EntityPanel(invoiceModel, new InvoiceEditPanel(invoiceModel.getEditModel()));
    invoicePanel.setIncludeDetailPanelTabPane(false);
 
    final SwingEntityModel invoiceLineModel = invoiceModel.getDetailModel(Chinook.T_INVOICELINE);
    final EntityPanel invoiceLinePanel = new EntityPanel(invoiceLineModel, new InvoiceLineEditPanel(invoiceLineModel.getEditModel()));
    final EntityTablePanel invoiceLineTablePanel = invoiceLinePanel.getTablePanel();
    invoiceLineTablePanel.setIncludeSouthPanel(false);
    invoiceLineTablePanel.setIncludeConditionPanel(false);
    invoiceLineTablePanel.getJTable().setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
    invoiceLineTablePanel.setPreferredSize(new Dimension(360, 40));
    invoiceLineTablePanel.getTableModel().getColumnModel().setColumnVisible(
            getModel().getEntities().getProperty(T_INVOICELINE, INVOICELINE_INVOICEID_FK), false);
    invoiceLinePanel.setIncludeControlPanel(false);
    ((InvoiceLineEditPanel) invoiceLinePanel.getEditPanel()).setTableSearchFeld(invoiceLinePanel.getTablePanel().getSearchField());
    invoiceLinePanel.initializePanel();
    ((InvoiceEditPanel) invoicePanel.getEditPanel()).setInvoiceLinePanel(invoiceLinePanel);
 
    invoicePanel.addDetailPanel(invoiceLinePanel);
    customerPanel.addDetailPanel(invoicePanel);
    panels.add(customerPanel);
 
    return panels;
  }
 
  @Override
  protected ChinookApplicationModel initializeApplicationModel(final EntityConnectionProvider connectionProvider) throws CancelException {
    return new ChinookApplicationModel(connectionProvider);
  }
 
  @Override
  protected ControlSet getToolsControlSet() {
    final ControlSet tools = super.getToolsControlSet();
    tools.addSeparator();
    tools.add(Controls.control(getModel()::updateInvoiceTotals, "Update invoice totals"));
 
    return tools;
  }
 
  @Override
  protected Version getClientVersion() {
    return new Version(0, 1, 0);
  }
 
  public static void main(final String[] args) throws CancelException {
    Locale.setDefault(new Locale("en", "EN"));
    EntityPanel.TOOLBAR_BUTTONS.set(true);
    EntityPanel.COMPACT_ENTITY_PANEL_LAYOUT.set(true);
    new ChinookAppPanel().startApplication("Chinook", null, false, UiUtil.getScreenSizeRatio(0.6), new User("scott", "tiger"));
  }
 
  public static final class ChinookApplicationModel extends SwingEntityApplicationModel {
 
    public ChinookApplicationModel(final EntityConnectionProvider connectionProvider) {
      super(connectionProvider);
      final SwingEntityModel artistModel = new SwingEntityModel(Chinook.T_ARTIST, connectionProvider);
      final SwingEntityModel albumModel = new SwingEntityModel(Chinook.T_ALBUM, connectionProvider);
      final SwingEntityModel trackModel = new SwingEntityModel(Chinook.T_TRACK, connectionProvider);
 
      albumModel.addDetailModel(trackModel);
      artistModel.addDetailModel(albumModel);
      addEntityModel(artistModel);
 
      final SwingEntityModel playlistModel = new SwingEntityModel(Chinook.T_PLAYLIST, connectionProvider);
      final SwingEntityModel playlistTrackModel = new SwingEntityModel(Chinook.T_PLAYLISTTRACK, connectionProvider);
 
      playlistModel.addDetailModel(playlistTrackModel);
      addEntityModel(playlistModel);
 
      final SwingEntityModel customerModel = new SwingEntityModel(Chinook.T_CUSTOMER, connectionProvider);
      final SwingEntityModel invoiceModel = new SwingEntityModel(Chinook.T_INVOICE, connectionProvider);
      final SwingEntityModel invoiceLineModel = new SwingEntityModel(Chinook.T_INVOICELINE, connectionProvider);
      invoiceLineModel.getTableModel().setQueryConfigurationAllowed(false);
      invoiceModel.addDetailModel(invoiceLineModel);
      invoiceModel.addLinkedDetailModel(invoiceLineModel);
      customerModel.addDetailModel(invoiceModel);
      addEntityModel(customerModel);
 
      artistModel.refresh();
      playlistModel.refresh();
      customerModel.refresh();
    }
 
    public void updateInvoiceTotals() throws DatabaseException {
      getConnectionProvider().getConnection().executeProcedure(P_UPDATE_TOTALS);
    }
  }
}
 

Application load test

File: org/jminor/framework/demos/chinook/testing/ChinookLoadTest.java -

/*
 * Copyright (c) 2004 - 2017, Björn Darri Sigurðsson. All Rights Reserved.
 */
package org.jminor.framework.demos.chinook.testing;
 
import org.jminor.common.User;
import org.jminor.common.model.CancelException;
import org.jminor.framework.db.EntityConnectionProviders;
import org.jminor.framework.demos.chinook.client.ui.ChinookAppPanel;
import org.jminor.framework.demos.chinook.domain.Chinook;
import org.jminor.framework.domain.Entities;
import org.jminor.framework.domain.Entity;
import org.jminor.framework.model.EntityApplicationModel;
import org.jminor.framework.model.EntityComboBoxModel;
import org.jminor.framework.model.EntityEditModel;
import org.jminor.framework.plugins.jasperreports.model.JasperReportsWrapper;
import org.jminor.swing.common.tools.ui.LoadTestPanel;
import org.jminor.swing.framework.model.SwingEntityEditModel;
import org.jminor.swing.framework.model.SwingEntityModel;
import org.jminor.swing.framework.model.SwingEntityTableModel;
import org.jminor.swing.framework.model.reporting.EntityReportUtil;
import org.jminor.swing.framework.tools.EntityLoadTestModel;
 
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
 
import static org.jminor.framework.demos.chinook.domain.Chinook.*;
 
public final class ChinookLoadTest extends EntityLoadTestModel<ChinookAppPanel.ChinookApplicationModel> {
 
  private static final Entities ENTITIES = new Chinook().registerDomain();
 
  private static final User UNIT_TEST_USER = new User(
          System.getProperty("jminor.unittest.username", "scott"),
          System.getProperty("jminor.unittest.password", "tiger"));
 
  private static final UsageScenario<ChinookAppPanel.ChinookApplicationModel> UPDATE_TOTALS =
          new AbstractEntityUsageScenario<ChinookAppPanel.ChinookApplicationModel>("updateTotals") {
    @Override
    protected void performScenario(final ChinookAppPanel.ChinookApplicationModel application) throws ScenarioException {
      try {
        final SwingEntityModel customerModel = application.getEntityModel(T_CUSTOMER);
        customerModel.getTableModel().refresh();
        selectRandomRows(customerModel.getTableModel(), RANDOM.nextInt(6) + 2);
        final SwingEntityModel invoiceModel = customerModel.getDetailModel(T_INVOICE);
        selectRandomRows(invoiceModel.getTableModel(), RANDOM.nextInt(6) + 2);
        final SwingEntityTableModel invoiceLineTableModel = invoiceModel.getDetailModel(T_INVOICELINE).getTableModel();
        final List<Entity> invoiceLines = invoiceLineTableModel.getAllItems();
        Entities.put(Chinook.INVOICELINE_QUANTITY, RANDOM.nextInt(4) + 1, invoiceLines);
 
        invoiceLineTableModel.update(invoiceLines);
 
        ((ChinookAppPanel.ChinookApplicationModel) application).updateInvoiceTotals();
      }
      catch (final Exception e) {
        throw new ScenarioException(e);
      }
    }
 
    @Override
    public int getDefaultWeight() {
      return 1;
    }
  };
 
  private static final UsageScenario<ChinookAppPanel.ChinookApplicationModel> VIEW_GENRE =
          new AbstractEntityUsageScenario<ChinookAppPanel.ChinookApplicationModel>("viewGenre") {
    @Override
    protected void performScenario(final ChinookAppPanel.ChinookApplicationModel application) throws ScenarioException {
      try {
        final SwingEntityModel genreModel = application.getEntityModel(T_GENRE);
        genreModel.getTableModel().refresh();
        selectRandomRow(genreModel.getTableModel());
        final SwingEntityModel trackModel = genreModel.getDetailModel(T_TRACK);
        selectRandomRows(trackModel.getTableModel(), 2);
        genreModel.getConnectionProvider().getConnection().selectDependentEntities(trackModel.getTableModel().getSelectionModel().getSelectedItems());
      }
      catch (final Exception e) {
        throw new ScenarioException(e);
      }
    }
 
    @Override
    public int getDefaultWeight() {
      return 10;
    }
  };
 
  private static final UsageScenario<ChinookAppPanel.ChinookApplicationModel> VIEW_CUSTOMER_REPORT =
          new AbstractEntityUsageScenario<ChinookAppPanel.ChinookApplicationModel>("viewCustomerReport") {
    @Override
    protected void performScenario(final ChinookAppPanel.ChinookApplicationModel application) throws ScenarioException {
      try {
        final SwingEntityTableModel customerModel = application.getEntityModel(T_CUSTOMER).getTableModel();
        customerModel.refresh();
        selectRandomRow(customerModel);
 
        final String reportPath = EntityApplicationModel.getReportPath() + "/customer_report.jasper";
        final Collection customerIDs =
                Entities.getDistinctValues(CUSTOMER_CUSTOMERID, customerModel.getSelectionModel().getSelectedItems());
        final HashMap<String, Object> reportParameters = new HashMap<>();
        reportParameters.put("CUSTOMER_IDS", customerIDs);
        EntityReportUtil.fillReport(new JasperReportsWrapper(reportPath, reportParameters),
                customerModel.getConnectionProvider());
      }
      catch (final Exception e) {
        throw new ScenarioException(e);
      }
    }
 
    @Override
    public int getDefaultWeight() {
      return 2;
    }
  };
 
  private static final UsageScenario<ChinookAppPanel.ChinookApplicationModel> VIEW_INVOICE =
          new AbstractEntityUsageScenario<ChinookAppPanel.ChinookApplicationModel>("viewInvoice") {
    @Override
    protected void performScenario(final ChinookAppPanel.ChinookApplicationModel application) throws ScenarioException {
      try {
        final SwingEntityModel customerModel = application.getEntityModel(T_CUSTOMER);
        customerModel.getTableModel().refresh();
        selectRandomRow(customerModel.getTableModel());
        final SwingEntityModel invoiceModel = customerModel.getDetailModel(T_INVOICE);
        selectRandomRow(invoiceModel.getTableModel());
      }
      catch (final Exception e) {
        throw new ScenarioException(e);
      }
    }
 
    @Override
    public int getDefaultWeight() {
      return 10;
    }
  };
 
  private static final UsageScenario<ChinookAppPanel.ChinookApplicationModel> VIEW_ALBUM =
          new AbstractEntityUsageScenario<ChinookAppPanel.ChinookApplicationModel>("viewAlbum") {
    @Override
    protected void performScenario(final ChinookAppPanel.ChinookApplicationModel application) throws ScenarioException {
      try {
        final SwingEntityModel artistModel = application.getEntityModel(T_ARTIST);
        artistModel.getTableModel().refresh();
        selectRandomRow(artistModel.getTableModel());
        final SwingEntityModel albumModel = artistModel.getDetailModel(T_ALBUM);
        selectRandomRow(albumModel.getTableModel());
      }
      catch (final Exception e) {
        throw new ScenarioException(e);
      }
    }
 
    @Override
    public int getDefaultWeight() {
      return 10;
    }
  };
 
  private static final UsageScenario<ChinookAppPanel.ChinookApplicationModel> INSERT_DELETE_ALBUM =
          new AbstractEntityUsageScenario<ChinookAppPanel.ChinookApplicationModel>("insertDeleteAlbum") {
    @Override
    protected void performScenario(final ChinookAppPanel.ChinookApplicationModel application) throws ScenarioException {
      final SwingEntityModel artistModel = application.getEntityModel(T_ARTIST);
      artistModel.getTableModel().refresh();
      selectRandomRow(artistModel.getTableModel());
      final Entity artist = artistModel.getTableModel().getSelectionModel().getSelectedItem();
      final SwingEntityModel albumModel = artistModel.getDetailModel(T_ALBUM);
      final EntityEditModel albumEditModel = albumModel.getEditModel();
      final Entity album = application.getEntities().entity(T_ALBUM);
      album.put(ALBUM_ARTISTID_FK, artist);
      album.put(ALBUM_TITLE, "Title");
 
      albumEditModel.setEntity(album);
      try {
        final Entity insertedAlbum = albumEditModel.insert().get(0);
        final SwingEntityEditModel trackEditModel = (SwingEntityEditModel) albumModel.getDetailModel(T_TRACK).getEditModel();
        final EntityComboBoxModel genreComboBoxModel = trackEditModel.getForeignKeyComboBoxModel(TRACK_GENREID_FK);
        selectRandomItem(genreComboBoxModel);
        final EntityComboBoxModel mediaTypeComboBoxModel = trackEditModel.getForeignKeyComboBoxModel(TRACK_MEDIATYPEID_FK);
        selectRandomItem(mediaTypeComboBoxModel);
        for (int i = 0; i < 10; i++) {
          trackEditModel.setValue(TRACK_ALBUMID_FK, insertedAlbum);
          trackEditModel.setValue(TRACK_NAME, "Track " + i);
          trackEditModel.setValue(TRACK_BYTES, 10000000);
          trackEditModel.setValue(TRACK_COMPOSER, "Composer");
          trackEditModel.setValue(TRACK_MILLISECONDS, 1000000);
          trackEditModel.setValue(TRACK_UNITPRICE, 2d);
          trackEditModel.setValue(TRACK_GENREID_FK, genreComboBoxModel.getSelectedValue());
          trackEditModel.setValue(TRACK_MEDIATYPEID_FK, mediaTypeComboBoxModel.getSelectedValue());
          trackEditModel.insert();
        }
 
        final SwingEntityTableModel trackTableModel = albumModel.getDetailModel(T_TRACK).getTableModel();
        trackTableModel.getSelectionModel().selectAll();
        trackTableModel.deleteSelected();
        albumEditModel.delete();
      }
      catch (final Exception e) {
        throw new ScenarioException(e);
      }
    }
 
    @Override
    public int getDefaultWeight() {
      return 3;
    }
  };
 
  public ChinookLoadTest() {
    super(UNIT_TEST_USER, Arrays.asList(VIEW_GENRE, VIEW_CUSTOMER_REPORT, VIEW_INVOICE, VIEW_ALBUM,
            UPDATE_TOTALS, INSERT_DELETE_ALBUM));
  }
 
  @Override
  protected ChinookAppPanel.ChinookApplicationModel initializeApplication() throws CancelException {
    final ChinookAppPanel.ChinookApplicationModel applicationModel = new ChinookAppPanel.ChinookApplicationModel(
            EntityConnectionProviders.connectionProvider(ENTITIES, getUser(), ChinookLoadTest.class.getSimpleName()));
    /* ARTIST
    *   ALBUM
    *     TRACK
    * GENRE
    *   GENRETRACK
    * PLAYLIST
    *   PLAYLISTTRACK
    * CUSTOMER
    *   INVOICE
    *     INVOICELINE
    */
    final SwingEntityModel artistModel = applicationModel.getEntityModel(T_ARTIST);
    final SwingEntityModel albumModel = artistModel.getDetailModel(T_ALBUM);
    final SwingEntityModel trackModel = albumModel.getDetailModel(T_TRACK);
    artistModel.addLinkedDetailModel(albumModel);
    albumModel.addLinkedDetailModel(trackModel);
 
    final SwingEntityModel playlistModel = applicationModel.getEntityModel(T_PLAYLIST);
    final SwingEntityModel playlistTrackModel = playlistModel.getDetailModel(T_PLAYLISTTRACK);
    playlistModel.addLinkedDetailModel(playlistTrackModel);
 
    final SwingEntityModel customerModel = applicationModel.getEntityModel(T_CUSTOMER);
    final SwingEntityModel invoiceModel = customerModel.getDetailModel(T_INVOICE);
    final SwingEntityModel invoicelineModel = invoiceModel.getDetailModel(T_INVOICELINE);
    customerModel.addLinkedDetailModel(invoiceModel);
    invoiceModel.addLinkedDetailModel(invoicelineModel);
 
    final SwingEntityModel genreModel = new SwingEntityModel(T_GENRE, applicationModel.getConnectionProvider());
    final SwingEntityModel genreTrackModel = new SwingEntityModel(T_TRACK, applicationModel.getConnectionProvider());
    genreModel.addDetailModel(genreTrackModel);
    genreModel.addLinkedDetailModel(genreTrackModel);
 
    applicationModel.addEntityModel(genreModel);
 
    return applicationModel;
  }
 
  public static void main(final String[] args) throws Exception {
    SwingUtilities.invokeLater(new Runner());
  }
 
  private static final class Runner implements Runnable {
    @Override
    public void run() {
      try {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
 
        new LoadTestPanel(new ChinookLoadTest()).showFrame();
      }
      catch (final Exception e) {
        throw new RuntimeException(e);
      }
    }
  }
}
 
documentation/tutorials/chinook.txt · Last modified: 2016/03/22 11:51 by darri