0

I have a service layer that Autowires a repository like so

@Service
public class OrderService {

    @Autowired
    private OrderRepository repository;

    public List<Order> retrieveOrders(String storeId) {
        try {

            return repository.getOrders(storeId)

        } catch(Exception e) {
            ...
        }
    }
}

I have new requirement that, depending on the store number, the repository query logic should change. I'm thinking of abstracting my repository and using a factory pattern. The current repository is an interface and extends JpaRepository; further more all of its functions are using @Query annotation to define the JPQL. This is an example with just one function, but there are several functions in my actual Repository

public interface OrderRepository extends JpaRepository {

    @Query("select o " +
        " from Orders o " +
        " where o.storeId = :storeId")
    public List<Order> getOrders(@Param(value = "storeId") String storeId);

}

The new requirements state that for a specific storeId, the SQL needs to change to something like this:

public interface OrderRepositoryStore886 extends JpaRepository {

    @Query("select o " +
        " from Order o " +
        " where o.storeId = :storeId " +
        "     and o.status IN (1,3,10,15) " +
        "     and EXISTS (SELECT c from CollabOrder WHERE c.orderId = o.orderId)")
    public List<Order> getOrders(@Param(value = "storeId") String storeId);

}

However all remaining stores should use the first SQL query.

I am trying to figure out how to conditionally @Autowire the repository based on storeId. I was thinking of creating a factory but I don't know how to make a factory that returns interface objects (I am still fairly new to java, this might be trivial but I've yet to figure it out). And if I try to turn the Repository interface into a class, it forces me to Override all of the abstract JpaRepository methods which is unnecessary extra code.

Geep
  • 3
  • 1
  • Does this answer your question? [How to do conditional auto-wiring in Spring?](https://stackoverflow.com/questions/19225115/how-to-do-conditional-auto-wiring-in-spring) – Dusayanta Prasad May 03 '20 at 14:45
  • It might be helpful if my Repository objects were classes, but since they are interfaces, I don't know how to apply the accepted answer in this case – Geep May 03 '20 at 14:57

1 Answers1

0

You are already on the right track. I would solve the problem but creating a factory method that returns the right bean interface.

The only drawback with this method is that you can't Autowire you beans. You should instantiate them on the fly.

create table orders
(
  id                      integer      not null,
  constraint pk_orders primary key(id)
);

@Entity
@Table(name = "orders")
@ToString
@Getter
@Setter
public class Order {

    @Id
    private long id;
}


public interface OrderRepository extends JpaRepository<Order, Long> {
    @Query("SELECT t FROM Order t")
    Order getOrder(String storeId);
}


@Repository
public interface OrderRepositoryStore100 extends OrderRepository {
    @Query("SELECT t FROM Order t WHERE t.id = 100")
    Order getOrder(@Param(value = "id") String storeId);
}

@Repository
public interface OrderRepositoryStore286 extends OrderRepository {
    @Query("SELECT t FROM Order t WHERE t.id = 286")
    Order getOrder(@Param(value = "id") String storeId);
}

public class OrderRepositoryStoreFactory {

    private static final String REPOSITORY_BASE_NAME = "orderRepositoryStore";

    public static OrderRepository getRepository(ApplicationContext context, String storeId) {

        return (OrderRepository) context.getBean(REPOSITORY_BASE_NAME + storeId);
    }
}

@Service
public class OrderService {

    @Autowired
    private ApplicationContext context;

    public Order retrieveOrders(String storeId) {
        try {

            OrderRepository repository = OrderRepositoryStoreFactory.getRepository(context, storeId);

            return repository.getOrder(storeId);

        } catch(Exception e) {
            return null;
        }
    }
}



@SpringBootApplication
public class FactoryApplication implements CommandLineRunner {

    @Autowired
    private ApplicationContext context;

    @Autowired
    private OrderService orderService;

    public static void main(String[] args) {
        SpringApplication.run(FactoryApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        Order order = orderService.retrieveOrders("100");
        System.out.println(order);

        order = orderService.retrieveOrders("286");
        System.out.println(order);
    }
}

And here is the output:

2020-05-03 20:23:34.567 INFO 33955 --- [ main] com.factory.FactoryApplication : Started FactoryApplication in 3.003 seconds (JVM running for 3.615) Order(id=100) Order(id=286)

Hope this helps

youness.bout
  • 343
  • 3
  • 9
  • It's possible I am missing something, because I am getting "NoSuchBeanDefinitionException: No bean named '' available" (In this example would be either orderRepositoryStore100 or orderRepositoryStore886). I verified they both have the @Repository annotation – Geep May 03 '20 at 19:23
  • Please check my answer again, I have added a fully functional example. If you still have the problem, it would be great if you share more code. – youness.bout May 03 '20 at 20:27
  • Thank you. Since you last posted I tried using @ Component instead of @ Repository and it worked (but maked no sense since to me since @ Repository is a type of @ Component). Toyed around with some other stuff and now @ Repository works. At this point I conceded I have no idea what I'm doing, and throwing darts blindly and seeing what sticks :) – Geep May 03 '20 at 20:49
  • You are welcome. Feel free to ask any question that bothers you. – youness.bout May 03 '20 at 21:00
  • Just a heads up, I wasn't able to get the OrderRepositoryStoreFactory tests to work using this way. However I replaced context.getBean() with JpaRepositoryFactory.getRepository() and now it works – Geep May 04 '20 at 13:40
  • That's great to hear. – youness.bout May 04 '20 at 16:02