1. Domain

package org.jminor.framework.demos.petstore.domain;

import org.jminor.framework.domain.Domain;
import org.jminor.framework.domain.entity.StringProvider;

import java.sql.Types;

import static org.jminor.framework.domain.entity.KeyGenerators.increment;
import static org.jminor.framework.domain.entity.OrderBy.orderBy;
import static org.jminor.framework.domain.property.Properties.*;

public final class Petstore extends Domain {

  public Petstore() {
    address();
    category();
    product();
    sellerContactInfo();
    item();
    tag();
    tagItem();
  }

  public static final String T_ADDRESS = "address";
  public static final String ADDRESS_ID = "Address id";
  public static final String ADDRESS_STREET_1 = "Street 1";
  public static final String ADDRESS_STREET_2 = "Street 2";
  public static final String ADDRESS_CITY = "City";
  public static final String ADDRESS_STATE = "State";
  public static final String ADDRESS_ZIP = "Zip";
  public static final String ADDRESS_LATITUDE = "Latitude";
  public static final String ADDRESS_LONGITUDE = "Longitude";

  void address() {
    define(T_ADDRESS, "petstore.address",
            primaryKeyProperty(ADDRESS_ID)
                    .columnName("addressid"),
            columnProperty(ADDRESS_STREET_1, Types.VARCHAR, ADDRESS_STREET_1)
                    .columnName("street1").maximumLength(55).nullable(false),
            columnProperty(ADDRESS_STREET_2, Types.VARCHAR, ADDRESS_STREET_2)
                    .columnName("street2").maximumLength(55),
            columnProperty(ADDRESS_CITY, Types.VARCHAR, ADDRESS_CITY)
                    .columnName("city").maximumLength(55).nullable(false),
            columnProperty(ADDRESS_STATE, Types.VARCHAR, ADDRESS_STATE)
                    .columnName("state").maximumLength(25).nullable(false),
            columnProperty(ADDRESS_ZIP, Types.INTEGER, ADDRESS_ZIP)
                    .columnName("zip").nullable(false),
            columnProperty(ADDRESS_LATITUDE, Types.DOUBLE, ADDRESS_LATITUDE)
                    .columnName("latitude").nullable(false).maximumFractionDigits(2),
            columnProperty(ADDRESS_LONGITUDE, Types.DOUBLE, ADDRESS_LONGITUDE)
                    .columnName("longitude").nullable(false).maximumFractionDigits(2))
            .keyGenerator(increment("petstore.address", "addressid"))
            .orderBy(orderBy().ascending(ADDRESS_CITY, ADDRESS_STREET_1, ADDRESS_STREET_2))
            .stringProvider(new StringProvider(ADDRESS_STREET_1).addText(" ")
                    .addValue(ADDRESS_STREET_2).addText(", ").addValue(ADDRESS_CITY).addText(" ")
                    .addValue(ADDRESS_ZIP).addText(", ").addValue(ADDRESS_STATE))
            .caption("Addresses");
  }

  public static final String T_CATEGORY = "category";
  public static final String CATEGORY_ID = "Category id";
  public static final String CATEGORY_NAME = "Name";
  public static final String CATEGORY_DESCRIPTION = "Description";
  public static final String CATEGORY_IMAGE_URL = "Image URL";

  void category() {
    define(T_CATEGORY, "petstore.category",
            primaryKeyProperty(CATEGORY_ID)
                    .columnName("categoryid"),
            columnProperty(CATEGORY_NAME, Types.VARCHAR, CATEGORY_NAME)
                    .columnName("name").maximumLength(25).nullable(false),
            columnProperty(CATEGORY_DESCRIPTION, Types.VARCHAR, CATEGORY_DESCRIPTION)
                    .columnName("description").maximumLength(255).nullable(false),
            columnProperty(CATEGORY_IMAGE_URL, Types.VARCHAR, CATEGORY_IMAGE_URL)
                    .columnName("imageurl").hidden(true))
            .keyGenerator(increment("petstore.category", "categoryid"))
            .orderBy(orderBy().ascending(CATEGORY_NAME))
            .stringProvider(new StringProvider(CATEGORY_NAME))
            .caption("Categories");
  }

