在调用 REST API 的情况下填充数据库列

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

Populate a database column by calling a rest API

问题

我有一个小的Spring Boot服务,其中包含一个数据库(我使用Spring Data JPA),我需要从另一个REST API获取一些额外的信息。所以我只能通过调用一个REST API并传递来自我的数据库的ID来获取这些额外的信息,然后保存它们。

因此,我创建了一个带有端点/migrate的控制器,我的想法是当我启动服务时,我可以调用这个端点,然后使用findAll()来加载所有条目,最后遍历它们并为每个条目调用第三方REST端点以获取额外的信息。到目前为止,一切都很好……但我不确定这里的最佳方法是什么。我的端点应该只是一个void方法,还是应该将其建模为ResponseEntity?我的问题更多是什么是最佳实践。

以下是一些代码示例:

@RestController
@RequiredArgsConstructor
@Slf4j
public class MigrationController {

    private final MigrationService migrationService;

    @GetMapping("/migrate")
    public ResponseEntity<MigrationResult> migrateData() {

        var migrationResult = migrationService.migrateData();
        // 这里不是最佳实践...
        if (migrationResult.getStatusCode().startsWith("2")) {
            return ResponseEntity.ok().body(migrationResult);
        } else {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(migrationResult);
        }
    }
}

这是MigrationService

@Service
@AllArgsConstructor
public class MigrationService {

    private final CustomerRepository customerRepository;
    private final OrderService orderService;

    public MigrationResult migrateData() {
        // ...
        // 你的代码逻辑
        // ...
        return migrationResult;
    }
}

这是我使用的ResponseEntity模型:

public class MigrationResult {
    @NotNull
    private String statusCode;
    @NotNull
    private String message;
}

正如你所看到的,我需要从所谓的orderService获取这个uniqueId,然后保存到我的Customer实体中。显然,这需要一些时间来完成(大约1000个条目->调用)。

我感到我所做的方式有点问题。但我不确定什么是最佳方法,即如何更好地执行此类操作,同时在错误消息和可能的超时方面保持HTTP兼容性(如果作业运行时间太长)。此外,没有UI界面 - 这就是我决定使用这个小的POJO MigrationResult 的原因,可以将其作为JSON返回。

英文:

I have a small spring boot service with a database (I use spring data JPA) and I need some additional info from another rest API. So I can get this additional info only by calling a rest API with ids from my database, and then save it.

So I created a controller with an endpoint /migrate and my idea is that when I start the service, I can call this endpoint, then I use findAll() to load all entries, and finally I iterate over them and call the 3rd party rest endpoint for each to get the additional info. So far so gut... but I am not sure what is the best approach here. Shall my endpoint be just a void method or, I should I model it ResponseEntity? My question is more or less what is the best practice here.

Here is some code.

@RestController
@RequiredArgsConstructor
@Slf4j
public class MigrationController {

private final MigrationService migrationService;

@GetMapping(&quot;/migrate&quot;)
public ResponseEntity&lt;MigrationResult&gt; migrateData() {

    var migrationResult = migrationService.migrateData();
    // this is not optimal here...
    if (migrationResult.getStatusCode().startsWith(&quot;2&quot;)) {
        return ResponseEntity.ok().body(migrationResult);
    } else {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(migrationResult);
    }
}

}

And here is the MigrationService :

@Service
@AllArgsConstructor
public class MigrationService {

    private final CustomerRepository customerRepository;
    private final OrderService orderService;

    public MigrationResult migrateData() {
        var updatedCustomersCounter = new AtomicInteger();
        var migrationResult = new MigrationResult();
        long start = System.currentTimeMillis();

        var customers = customerRepository.findAll();

        if (customers.isEmpty()) {
            return MigrationResult.builder()
                .message(&quot;No customer datafound in database&quot;)
                .statusCode(&quot;404&quot;)
                .build();
        }

        for (Customer customer : customers) {
            try {
                var order = orderService.getOrderById(customer.getOrderId());

                if (order != null &amp;&amp; !order.getOrders().isEmpty()) {
                    customer.setUniqueId(order.getOrders().get(0).getUniqueId());
                    updatedCustomersCounter.getAndIncrement();
                    customerRepository.save(customer);
                }

                if (order != null &amp;&amp; order.getOrders().isEmpty()) {
                    return MigrationResult.builder()
                        .message(&quot;Migration of unique Id for oder id &quot; + order.getOrderId() + &quot; failed.&quot;)
                        .statusCode(&quot;500&quot;)
                        .build();
                }

            } catch (Exception e) {
                migrationResult.setMessage(&quot;Unexpected error while updating customer.&quot;);
                migrationResult.setStatusCode(&quot;500&quot;);
            }

            migrationResult.setMessage(
                &quot;Migration of &quot; + updatedCustomersCounter.get() + &quot; customers finished in: &quot; + (System.currentTimeMillis() - start)
                    + &quot;ms. Total number of entries with present orderId &quot; + filteredDataPlans.size());
            migrationResult.setStatusCode(&quot;200&quot;);
        }

        return migrationResult;
    }

}

Here is my model I use with the ResponseEntity:

public class MigrationResult {
    @NotNull
    private String statusCode;
    @NotNull
    private String message;
}

As you can see, I need this uniqueId from so-called orderService and then I save it to my Customer Entity. Obviously that will take some time to finish (around 1000 entries -> calls).
I feel it is 'fishy' the way I've done it. But I am not sure what is the best approach, i.e. what is the better way to do something like this and still be HTTP conform regarding error messages and possible timeout (if the job runs too long). Also, there is no UI - That's why I decided to use this small POJO MigrationResult with message that I could become as JSON.

答案1

得分: 2

依赖于您的需求。如果您不需要这些信息,您可以简单地返回HttpStatus.Ok,但我认为最好的方法是在迁移任务完成后返回一个报告。我更喜欢将这些信息存储在单独的变量中,而不是将信息包装在字符串变量中,并创建一个方法来构建要显示的数据消息。

public class MigrationReport {
    private String status;
    private String startDate;
    private String numberOfusers;
    private Long durationTime;
    private String message;
    
    // getter && setter methods
}

建议:使用 Instant.now() 替代 long start = System.currentTimeMillis();

英文:

It depends on requirements you have .If you don't need such information you can simply return HttpStatus.Ok, but i think best approach would be to return a report after migration task is completed.Instead of wrapping information on String variable i would prefer to store this info in separate variables and create a method to build message with data you want to display.

public class MigrationReport{
  
   private String status;
   private String startDate;
   private String numberOfusers;
   private Long   durationTime;
   //
   // 
   private String message;
   
   //getter &amp;&amp; setter methods 

} 

Suggestion : Instead of long start = System.currentTimeMillis(); use Instant.now()

huangapple
  • 本文由 发表于 2020年8月9日 06:51:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/63320930.html
匿名

发表评论

匿名网友

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

确定