.. ******************************************************************************* * MetaFactory.io R2 * Technical Documentation * * File name : M16_Repository.rst * Author : Sandra Kunnen * Created by : Niels de Nies * * MetaFactory BV Amsterdam ******************************************************************************* .. include:: /inline-images.txt .. include:: /metadata_include.txt .. include:: /Miscellaneous/UnderConstruction.txt Repository ========== .. include:: /Miscellaneous/AboutSandra.txt In previous blogs we focused on creating Java POJO classes with some fields and methods. Now let’s start using these POJO classes by creating some repository interfaces and implementations. Enrich a POJO with JPA ---------------------- Before we can work on our repository classes, we need to enrich our POJO classes. We need entities which can be used by the repositories. Luckily, we now know exactly how we can very easily enrich the classes: we can create a JPA code instruction. Let's first check how we would like the result to look like: .. code-block:: java :caption: Desired JPA code instruction :name: M16_Repository_01 :linenos: @Entity @Table(name = "author") public class Author { @Id @Column(name = "id") private Long id; @Column(name = "first_name") private String firstName; @Column(name = "last_name") private String lastName; @Column(name = "book") @OneToMany private List bookList; // Equals, hashCode, and toString methods // Getters and setters } As you can see: - we added some annotations to the class and to the fields. - we have the class as an entity and table with the name “author”. - we also introduced a new field *id* as table primary key. With the knowledge from earlier blogs, we can hopefully already imagine how the code instruction to add these annotations will look like: .. code-block:: xml :caption: JPA code instruction :name: M16_Repository_02 :linenos: :emphasize-lines: 15,28,37,48 Created by java/pojo/fields-jpa.xml javax.persistence.Entity javax.persistence.Table @Entity @Table(name = "${getTableName(${object.name})}") javax.persistence.Id javax.persistence.Column @Id @Column(name = "id") java.lang.Long javax.persistence.Column @Column(name = "${var1}") ${attribute.type} javax.persistence.Column javax.persistence.OneToOne @Column(name = "${var1}") @OneToOne ${createPojoClassName(${reference.type})} java.util.List javax.persistence.Column javax.persistence.OneToMany @Column(name = "${var1}") @OneToMany ]]> We created this code instruction as an addition to the existing code instruction for the POJO fields and methods. The only thing this code instruction does is adding the extra field *id*, some new annotations and the necessary imports. We introduced 2 new functions: *getTableName* and *getColumnName*. :numref:`M16_Repository_03` shows the implementation for the getTableName function and the related util function: .. code-block:: :caption: getTableName function :name: M16_Repository_03 :linenos: <#import "/library/util.ftl" as util /> <#--stop if arg1 is null--> <#if !(arg1)??> <#stop "arg1 (name of model object) not found in context" > ${util.camelToSnakeCase(arg1)} <#function camelToSnakeCase value> <#return value?uncap_first?replace("[A-Z]", "_$0", "r")?lower_case /> Now *getTableName* and *getColumnName* have the same result. They use the name of a model object, attribute, or reference and convert it from CamelCase to snake_case. Still we introduced them as separate functions so that we can easily change the implementation for either one without affecting the other. |Question S| Why? Now that we converted our POJO classes to entities, we can continue with the creation of the repositories. Repository implementation ------------------------- There are many, many ways of implementing a repository layer. Luckily, with MetaFactory's Code Composer you are in full control of the architecture. Everything is possible. In our example we take a look at JPA in combination with the JPA2 EntityManager. In many applications it is useful to introduce a repository interface. Let us start with writing an interface with definitions for 2 methods, *get* and *save*: .. code-block:: java :caption: Repository interface :name: M16_Repository_04 :linenos: public interface IAuthorRepository { Author get(Long id); Author save(Author entity); } Now let's have a look at the corresponding code instruction: .. code-block:: xml :caption: Code instruction Repository Interface :name: M16_Repository_05 :linenos: Find entity by primary key. ${createPojoFQCN(${object.name})} The retrieved entity. ${createPojoClassName(${object.name})} The id of the entity to be retrieved. Long Generic save method ${createPojoFQCN(${object.name})} The saved entity. ${createPojoClassName(${object.name})} The entity to be saved. ${createPojoClassName(${object.name})} Just like we have defined classes before, here we start with defining an interface. The rest of the code instruction is quite similar to what we have seen before. We define in our example 2 methods without bodies, as is custom for interfaces. An implementation class of this interface could look like this: .. code-block:: java :caption: Desired interface class :name: M16_Repository_06 :linenos: public class AuthorRepository implements IAuthorRepository { private Class persistentClass; @PersistenceContext private EntityManager entityManager; @Override public Author get(final Long id) { return entityManager.find(this.persistentClass, id); } @Override public Author save(final Author author) { Author result; if (!entityManager.contains(author)) { entityManager.persist(author); } result = author; return result; } public Class getPersistentClass() { return this.persistentClass; } public void setPersistentClass(final Class persistentClass) { this.persistentClass = persistentClass; } } And here its accompanying code instruction: .. code-block:: xml :caption: Code instruction interface class :name: M16_Repository_07 :linenos: :emphasize-lines: 9,11,42 ${createRepositoryJPAInterfaceFQCN(${object.name})} ${createRepositoryJPAInterfaceName(${object.name})} ]]> javax.persistence.PersistenceContext javax.persistence.EntityManager @PersistenceContext EntityManager Find entity by primary key. @Override ${createPojoFQCN(${object.name})} The retrieved entity. ${var1} The id of the entity to be retrieved. Long return entityManager.find(this.persistentClass, id); Generic save method @Override ${createPojoFQCN(${object.name})} The saved entity. ${var1} The entity to be saved. ${var1} ${var1} result; if (!entityManager.contains(${var2})) { entityManager.persist(${var2}); } result = ${var2}; return result; Variables --------- This is the first time that we see variables defined in a code instruction (see line 9 and line 42). Often, we have strings like the name of classes or objects that are used in multiple places in a code instruction. Instead of having to use the same function repeatedly, we can store the result in a variable and use the variable throughout the code instruction. Variables can be used in various places within the code instruction, like in method, fields, and parameter implementations. Although it is not necessary, it is good practice to keep variables as local as possible. Variable 1 (line 9) for example is used in multiple methods and fields and therefore defined at class level. Variable 2 (line 42) is only used for a method implementation and is therefore defined locally in the method. Apart from the variables, we have seen most of the other properties before in code instructions or they speak for themselves. With the *implements* property (line 11) on class level for example we define the interface that the class implements.