  public static final String T_PRODUCT = "product";
  public static final String PRODUCT_ID = "Product id";
  public static final String PRODUCT_CATEGORY_ID = "Category id";
  public static final String PRODUCT_CATEGORY_FK = "Category";
  public static final String PRODUCT_NAME = "Name";
  public static final String PRODUCT_DESCRIPTION = "Description";
  public static final String PRODUCT_IMAGE_URL = "Image URL";

  void product() {
    define(T_PRODUCT, "petstore.product",
            primaryKeyProperty(PRODUCT_ID)
                    .columnName("productid"),
            foreignKeyProperty(PRODUCT_CATEGORY_FK, PRODUCT_CATEGORY_FK, T_CATEGORY,
                    columnProperty(PRODUCT_CATEGORY_ID)
                            .columnName("categoryid")).nullable(false),
            columnProperty(PRODUCT_NAME, Types.VARCHAR, PRODUCT_NAME)
                    .columnName("name").maximumLength(25).nullable(false),
            columnProperty(PRODUCT_DESCRIPTION, Types.VARCHAR, PRODUCT_DESCRIPTION)
                    .columnName("description").maximumLength(255).nullable(false),
            columnProperty(PRODUCT_IMAGE_URL, Types.VARCHAR, PRODUCT_IMAGE_URL)
                    .columnName("imageurl").maximumLength(55).hidden(true))
            .keyGenerator(increment("petstore.product", "productid"))
            .orderBy(orderBy().ascending(PRODUCT_NAME))
            .stringProvider(new StringProvider(PRODUCT_CATEGORY_FK)
                    .addText(" - ").addValue(PRODUCT_NAME))
            .caption("Products");
  }

  public static final String T_SELLER_CONTACT_INFO = "sellercontactinfo";
  public static final String SELLER_CONTACT_INFO_ID = "Contactinfo id";
  public static final String SELLER_CONTACT_INFO_FIRST_NAME = "First name";
  public static final String SELLER_CONTACT_INFO_LAST_NAME = "Last name";
  public static final String SELLER_CONTACT_INFO_EMAIL = "Email";

  void sellerContactInfo() {
    define(T_SELLER_CONTACT_INFO, "petstore.sellercontactinfo",
            primaryKeyProperty(SELLER_CONTACT_INFO_ID)
                    .columnName("contactinfoid"),
            columnProperty(SELLER_CONTACT_INFO_FIRST_NAME, Types.VARCHAR, SELLER_CONTACT_INFO_FIRST_NAME)
                    .columnName("firstname").maximumLength(24).nullable(false),
            columnProperty(SELLER_CONTACT_INFO_LAST_NAME, Types.VARCHAR, SELLER_CONTACT_INFO_LAST_NAME)
                    .columnName("lastname").maximumLength(24).nullable(false),
            columnProperty(SELLER_CONTACT_INFO_EMAIL, Types.VARCHAR, SELLER_CONTACT_INFO_EMAIL)
                    .columnName("email").maximumLength(24).nullable(false))
            .keyGenerator(increment("petstore.sellercontactinfo", "contactinfoid"))
            .orderBy(orderBy()
                    .ascending(SELLER_CONTACT_INFO_LAST_NAME, SELLER_CONTACT_INFO_FIRST_NAME))
            .stringProvider(new StringProvider(SELLER_CONTACT_INFO_LAST_NAME)
                    .addText(", ").addValue(SELLER_CONTACT_INFO_FIRST_NAME))
            .searchPropertyIds(SELLER_CONTACT_INFO_LAST_NAME, SELLER_CONTACT_INFO_FIRST_NAME)
            .caption("Seller info");
  }

