We write an Eclipse client to the database using hibernate and fishbolt technologies
This and other examples are available for download here.
In this section, we create a separate eclise plugin that will contain a data model (to separate data from the user interface is a good tone, as we know)
After creating the plugin, we will write the necessary dependencies:
And add a line to the file
Department Data Object: Department Data Object Manager : Employee Data Object : Employee Data Object Manager:
Let's create some Tuplizer-class (mentioned in the annotation of the classes of data objects of the same name): create
The employee data object contains a field
We register the factory providers - declare the extension of the point
file




Let's create the company database (Postgres - server). And create the tables:
We create a user interface plugin and add dependencies.
As we can see, among the dependencies is the previously created plugin - data model (
Create wizard classes to create a data object. Below is the wizard code for the employee data object: Register the corresponding extension. The first page of the wizard for creating an employee type object is as follows: Add the code for the user interface command (UI action), which opens the wizard's dialog for creating the employee object "


Создадим классы фабрик редакторов для объектов данных
Нам также необходимо создать класс – провайдер редактора и зарегистрировать его:
И объявить редактор:
Созданный редактор для объекта типа «Сотрудник» выглядит следующим образом:
Добавляем код команды пользовательского интерфейса (UI action), которая открывает редактор для объектов данных
Для примера, также добавляем код команды пользовательского интерфейса (UI action), которая открывает редактор для объектов данных в специальной перспективе
Ниже показан класс диалогового окна, который переводит сотрудника из одного отдела в другой:
Так выглядит диалоговое окно для перевода сотрудника из одного отдела в другой:
Create a tree that shows all departments and their employees: Register the corresponding extension ( ) as a result of our efforts:


Add the extension
result:
Create a panel that will search for employees by the entered parameters and display the search result in the table.
Plugin - data model
In this section, we create a separate eclise plugin that will contain a data model (to separate data from the user interface is a good tone, as we know)
Create a plugin
After creating the plugin, we will write the necessary dependencies:


MANIFEST.MF
(to use a single class loader, which is required when working with hibernate libraries): Eclipse-RegisterBuddy: org.fishbolt.model.hibernate.annotations
Create data object classes and data object managers
Department Data Object: Department Data Object Manager : Employee Data Object : Employee Data Object Manager:
/**
* Data object presenting department
*/
@DisplayLabel("Department")
@ImageResource("department.gif")
@Entity
@Tuplizer(impl = CompanyModelTuplizer.class)
@AccessType("org.fishbolt.model.hibernate.FieldAccessor")
public class Department
extends HDataObject {
@Id
@GeneratedValue
@DisplayLabel("Identity")
public final static FieldDeclaration id =
new FieldDeclaration(Department.class);
@ObjectPresentationField
@Column(unique=true, nullable=false)
@DisplayLabel("Name")
@Width(250)
public final static FieldDeclaration name =
new FieldDeclaration(Department.class);
@DisplayLabel("Employees")
@Relation(inverse="department")
@OneToMany(targetEntity=Employee.class)
@JoinColumn(name="department")
public final static FieldDeclaration> employees =
new FieldDeclaration>(Department.class);
public Department(DepartmentManager manager) {
super(manager);
}
public Department() {
super();
}
}
/**
* Manager class for {@link Department} data object.
*/
public class DepartmentManager
extends HDataObjectManager{
/**
* Constructor inherited from the super class
*/
public DepartmentManager(IDataModel dataModel,
ObjectDescriptor objectDescriptor) {
super(dataModel, objectDescriptor);
}
/**
* Searches for a department by its name
* @param name - the name of the department to search for
* @return department or null
*/
public Department findDepartment(String name){
String hql = "from Department where name=?";
QueryUniqueResultCommand cmd =
new QueryUniqueResultCommand(hql);
cmd.setParameters(new Parameter(name));
return this.getModel().processCommand(cmd);
}
}
/**
* Data object presenting employee
*/
@DisplayLabel("Employee")
@ImageResource("employee.gif")
@Entity
@Tuplizer(impl = CompanyModelTuplizer.class)
@AccessType("org.fishbolt.model.hibernate.FieldAccessor")
public class Employee extends HDataObject {
@Id
@GeneratedValue
@DisplayLabel("Identity")
public final static FieldDeclaration id =
new FieldDeclaration(Employee.class);
@Transient
@ObjectPresentationField
@DisplayLabel("Name")
@Width(250)
public final static FieldDeclaration name =
new FieldDeclaration(Employee.class);
@Column(nullable=false)
@DisplayLabel("First Name")
public final static FieldDeclaration firstName =
new FieldDeclaration(Employee.class);
@Column(nullable=false)
@DisplayLabel("Last Name")
public final static FieldDeclaration lastName =
new FieldDeclaration(Employee.class);
@DisplayLabel("Phone Number")
public final static FieldDeclaration phoneNumber =
new FieldDeclaration(Employee.class);
@SimpleDateFormatPresentation("dd/MM/yyyy")
@DisplayLabel("Birthday")
public final static FieldDeclaration birthday =
new FieldDeclaration(Employee.class);
@DisplayLabel("Salary")
@Column(nullable=false)
@DecimalFormatPresentation("#,##0.00")
@PresentationDecorator(prefix="$ ")
public final static FieldDeclaration salary =
new FieldDeclaration(Employee.class);
@DisplayLabel("Health")
@NumberFormatPresentation(NumberFormatInstance.percent)
public final static FieldDeclaration health =
new FieldDeclaration(Employee.class);
@DisplayLabel("Department")
@Relation(inverse="employees")
@ManyToOne(optional=false)
@JoinColumn(name="department")
public final static FieldDeclaration department =
new FieldDeclaration(Employee.class);
public Employee(EmployeeManager manager) {
super(manager);
}
public Employee() {
super();
}
}
/**
* Manager class for {@link Employee} data object.
*/
public class EmployeeManager
extends HDataObjectManager{
/**
* Constructor inherited from the super class
*/
public EmployeeManager(IDataModel dataModel,
ObjectDescriptor objectDescriptor) {
super(dataModel, objectDescriptor);
}
/**
* Searches for an employee among all employees of a company
* @param firstName - employee's first name
* @param lastName - employee's last name
* @return List
of employees
*/
public List findEmployee(String firstName,String lastName){
String hql = "from Employee where firstName=? and lastName = ?";
QueryListCommand cmd = new QueryListCommand(hql);
cmd.setParameters(
new Parameter(firstName),
new Parameter(lastName));
return this.getModel().processCommand(cmd);
}
/**
* Searches for an employee in a department
* @param firstName - employee's first name
* @param lastName - employee's last name
* @param department
* @return List
of employees
*/
public List findEmployee(String firstName,String lastName, Department department){
String hql = "from Employee where firstName=? and lastName=? and department=?";
QueryListCommand cmd = new QueryListCommand(hql);
cmd.setParameters(
new Parameter(firstName),
new Parameter(lastName),
new Parameter(department));
return this.getModel().processCommand(cmd);
}
}
Declaring a data model class
/**
* Company model class
*/
public class CompanyModel extends DataModel {
public final static
ObjectDescriptor employee =
new EObjectDescriptor(CompanyModel.class);
public final static
ObjectDescriptor department =
new EObjectDescriptor(CompanyModel.class);
public static ModelDescriptor getDescriptor(){
return CompanyModel.department.getModelDescriptor();
}
private static CompanyModel instance;
public static CompanyModel getInstance(){
return (instance == null) ?
instance = new CompanyModel(getDescriptor()) :
instance;
}
private CompanyModel(ModelDescriptor modelDescriptor) {
super(modelDescriptor);
}
}
Customize Hibernate
Let's create some Tuplizer-class (mentioned in the annotation of the classes of data objects of the same name): create
public class CompanyModelTuplizer extends DataObjectTuplizer {
/**
* Constructor inherited from the super class
*/
public CompanyModelTuplizer(EntityMetamodel entityMetamodel,
PersistentClass mappedEntity) {
super(entityMetamodel, mappedEntity);
}
@Override
protected ModelDescriptor getModelDescriptor() {
return CompanyModel.getDescriptor();
}
}
hibernate.cfg.xml
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
org.hibernate.dialect.PostgreSQLDialect
org.postgresql.Driver
jdbc:postgresql://localhost/company
postgres
********
Calculated fields
The employee data object contains a field
name
. This field is calculated and, accordingly, is not reflected in the database. Below, we implement an algorithm for calculating this field:
We associate the implemented calculation algorithm with a data field in the codepublic class EmployeeFullNameCalculation
implements IFieldCalculation {
public FieldPath[] getInfluencingPaths(FieldDescriptor dependent) {
return new FieldPath[]{
new FieldPath(true, CompanyModel.employee,Employee.firstName),
new FieldPath(true, CompanyModel.employee,Employee.lastName)
};
}
public String evaluateValue(IDataField toEvaluate) {
Employee employee = (Employee)toEvaluate.getDataObject();
return ModelUtil.getValue(employee,Employee.firstName)+
" " + ModelUtil.getValue(employee,Employee.lastName);
}
}
public class CompanyCalculationProviderFactory
extends FieldCalculationProviderFactorySafe{
/**
* Binds calculated fields with calculation classes
*/
@Override
protected void bindCalculations() {
// binding Employee.name
with EmployeeFullNameCalculation
bindCalculation(CompanyModel.employee,Employee.name,
new EmployeeFullNameCalculation());
}
}
Plugin manifest changes
We register the factory providers - declare the extension of the point
ModelAdapterFactory
: The 
plugin.xml
looks as follows:
Export the packages:
And add the driver library to the database:
point="org.fishbolt.model.ModelAdapterFactory">



The plugin as a result

Create database tables
Let's create the company database (Postgres - server). And create the tables:
CREATE TABLE department
(
id integer NOT NULL,
name character varying(255) NOT NULL,
CONSTRAINT department_pkey PRIMARY KEY (id),
CONSTRAINT department_name_key UNIQUE (name)
)
CREATE TABLE employee
(
id integer NOT NULL,
firstname character varying(255) NOT NULL,
lastname character varying(255) NOT NULL,
phonenumber character varying(255),
birthday date,
health double precision,
parttime boolean,
salary numeric(19,2),
department integer NOT NULL,
CONSTRAINT employee_pkey PRIMARY KEY (id),
CONSTRAINT fk4afd4acec0414ee2 FOREIGN KEY (department)
REFERENCES department (id)
)
Plugin - user interface
We create a user interface plugin and add dependencies.

example.org.fishbolt.model.hibernate.annotations
)Wizard for creating data objects
Create wizard classes to create a data object. Below is the wizard code for the employee data object: Register the corresponding extension. The first page of the wizard for creating an employee type object is as follows: Add the code for the user interface command (UI action), which opens the wizard's dialog for creating the employee object "
/**
* Wizard for adding new employees
*/
public class NewEmployeeWizard extends ObjectNewWizard {
public NewEmployeeWizard() {
// indicating the descriptor of a data object
// whose instances will be created by the wizard
super(CompanyModel.employee);
}
@Override
protected IDataModel getDataModel(IWorkbench workbench,
IStructuredSelection selection) {
// providing a reference to the data model
// to which new Employee instances will be added
return CompanyModel.getInstance();
}
@SuppressWarnings("unchecked")
@Override
public void init(IWorkbench workbench,
IStructuredSelection selection){
super.init(workbench, selection);
// setting a default value to Employee.department
Object selectionObject = selection.getFirstElement();
if (selectionObject instanceof Department){
ModelUtil.setValue(this.wizardObject,
Employee.department,(Department)selectionObject);
}
String name =
LabelProvider.getDisplayLabel(CompanyModel.employee);
// adding pages for step-by-step creation of Employee instances
this.addPage(
new ObjectSimpleWizardPage(name,name,null,
Employee.firstName,
Employee.lastName,
Employee.department,
Employee.phoneNumber));
this.addPage(
new ObjectSimpleWizardPage(name,name,null,
Employee.birthday,
Employee.salary,
Employee.health));
}
}


