Home > Blockchain >  Caching data from database and boiler plate code
Caching data from database and boiler plate code

Time:01-03

Let's say I have this Util class:

public class Resources {

    public static List<Product> PRODUCTS;

    public static List<Customer> CUSTOMERS;


    public static Product getProductById(int id) {
        for (Product product : PRODUCTS) {
            if (product.getIdProduct() == id) {
                return product;
            }
        }
        return null;
    }

    public static Customer getCustomerById(int id) {
        for (Customer customer : CUSTOMERS) {
            if (customer.getId() == id) {
                return customer;
            }
        }
        return null;
    }

}

  • This serves as caching mechanism for entities Product and Customer, which were retrieved from database. So whenever I need to work with Customer (or Product) first I take a look at the Resources.CUSTOMERS (PRODUCTS) if it's there I save the time and don't have to call server. Not sure if Spring can handle it on itself, so I did it that way.
  • My issue is huge amount of boiler plate code here. If I will have 10, 20,... entities I will have to write 10, 20,... getter method and all of them will do pretty same thing. What is the best acceptable solution respecting some best practices?

CodePudding user response:

You could use a generic method to retrieve an entity by it's ID. Just introduce a type parameter to the method, which represents the type of entity being retrieved.

Then you can use a single method for your getCustomerById(int id) and getProductById(int id) methods:

public class Resources {
public static <T> T getEntityById(List<T> entities, int id, Function<T, Integer> idExtractor) {
        for (T entity : entities) {
            if (idExtractor.apply(entity) == id) {
                return entity;
            }
        }
        return null;
    }

}

You can then call this method to retrieve a Product or Customer entity like this:

Product product = Resources.getEntityById(PRODUCTS, productId, Product::getId);
Customer customer = Resources.getEntityById(CUSTOMERS, customerId, Customer::getId);

This will make it a lot easier adding support for additional entity types in the future.

CodePudding user response:

Not sure if Spring can handle it on itself

Most likely, it can. For instance, if you access the database with JPA, JPA caching does exactly this.

Configuring that is a bit of work, because you need to specify eviction policies (i.e. what happens if memory runs out). The implementations also take care of thread safety, and many offer storage on disk or replicating storage in a cluster.

Of course, you can reinvent the wheel. It might even make sense if the wheel you need is much simpler, for instance because you never update the cache once constructed (so thread safety is trivial) and the entire table fits into memory (so you don't need to evict).

To do that, I'd first choose a more appropriate data structure that offers efficient lookup by key, i.e. a Map. And then I would combine all these redundant methods into one:

class Resources {
    static Map<Class<?>, Map<Integer, ?>> CACHE = new HashMap<>(); 
    // ... or something thread safe, if you intend to update the cache later

    public getEntity<E>(Class<E> entityClass, int id) {
        return entityClass.cast(CACHE.get(entityClass).get(id));
    }
}

Usage

    Product p = Resources.getEntity(Product.class, 42);
    Customer c = Resources.getEntity(Customer.class, 36);
  • Related