  public static final String T_ITEM = "item";
  public static final String ITEM_ID = "Item id";
  public static final String ITEM_PRODUCT_ID = "Product id";
  public static final String ITEM_PRODUCT_FK = "Product";
  public static final String ITEM_NAME = "Name";
  public static final String ITEM_DESCRIPTION = "Description";
  public static final String ITEM_IMAGE_URL = "Image URL";
  public static final String ITEM_IMAGE_THUMB_URL = "Image thumbnail URL";
  public static final String ITEM_PRICE = "Price";
  public static final String ITEM_C0NTACT_INFO_ID = "Contactinfo id";
  public static final String ITEM_C0NTACT_INFO_FK = "Contact info";
  public static final String ITEM_ADDRESS_ID = "Address id";
  public static final String ITEM_ADDRESS_FK = "Address";
  public static final String ITEM_DISABLED = "Disabled";

  void item() {
    define(T_ITEM, "petstore.item",
            primaryKeyProperty(ITEM_ID)
                    .columnName("itemid"),
            foreignKeyProperty(ITEM_PRODUCT_FK, ITEM_PRODUCT_FK, T_PRODUCT,
                    columnProperty(ITEM_PRODUCT_ID)
                            .columnName("productid"))
                    .fetchDepth(2).nullable(false),
            columnProperty(ITEM_NAME, Types.VARCHAR, ITEM_NAME)
                    .columnName("name").maximumLength(30).nullable(false),
            columnProperty(ITEM_DESCRIPTION, Types.VARCHAR, ITEM_DESCRIPTION)
                    .columnName("description").maximumLength(500).nullable(false),
            columnProperty(ITEM_IMAGE_URL, Types.VARCHAR, ITEM_IMAGE_URL)
                    .columnName("imageurl").maximumLength(55).hidden(true),
            columnProperty(ITEM_IMAGE_THUMB_URL, Types.VARCHAR, ITEM_IMAGE_THUMB_URL)
                    .columnName("imagethumburl").maximumLength(55).hidden(true),
            columnProperty(ITEM_PRICE, Types.DECIMAL, ITEM_PRICE)
                    .columnName("price").nullable(false).maximumFractionDigits(2),
            foreignKeyProperty(ITEM_C0NTACT_INFO_FK, ITEM_C0NTACT_INFO_FK, T_SELLER_CONTACT_INFO,
                    columnProperty(ITEM_C0NTACT_INFO_ID).columnName("contactinfo_contactinfoid"))
                    .nullable(false),
            foreignKeyProperty(ITEM_ADDRESS_FK, "Address", T_ADDRESS,
                    columnProperty(ITEM_ADDRESS_ID).columnName("address_addressid"))
                    .nullable(false),
            booleanProperty(ITEM_DISABLED, Types.INTEGER, ITEM_DISABLED, 1, 0)
                    .columnName("disabled").defaultValue(false))
            .keyGenerator(increment("petstore.item", "itemid"))
            .orderBy(orderBy().ascending(ITEM_NAME))
            .stringProvider(new StringProvider(ITEM_PRODUCT_FK)
                    .addText(" - ").addValue(ITEM_NAME))
            .caption("Items");
  }

  public static final String T_TAG = "tag";
  public static final String TAG_ID = "Tag id";
  public static final String TAG_TAG = "Tag";
  public static final String TAG_REFCOUNT = "Reference count";

  void tag() {
    define(T_TAG, "petstore.tag",
            primaryKeyProperty(TAG_ID)
                    .columnName("tagid"),
            columnProperty(TAG_TAG, Types.VARCHAR, TAG_TAG)
                    .columnName("tag").maximumLength(30).nullable(false),
            subqueryProperty(TAG_REFCOUNT, Types.INTEGER, TAG_REFCOUNT,
                    "select count(*) from petstore.tag_item where tagid = tag.tagid")
                    .columnName("refcount"))
            .keyGenerator(increment("petstore.tag", "tagid"))
            .orderBy(orderBy().ascending(TAG_TAG))
            .selectTableName("petstore.tag tag")
            .stringProvider(new StringProvider(TAG_TAG))
            .caption("Tags");
  }

