Below you’ll find a full (albeit simple) CRUD client for two tables, chinook.artist and chinook.album, with a master detail relationship.
package is.codion.demos.chinook.tutorial;
import is.codion.common.db.database.Database;
import is.codion.common.user.User;
import is.codion.common.version.Version;
import is.codion.framework.db.EntityConnectionProvider;
import is.codion.framework.db.local.LocalEntityConnectionProvider;
import is.codion.framework.domain.DomainModel;
import is.codion.framework.domain.DomainType;
import is.codion.framework.domain.entity.EntityType;
import is.codion.framework.domain.entity.StringFactory;
import is.codion.framework.domain.entity.attribute.Column;
import is.codion.framework.domain.entity.attribute.ForeignKey;
import is.codion.swing.common.ui.component.table.FilterTable;
import is.codion.swing.common.ui.laf.LookAndFeelProvider;
import is.codion.swing.framework.model.SwingEntityApplicationModel;
import is.codion.swing.framework.model.SwingEntityEditModel;
import is.codion.swing.framework.model.SwingEntityModel;
import is.codion.swing.framework.ui.EntityApplicationPanel;
import is.codion.swing.framework.ui.EntityApplicationPanel.Builder.ConnectionProviderFactory;
import is.codion.swing.framework.ui.EntityEditPanel;
import is.codion.swing.framework.ui.EntityPanel;
import com.formdev.flatlaf.intellijthemes.FlatAllIJThemes;
import javax.swing.JTable;
import java.util.Arrays;
import java.util.List;
import static is.codion.demos.chinook.tutorial.ClientTutorial.Chinook.Album;
import static is.codion.demos.chinook.tutorial.ClientTutorial.Chinook.Artist;
import static is.codion.framework.domain.DomainType.domainType;
import static is.codion.framework.domain.entity.KeyGenerator.automatic;
import static is.codion.swing.common.ui.layout.Layouts.gridLayout;
import static is.codion.swing.framework.ui.EntityEditPanel.ControlKeys.INSERT;
/**
* When running this make sure the chinook demo module directory is the
* working directory, due to a relative path to a db init script
*/
public final class ClientTutorial {
public static final class Chinook extends DomainModel {
static final DomainType DOMAIN = domainType(Chinook.class);
public interface Artist {
EntityType TYPE = DOMAIN.entityType("chinook.artist");
Column<Integer> ID = TYPE.integerColumn("artistid");
Column<String> NAME = TYPE.stringColumn("name");
Column<Integer> NUMBER_OF_ALBUMS = TYPE.integerColumn("number_of_albums");
}
public interface Album {
EntityType TYPE = DOMAIN.entityType("chinook.album");
Column<Integer> ID = TYPE.integerColumn("albumid");
Column<String> TITLE = TYPE.stringColumn("title");
Column<Integer> ARTIST_ID = TYPE.integerColumn("artistid");
ForeignKey ARTIST_FK = TYPE.foreignKey("artist_fk", ARTIST_ID, Artist.ID);
}
public Chinook() {
super(DOMAIN);
add(Artist.TYPE.define(
Artist.ID.define()
.primaryKey(),
Artist.NAME.define()
.column()
.caption("Name")
.searchable(true)
.nullable(false)
.maximumLength(120),
Artist.NUMBER_OF_ALBUMS.define()
.subquery("SELECT COUNT(*) FROM chinook.album " +
"WHERE album.artistid = artist.artistid")
.caption("Albums"))
.keyGenerator(automatic("chinook.artist"))
.stringFactory(Artist.NAME)
.caption("Artists")
.build());
add(Album.TYPE.define(
Album.ID.define()
.primaryKey(),
Album.ARTIST_ID.define()
.column()
.nullable(false),
Album.ARTIST_FK.define()
.foreignKey()
.caption("Artist"),
Album.TITLE.define()
.column()
.caption("Title")
.nullable(false)
.maximumLength(160))
.keyGenerator(automatic("chinook.artist"))
.stringFactory(StringFactory.builder()
.value(Album.ARTIST_FK)
.text(" - ")
.value(Album.TITLE)
.build())
.caption("Albums")
.build());
}
}
private static final class ArtistEditPanel extends EntityEditPanel {
private ArtistEditPanel(SwingEntityEditModel editModel) {
super(editModel);
}
@Override
protected void initializeUI() {
initialFocusAttribute().set(Artist.NAME);
createTextField(Artist.NAME)
.columns(15);
addInputPanel(Artist.NAME);
}
}
private static final class AlbumEditPanel extends EntityEditPanel {
private AlbumEditPanel(SwingEntityEditModel editModel) {
super(editModel);
}
@Override
protected void initializeUI() {
initialFocusAttribute().set(Album.ARTIST_FK);
createForeignKeySearchField(Album.ARTIST_FK)
.columns(15);
createTextField(Album.TITLE)
.action(control(INSERT).get())
.columns(15);
setLayout(gridLayout(2, 1));
addInputPanel(Album.ARTIST_FK);
addInputPanel(Album.TITLE);
}
}
private static final class ApplicationModel extends SwingEntityApplicationModel {
private ApplicationModel(EntityConnectionProvider connectionProvider) {
super(connectionProvider);
SwingEntityModel artistModel = new SwingEntityModel(Artist.TYPE, connectionProvider);
SwingEntityModel albumModel = new SwingEntityModel(Album.TYPE, connectionProvider);
artistModel.addDetailModel(albumModel);
artistModel.tableModel().refresh();
addEntityModel(artistModel);
}
}
private static final class ApplicationPanel extends EntityApplicationPanel<ApplicationModel> {
private ApplicationPanel(ApplicationModel applicationModel) {
super(applicationModel);
}
@Override
protected List<EntityPanel> createEntityPanels() {
SwingEntityModel artistModel = applicationModel().entityModel(Artist.TYPE);
SwingEntityModel albumModel = artistModel.detailModel(Album.TYPE);
EntityPanel artistPanel = new EntityPanel(artistModel, new ArtistEditPanel(artistModel.editModel()));
EntityPanel albumPanel = new EntityPanel(albumModel, new AlbumEditPanel(albumModel.editModel()));
artistPanel.addDetailPanel(albumPanel);
return List.of(artistPanel);
}
}
private static final class LocalConnectionProviderFactory implements ConnectionProviderFactory {
@Override
public EntityConnectionProvider create(User user, DomainType domainType,
String clientType, Version clientVersion) {
return LocalEntityConnectionProvider.builder()
.user(user)
.domain(new Chinook())
.clientType(clientType)
.build();
}
}
public static void main(String[] args) {
Database.DATABASE_URL.set("jdbc:h2:mem:h2db");
Database.DATABASE_INIT_SCRIPTS.set("src/main/sql/create_schema.sql");
Arrays.stream(FlatAllIJThemes.INFOS)
.forEach(LookAndFeelProvider::addLookAndFeel);
EntityPanel.Config.TOOLBAR_CONTROLS.set(true);
FilterTable.AUTO_RESIZE_MODE.set(JTable.AUTO_RESIZE_ALL_COLUMNS);
EntityApplicationPanel.builder(ApplicationModel.class, ApplicationPanel.class)
.applicationModelFactory(ApplicationModel::new)
.applicationPanelFactory(ApplicationPanel::new)
.connectionProviderFactory(new LocalConnectionProviderFactory())
.defaultLookAndFeelClassName("com.formdev.flatlaf.intellijthemes.materialthemeuilite.FlatMaterialDarkerIJTheme")
.applicationName("Artists and Albums")
.defaultLoginUser(User.parse("scott:tiger"))
.start();
}
}