.. ******************************************************************************* * MetaFactory.io R2 * Technical Documentation * * File name : M13_PojoClasses.rst * Author : Sandra Kunnen * Created by : Niels de Nies * * MetaFactory BV Amsterdam ******************************************************************************* .. include:: /inline-images.txt .. include:: /metadata_include.txt Pojo Classes ============ .. include:: /Miscellaneous/AboutSandra.txt In this first blog, we will start with the creation of Java POJO classes which represent data from a database. We will create a so-called code instruction, which in combination with a data model will form the basis of generating code. We will also explain how functions can be used to apply a consistent naming of classes, fields, and methods for example. Finally, we will be adding methods to our classes. Data model ---------- The data model which we will use in this article looks like this: .. figure:: images/M13_DataModel.png :name: DataModel :scale: 100% Data model .. code-block:: xml :caption: Model object :name: M13_pojo_0 :linenos: Let’s start with creating a very simple class for the entity Book: .. code-block:: java :caption: class Book :name: M13_pojo_1 :linenos: package org.blog1.pojo; public class Book { private String name; private String description; private Author author; // Getters and setters } There are quite some similarities between the data model and the entity for Book. We take advantage of these similarities to create ‘code instructions’, which will contain the blueprint for, in our case, Java classes. As all POJO classes have a similar structure, it is very convenient to use these code instructions to create more classes. Code instruction ---------------- For our POJO classes we can define the following code instruction using XML: .. code-block:: xml :caption: Code instruction :name: M13_pojo_2 :linenos: :emphasize-lines: 9-11 ${attribute.type} ${reference.type} This is a good example of using Metaprogramming to describe POJO's. Let’s first have a look at how a class is defined (from line 9 on): .. list-table:: :widths: 20 60 :header-rows: 0 :class: tight-table * - *name=”${object.name}”* - This property describes the access modifier, in this case, of the class. * - *visibility=”public”* - This property describes the access modifier, in this case, of the class. * - *foreach=”object”* - The property defines in this case that we want to generate a class for every object defined in our data model. This really emphasizes the advantages of a tool like MetaFactory's Code Composer. Instead of having to write similar POJO classes for every entity by hand, you can now generate classes like these for all objects in the data model. You only need to define the desired objects. Next we have two different types of fields defined in our code instruction: one based on the attributes and one based on the references in our data model. Our model object Book (see :ref:`M13_pojo_0`) for example contains two attributes: name and description. For each of these attributes we want to generate a field with a private access modifier. The name of the field will now be based on the name of the attribute in our data model (i.e. description). The child element datatype in our code instruction describes the type of the Java field, based on the type of the attribute in the data model (i.e. String). The second type of field is based on the references and follows the same logic as for the attributes. Now we can use this code instruction to generate, with one mouse click, both the Book and Author classes by means of the Code Composer. The Author class should look like this: .. code-block:: java :caption: class Author :name: M13_pojo_3 :linenos: package org.blog1.pojo; public class Author { private String firstName; private String lastName; private Book book; } The fields *firstName* and *lastName* are based on the 2 attributes of the Author model object and the field *book* is created based on the reference of Author to Book in the Author object. Functions --------- Naming is an important aspect of Metaprogramming. In :numref:`M13_pojo_2` (line 9) we used the name of the model object for naming our Java POJO class. In this case that might be sufficient, but there are plenty of other situations where you might want to apply more advanced naming conventions. In MetaFactory we therefore have something called “code instruction functions”. These functions can be used in code instructions to describe all kinds of properties. Let’s have a look again at the names of the POJO classes and try to replace those with a function: .. code-block:: xml :caption: Function to create a class name :name: M13_pojo_4 :linenos: (...) ${arg1} (...) Applying this function to our code instruction, it will look like this: .. code-block:: xml :caption: Code instruction :name: M13_pojo_5 :linenos: :emphasize-lines: 9,22 ${attribute.type} ${createPojoClassName(${reference.type})} We have first used this function to determine the class name and currently it will have the same result as in :numref:`M13_pojo_0`. One major advantage of this solution is that if we want to change the names of our POJO classes, for example to BookEntity and AuthorEntity, we only have to adjust this one function and give the Code Composer a kick again. All classes created based on this code instruction will be updated with this new naming convention. Even better, also the type of our reference fields will automatically be adjusted. This is just a very simple example, but hopefully you recognize how convenient these functions are. They will result in consistent naming and are less error prone than changing all names manually. Methods ------- Our Java POJOs can use some getter and setter methods for access to the private fields. You need only a small adjustment to the code instruction to generate getters and setters: .. code-block:: xml :caption: Introducing Getters and Setters :name: M13_pojo_6 :linenos: :emphasize-lines: 15,22 ${attribute.type} ${createPojoClassName(${reference.type})} By adding the access property with value r (read), w (write), or rw, we can indicate whether we want getter and/or setter methods to be generated. To really complete the POJO classes we can also add implementations for *equals* and *hashCode* methods. Let’s further expand the code instruction and add these methods: .. code-block:: xml :caption: equals and hashCode methods :name: M13_pojo_7 :linenos: :emphasize-lines: 16,28 @Override Return true if object is the same as the argument object, otherwise return false boolean The reference object with which to compare Object ${fmsnippet.java.pojo.method.equals} @Override A hash code value for this object integer ${fmsnippet.java.pojo.method.hash_code} Lines 16 and 28 tell the Code Composer to call Freemarker snippets for the generation of the methods' body. The Code Composer will then generate these 2 classes: .. code-block:: java :caption: class Book :name: M13_pojo_8 :linenos: package org.blog1.pojo; import java.util.Objects; public class Book { private String name; private String description; private Author author; /** * equals – Fields used as business key: 1) name 2) description. * * @param other The reference object with which to compare * @return boolean Return true if object is the same as the argument object, otherwise return false */ @Override Public Boolean equals(final Object other) { if (this == other) { return true; } if (!(other instanceof Book)) { return false; } final Book otherBook = (Book) other; boolean result; result = Objects.equals(getName(), otherBook.getName()); result = result && Objects.equals(getDescription(), otherBook.getDescription()); return result; } /** * hashCode – Fields used as business key: 1) name 2) description. * * @return integer A hash code value for this object */ @Override Public int hashCode() { Return Objects.hash(getName(), getDescription()); } /** * Getter for property name. * * @return Value of property name */ public String getName() { return this.name; } /** * Setter for property name. * * @param name New value of property name */ public void setName(final String name) { this.name = name; } /** * Getter for property description. * * @return Value of property description */ public String getDescription() { return this.description; } /** * Setter for property description. * * @param name New value of property description */ public void setDescription(final String description) { this.description = description; } /** * Getter for property author. * * @return Value of property author */ public Author getAuthor() { return this.author; } /** * Setter for property author. * * @param author New value of property author */ public void setAuthor(final Author author) { this.author = author; } } .. code-block:: java :caption: class Author :name: M13_pojo_9 :linenos: package org.blog1.pojo; import java.util.Objects; public class Author { private String firstName; private String lastName; private Book book; /** * equals – Fields used as business key: 1) firstName 2) lastName. * * @param other The reference object with which to compare * @return boolean Return true if object is the same as the argument object, otherwise return false */ @Override Public Boolean equals(final Object other) { if (this == other) { return true; } if (!(other instanceof Author)) { return false; } final Author otherAuthor = (Author) other; boolean result; result = Objects.equals(getFirstName(), otherAuthor.getFirstName()); result = result && Objects.equals(getLastName(), otherAuthor.getLastName()); return result; } /** * hashCode – Fields used as business key: 1) firstName 2) lastName. * * @return integer A hash code value for this object */ @Override Public int hashCode() { Return Objects.hash(getFirstName(), getLastName()); } /** * Getter for property firstName. * * @return Value of property firstName */ public String getFirstName() { return this.firstName; } /** * Setter for property firstName. * * @param firstName New value of property firstName */ public void setFirstName(final String firstname) { this.firstName = firstName; } /** * Getter for property lastName. * * @return Value of property lastName */ public String getLastName() { return this.lastName; } /** * Setter for property lastName. * * @param lastName New value of property lastName */ public void setLastName(final String lastName) { this.lastName = lastName; } /** * Getter for property book. * * @return Value of property book */ public Book getBook() { return this.book; } /** * Setter for property book. * * @param book New value of property book */ public void setBook(final Book book) { this.book = book; } } In the next blog of this series, we'll explain how you can use the Freemarker template engine in various ways.