  public static final String T_TAG_ITEM = "tag_item";
  public static final String TAG_ITEM_ITEM_ID = "Item id";
  public static final String TAG_ITEM_ITEM_FK = "Item";
  public static final String TAG_ITEM_TAG_ID = "Tag id";
  public static final String TAG_ITEM_TAG_FK = "Tag";

  void tagItem() {
    define(T_TAG_ITEM, "petstore.tag_item",
            foreignKeyProperty(TAG_ITEM_ITEM_FK, TAG_ITEM_ITEM_FK, T_ITEM,
                    primaryKeyProperty(TAG_ITEM_ITEM_ID)
                            .columnName("itemid").primaryKeyIndex(0))
                    .nullable(false),
            foreignKeyProperty(TAG_ITEM_TAG_FK, TAG_ITEM_TAG_FK, T_TAG,
                    primaryKeyProperty(TAG_ITEM_TAG_ID)
                            .columnName("tagid").primaryKeyIndex(1))
                    .nullable(false))
            .stringProvider(new StringProvider(TAG_ITEM_ITEM_FK)
                    .addText(" - ").addValue(TAG_ITEM_TAG_FK))
            .caption("Item tags");
  }
}

1.1. Domain unit test

package org.jminor.framework.demos.petstore.domain;

import org.jminor.framework.domain.entity.test.EntityTestUnit;

import org.junit.jupiter.api.Test;

import static org.jminor.framework.demos.petstore.domain.Petstore.*;

public class PetstoreTest extends EntityTestUnit {

  public PetstoreTest() {
    super(Petstore.class.getName());
  }

  @Test
  public void address() throws Exception {
    test(T_ADDRESS);
  }

  @Test
  public void category() throws Exception {
    test(T_CATEGORY);
  }

  @Test
  public void item() throws Exception {
    test(T_ITEM);
  }

  @Test
  public void product() throws Exception {
    test(T_PRODUCT);
  }

  @Test
  public void sellerInfo() throws Exception {
    test(T_SELLER_CONTACT_INFO);
  }

  @Test
  public void tag() throws Exception {
    test(T_TAG);
  }

  @Test
  public void tagItem() throws Exception {
    test(T_TAG_ITEM);
  }
}

2. Model

2.1. Main application model

package org.jminor.framework.demos.petstore.model;

import org.jminor.framework.db.EntityConnectionProvider;
import org.jminor.swing.framework.model.SwingEntityApplicationModel;
import org.jminor.swing.framework.model.SwingEntityModel;

import static org.jminor.framework.demos.petstore.domain.Petstore.*;

public final class PetstoreAppModel extends SwingEntityApplicationModel {

  public PetstoreAppModel(final EntityConnectionProvider connectionProvider) {
    super(connectionProvider);
    final SwingEntityModel categoryModel = new SwingEntityModel(T_CATEGORY, connectionProvider);
    final SwingEntityModel productModel = new SwingEntityModel(T_PRODUCT, connectionProvider);
    final SwingEntityModel itemModel = new SwingEntityModel(T_ITEM, connectionProvider);
    final SwingEntityModel tagItemModel = new SwingEntityModel(T_TAG_ITEM, connectionProvider);
    itemModel.addDetailModels(tagItemModel);
    productModel.addDetailModels(itemModel);
    categoryModel.addDetailModels(productModel);
    addEntityModel(categoryModel);
  }
}

3. UI

package org.jminor.framework.demos.petstore.ui;

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.JLabel;

import static org.jminor.framework.demos.petstore.domain.Petstore.*;

public class AddressEditPanel extends EntityEditPanel {

  public AddressEditPanel(final SwingEntityEditModel model) {
    super(model);
  }

