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 - 2018, Björn Darri Sigurðsson. All Rights Reserved.
 */
package org.jminor.framework.demos.chinook.domain;
 
import org.jminor.framework.domain.Property;
 
/**
 * User: darri
 * Date: 2.1.2018
 * Time: 18:23
 */
public interface Chinook {
 
  String T_ARTIST = "artist@chinook";
  String ARTIST_ARTISTID = "artistid";
  String ARTIST_NAME = "name";
 
  String T_ALBUM = "album@chinook";
  String ALBUM_ALBUMID = "albumid";
  String ALBUM_TITLE = "title";
  String ALBUM_ARTISTID = "artistid";
  String ALBUM_ARTISTID_FK = "artistid_fk";
 
  String T_EMPLOYEE = "employee@chinook";
  String EMPLOYEE_EMPLOYEEID = "employeeid";
  String EMPLOYEE_LASTNAME = "lastname";
  String EMPLOYEE_FIRSTNAME = "firstname";
  String EMPLOYEE_TITLE = "title";
  String EMPLOYEE_REPORTSTO = "reportsto";
  String EMPLOYEE_REPORTSTO_FK = "reportsto_fk";
  String EMPLOYEE_BIRTHDATE = "birthdate";
  String EMPLOYEE_HIREDATE = "hiredate";
  String EMPLOYEE_ADDRESS = "address";
  String EMPLOYEE_CITY = "city";
  String EMPLOYEE_STATE = "state";
  String EMPLOYEE_COUNTRY = "country";
  String EMPLOYEE_POSTALCODE = "postalcode";
  String EMPLOYEE_PHONE = "phone";
  String EMPLOYEE_FAX = "fax";
  String EMPLOYEE_EMAIL = "email";
 
  String T_CUSTOMER = "customer@chinook";
  String CUSTOMER_CUSTOMERID = "customerid";
  String CUSTOMER_FIRSTNAME = "firstname";
  String CUSTOMER_LASTNAME = "lastname";
  String CUSTOMER_COMPANY = "company";
  String CUSTOMER_ADDRESS = "address";
  String CUSTOMER_CITY = "city";
  String CUSTOMER_STATE = "state";
  String CUSTOMER_COUNTRY = "country";
  String CUSTOMER_POSTALCODE = "postalcode";
  String CUSTOMER_PHONE = "phone";
  String CUSTOMER_FAX = "fax";
  String CUSTOMER_EMAIL = "email";
  String CUSTOMER_SUPPORTREPID = "supportrepid";
  String CUSTOMER_SUPPORTREPID_FK = "supportrepid_fk";
 
  String T_GENRE = "genre@chinook";
  String GENRE_GENREID = "genreid";
  String GENRE_NAME = "name";
 
  String T_MEDIATYPE = "mediatype@chinook";
  String MEDIATYPE_MEDIATYPEID = "mediatypeid";
  String MEDIATYPE_NAME = "name";
 