public class OpenNewEmployeeWizardAction extends OpenNewWizardAction {
@SuppressWarnings("unchecked")
public OpenNewEmployeeWizardAction(ViewerController controller) {
super(controller, "New Employee...","Creates a new employee");
}
@Override
public INewWizard getNewWizard() {
return new NewEmployeeWizard();
}
@Override
public void updateEnable() {
this.setEnabled(getActionObject() instanceof Department);
}
}
Editor for data objects
Создадим классы фабрик редакторов для объектов данных
/**
* EditorFactory for Employee
s
*/
public class EmployeeEditorFactory extends EditorFactory {
EmployeeEditorFactory() {
// Indicating an Employee object via its descriptor
// and creating editor pages
super(CompanyModel.employee,
new SimpleEditorPageFactory(
"Personal information",
Employee.firstName,
Employee.lastName,
Employee.phoneNumber,
Employee.birthday,
Employee.health),
new SimpleEditorPageFactory(
"Department information",
Employee.department,
Employee.salary)
);
}
}
/**
* EditorFactory for Department
s
*/
public class DepartmentEditorFactory extends EditorFactory {
DepartmentEditorFactory() {
// Indicating a Department object via its descriptor
// and creating editor pages
super(CompanyModel.department,
new SimpleEditorPageFactory(
LabelProvider.getDisplayLabel(CompanyModel.department),
Department.name)
);
}
}
Нам также необходимо создать класс – провайдер редактора и зарегистрировать его:
public class CompanyEditorProvider extends EditorProvider {
public CompanyEditorProvider(){
register(new DepartmentEditorFactory());
register(new EmployeeEditorFactory());
}
}