  @Override
  protected void initializeUI() {
    setInitialFocusProperty(ADDRESS_CITY);

    createTextField(ADDRESS_CITY).setColumns(12);
    createTextField(ADDRESS_STATE).setColumns(12);
    createTextField(ADDRESS_ZIP).setColumns(12);
    createTextField(ADDRESS_STREET_1).setColumns(12);
    createTextField(ADDRESS_STREET_2).setColumns(12);
    createTextField(ADDRESS_LATITUDE);
    createTextField(ADDRESS_LONGITUDE);

    setLayout(new FlexibleGridLayout(4, 2, 5, 5));
    addPropertyPanel(ADDRESS_CITY);
    addPropertyPanel(ADDRESS_STATE);
    add(new JLabel());
    addPropertyPanel(ADDRESS_ZIP);
    addPropertyPanel(ADDRESS_STREET_1);
    addPropertyPanel(ADDRESS_STREET_2);
    addPropertyPanel(ADDRESS_LATITUDE);
    addPropertyPanel(ADDRESS_LONGITUDE);
  }
}
package org.jminor.framework.demos.petstore.ui;

import org.jminor.swing.common.ui.layout.FlexibleGridLayout;
import org.jminor.swing.framework.model.SwingEntityEditModel;
import org.jminor.swing.framework.ui.EntityEditPanel;

import static org.jminor.framework.demos.petstore.domain.Petstore.*;

public class CategoryEditPanel extends EntityEditPanel {

  public CategoryEditPanel(final SwingEntityEditModel model) {
    super(model);
  }

  @Override
  protected void initializeUI() {
    setInitialFocusProperty(CATEGORY_NAME);

    createTextField(CATEGORY_NAME).setColumns(10);
    createTextField(CATEGORY_DESCRIPTION).setColumns(18);
    createTextField(CATEGORY_IMAGE_URL);

    setLayout(new FlexibleGridLayout(2, 2, 5, 5));
    addPropertyPanel(CATEGORY_NAME);
    addPropertyPanel(CATEGORY_DESCRIPTION);
    addPropertyPanel(CATEGORY_IMAGE_URL);
  }
}
package org.jminor.framework.demos.petstore.ui;

import org.jminor.swing.common.ui.layout.FlexibleGridLayout;
import org.jminor.swing.framework.model.SwingEntityEditModel;
import org.jminor.swing.framework.ui.EntityEditPanel;

import static org.jminor.framework.demos.petstore.domain.Petstore.*;

public class ContactInfoEditPanel extends EntityEditPanel {

  public ContactInfoEditPanel(final SwingEntityEditModel model) {
    super(model);
  }

  @Override
  protected void initializeUI() {
    setInitialFocusProperty(SELLER_CONTACT_INFO_LAST_NAME);

    createTextField(SELLER_CONTACT_INFO_LAST_NAME).setColumns(10);
    createTextField(SELLER_CONTACT_INFO_FIRST_NAME);
    createTextField(SELLER_CONTACT_INFO_EMAIL);

    setLayout(new FlexibleGridLayout(3, 1, 5, 5));
    addPropertyPanel(SELLER_CONTACT_INFO_LAST_NAME);
    addPropertyPanel(SELLER_CONTACT_INFO_FIRST_NAME);
    addPropertyPanel(SELLER_CONTACT_INFO_EMAIL);
  }
}
package org.jminor.framework.demos.petstore.ui;

import org.jminor.swing.common.ui.Components;
import org.jminor.swing.common.ui.layout.FlexibleGridLayout;
import org.jminor.swing.common.ui.textfield.TextFields;
import org.jminor.swing.common.ui.textfield.TextInputPanel;
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.EntityPanelBuilder;

import static org.jminor.framework.demos.petstore.domain.Petstore.*;

public class ItemEditPanel extends EntityEditPanel {

  public ItemEditPanel(final SwingEntityEditModel model) {
    super(model);
  }