  String T_TRACK = "track@chinook";
  String TRACK_TRACKID = "trackid";
  String TRACK_NAME = "name";
  String TRACK_ARTIST_DENORM = "artist_denorm";
  String TRACK_ALBUMID = "albumid";
  String TRACK_ALBUMID_FK = "albumid_fk";
  String TRACK_MEDIATYPEID = "mediatypeid";
  String TRACK_MEDIATYPEID_FK = "mediatypeid_fk";
  String TRACK_GENREID = "genreid";
  String TRACK_GENREID_FK = "genreid_fk";
  String TRACK_COMPOSER = "composer";
  String TRACK_MILLISECONDS = "milliseconds";
  String TRACK_MINUTES_SECONDS_DERIVED = "minutes_seconds_transient";
  String TRACK_BYTES = "bytes";
  String TRACK_UNITPRICE = "unitprice";
 
  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";
          };
 
  String T_INVOICE = "invoice@chinook";
  String INVOICE_INVOICEID = "invoiceid";
  String INVOICE_INVOICEID_AS_STRING = "invoiceid || ''";
  String INVOICE_CUSTOMERID = "customerid";
  String INVOICE_CUSTOMERID_FK = "customerid_fk";
  String INVOICE_INVOICEDATE = "invoicedate";
  String INVOICE_BILLINGADDRESS = "billingaddress";
  String INVOICE_BILLINGCITY = "billingcity";
  String INVOICE_BILLINGSTATE = "billingstate";
  String INVOICE_BILLINGCOUNTRY = "billingcountry";
  String INVOICE_BILLINGPOSTALCODE = "billingpostalcode";
  String INVOICE_TOTAL = "total";
  String INVOICE_TOTAL_SUB = "total_sub";
 
  String T_INVOICELINE = "invoiceline@chinook";
  String INVOICELINE_INVOICELINEID = "invoicelineid";
  String INVOICELINE_INVOICEID = "invoiceid";
  String INVOICELINE_INVOICEID_FK = "invoiceid_fk";
  String INVOICELINE_TRACKID = "trackid";
  String INVOICELINE_TRACKID_FK = "trackid_fk";
  String INVOICELINE_UNITPRICE = "unitprice";
  String INVOICELINE_QUANTITY = "quantity";
  String INVOICELINE_TOTAL = "total";
 
  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;
          };
 
  String T_PLAYLIST = "playlist@chinook";
  String PLAYLIST_PLAYLISTID = "playlistid";
  String PLAYLIST_NAME = "name";
 
  String T_PLAYLISTTRACK = "playlisttrack@chinook";
  String PLAYLISTTRACK_PLAYLISTID = "playlistid";
  String PLAYLISTTRACK_PLAYLISTID_FK = "playlistid_fk";
  String PLAYLISTTRACK_TRACKID = "trackid";
  String PLAYLISTTRACK_TRACKID_FK = "trackid_fk";
  String PLAYLISTTRACK_ALBUM_DENORM = "album_denorm";
  String PLAYLISTTRACK_ARTIST_DENORM = "artist_denorm";
 
  String P_UPDATE_TOTALS = "chinook.update_totals_procedure";
}

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

/*
 * Copyright (c) 2004 - 2018, Björn Darri Sigurðsson. All Rights Reserved.
 */
package org.jminor.framework.demos.chinook.domain;
 
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.domain.Entities;
import org.jminor.framework.domain.Entity;
import org.jminor.framework.domain.Properties;
 
import java.sql.Types;
import java.text.NumberFormat;
import java.util.List;
 
import static org.jminor.framework.demos.chinook.domain.Chinook.*;
 
public final class ChinookDomain extends Entities {
 
  public ChinookDomain() {
    artist();
    album();
    employee();
    customer();
    genre();
    mediaType();
    track();
    invoice();
    invoiceLine();
    playlist();
    playlistTrack();
    dbOperations();
  }
 
  void artist() {
    define(T_ARTIST, "chinook.artist",
            Properties.primaryKeyProperty(ARTIST_ARTISTID, Types.BIGINT),
            Properties.columnProperty(ARTIST_NAME, Types.VARCHAR, "Name")
                    .setNullable(false)
                    .setMaxLength(120)
                    .setPreferredColumnWidth(160))
            .setKeyGenerator(automaticKeyGenerator("chinook.artist"))
            .setOrderBy(orderBy().ascending(ARTIST_NAME))
            .setStringProvider(new Entities.StringProvider(ARTIST_NAME))
            .setSearchPropertyIds(ARTIST_NAME)
            .setCaption("Artists");
  }
 
