Dear JOOQ Community, after a lot of work i've created a JavaFX tableView dynamically showing a JOOQ Result / Record including the posibillity to edit and save the entries - i want to share it with you maybe it'll help some others too. There's an option to show multiple entries by a JOOQ Result (like a normal tableView) and also the possibility to show a single Record (inverted). It's coded quick and dirty so there'll be a lot of potential to improve the code - any recommendations welcome.
My Question: how to get the name of the table out of the record? during Debugging it's possible to see the table name but is there a function to get it? <https://lh3.googleusercontent.com/-RZ5bD3T4Pbk/WR_sWppS6VI/AAAAAAAAW3Y/5-Tt5oceOlM8FE9rCA4Muj5FQZab4d9XQCLcB/s1600/Unbenannt.JPG> --- The "Main" class: public JooqRecordTableView(Table table, Result result) = creating a TableView with multiple entries public JooqRecordTableView(Table table, Record bRecord) = creating a TableView with a single Record entry - inverted View - adding also a comment column <https://lh3.googleusercontent.com/-HxsJvQ3BhMY/WR_vwaNLa1I/AAAAAAAAW3k/hZCmQ5cJSFY9J0K_glMGUWz5DuS2q8fggCLcB/s1600/single-inverted.JPG> import javafx.beans.property.SimpleObjectProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.scene.control.Alert; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import org.jooq.*; import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; import static java.util.stream.Collectors.toList; public class JooqRecordTableView<B extends Record> extends TableView { private static PropertiesHandle propertiesHandle; private Table<?> table; private ObservableList<B> records = FXCollections.observableArrayList(); private String propertyDirectory = "./resources/tables/"; private B templateRecord = null; /* Constructor for standard tableView with JOOQ Result */ public JooqRecordTableView(Table table, Result result) { this.table = table; tableRecords(result); loadPropertiesForTable(); tableColumns(); this.setEditable(true); } /* generate Table Columns */ private <R extends Record, O extends Object> void tableColumns() { List<TableColumn<R, ?>> tableColumns = new ArrayList<>(); if (templateRecord != null) { tableColumns.addAll(Stream.of(templateRecord.fields()) .map((Field<?> f) -> { TableColumn<R, ?> column = new TableColumn<>(f.getName()); column.setVisible((boolean) propertiesHandle.getProperty(f.getName() + "-visible", boolean.class)); column.setEditable((boolean) propertiesHandle.getProperty(f.getName() + "-editable", boolean.class)); column.setText(propertiesHandle.getProperty(f.getName(), String.class).toString()); column.setCellValueFactory(d -> new SimpleObjectProperty(d.getValue().get(f))); List<?> preloadValues = propertiesHandle.getPropertyValueList(f.getName(), String.class); column.setCellFactory(param -> new CellUniversalEditor(f, preloadValues)); column.setOnEditCommit( t -> { Field columnField = f; Object newValue = t.getNewValue(); if (f.getDataType().hasLength()) { String s = (String) newValue; if (f.getDataType().length() > s.length()) { records.get(t.getTablePosition().getRow()).set(columnField, newValue); ((UpdatableRecord) records.get(t.getTablePosition().getRow())).update(); System.out.println("Updating: " + f.getName() + "/" + newValue); } else { Alert alert = new Alert(Alert.AlertType.ERROR); alert.setHeaderText("Error: too much characters"); alert.setContentText("Max.: " + f.getDataType().length()); alert.showAndWait(); } } else { records.get(t.getTablePosition().getRow()).set(columnField, newValue); ((UpdatableRecord) records.get(t.getTablePosition().getRow())).update(); System.out.println("Updating: " + f.getName() + "/" + newValue); } this.refresh(); } ); return column; }) .collect(toList())); for (TableColumn tableColumn : tableColumns) { this.getColumns().add(tableColumn); } } } private void tableRecords(Result result) { for (Object o : result) { B record = (B) o; records.add(record); } if (records.size() > 0) { templateRecord = records.get(0); } this.setItems(records); this.refresh(); } /* Constructor for inverted >single< Record tableView with JOOQ Record */ public JooqRecordTableView(Table table, Record bRecord) { this.setEditable(true); this.table = table; loadPropertiesForTable(); Record dataRecord = bRecord; ObservableList<ColumnInformation> data = FXCollections.observableArrayList(); //prepare ColumnInformation for each Field for (Field field : bRecord.fields()) { String comment = propertiesHandle.getProperty(field.getName() + "-comment", String.class).toString(); String tooltip = propertiesHandle.getProperty(field.getName() + "-tooltip", String.class).toString(); Boolean visible = (Boolean) propertiesHandle.getProperty(field.getName() + "-visible", Boolean.class); Boolean editable = (Boolean) propertiesHandle.getProperty(field.getName() + "-editable", Boolean.class); if (comment.equals(field.getName() + "-comment")) { comment = ""; } if (tooltip.equals(field.getName() + "-tooltip")) { tooltip = ""; } List<?> preloadObjects = propertiesHandle.getPropertyValueList(field.getName(), String.class); if (visible) { data.add(new ColumnInformation(propertiesHandle.getProperty(field.getName(), String.class).toString(), field.getValue(bRecord), comment, field, tooltip, editable, visible, preloadObjects)); } } getItems().addAll(data); // table definition TableColumn<ColumnInformation, String> nameColumn = new TableColumn<>("Parameter"); nameColumn.setCellValueFactory(d -> new SimpleObjectProperty(d.getValue().getName())); nameColumn.setEditable(false); TableColumn<ColumnInformation, Object> valueColumn = new TableColumn<>("Wert"); valueColumn.setEditable(true); valueColumn.setCellValueFactory(d -> new SimpleObjectProperty(d.getValue().getValue())); CellUniversalEditor cellUniversalEditor = new CellUniversalEditor(); valueColumn.setCellFactory(param -> new CellUniversalEditor()); valueColumn.setOnEditCommit( t -> { ColumnInformation cellData = data.get(t.getTablePosition().getRow()); Field columnField = cellData.getField(); Object newValue = t.getNewValue(); if (columnField.getDataType().hasLength()) { String s = (String) newValue; if (columnField.getDataType().length() > s.length()) { data.get(t.getTablePosition().getRow()).setValue(newValue); dataRecord.set(columnField, newValue); ((UpdatableRecord) dataRecord).update(); System.out.println("Updating: " + columnField.getName() + "/" + newValue); } else { Alert alert = new Alert(Alert.AlertType.ERROR); alert.setHeaderText("Error: too much characters"); alert.setContentText("Max.: " + f.getDataType().length()); alert.showAndWait(); } } else { data.get(t.getTablePosition().getRow()).setValue(newValue); dataRecord.set(columnField, newValue); ((UpdatableRecord) dataRecord).update(); System.out.println("Updating: " + columnField.getName() + "/" + newValue); } this.refresh(); } ); TableColumn<ColumnInformation, Object> commentColumn = new TableColumn<>("Kommentar"); commentColumn.setCellValueFactory(d -> new SimpleObjectProperty(d.getValue().getComment())); commentColumn.setEditable(false); valueColumn.setSortable(false); getColumns().setAll(nameColumn, valueColumn, commentColumn); } /* Load Properties File for Table*/ private void loadPropertiesForTable() { String propertyPath = propertyDirectory + table.getName(); propertiesHandle = new PropertiesHandle(propertyPath); } } class ColumnInformation { private String name; private Object value; private String comment; private Field<?> field; private String tooltipText; private List<?> preloadValues; private Boolean editable; private Boolean visible; ColumnInformation(String name, Object value, String comment, Field<?> field, String tooltipText, Boolean editable, Boolean visible, List<?> preloadValues) { this.setName(name); this.setValue(value); this.setComment(comment); this.setField(field); this.setTooltipText(tooltipText); this.setVisible(visible); this.setEditable(editable); this.setPreloadValues(preloadValues); } public String getTooltipText() { return tooltipText; } public void setTooltipText(String tooltipText) { this.tooltipText = tooltipText; } public Field<?> getField() { return field; } public void setField(Field<?> field) { this.field = field; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } public String getComment() { return comment; } public void setComment(String comment) { this.comment = comment; } public Boolean getEditable() { return editable; } public void setEditable(Boolean editable) { this.editable = editable; } public Boolean getVisible() { return visible; } public void setVisible(Boolean visible) { this.visible = visible; } public List<?> getPreloadValues() { return preloadValues; } public void setPreloadValues(List<?> preloadValues) { this.preloadValues = preloadValues; } } The "universall cell" class representing a TableCell and dynamically adapts to record column Datatype import javafx.scene.Node; import javafx.scene.control.*; import javafx.scene.input.KeyCode; import javafx.util.StringConverter; import org.jooq.Field; import org.jooq.Record; import org.jooq.Table; import tornadofx.control.DateTimePicker; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; class CellUniversalEditor<R extends Record> extends TableCell<Table<R>, Object> { private TextField textField; private CheckBox checkBox; private DateTimePicker dateTimePicker; private DatePicker datePicker; private Label valueLabel = new Label(); private Spinner<Integer> integerSpinner; private ComboBox comboBox; private Field field = null; private Object graphicElement; private List<?> preloadValues = new ArrayList<>(); private Boolean cellEditable = false; public CellUniversalEditor() { } /* Constructor with preloadValues */ public CellUniversalEditor(Field field, List<?> preloadValues) { this.field = field; this.preloadValues = preloadValues; } public Boolean getCellEditable() { return cellEditable; } public void setCellEditable(Boolean cellEditable) { this.cellEditable = cellEditable; } /* Override Functions for TableCell */ @Override public void startEdit() { String dataType = null; Object value = null; ColumnInformation columnInformation = null; value = getValue(); if (field == null) { columnInformation = (ColumnInformation) getTableRow().getItem(); dataType = columnInformation.getField().getDataType().getType().getSimpleName(); preloadValues = columnInformation.getPreloadValues(); cellEditable = columnInformation.getEditable(); } else { dataType = field.getDataType().getType().getSimpleName(); cellEditable = getTableColumn().isEditable(); } if (cellEditable) { super.startEdit(); switch (dataType) { case "Boolean": checkBox = new CheckBox(); Boolean aBoolean = value == null ? false : (boolean) value; checkBox.setSelected(aBoolean); checkBox.setOnAction(event -> commitEdit(checkBox.isSelected())); graphicElement = checkBox; break; case "LocalDateTime": dateTimePicker = new DateTimePicker(); dateTimePicker.setDateTimeValue((LocalDateTime) getValue()); dateTimePicker.setOnAction(event -> commitEdit(dateTimePicker.getDateTimeValue())); graphicElement = dateTimePicker; break; case "LocalDate": datePicker = new DateTimePicker(); datePicker.setValue((LocalDate) getValue()); datePicker.setOnAction(event -> commitEdit(datePicker.getValue())); graphicElement = datePicker; break; case "Integer": integerSpinner = new Spinner<>(); Integer integer = value == null ? 0 : (int) value; SpinnerValueFactory<Integer> valueFactory = new SpinnerValueFactory.IntegerSpinnerValueFactory(0, Integer.MAX_VALUE, integer); integerSpinner.setValueFactory(valueFactory); integerSpinner.setEditable(true); integerSpinner.getEditor().setOnMouseClicked(event -> integerSpinner.getEditor().selectAll()); integerSpinner.getEditor().setOnKeyPressed(t -> { if (t.getCode() == KeyCode.ENTER) { String integerString = integerSpinner.getEditor().getText(); if (valueFactory != null) { StringConverter<Integer> converter = valueFactory.getConverter(); if (converter != null) { try { valueFactory.setValue(converter.fromString(integerString)); commitEdit(integerSpinner.valueProperty().getValue()); } catch (NumberFormatException nfe) { integerSpinner.getEditor().setText(converter.toString(valueFactory.getValue())); } } } } else if (t.getCode() == KeyCode.ESCAPE) { cancelEdit(); } }); integerSpinner.getEditor().selectAll(); graphicElement = integerSpinner; break; case "String": if (preloadValues.size() > 0) { comboBox = new ComboBox<>(); comboBox.getItems().add(""); comboBox.getItems().addAll(preloadValues); if (getValue() != null) { comboBox.getSelectionModel().select(getValue()); } comboBox.setOnAction(event -> commitEdit(comboBox.getValue())); graphicElement = comboBox; } else { String s = value == null ? "" : (String) value; textField = new TextField(s); textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2); textField.setOnKeyPressed(t -> { if (t.getCode() == KeyCode.ENTER) { commitEdit(textField.getText()); } else if (t.getCode() == KeyCode.ESCAPE) { cancelEdit(); } }); graphicElement = textField; } break; default: Alert alert = new Alert(Alert.AlertType.ERROR); alert.setHeaderText("Form Error"); alert.setContentText("Datatype not supported!"); alert.showAndWait(); } setGraphic((Node) graphicElement); setContentDisplay(ContentDisplay.GRAPHIC_ONLY); } } @Override public void cancelEdit() { super.cancelEdit(); setGraphic(valueLabel); } @Override public void updateItem(Object item, boolean empty) { super.updateItem(item, empty); setGraphic(valueLabel); setContentDisplay(ContentDisplay.GRAPHIC_ONLY); if (empty || item == null) { setText(""); valueLabel.setText(""); } else { if (isEditing()) { } else { setText(getValue().toString()); switch (item.getClass().getSimpleName()) { case "Boolean": if ((Boolean) item == true) { valueLabel.setText("Yes"); } else { valueLabel.setText("No"); } break; case "LocalDate": valueLabel.setText(date2GermanFormat((LocalDate) item)); break; case "LocalDateTime": valueLabel.setText(datetime2GermanFormat((LocalDateTime) item)); break; default: valueLabel.setText(getValue().toString()); } } } } private Object getValue() { return getItem() == null ? null : getItem(); } } The Properties File - handling the entries for each Table / Column and creating - adding new Information if needed import org.apache.commons.io.FileUtils; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; public class PropertiesHandle { private SortedProperties properties; private String propertyPath; public PropertiesHandle(String propertyPath) { this.propertyPath = propertyPath + ".properties"; Path path = Paths.get(this.propertyPath); if (Files.exists(path)) { properties = new SortedProperties(); try { properties.load(new FileInputStream(this.propertyPath)); } catch (IOException e) { e.printStackTrace(); } } else { properties = new SortedProperties(); writeProperties2File(); } } private void writeProperties2File() { File file = new File(propertyPath); try { FileOutputStream fileOut = FileUtils.openOutputStream(file); properties.store(fileOut, propertyPath); fileOut.close(); } catch (IOException e) { e.printStackTrace(); } } public Object getProperty(String key, Class aClass) { Object o = null; String value = properties.getProperty(key); //if key exists if (value != null) { //if key is empty if (!value.equals("")) { switch (aClass.getSimpleName()) { case "boolean": o = Boolean.valueOf(value); break; case "Boolean": o = Boolean.valueOf(value); break; default: o = aClass.cast(value); } } else { switch (aClass.getSimpleName()) { case "boolean": o = new Boolean(false); break; case "Boolean": o = new Boolean(false); break; default: o = key; } } //if key not exists } else { properties.setProperty(key, ""); writeProperties2File(); switch (aClass.toString()) { case "boolean": o = new Boolean(false); break; default: o = key; } } return o; } public List getPropertyValueList(String key, Class aClass) { List objects = new ArrayList<>(); String sValue = (String) getProperty(key + "-values", String.class); if (!sValue.equals(key + "-values")) { List<String> strings = Arrays.asList(sValue.split(",")); for (String s : strings){ objects.add(s.trim()); } } return objects; } } class SortedProperties extends Properties { public Enumeration keys() { Enumeration keysEnum = super.keys(); Vector<String> keyList = new Vector<String>(); while (keysEnum.hasMoreElements()) { keyList.add((String) keysEnum.nextElement()); } Collections.sort(keyList); return keyList.elements(); } } -- You received this message because you are subscribed to the Google Groups "jOOQ User Group" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
