Commit 663586eebb22972a05a67c5b7460c5e73db0aba1

Authored by Benjamin Rühl
1 parent aff23f2f

Add documentation about persistence layer

README.md
@@ -195,7 +195,7 @@ NOTE: Localtunnel tends to crash every now and then, displaying an error message @@ -195,7 +195,7 @@ NOTE: Localtunnel tends to crash every now and then, displaying an error message
195 195
196 ## Debugging 196 ## Debugging
197 197
198 -At the moment, the Spring Boot server is always started in debug mode. To attach the process using IntelliJ, a remote debug configuration has to be created. To do so, perform the following steps: 198 +At the moment, the JBoss server is always started with open debugging ports. To attach to the process using IntelliJ, a remote debug configuration has to be created. To do so, perform the following steps:
199 199
200 - Go to `Run` -> `Edit Configurations...` 200 - Go to `Run` -> `Edit Configurations...`
201 - Add a new configuration using the button in the top left corner of the dialog 201 - Add a new configuration using the button in the top left corner of the dialog
@@ -217,7 +217,7 @@ The following issues are currently known in the application: @@ -217,7 +217,7 @@ The following issues are currently known in the application:
217 217
218 ### Wildfly 218 ### Wildfly
219 219
220 -- During the boot process of the Wildfly server, the error `Mount point not found` occurs 220 +- During the boot process of the Wildfly server, the error `Mount point not found` occurs, wich does not seem to have any impact on the running system
221 221
222 <!-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --> 222 <!-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -->
223 223
docu/img/persistence/domain-model.png 0 → 100644

57.1 KB

