Hammer L Under construction

4.3. Repository

About the author

Sandra L Sandra is the best. Toevoegen: iets over Sandra en over wat maakt dat ze plezier heeft in haar werk.

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.

4.3.1. 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:

Listing 4.15 Desired JPA code instruction
 1@Entity
 2@Table(name = "author")
 3public class Author {
 4
 5        @Id
 6        @Column(name = "id")
 7        private Long id;
 8
 9        @Column(name = "first_name")
10        private String firstName;
11
12        @Column(name = "last_name")
13        private String lastName;
14
15        @Column(name = "book")
16        @OneToMany
17        private List<Book> bookList;
18
19        // Equals, hashCode, and toString methods
20
21        // Getters and setters
22
23}

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:

Listing 4.16 JPA code instruction
 1<java_package xmlns="https://metafactory.io/xsd/v1/java-codeinstruction"
 2                          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3                          xsi:schemaLocation="https://metafactory.io/xsd/v1/java-codeinstruction
 4                                              https://metafactory.io/xsd/v1/java-codeinstruction.xsd"
 5                          name="${createPojoPackageName()}">
 6
 7
 8        <class name="${createPojoClassName(${object.name})}"
 9                   foreach="object">
10                <apicommentline>Created by java/pojo/fields-jpa.xml
11                </apicommentline>
12                <import>javax.persistence.Entity</import>
13                <import>javax.persistence.Table</import>
14                <annotation>@Entity</annotation>
15                <annotation>@Table(name = "${getTableName(${object.name})}")
16                </annotation>
17
18                <field name="id">
19                        <import>javax.persistence.Id</import>
20                        <import>javax.persistence.Column</import>
21                        <annotation>@Id</annotation>
22                        <annotation>@Column(name = "id")</annotation>
23                        <datatype>java.lang.Long</datatype>
24                </field>
25                <field name="${attribute.name}"
26                           access="rw"
27                           foreach="attribute"
28                           var1="${getColumnName(${attribute.name})}">
29                        <import>javax.persistence.Column</import>
30                        <annotation>@Column(name = "${var1}")</annotation>
31                        <datatype>${attribute.type}</datatype>
32                </field>
33                <field name="${reference.name}"
34                           access="rw"
35                           foreach="reference"
36                           condition="${reference.multiplicity}=0..1 OR ${reference.multiplicity}=1..1"
37                           var1="${getColumnName(${reference.name})}">
38                        <import>javax.persistence.Column</import>
39                        <import>javax.persistence.OneToOne</import>
40                        <annotation>@Column(name = "${var1}")</annotation>
41                        <annotation>@OneToOne</annotation>
42                        <datatype>${createPojoClassName(${reference.type})}</datatype>
43                </field>
44                <field name="${reference.name}List"
45                           access="rw"
46                           foreach="reference"
47                           condition="${reference.multiplicity}=0..n OR ${reference.multiplicity}=1..n"
48                           var1="${getColumnName(${reference.name})}">
49                        <import>java.util.List</import>
50                        <import>javax.persistence.Column</import>
51                        <import>javax.persistence.OneToMany</import>
52                        <annotation>@Column(name = "${var1}")</annotation>
53                        <annotation>@OneToMany</annotation>
54                        <datatype><!
55                                [CDATA[List<${createPojoClassName(${reference.type})}>]]>
56                        </datatype>
57                </field>
58        </class>
59</java_package>

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. Listing 4.17 shows the implementation for the getTableName function and the related util function:

Listing 4.17 getTableName function
 1<#import "/library/util.ftl" as util />
 2
 3<#--stop if arg1 is null-->
 4<#if !(arg1)??>  <#stop "arg1 (name of model object) not found in context" ></#if>
 5
 6${util.camelToSnakeCase(arg1)}
 7
 8<#function camelToSnakeCase value>
 9        <#return value?uncap_first?replace("[A-Z]", "_$0", "r")?lower_case />
10</#function>

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.

4.3.2. 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:

Listing 4.18 Repository interface
1public interface IAuthorRepository {
2
3Author get(Long id);
4
5Author save(Author entity);
6
7}

Now let’s have a look at the corresponding code instruction:

Listing 4.19 Code instruction Repository Interface
 1<java_package xmlns="https://metafactory.io/xsd/v1/java-codeinstruction"
 2              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3              xsi:schemaLocation="https://metafactory.io/xsd/v1/java-codeinstruction
 4                                  https://metafactory.io/xsd/v1/java-codeinstruction.xsd"
 5              name="${createRepositoryJPAInterfacePackageName()}">
 6
 7        <interface name="${createRepositoryJPAInterfaceName(${object.name})}"
 8                   foreach="object">
 9
