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());
}
}
In the first step, expose two protected methods:
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).