  @Override
  protected void initializeUI() {
    setInitialFocusProperty(ITEM_PRODUCT_FK);

    createForeignKeyComboBox(ITEM_PRODUCT_FK);
    createTextField(ITEM_NAME).setColumns(12);
    final TextInputPanel descriptionPanel = createTextInputPanel(ITEM_DESCRIPTION);
    descriptionPanel.getTextField().setColumns(14);
    descriptionPanel.getButton().setFocusable(false);
    createTextField(ITEM_PRICE);
    final EntityComboBox contactInfoBox = createForeignKeyComboBox(ITEM_C0NTACT_INFO_FK);
    Components.setPreferredWidth(contactInfoBox, 140);
    contactInfoBox.setPopupWidth(200);
    final EntityComboBox addressBox = createForeignKeyComboBox(ITEM_ADDRESS_FK);
    Components.setPreferredWidth(addressBox, 140);
    addressBox.setPopupWidth(200);
    addressBox.setPreferredSize(TextFields.getPreferredTextFieldSize());
    createTextField(ITEM_IMAGE_URL).setColumns(14);
    createTextField(ITEM_IMAGE_THUMB_URL).setColumns(14);
    createNullableCheckBox(ITEM_DISABLED, null, false);

    setLayout(new FlexibleGridLayout(3, 3, 5, 5));
    addPropertyPanel(ITEM_PRODUCT_FK);
    addPropertyPanel(ITEM_NAME);
    add(createPropertyPanel(ITEM_DESCRIPTION));
    addPropertyPanel(ITEM_PRICE);
    add(createPropertyPanel(ITEM_C0NTACT_INFO_FK, Components.createEastButtonPanel(contactInfoBox,
            createEditPanelAction(contactInfoBox, new EntityPanelBuilder(T_SELLER_CONTACT_INFO)
                    .setEditPanelClass(ContactInfoEditPanel.class)), false)));
    add(createPropertyPanel(ITEM_ADDRESS_FK, Components.createEastButtonPanel(addressBox,
            createEditPanelAction(addressBox, new EntityPanelBuilder(T_ADDRESS)
                    .setEditPanelClass(AddressEditPanel.class)), false)));
    addPropertyPanel(ITEM_IMAGE_URL);
    addPropertyPanel(ITEM_IMAGE_THUMB_URL);
    addPropertyPanel(ITEM_DISABLED);
  }
}
package org.jminor.framework.demos.petstore.ui;

import org.jminor.swing.common.ui.layout.FlexibleGridLayout;
import org.jminor.swing.framework.model.SwingEntityEditModel;
import org.jminor.swing.framework.ui.EntityEditPanel;

import static org.jminor.framework.demos.petstore.domain.Petstore.*;

public class ProductEditPanel extends EntityEditPanel {

  public ProductEditPanel(final SwingEntityEditModel model) {
    super(model);
  }

  @Override
  protected void initializeUI() {
    setInitialFocusProperty(PRODUCT_CATEGORY_FK);

    createForeignKeyComboBox(PRODUCT_CATEGORY_FK);
    createTextField(PRODUCT_NAME);
    createTextField(PRODUCT_DESCRIPTION).setColumns(16);

    setLayout(new FlexibleGridLayout(3, 1, 5, 5));
    addPropertyPanel(PRODUCT_CATEGORY_FK);
    addPropertyPanel(PRODUCT_NAME);
    addPropertyPanel(PRODUCT_DESCRIPTION);
  }
}
package org.jminor.framework.demos.petstore.ui;

import org.jminor.framework.demos.petstore.domain.Petstore;
import org.jminor.swing.common.ui.Components;
import org.jminor.swing.common.ui.layout.FlexibleGridLayout;
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.EntityPanelBuilder;

import static org.jminor.framework.demos.petstore.domain.Petstore.TAG_ITEM_ITEM_FK;
import static org.jminor.framework.demos.petstore.domain.Petstore.TAG_ITEM_TAG_FK;

public class TagItemEditPanel extends EntityEditPanel {