docu/persistence.md 0 → 100644
  1 +# BeuthBot persistence FAQ
  2 +
  3 +## Which technologies are used in the persistence layer?
  4 +
  5 +- a **PostgreSQL** database is running as a seperate docker container
  6 +- **Adminer** is used as a web client for inspection and manipulation of the database; it is running in its own docker container
  7 +- to enable the communication between java and the database, a database driver is required which is the **postgres jdbc driver** in our case
  8 +- **JPA** is the specification which is used as interface for every interaction with the database using java
  9 +- **Hibernate** implements JPA and is used as the persistence provider which does the actual object-relational mapping
  10 +
  11 +## Where can the configuration files be found?
  12 +
  13 +- the jdbc driver can be found in `/docker/postgres/volumes`
  14 +- the connection between the JBoss server and the database is configured as a subsystem in `/docker/wildfly/standalone.xml`
  15 +- the configuration of Hibernate is located in `common/resources/META-INF/persistence.xml`
  16 +
  17 +## How to interact with persisted entities?
  18 +
  19 +- Data Access Objects (DAOs) are designed as an encapsulation of JPA / Hibernate
  20 + - DAO interfaces provide CRUD functionality
  21 + - DAO implementations use JPA to perform operations on the database
  22 +- The following snippet shows tha base interface for all DAOs:
  23 +
  24 +```java
  25 +public interface GenericDAO<T extends Entity, ID extends Serializable> {
  26 +
  27 + T findById(ID id);
  28 +
  29 + List<T> findAll();
  30 +
  31 + T saveOrUpdate(T entity);
  32 +
  33 + void delete(T entity);
  34 +}
  35 +```
  36 +
  37 +- The following snippet shows a DAO interface for a specific entity having the basic CRUD functionality and optional extensions to that
  38 +
  39 +```java
  40 +public interface AppUserDAO extends GenericDAO<AppUser, Long> {
  41 + AppUser createUser();
  42 +}
  43 +```
  44 +
  45 +# How can a DAO instance be used?
  46 +
  47 +- using dependency injection in a class that is managed by JBoss
  48 +
  49 +```java
  50 +@Resource(lookup = "java:global/global/AppUserDAO")
  51 +private AppUserDAO userDAO;
  52 +```
  53 +
  54 +# How to create an entity?
  55 +
  56 +- currently a DAO is responsible for creating instances of its context entity, e.g. `AppUser newUser = userDAO.createUser()`
  57 +- direct creation of an entity like `AppUser newUser = new AppUser()` were not possible at first, but could be used now, too
  58 +- When the persistence layer was designed, direct references to the entity classes were impossible to use, because those classes were placed in the `global` module. This location changed to `common` because this solved some issues that could no be fixed otherwise, although this solution is less clean.
  59 +
  60 +```java
  61 +public interface AppUserDAO extends GenericDAO<AppUser, Long> {
  62 +
  63 + AppUser createUser();
  64 +}
  65 +```
  66 +
  67 +- the entity can then be modified and updated in the database using the DAO instance
  68 +
  69 +# AppUsers have generic attributes. How can they be used?
  70 +
  71 +- the **Entity-Attribute-Value model (EAV)** is a common pattern in relational database design which aims at structuring a database schema in rows instead of columns; please do some research to really understand it
  72 +- the pattern is implemented and used in the `AppUser` entity to give developers the ability to define attributes at runtime
  73 +- the advantage here is that attributes can be created and retrieved in `Drools` without changing the actual entity class
  74 +- the disadvantage is that a developer cannot rely on an attribute being present and having the expected data type
  75 +- the following snippet is a usage expample of the implemented pattern
  76 +
  77 +```java
  78 +AppUser u = userDAO.createUser();
  79 +
  80 +u.setProperty("test_property_string", "test_value");
  81 +u.setProperty("test_property_bool", true);
  82 +u.setProperty("test_property_double", 3.65);
  83 +
  84 +userDAO.saveOrUpdate(u);
  85 +```
  86 +
  87 +- the shown syntax is achieved using custom json serialization as a convenience method
  88 +- instead of this, the EAV structure can be build manually, too
  89 +- please note that complex data types and lists are not supported using this syntax at the moment; to add support, the custom serializers and deserializers found in `common/persistence` must be extended
  90 +
  91 +# How does the implementation of the EAV pattern work internally for the AppUser entity?
  92 +
  93 +![Domain model as UML class diagram](img/persistence/domain-model.png)
  94 +
  95 +# How to retrieve the generic attributes of an AppUser?
  96 +
  97 +- the following snippet shows the retrieval using the convenience method
  98 +- instead of this, the EAV structure can be queried an walked manually, too
  99 +
  100 +```java
  101 +AppUser u = userDAO.findById(1);
  102 +
  103 +String testValueString = u.getProperty("test_property_string", String.class);
  104 +boolean testValueBool = u.getProperty("test_property_bool", Boolean.class);
  105 +Double testValueDouble = u.getProperty("test_property_double", Double.class);
  106 +```
  107 +
  108 +# How does the convenience method for accessing the `GenericEntity` hierarchy actually work?
  109 +
  110 +A trivial implementation providing access to the structure with getters and setters is provided, but it gives the responsibility of traversing this structure to the using component. To simplify this for developers, a mechanic using custom JSON serializers and deserializers has been implemented.
  111 +When setting a generic attribute, the given value is serialized to JSON using a common serializer. The value as JSON is then given to the custom deserializers which can create a GenericEntity structure from this JSON.
  112 +When retrieving the value later, the GenericAttribute hierarchy behind the given attribute key is serialized to JSON using the custom serializers as if it would be an unvariable implemented structure instead of the dynamic EAV model. This JSON is then given to a common deserializer which creates an instance of the desired type from it.
  113 +<br>
  114 +
  115 +To understand the flow better, see the following example.
  116 +A common serialization of a `GenericEntity` hierarchy could look like the following:
  117 +
  118 +```json
  119 +{
  120 + "name": "user",
  121 + "attributes": [
  122 + {
  123 + "name": "test_property_string",
  124 + "values": [
  125 + {
  126 + "valueAsLong": null,
  127 + "valueAsBool": null,
  128 + "valueAsDouble": null,
  129 + "valueAsString": "test_value",
  130 + "valueAsEntity": null,
  131 + }
  132 + ]
  133 + },
  134 + {
  135 + "name": "test_property_bool",
  136 + "values": [
  137 + {
  138 + "valueAsLong": null,
  139 + "valueAsBool": true,
  140 + "valueAsDouble": null,
  141 + "valueAsString": null,
  142 + "valueAsEntity": null,
  143 + }
  144 + ]
  145 + }
  146 + {
  147 + "name": "test_property_object",
  148 + "values": [
  149 + {
  150 + "valueAsLong": null,
  151 + "valueAsBool": true,
  152 + "valueAsDouble": null,
  153 + "valueAsString": null,
  154 + "valueAsEntity": {
  155 + "name": "FavoriteFood",
  156 + "attributes": [...]
  157 + }
  158 + }
  159 + ]
  160 + }
  161 + ]
  162 +}
  163 +```
  164 +
  165 +The custom serializers instead have something like the following output:
  166 +
  167 +```json
  168 +{
  169 + "test_property_string": "test_value",
  170 + "test_property_bool": true,
  171 + "test_property_object": {
  172 + ...
  173 + }
  174 +}
  175 +```
  176 +
  177 +This result can be used as input for a common JSON deserializer which can create an instance of a desired type from it as long as the type has exactly the same attributes as the structure has in JSON.
0 \ No newline at end of file 178 \ No newline at end of file