Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JPA: Singleton Entity

I got a case to deal with an Entity that has one and only one instance.

The case is to model a Configuration class, which will be created once, updated many times and never deleted.

The main idea, is that the entity will be like the properties file but persisted in the database.

Is there any recommandations for modeling this class, any tips or ideas ?

like image 452
Jason Bourne Avatar asked Aug 20 '16 11:08

Jason Bourne


2 Answers

I'll answer a more general question - How implement a DB stored application configuration nicely?

In general, you may have a configuration for different scopes of your application, rather than a single all in one Configuration. I.e. for example you may have CarConfig, CacheConfig, BookingConfig, etc, including some general AppConfig as well. Sometimes you need to add/remove properties over the development.

So to handle it conveniently, I suggest that instead having an individual table for each config (prons will be described below), you have a single string key/value Configuration table that will serve all configs. And here is how:

For each config, create an interface like that:

public interface CarConfig {       
    @PropertyName("default.color")
    @DefaultValue("red")
    String getDefaultColor();

    void setDefaultColor(String color);

    @PropertyName("max.size")
    @DefaultValue(100)
    int getMaxSize();

    void setMaxSize(int size);

    ...
}

On application startup, a single config implementation is instantiated and then shared across the application. The implementation is done by creating a proxy.

For getters the proxy analyzes the invoked method (for ex: getDefaultColor()) - it gets the property name from the annotation @PropertyName. It then queries the configuration table in the database with this property name. The value is converted (as it's stored as a String in the table) to the method return type (in case of getMaxSize() to int) and returned.

For setters the proxy saves the given values to the configuration table. No need to duplicate @PropertyName - it can be derived from the corresponding getter.

So whenever you need a config property value, you go like:

CarConfig carConfig = configs.get(CarConfig.class);
String defaultColor = carConfig.getDefaultColor();

Why config implementations should be singletons and shared across the applicaton? Because in this case you can enable caching - if you ensure that config properties are always used only via your configs, you can read all the values at startup and store them in memory, and then not query the database at all, since there is no any other source of change except your config class. Setters would update the cached values as well as the database configuration table.

Configuration table would look similar to:

|     key       |value |
...
|"default.color"|"blue"|
|"max.size"     |"85"  |
...

So in case of a single configuration table adding a new property is just the matter of adding the getter and setter to the class - the proxy will automatically pick it up.

A similar architecture is used in the CUBA open source framework - https://www.cuba-platform.com/.

On the contrary, having a separate table for separate config has such prons as:

  • for each config you need create a new table;
  • adding/removing a new config property requires a table schema change.
like image 126
Artem Novikov Avatar answered Nov 09 '22 05:11

Artem Novikov


I wouldn't model it as a single JPA entity, but rather using a Facade that implements the following methods

Configuration getConfiguration(String key);
Configuration addConfiguration(Configuration configuration);
Configuration updateConfiguration(Configuration configuration);
void deleteConfiguration(Configuration configuration);
Collection<Configuration> getCompleteConfiguration();

You are, of course, free to add additional methods if you need them. This facade uses a repository to perform the persistence related tasks. Depending on your setup, complexity and architecture you might want to add a mapping between the actual configuration JPA entity used in the repository and the Configuration class the facade uses.

The actual ConfigurationEntity could look like this (feel free to add additional JPA annotations and/or columns)

@Entity
@Table(name = "CONFIGURATION")
public class ConfigurationEntity {

    @Id
    @GeneratedValue
    private Long id;
    private String key;
    private String value;
....
like image 28
Guenther Avatar answered Nov 09 '22 05:11

Guenther