public class OpenInEditorAction extends OpenEditorAction {
@SuppressWarnings("unchecked")
public OpenInEditorAction(ViewerController controller) {
super("example.model.eclipse.DataObjectEditor", controller);
}
}
Для примера, также добавляем код команды пользовательского интерфейса (UI action), которая открывает редактор для объектов данных в специальной перспективе

public class OpenInEditorPerspectiveAction extends OpenEditorPerspectiveAction {
@SuppressWarnings("unchecked")
public OpenInEditorPerspectiveAction(ViewerController controller) {
super("example.model.eclipse.perspectives.ObjectEditorPerspective",
"example.model.eclipse.DataObjectEditor",
controller);
}
}
Диалог для изменения значения поля данных
Ниже показан класс диалогового окна, который переводит сотрудника из одного отдела в другой:
/**
* Dialog that moves an employee from one department to another
*/
public class ChangeDepartmentDialog
extends Dialog implements IWidgetsContext{
private Employee employee;
private ContextControllerBinder contextBinder;
public ChangeDepartmentDialog(Shell parentShell,Employee employee) {
super(parentShell);
this.employee = employee;
}
/**
* Returns ContextControllerBinder that binds
* user interface components with data model components
* through controllers
*/
public ContextControllerBinder getContextBinder() {
return contextBinder == null ?
contextBinder = new ContextControllerBinder(this) : contextBinder;
}
/**
* Creates dialog's contents
* and binds user interface components with data model components
*/
@Override
protected Control createDialogArea(Composite parent)
{
// dialog title
parent.getShell().setText(PresenterProvider.getPresentation(employee));
Composite result = new Composite(parent, SWT.NONE);
result.setLayout(new GridLayout(2, true));
Label decription = new Label(result, SWT.NULL);
decription.setLayoutData(
new GridData(GridData.FILL,GridData.FILL,true,true,2,1));
decription.setText("To move the employee to another department, " +
"select the department and press OK.");
// Label presenting the data object field
Label label = new Label(result, SWT.NULL);
label.setLayoutData(new GridData());
// Combo presenting data field values
Combo combo = new Combo(result, SWT.BORDER | SWT.DROP_DOWN);
combo.setLayoutData(
new GridData(GridData.FILL,GridData.CENTER,true,false));
ContextControllerBinder binder = getContextBinder();
// binding Label to FieldDescriptor
// (the controller that is responsible for interaction
// between Label and FieldDescriptor is created automatically)
binder.bind(label,
CompanyModel.employee.getFieldDescriptor(Employee.department));
// binding data field to Combo
// (the controller that is responsible for interaction
// between data field and Combo is created automatically)
binder.bind(combo,
employee.getDataField(Employee.department));
return result;
}
/**
* Reacts to changes resulted from user's actions on the UI
* components (controls) that were bound to data model
* components using ContextControllerBinder
*/
@SuppressWarnings("unchecked")
public void contextWidgetChanged(
EditableWidgetController controller,
PresentationException exception) {
if (exception!= null){
MessageDialog.openError(this.getShell(),
"The value is incorrect.",
"Please specify a correct value.");
return;
}
Collection> problems
= employee.validate(null);
if (problems.isEmpty()) return;
showProblems(problems);
employee.refresh();
}
/**
* Shows information about the data object problems
* @param problems
*/
protected void showProblems(
Collection> problems){
StringBuffer msg = new StringBuffer();
for (ValidationMessage problem : problems){
msg.append(problem.getDescription());
msg.append("\\;n");
}
MessageDialog.openError(this.getShell(),
"The value is incorrect.",msg.toString());
}
/**
* Cancel button handler
*/
@Override
protected void cancelPressed() {
employee.refresh();
super.cancelPressed();
}
/**
* OK button handler
*/
@Override
protected void okPressed() {
employee.store();
super.okPressed();
}
}
Так выглядит диалоговое окно для перевода сотрудника из одного отдела в другой:

Дерево «Отдел» — «Сотрудник»
Create a tree that shows all departments and their employees: Register the corresponding extension ( ) as a result of our efforts:
/**
* Creates a view that contains
* the tree of departments and employees
*/
public class DepartmentEmployeesView extends ViewPart {
private TreeViewerController treeController = null;
@Override
public void createPartControl(Composite parent) {
// creating a controller for TreeViewer
treeController = new TreeViewerController(parent);
// setting layout
GridData data = new GridData(GridData.FILL,GridData.FILL,true,true);
treeController.getViewer().getControl().setLayoutData(data);
// Using ColumnData to wrap up data objects to be represented in tree nodes.
// Data objects are specified through their descriptors
treeController.setColumnProvider(
new ColumnProvider(
new ColumnData(CompanyModel.department).add(CompanyModel.employee)));
// Wrapping data in TreeDataObjects and passing it to the controller.
// To put it in more details:
// - indicating data objects to be represented in parent nodes.
// In this case, these are objects of the Deparment type.
// The Department objects are indicated through their manager (DepartmentManager).
// - indicating data objects to be represented in child nodes.
// They are indicated through a bidirectional association.
// In this case, the bidirectional association is expressed by Department.employees
CompanyModel model = CompanyModel.getInstance();
DepartmentManager manager = model.getDataObjectManager(CompanyModel.department);
treeController.setDataObjects(
new TreeDataObjects(
new ManagerDataObjects(manager),
new ChildRelation(CompanyModel.department,Department.employees)));
// building a context menu for the tree
ContextMenu menu = new ContextMenu();
menu.add(new OpenInEditorAction(treeController));
menu.add(new OpenInEditorPerspectiveAction(treeController));
menu.add(new OpenNewDepartmentWizardAction(treeController));
menu.add(new OpenNewEmployeeWizardAction(treeController));
menu.add(new ChangeEmployeeDepartmentAction(treeController));
menu.add(new ShowDepartmentEmployeesAction(treeController));
menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
menu.add(new RemoveDataObjectAction(treeController));
treeController.setContextMenu(this.getSite(),menu);
}
@Override
public void setFocus() {
this.treeController.getViewer().getControl().setFocus();
}
}
org.eclipse.ui.views


