Search code examples
javaoopinheritanceinterfaceabstract-class

Java make better use of inheritance and abstract class


I have the following structure in my project. My question is how can I avoid adding more and more queries to abstract service? As I keep adding modules, the lines of code in abstract service will keep increasing and I do not want that.

public abstract class AbstractService < T, C > implements GenericService < T, C > {

    @Autowired
    private QueryReader queryReader;

    private String readQuery(String query) {
        return queryReader.getPropertyValue(query);
    }

    public String getOrdersQuery(List < Criteria> request) {
        return addConditionsToQuery(getOrdersQuery(), request);
    }

    public String getCustomersQuery(List < Criteria> request) {
        return addConditionsToQuery(getCustomersQuery(), request);
    }

    private String addConditionsToQuery(String sql, List < Criteria> conditions) {
        return QueryHelper.addConditionsToQuery(conditions, sql);
    }


    private String getOrdersQuery() {
        return readQuery("queries.orders.retrieve");
    }

    private String getCustomersQuery() {
        return readQuery("queries.customers.retrieve");
    }

    public String updateCustomerQuery() {
        return readQuery("queries.customer.update");
    }

}

Generic Service goes like this -

public interface GenericService<T,C> {

    void update(T t);

    void create(T t);

    T search(C t);
}

Here is my Order service -

@Service
public class OrderService extends AbstractService<OrderModel, List<Criteria>> {

    @Autowired
    private OrderRepository orderRepository;

    @Override
    public OrderModel search(List<Criteria> request) {
        String query = getOrdersQuery(request);
        List<OrderEntity> orderEntities = orderRepository.findByQuery(query);
        // entity dto conversion and so on
        // ....
        return orderModel;

    }
}

Here is my Customer Service -

@Service
public class CustomerService extends AbstractService < CustomerModel, List < Criteria >> {

    @Autowired
    private CustomerRepository customerRepository;

    @Override
    public CustomerModel search(List < Criteria > request) {
        String query = getCustomersQuery(request);
        List < CustomerEntity > customerEntities = customerRepository.findByQuery(request);
        // entity dto conversion and so on
        // ....
        return customerModel;

    }

    @Transactional
    @Override
    public void update(CustomerModel request) {
        // dto to entity conversion
        // .....
        // update entity with new values 
        customerRepository.update(orderEntity, updateCustomerQuery());
    }

}

Solution

  • In the first step, expose two protected methods:

    • addConditionsToQuery
    • readQuery

    And get read of all the "service specific" methods. It doesn't make sense that an abstract class knows something about its children.

    
    public abstract class AbstractService < T, C > implements GenericService < T, C > {
    
        @Autowired
        private QueryReader queryReader;
    
        protected String readQuery(String query) {
            return queryReader.getPropertyValue(query);
        }
    
        protected String addConditionsToQuery(String sql, List < Criteria> conditions) {
            return QueryHelper.addConditionsToQuery(conditions, sql);
        }
    }
    

    Now, the OrdersService for example, should know from where its query should be resolved

    @Service
    public class OrderService extends AbstractService<OrderModel, List<Criteria>> {
    
        @Autowired
        private OrderRepository orderRepository;
    
        @Override
        public OrderModel search(List<Criteria> request) {
            String sql = readQuery("queries.orders.retrieve"); 
            String query = addConditionsToQuery(sql, request);
            List<OrderEntity> orderEntities = orderRepository.findByQuery(query);
            // entity dto conversion and so on
            // ....
            return orderModel;
    
        }
    }
    

    This is alone directly answers your question.

    All-in-all, since we're talking about SQLs and you have spring here, its probably not an educational project, so probably you can consider using sql management libraries like MyBatis that might be a good solution because it also manages SQL queries in Files (externally to the code) and provides a template engine for dynamic SQL queries Generation .

    I've mentioned MyBatis only because its kind of close in design concepts to what you've presented in the question. There are also other alternatives if you don't want to deal with Hibernate/JPA/Spring Data (Jooq, jdbi to name a few).