  void album() {
    define(T_ALBUM, "chinook.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))
            .setKeyGenerator(automaticKeyGenerator("chinook.album"))
            .setOrderBy(orderBy().ascending(ALBUM_ARTISTID, ALBUM_TITLE))
            .setStringProvider(new Entities.StringProvider(ALBUM_TITLE))
            .setSearchPropertyIds(ALBUM_TITLE)
            .setCaption("Albums");
  }
 
  void employee() {
    define(T_EMPLOYEE, "chinook.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))
            .setKeyGenerator(automaticKeyGenerator("chinook.employee"))
            .setOrderBy(orderBy().ascending(EMPLOYEE_LASTNAME, EMPLOYEE_FIRSTNAME))
            .setStringProvider(new Entities.StringProvider(EMPLOYEE_LASTNAME)
                    .addText(", ").addValue(EMPLOYEE_FIRSTNAME))
            .setSearchPropertyIds(EMPLOYEE_FIRSTNAME, EMPLOYEE_LASTNAME, EMPLOYEE_EMAIL)
            .setCaption("Employees");
  }
 
  void customer() {
    define(T_CUSTOMER, "chinook.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)))
            .setKeyGenerator(automaticKeyGenerator("chinook.customer"))
            .setOrderBy(orderBy().ascending(CUSTOMER_LASTNAME, CUSTOMER_FIRSTNAME))
            .setStringProvider(new Entities.StringProvider(CUSTOMER_LASTNAME)
                    .addText(", ").addValue(CUSTOMER_FIRSTNAME))
            .setSearchPropertyIds(CUSTOMER_FIRSTNAME, CUSTOMER_LASTNAME, CUSTOMER_EMAIL)
            .setCaption("Customers");
  }
 
  void genre() {
    define(T_GENRE, "chinook.genre",
            Properties.primaryKeyProperty(GENRE_GENREID, Types.BIGINT),
            Properties.columnProperty(GENRE_NAME, Types.VARCHAR, "Name")
                    .setNullable(false)
                    .setMaxLength(120)
                    .setPreferredColumnWidth(160))
            .setKeyGenerator(automaticKeyGenerator("chinook.genre"))
            .setOrderBy(orderBy().ascending(GENRE_NAME))
            .setStringProvider(new Entities.StringProvider(GENRE_NAME))
            .setSearchPropertyIds(GENRE_NAME)
            .setSmallDataset(true)
            .setCaption("Genres");
  }
 
  void mediaType() {
    define(T_MEDIATYPE, "chinook.mediatype",
            Properties.primaryKeyProperty(MEDIATYPE_MEDIATYPEID, Types.BIGINT),
            Properties.columnProperty(MEDIATYPE_NAME, Types.VARCHAR, "Name")
                    .setNullable(false)
                    .setMaxLength(120)
                    .setPreferredColumnWidth(160))
            .setKeyGenerator(automaticKeyGenerator("chinook.mediatype"))
            .setStringProvider(new Entities.StringProvider(MEDIATYPE_NAME))
            .setSmallDataset(true)
            .setCaption("Media types");
  }
 
  void track() {
    define(T_TRACK, "chinook.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))
            .setKeyGenerator(automaticKeyGenerator("chinook.track"))
            .setOrderBy(orderBy().ascending(TRACK_NAME))
            .setStringProvider(new Entities.StringProvider(TRACK_NAME))
            .setSearchPropertyIds(TRACK_NAME)
            .setCaption("Tracks");
  }
 
  private static final String INVOICE_TOTAL_SUBQUERY = "select sum(unitprice * quantity) from chinook.invoiceline where invoiceid = invoice.invoiceid";
 
  void invoice() {
    define(T_INVOICE, "chinook.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))
            .setKeyGenerator(automaticKeyGenerator("chinook.invoice"))
            .setOrderBy(orderBy().ascending(INVOICE_CUSTOMERID).descending(INVOICE_INVOICEDATE))
            .setStringProvider(new Entities.StringProvider(INVOICE_INVOICEID))
            .setSearchPropertyIds(INVOICE_INVOICEID_AS_STRING)
            .setCaption("Invoices");
  }
 
  void invoiceLine() {
    define(T_INVOICELINE, "chinook.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))
            .setKeyGenerator(automaticKeyGenerator("chinook.invoiceline"))
            .setCaption("Invoice lines");
  }
 
  void playlist() {
    define(T_PLAYLIST, "chinook.playlist",
            Properties.primaryKeyProperty(PLAYLIST_PLAYLISTID, Types.BIGINT),
            Properties.columnProperty(PLAYLIST_NAME, Types.VARCHAR, "Name")
                    .setNullable(false)
                    .setMaxLength(120)
                    .setPreferredColumnWidth(160))
            .setKeyGenerator(automaticKeyGenerator("chinook.playlist"))
            .setOrderBy(orderBy().ascending(PLAYLIST_NAME))
            .setStringProvider(new Entities.StringProvider(PLAYLIST_NAME))
            .setSearchPropertyIds(PLAYLIST_NAME)
            .setCaption("Playlists");
  }
 
  void playlistTrack() {
    define(T_PLAYLISTTRACK, "chinook.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");
  }
 
  void dbOperations() {
    addOperation(new UpdateTotalsProcedure(P_UPDATE_TOTALS));
  }
 
  private static final class UpdateTotalsProcedure extends AbstractProcedure<LocalEntityConnection> {
 
    private 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 - 2018, 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.testing.EntityTestUnit;
 
import org.junit.Test;
 
import static org.jminor.framework.demos.chinook.domain.Chinook.*;
 
public class ChinookTest extends EntityTestUnit {
 
  private static final User UNIT_TEST_USER = new User(
          System.getProperty("jminor.unittest.username", "scott"),
          System.getProperty("jminor.unittest.password", "tiger").toCharArray());
 
  public ChinookTest() {
    super(ChinookDomain.class.getName());
  }
 
  @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 - 2018, 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 - 2018, 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 - 2018, 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 - 2018, 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 - 2018, 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 - 2018, 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 - 2018, 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 - 2018, 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 - 2018, 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 - 2018, 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 - 2018, 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().getDomain().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().getDomain().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 - 2018, 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.common.model.table.ColumnConditionModel;
import org.jminor.common.model.table.DefaultColumnConditionModel;
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 javax.swing.SwingUtilities;
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> {
 
  /* ARTIST
    *   ALBUM
    *     TRACK
    * PLAYLIST
    *   PLAYLISTTRACK
    * CUSTOMER
    *   INVOICE
    *     INVOICELINE
    */
  @Override
  protected void setupEntityPanelProviders() {
    final Entities domain = getModel().getDomain();
    final EntityPanelProvider trackProvider = new EntityPanelProvider(T_TRACK, domain.getCaption(T_TRACK));
    trackProvider.setEditPanelClass(TrackEditPanel.class);
 
    final EntityPanelProvider customerProvider = new EntityPanelProvider(T_CUSTOMER, domain.getCaption(T_CUSTOMER));
    customerProvider.setEditPanelClass(CustomerEditPanel.class);
    customerProvider.setTablePanelClass(CustomerTablePanel.class);
 
    final EntityPanelProvider genreProvider = new EntityPanelProvider(T_GENRE, domain.getCaption(T_GENRE));
    genreProvider.setEditPanelClass(GenreEditPanel.class);
    genreProvider.addDetailPanelProvider(trackProvider).setDetailPanelState(EntityPanel.PanelState.HIDDEN);
 
    final EntityPanelProvider mediaTypeProvider = new EntityPanelProvider(T_MEDIATYPE, domain.getCaption(T_MEDIATYPE));
    mediaTypeProvider.setEditPanelClass(MediaTypeEditPanel.class);
    mediaTypeProvider.addDetailPanelProvider(trackProvider).setDetailPanelState(EntityPanel.PanelState.HIDDEN);
 
    final EntityPanelProvider employeeProvider = new EntityPanelProvider(T_EMPLOYEE, domain.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().getDomain().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);
    DefaultColumnConditionModel.AUTOMATIC_WILDCARD.set(ColumnConditionModel.AutomaticWildcard.POSTFIX);
    DefaultColumnConditionModel.CASE_SENSITIVE.set(false);
    EntityConnectionProvider.CLIENT_DOMAIN_CLASS.set("org.jminor.framework.demos.chinook.domain.ChinookDomain");
    SwingUtilities.invokeLater(() -> new ChinookAppPanel().startApplication("ChinookDomain", null, false,
          UiUtil.getScreenSizeRatio(0.6), new User("scott", "tiger".toCharArray())));
  }
 
  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 - 2018, 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 User UNIT_TEST_USER = new User(
          System.getProperty("jminor.unittest.username", "scott"),
          System.getProperty("jminor.unittest.password", "tiger").toCharArray());
 
  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.getDomain().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("org.jminor.framework.demos.chinook.domain.ChinookDomain",
                    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: 2018/01/25 16:43 by darri