  public TagItemEditPanel(final SwingEntityEditModel model) {
    super(model);
  }

  @Override
  protected void initializeUI() {
    setLayout(new FlexibleGridLayout(2, 1, 5, 5));
    final EntityComboBox itemBox = createForeignKeyComboBox(TAG_ITEM_ITEM_FK);
    setInitialFocusComponent(itemBox);
    itemBox.setPopupWidth(240);
    Components.setPreferredWidth(itemBox, 180);
    addPropertyPanel(TAG_ITEM_ITEM_FK);
    final EntityComboBox itemTagBox = createForeignKeyComboBox(TAG_ITEM_TAG_FK);
    add(createPropertyPanel(TAG_ITEM_TAG_FK, Components.createEastButtonPanel(itemTagBox,
            createEditPanelAction(itemTagBox, new EntityPanelBuilder(Petstore.T_TAG)
                    .setEditPanelClass(TagEditPanel.class)), false)));
  }
}
package org.jminor.framework.demos.petstore.ui;

import org.jminor.swing.common.ui.layout.FlexibleGridLayout;
import org.jminor.swing.framework.model.SwingEntityEditModel;
import org.jminor.swing.framework.ui.EntityEditPanel;

import static org.jminor.framework.demos.petstore.domain.Petstore.TAG_TAG;

public class TagEditPanel extends EntityEditPanel {

  public TagEditPanel(final SwingEntityEditModel model) {
    super(model);
  }

  @Override
  protected void initializeUI() {
    setInitialFocusProperty(TAG_TAG);

    createTextField(TAG_TAG).setColumns(16);

    setLayout(new FlexibleGridLayout(1, 1, 5, 5));
    addPropertyPanel(TAG_TAG);
  }
}

4. Main application panel

package org.jminor.framework.demos.petstore.ui;

import org.jminor.common.model.CancelException;
import org.jminor.common.user.Users;
import org.jminor.framework.db.EntityConnectionProvider;
import org.jminor.framework.demos.petstore.model.PetstoreAppModel;
import org.jminor.swing.common.ui.Windows;
import org.jminor.swing.framework.ui.EntityApplicationPanel;
import org.jminor.swing.framework.ui.EntityPanel;
import org.jminor.swing.framework.ui.EntityPanelBuilder;

import java.util.Locale;

import static org.jminor.framework.demos.petstore.domain.Petstore.*;

public final class PetstoreAppPanel extends EntityApplicationPanel<PetstoreAppModel> {

  @Override
  protected void setupEntityPanelBuilders() {
    /* CATEGORY
     *   PRODUCT
     *     ITEM
     *       ITEMTAG
     */
    final EntityPanelBuilder tagItemProvider = new EntityPanelBuilder(T_TAG_ITEM)
            .setEditPanelClass(TagItemEditPanel.class);
    final EntityPanelBuilder itemProvider = new EntityPanelBuilder(T_ITEM)
            .setEditPanelClass(ItemEditPanel.class);
    itemProvider.addDetailPanelBuilder(tagItemProvider).setDetailPanelState(EntityPanel.PanelState.HIDDEN);
    final EntityPanelBuilder productProvider = new EntityPanelBuilder(T_PRODUCT)
            .setEditPanelClass(ProductEditPanel.class);
    productProvider.addDetailPanelBuilder(itemProvider).setDetailSplitPanelResizeWeight(0.3);
    final EntityPanelBuilder categoryProvider = new EntityPanelBuilder(T_CATEGORY)
            .setEditPanelClass(CategoryEditPanel.class);
    categoryProvider.addDetailPanelBuilder(productProvider).setDetailSplitPanelResizeWeight(0.3);

    addEntityPanelBuilder(categoryProvider);

    final EntityPanelBuilder addressProvider = new EntityPanelBuilder(T_ADDRESS)
            .setEditPanelClass(AddressEditPanel.class);
    final EntityPanelBuilder contactInfoProvider = new EntityPanelBuilder(T_SELLER_CONTACT_INFO)
            .setEditPanelClass(ContactInfoEditPanel.class);
    contactInfoProvider.addDetailPanelBuilder(itemProvider);
    final EntityPanelBuilder tagProvider = new EntityPanelBuilder(T_TAG)
            .setEditPanelClass(TagEditPanel.class);
    tagProvider.addDetailPanelBuilder(tagItemProvider).setDetailPanelState(EntityPanel.PanelState.HIDDEN);

    addSupportPanelBuilders(addressProvider, contactInfoProvider, tagProvider);
  }

