Spring Boot – 如何创建不同JpaRepository接口的工厂

huangapple go评论65阅读模式
英文:

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&lt;Order&gt; 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&#39;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(&quot;select o &quot; +
    &quot; from Orders o &quot; +
    &quot; where o.storeId = :storeId&quot;)
public List&lt;Order&gt; getOrders(@Param(value = &quot;storeId&quot;) 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(&quot;select o &quot; +
    &quot; from Order o &quot; +
    &quot; where o.storeId = :storeId &quot; +
    &quot;     and o.status IN (1,3,10,15) &quot; +
    &quot;     and EXISTS (SELECT c from CollabOrder WHERE c.orderId = o.orderId)&quot;)
public List&lt;Order&gt; getOrders(@Param(value = &quot;storeId&quot;) 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&#39;t know how to make a factory that returns interface objects (I am still fairly new to java, this might be trivial but I&#39;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&#39;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 = &quot;orders&quot;)
    @ToString
    @Getter
    @Setter
    public class Order {
    
        @Id
        private long id;
    }


    public interface OrderRepository extends JpaRepository&lt;Order, Long&gt; {
        @Query(&quot;SELECT t FROM Order t&quot;)
        Order getOrder(String storeId);
    }


    @Repository
    public interface OrderRepositoryStore100 extends OrderRepository {
        @Query(&quot;SELECT t FROM Order t WHERE t.id = 100&quot;)
        Order getOrder(@Param(value = &quot;id&quot;) String storeId);
    }
    
    @Repository
    public interface OrderRepositoryStore286 extends OrderRepository {
        @Query(&quot;SELECT t FROM Order t WHERE t.id = 286&quot;)
        Order getOrder(@Param(value = &quot;id&quot;) String storeId);
    }
    
    public class OrderRepositoryStoreFactory {
    
        private static final String REPOSITORY_BASE_NAME = &quot;orderRepositoryStore&quot;;
    
        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(&quot;100&quot;);
    		System.out.println(order);
    
    		order = orderService.retrieveOrders(&quot;286&quot;);
    		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>



huangapple
  • 本文由 发表于 2020年5月3日 22:37:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/61576238.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定