10                <method name="get"
11                        visibility="">
12                        <apicommentline>Find entity by primary key.</apicommentline>
13                        <import>${createPojoFQCN(${object.name})}</import>
14                        <parameter name="return">
15                                <apicommentline>The retrieved entity.</apicommentline>
16                                <datatype>${createPojoClassName(${object.name})}</datatype>
17                        </parameter>
18                        <parameter name="id">
19                                <apicommentline>The id of the entity to be retrieved.</apicommentline>
20                                <datatype>Long</datatype>
21                        </parameter>
22                </method>
23
24                <method name="save"
25                        visibility="">
26                        <apicommentline>Generic save method</apicommentline>
27                        <import>${createPojoFQCN(${object.name})}</import>
28                        <parameter name="return">
29                                <apicommentline>The saved entity.</apicommentline>
30                                <datatype>${createPojoClassName(${object.name})}</datatype>
31                        </parameter>
32                        <parameter name="entity">
33                                <apicommentline>The entity to be saved.</apicommentline>
34                                <datatype>${createPojoClassName(${object.name})}</datatype>
35                        </parameter>
36                </method>
37
38        </interface>
39</java_package>

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:

Listing 4.20 Desired interface class
 1public class AuthorRepository implements IAuthorRepository {
 2
 3        private Class<Author> persistentClass;
 4
 5        @PersistenceContext
 6        private EntityManager entityManager;
 7
 8        @Override
 9        public Author get(final Long id) {
10                return entityManager.find(this.persistentClass, id);
11        }
12
13        @Override
14        public Author save(final Author author) {
15                Author result;
16                if (!entityManager.contains(author)) {
17                        entityManager.persist(author);
18                }
19                result = author;
20                return result;
21        }
22
23        public Class<Author> getPersistentClass() {
24                return this.persistentClass;
25        }
26
27        public void setPersistentClass(final Class<Author> persistentClass) {
28                this.persistentClass = persistentClass;
29        }
30
31}

And here its accompanying code instruction:

Listing 4.21 Code instruction interface class
 1<java_package xmlns="https://metafactory.io/xsd/v1/java-codeinstruction"
 2              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3              xsi:schemaLocation="https://metafactory.io/xsd/v1/java-codeinstruction
 4                                  https://metafactory.io/xsd/v1/java-codeinstruction.xsd"
 5                          name="${createRepositoryJPAClassPackageName()}">
 6
 7        <class name="${createRepositoryJPAClassName(${object.name})}"
 8               foreach="object"
 9               var1="${createPojoClassName(${object.name})}">
10                <import>${createRepositoryJPAInterfaceFQCN(${object.name})}</import>
11                <implements>${createRepositoryJPAInterfaceName(${object.name})}</implements>
12                <field name="persistentClass"
13                       access="rw">
14                        <datatype><![CDATA[Class<${var1}>]]></datatype>
15                </field>
16                <field name="entityManager">
17                        <import>javax.persistence.PersistenceContext</import>
18                        <import>javax.persistence.EntityManager</import>
19                        <annotation>@PersistenceContext</annotation>
20                        <datatype>EntityManager</datatype>
21                </field>
22
23                <method name="get">
24                        <apicommentline>Find entity by primary key.</apicommentline>
25                        <annotation>@Override</annotation>
26                        <import>${createPojoFQCN(${object.name})}</import>
27                        <parameter name="return">
28                                <apicommentline>The retrieved entity.</apicommentline>
29                                <datatype>${var1}</datatype>
30                        </parameter>
31                        <parameter name="id">
32                                <apicommentline>The id of the entity to be retrieved.
33                                </apicommentline>
34                                <datatype>Long</datatype>
35                        </parameter>
36                        <body>
37                                return entityManager.find(this.persistentClass, id);
38                        </body>
39                </method>
40
41                <method name="save"
42                        var2="${firstLower(${var1})}">
43                        <apicommentline>Generic save method</apicommentline>
44                        <annotation>@Override</annotation>
45                        <import>${createPojoFQCN(${object.name})}</import>
46                        <parameter name="return">
47                                <apicommentline>The saved entity.</apicommentline>
48                                <datatype>${var1}</datatype>
49                        </parameter>
50                        <parameter name="${var2}">
51                                <apicommentline>The entity to be saved.</apicommentline>
52                                <datatype>${var1}</datatype>
53                        </parameter>
54                        <body>
55                                ${var1} result;
56                                if (!entityManager.contains(${var2})) {
57                                        entityManager.persist(${var2});
58                                }
59                                result = ${var2};
60                                return result;
61                        </body>
62                </method>
63        </class>
64</java_package>

4.3.3. 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.