  @Override
  protected PetstoreAppModel initializeApplicationModel(final EntityConnectionProvider connectionProvider)
          throws CancelException {
    return new PetstoreAppModel(connectionProvider);
  }

  public static void main(final String[] args) {
    Locale.setDefault(new Locale("en"));
    EntityPanel.TOOLBAR_BUTTONS.set(true);
    EntityConnectionProvider.CLIENT_DOMAIN_CLASS.set("org.jminor.framework.demos.petstore.domain.Petstore");
    new PetstoreAppPanel().startApplication("The Pet Store", null, false,
            Windows.getScreenSizeRatio(0.8), Users.parseUser("scott:tiger"));
  }
}

5. Load test

package org.jminor.framework.demos.petstore.testing;

import org.jminor.common.model.CancelException;
import org.jminor.common.user.User;
import org.jminor.common.user.Users;
import org.jminor.framework.db.EntityConnectionProviders;
import org.jminor.framework.demos.petstore.domain.Petstore;
import org.jminor.framework.demos.petstore.model.PetstoreAppModel;
import org.jminor.swing.common.tools.ui.LoadTestPanel;
import org.jminor.swing.framework.model.SwingEntityModel;
import org.jminor.swing.framework.tools.EntityLoadTestModel;

import javax.swing.SwingUtilities;
import javax.swing.UIManager;

import static java.util.Collections.singletonList;

public final class PetstoreLoadTest extends EntityLoadTestModel<PetstoreAppModel> {

  private static final User UNIT_TEST_USER =
          Users.parseUser(System.getProperty("jminor.test.user", "scott:tiger"));

  public PetstoreLoadTest() {
    super(UNIT_TEST_USER, singletonList(new AbstractUsageScenario<PetstoreAppModel>("selectRecords") {
      @Override
      protected void performScenario(final PetstoreAppModel application) {
        final SwingEntityModel categoryModel = application.getEntityModels().iterator().next();
        categoryModel.getTableModel().getSelectionModel().clearSelection();
        categoryModel.refresh();
        selectRandomRow(categoryModel.getTableModel());
        selectRandomRow(categoryModel.getDetailModels().iterator().next().getTableModel());
        selectRandomRow(categoryModel.getDetailModels().iterator().next().getDetailModels().iterator().next().getTableModel());
      }
    }));
  }

  @Override
  protected PetstoreAppModel initializeApplication() throws CancelException {
    final PetstoreAppModel applicationModel = new PetstoreAppModel(
            EntityConnectionProviders.connectionProvider().setDomainClassName(Petstore.class.getName())
                    .setClientTypeId(getClass().getSimpleName()).setUser(getUser()));
    final SwingEntityModel categoryModel = applicationModel.getEntityModels().iterator().next();
    categoryModel.addLinkedDetailModel(categoryModel.getDetailModels().iterator().next());
    final SwingEntityModel productModel = categoryModel.getDetailModels().iterator().next();
    productModel.addLinkedDetailModel(productModel.getDetailModels().iterator().next());
    final SwingEntityModel itemModel = productModel.getDetailModels().iterator().next();
    itemModel.addLinkedDetailModel(itemModel.getDetailModels().iterator().next());

    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 PetstoreLoadTest()).showFrame();
      }
      catch (final Exception e) {
        e.printStackTrace();
      }
    }
  }
}