英文:
Spring Boot - How to Create Factory of different JpaRepository Interfaces
问题
我有一个服务层,像这样自动装配存储库
@Service
public class OrderService {
@Autowired
private OrderRepository repository;
public List<Order> retrieveOrders(String storeId) {
try {
return repository.getOrders(storeId);
} catch(Exception e) {
...
}
}
}
我有一个新的需求,根据商店编号的不同,存储库的查询逻辑应该更改。我考虑将存储库抽象化并使用工厂模式。当前的存储库是一个接口并扩展了JpaRepository;此外,它的所有函数都使用@Query注解来定义JPQL。这只是一个示例,但是我的实际存储库中有几个函数
public interface OrderRepository extends JpaRepository {
@Query("select o " +
" from Orders o " +
" where o.storeId = :storeId")
public List<Order> getOrders(@Param(value = "storeId") String storeId);
}
新的要求是,对于特定的storeId,SQL需要更改为类似这样的:
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);
}
但是,所有其他商店应该使用第一个SQL查询。
我正在努力弄清楚如何根据storeId条件性地@Autowire存储库。我考虑创建一个工厂,但我不知道如何创建一个返回接口对象的工厂(我在Java方面仍然是新手,这可能是微不足道的,但我还没有弄清楚)。如果我尝试将Repository接口变成一个类,它会强制我覆盖所有抽象的JpaRepository方法,这是不必要的额外代码。
<details>
<summary>英文:</summary>
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.
</details>
# 答案1
**得分**: 0
以下是翻译好的部分:
你已经走在正确的道路上。我会通过创建一个工厂方法来解决这个问题,该方法返回正确的bean接口。
这种方法的唯一缺点是你不能自动装配你的bean。你应该在运行时实例化它们。
创建表 orders
(
id 整数 非空,
主键约束 pk_orders(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);
}
}
希望这对你有所帮助。
<details>
<summary>英文:</summary>
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
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论