Employee table
/**
* Creates a view that contains
* a table displaying information about employees
*/
public class EmployeesView extends ViewPart{
private TableViewerController tableController = null;
/**
* Opens the view and passes to the controller
* a collection of Employee
s to be represented
* in a table within the view.
*/
public static void setEmployees(List employess){
EmployeesView view = getView();
view.tableController.setDataObjects(
new CollectionDataObjects(employess));
}
/**
* Opens the view and passes to the controller
* a collection of Employee
s to be represented
* in a table within the view.
* The collection of Employee
s is specified via
* the association with the Department
object (Department.employees)
*/
public static void setDepartment(Department department){
EmployeesView view = getView();
IDataField> field =
department.getDataField(Department.employees);
view.tableController.setDataObjects(new RelationDataObjects(field));
}
@Override
public void createPartControl(Composite parent) {
// Creating a controller
tableController = new TableViewerController(parent);
// Setting layout
GridData data = new GridData(GridData.FILL,GridData.FILL,true,true);
tableController.getViewer().getControl().setLayoutData(data);
// Specifying data presentation.
// Passing to the controller an OddBackgroundColorPresentation instance
// that pastes the background of even table cells
// with the color specified. Default is RGB(230,230,255)
tableController.setDataPresenation(
new EvenBackgroundColorPresentation(tableController));
// Configuring table columns.
// Using ColumnData to wrap up data object fields to be represented in tree nodes.
// (Data object fields are indicated through FieldDeclaration
s)
tableController.setColumnProvider(
new ColumnProvider(
new ColumnData(CompanyModel.employee,Employee.firstName),
new ColumnData(CompanyModel.employee,Employee.lastName),
new ColumnData(CompanyModel.employee,Employee.phoneNumber),
new ColumnData(CompanyModel.employee,Employee.salary)));
// Adding actions that open a dialog for column selection
ChangeColumnsAction.registerInMenu(this, tableController);
ChangeColumnsAction.registerInToolBar(this, tableController);
// adding a context menu
ContextMenu menu = new ContextMenu();
menu.add(new OpenInEditorAction(tableController));
menu.add(new OpenInEditorPerspectiveAction(tableController));
menu.add(new ChangeEmployeeDepartmentAction(tableController));
menu.add(new RemoveDataObjectAction(tableController));
tableController.setContextMenu(this.getSite(),menu);
}
@Override
public void setFocus() {
this.tableController.getViewer().getControl().setFocus();
}
/**
* Opens EmployeesView
*/
private static EmployeesView getView(){
IWorkbenchPage page = PlatformUI.getWorkbench().
getActiveWorkbenchWindow().getActivePage();
EmployeesView result = null;
try {
for (IViewReference ref : page.getViewReferences()) {
if ("example.model.eclipse.views.EmployeesView"
.equals(ref.getId())) {
result = (EmployeesView) ref.getView(true);
break;
}
}
if (result == null)
result = (EmployeesView) page.showView(
"example.model.eclipse.views.EmployeesView");
} catch (PartInitException e) {
throw new RuntimeException(e);
}
return result;
}
}
Add the extension


Employee Search Bar
Create a panel that will search for employees by the entered parameters and display the search result in the table.
public class SearchEmployeeView extends ViewPart
implements IWidgetsContext{
private Label firstNameLabel;
private Text firstName;
private Label secondNameLabel;
private Text secondName;
private Label departmentLabel;
private DialogChooser department;
private Button searchButton;
private ContextControllerBinder contextBinder;
private SelectionAdapter doSearch = new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
CompanyModel model = CompanyModel.getInstance();
EmployeeManager manager = model.getDataObjectManager(CompanyModel.employee);
String first = firstName.getText();
String second = secondName.getText();
Department d = department.getObject();
List