【问题标题】:How to implement the simplest alien in a REST webservice?如何在 REST Web 服务中实现最简单的外星人?
【发布时间】:2017-01-24 19:39:17
【问题描述】:

我需要一个直接从stored procedure 返回数据的 JSON 端点。示例:

@Procedure("complex")
String complexStoredProcedure();

最简单的情况是不带参数的 GET 端点...返回的数据是一个 (blabck-box) JSON,与 Spring entity 没有关系,也没有特殊的数据类型 (我的 Spring 应用程序的“外星数据类型”)...

如果你需要一个例子,假设一个简单的控制器为

@RequestMapping(value="/howto", method=RequestMethod.GET)
@ResponseStatus(HttpStatus.OK)
@Procedure(name = "MyRandom.random")  // ?? not work
public String howto() { 
    double x = random();  // from stored-procedure call, how to do it?
    return "{\"result\":"+x+"}";
}

如何实现?也就是说,实现控制器方法的最简单方法(对存储过程的调用)?


编辑:清理冗余代码片段,转移到answer-Wiki below

【问题讨论】:

  • 你能连接实体管理器并运行native query吗?
  • @approxiblue 这并不理想(使用带有 JPA 2.1 规范的 Hibernate),但是是的,如果今天没有好的选择,我可以使用一些原生 PostgreSQL 9.6。
  • 有关更多上下文,另请参阅stackoverflow.com/q/41880120/287948

标签: rest stored-procedures spring-boot


【解决方案1】:

我不确定我是否理解你的问题,但这是我的解决方案。

我在oracle DB 上创建了一个简单的程序:

PROCEDURE TESTPROCOUTPUT
   (param2 OUT VARCHAR2)
   IS
   GETPARAM VARCHAR2(100);
BEGIN
    param2 := 'procedure Called';
END TESTPROCOUTPUT;

要在 Spring 环境中使用 @Procedure,您需要一个 Entity 和一个 Repository。所以我创建了一个简单的Entity,它是Repository

@Entity
public class City {

    @Id
    private String cityCode;

    //...getter/setter
}

存储库:

public interface LandRepository extends CrudRepository<City, String> {

    @Procedure(name="TESTPROCOUTPUT", outputParameterName="param2")
    String TESTPROCOUTPUT();
}

方法名称必须与过程名称相同,这一点很重要。 (不确定它是否只是用于 oracle 的情况。我认为也可能是 camelCase)

所以在您的控制器中,您现在可以轻松地自动装配存储库(或者如果您有接口的实现,请使用它)。

@Controller
public class CityController {

    private CityRepository cityRepository;

    @RequestMapping(value="/howto", method=RequestMethod.GET)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public String howto() { 
        String s = cityRepository.TESTPROCOUTPUT();
        return "{\"result\":" + s + "}";
    }

    public CityRepository getCityRepository () {
        return cityRepository;
    }

    @Autowired
    public void setCityRepository (CityRepository cityRepository) {
        this.cityRepository= cityRepository;
    }
}

结果是:

所以你不能在非存储库方法上使用@Procedure 注解。

直接在存储库方法上声明 JPA 2.1 存储过程映射的注释。

【讨论】:

  • 我在 Wiki-answer 中展示了一个真实案例完整文件(包括所有非焦点方法、导入等),以展示我在代码成瘾中看到的“简单”...但是在这里你可以用更清晰(焦点)的代码来说明......好吧,使用 @Autowired setCityRepository 很好(!),以减少对 service 文件的需求,但是如何用它? 一般 @Autowired setXxRepository 有一个很好的链接? 我尝试在我的项目中重现语法但未编译,“找不到符号”错误。 PS:当我使用 service 文件时一切正常。
  • @PeterKrauss 你能展示你的服务文件吗?是用来构建Spring的应用上下文的吗?您还需要什么来回答您的问题?
【解决方案2】:

我正在尝试回应...对于我的观点和我的基本测试而言,这只是“最简单的”,您可以展示另一种解决方案来获得赏金。

从“https://stackoverflow.com/q/41880120/287948”上下文开始......而且,由于我使用的是 PostgreSQL(SELECT f(x) 有效),@Query(带有nativeQuery = true)是@Procedure 的解决方法。 ..

此答案的问题:未使用 @Procedure... 在帕特里克的回答之后,我看到(并编辑了这一行)您可以将 @Query 替换为 @Procedure(以及帕特里克指定的其他内容)该方法是一样的!


在现有的中添加“alien @Query”的三个步骤

任何Spring都可以使用任何@Query,因此域选择只是一种“房屋组织”和语义,不受您本机SQL代码和domain/repository选择的约束。

  1. 在域的domain.repository 包文件中,使用@Query 添加一个方法,并使用其他导入 添加所有查询上下文导入(QueryAnnotation、JpaRepository、query.Param 等,如果需要);

  2. 在域的service 包文件中,添加新的自定义“查找”方法定义。

  3. 在controll的方法中,调用service中定义的方法。


用真实文件说明

Step1:将新的@Query 添加到现有的存储库文件中,例如。 myprj/address/domain/repository/ICityRepository.java

package com.myprj.address.domain.repository;  // old

import com.myprj.address.domain.entity.City;   //old
// ... other project's specific (old)
import org.springframework.data.jpa.repository.JpaRepository; 
import org.springframework.data.jpa.repository.Query; // new
import org.springframework.data.repository.query.Param; // new
import org.springframework.data.jpa.repository.query.Procedure; // new

@Repository
public interface ICityRepository extends BaseRepository<City, Long> { //old
    Page<City> findByState(State state, Pageable pageable); //old

    // here an alien example! (simplest is a call to a constant value)
    @Query(nativeQuery = true, value= "SELECT 1234.5678") // NEW! 
    Double findCustom();

}

外星人就在那里!

Step2:导入存储库并将定义的findCustom() 添加到现有服务文件中,例如。 myprj/address/service/CityService.java

package com.myprj.address.service; // old

import com.myprj.address.domain.entity.City;
// ... other project's specific (old)

@Service   
public class CityService 
       extends BaseService<City, ICityRepository, Long> {   // old

    @Autowired
    public CityService(ICityRepository repository) {super(repository);} // old

    public Page<City> findByState(State state, Pageable pageable) {
        return repository.findByState(state, pageable);
    } // old

    public Double findCustom() { return repository.findCustom(); }  // NEW!!

}

Step3:将定义的cityService.findCustom() 添加到现有的控制器文件中,例如。 myprj/address/controller/CityController.java ... 是一个虚拟的endpoint,用来测试和显示查询结果,

package com.myprj.address.controller;  // old

import com.myprj.address.service.CityService;  // reuse old
// ... other project's specific (old)

@RestController   // old
@RequestMapping(value = "/zip", produces = "application/json")  // old
public class ZipController {   // old

    @Autowired  // old
    private CityService cityService;  // old, so reuse it

    // .. many many endpoints ... OLD    

    // NEW!!
    @RequestMapping(value="/dummy", method=RequestMethod.GET)
    @ResponseStatus(HttpStatus.OK)
    public String dummy() {
        double x = cityService.findCustom();
        return "{\"success\":"+x+"}";
    }
}

减少到 2 个步骤和 2 个文件

正如 Patrick 所展示的,您可以将 @Autowired 添加到存储库中进行设置,并直接在 Controller.java 文件中使用 repository.findCustom()

【讨论】:

    猜你喜欢
    • 2010-12-02
    • 2014-09-16
    • 2012-03-18
    • 2014-08-10
    • 1970-01-01
    • 2011-10-03
    • 2010-11-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多