• 如何使用 Spring Data JPA 在存储过程中使用 IN、OUT 和 INOUT 参数


    介绍

    在本教程中,我将向您展示如何使用 Spring Data JPA 在存储过程中使用 IN、OUT 和 INOUT 参数。我还将调用一个完全没有参数的存储过程。

    我将使用 MySQL 服务器,因此 MySQL 服务器可能存在一些限制,或者 Spring Data JPA 本身在调用存储过程时可能存在一些限制,但我至少会向您展示如何根据传递给存储过程的参数类型以不同的方式调用存储过程。

    例如,与Oracle数据库不同,MySQL服务器不支持,因此您不能在实体类中使用参数类型。在这种情况下,您不能使用 Spring Data JPA 存储库样式方法来调用存储过程,您需要作为本机查询调用,或者您需要从中创建实例。REF_CURSORREF_CURSOR@NamedStoredProcedureQueryStoredProcedureQueryEntityManager

    相关文章:

    先决条件

    Java 至少 8, Gradle 6.1.1 – 6.7.1, Maven 3.6.3, MySQL 8.0.17 – 8.0.22, Spring Data JPA 2.2.5 – 2.4.3

    项目设置

    您可以在自己喜欢的 IDE 或工具中创建基于 gradle 或 maven 的项目。该项目的名称是spring-data-jpa-storage-procedure

    如果你在Eclipse中创建基于gradle的项目,那么你可以使用下面的build.gradle脚本:

    1. buildscript {
    2. ext {
    3. springBootVersion = '2.2.5.RELEASE' to 2.4.3
    4. }
    5. repositories {
    6. mavenCentral()
    7. }
    8. dependencies {
    9. classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    10. }
    11. }
    12. plugins {
    13. id 'java-library'
    14. id 'org.springframework.boot' version "${springBootVersion}"
    15. }
    16. sourceCompatibility = 12
    17. targetCompatibility = 12
    18. repositories {
    19. mavenCentral()
    20. }
    21. dependencies {
    22. implementation("org.springframework.boot:spring-boot-starter:${springBootVersion}")
    23. implementation("org.springframework.boot:spring-boot-starter-data-jpa:${springBootVersion}")
    24. implementation('mysql:mysql-connector-java:8.0.17') to 8.0.22
    25. //required only if jdk 9 or higher version is used
    26. runtimeOnly('javax.xml.bind:jaxb-api:2.4.0-b180830.0359')
    27. }

    如果你在Eclipse中创建基于maven的项目,那么你可以使用下面的pom.xml文件:

    1. "1.0" encoding="UTF-8"?>
    2. <project xmlns="http://maven.apache.org/POM/4.0.0"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    5. <modelVersion>4.0.0modelVersion>
    6. <groupId>com.roytutsgroupId>
    7. <artifactId>spring-data-jpa-stored-procedureartifactId>
    8. <version>0.0.1-SNAPSHOTversion>
    9. <properties>
    10. <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
    11. <maven.compiler.source>12maven.compiler.source>
    12. <maven.compiler.target>12maven.compiler.target>
    13. properties>
    14. <parent>
    15. <groupId>org.springframework.bootgroupId>
    16. <artifactId>spring-boot-starter-parentartifactId>
    17. <version>2.2.5.RELEASE to 2.4.3version>
    18. parent>
    19. <dependencies>
    20. <dependency>
    21. <groupId>org.springframework.bootgroupId>
    22. <artifactId>spring-boot-starter-webartifactId>
    23. dependency>
    24. <dependency>
    25. <groupId>org.springframework.bootgroupId>
    26. <artifactId>spring-boot-starter-data-jpaartifactId>
    27. dependency>
    28. <dependency>
    29. <groupId>mysqlgroupId>
    30. <artifactId>mysql-connector-javaartifactId>
    31. dependency>
    32. <dependency>
    33. <groupId>javax.xml.bindgroupId>
    34. <artifactId>jaxb-apiartifactId>
    35. <scope>runtimescope>
    36. dependency>
    37. dependencies>
    38. <build>
    39. <plugins>
    40. <plugin>
    41. <groupId>org.springframework.bootgroupId>
    42. <artifactId>spring-boot-maven-pluginartifactId>
    43. plugin>
    44. plugins>
    45. build>
    46. project>

    MySQL 表

    我将在roytuts数据库下创建一个名为user_details到 MySQL 服务器的表。

    1. CREATE TABLE IF NOT EXISTS `user_details` (
    2. `id` int unsigned COLLATE utf8mb4_unicode_ci NOT NULL AUTO_INCREMENT,
    3. `first_name` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
    4. `last_name` varchar(15) COLLATE utf8mb4_unicode_ci NOT NULL,
    5. `email` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
    6. `dob` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL,
    7. PRIMARY KEY (`id`)
    8. ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

    现在,我将立即转储一些数据以测试我们的应用程序。

    1. INSERT INTO `user_details` (`id`, `first_name`, `last_name`, `email`, `dob`) VALUES
    2. (7, 'Soumitra', 'Roy', 'contact@roytuts.com', '30-08-2016'),
    3. (8, 'Souvik', 'Sanyal', 'souvik.sanyal@email.com', '30-09-1991'),
    4. (9, 'Abc', 'Xyz', 'abc@xyz.com', '12-01-1998'),
    5. (10, 'Liton', 'Sarkar', 'liton@email.com', '30-08-2012');

    存储过程

    我将创建三个过程来处理存储过程中的 IN、OUT 和 INOUT 参数。

    下面的存储过程从表中返回多行,我没有向其传递任何参数。

    1. DELIMITER //
    2. CREATE DEFINER=`root`@`localhost` PROCEDURE `get_users`()
    3. BEGIN
    4. SELECT * FROM user_details;
    5. END//
    6. DELIMITER ;

    下面的存储过程采用两个参数 – IN 和 OUT。In 参数接受输入,OUT 参数提供输出。

    以下存储过程在连接名字和姓氏后返回用户的全名。

    1. DELIMITER //
    2. CREATE DEFINER=`root`@`localhost` PROCEDURE `get_user_full_name_in_out`(
    3. IN `user_id` INT,
    4. OUT `full_name` VARCHAR(50)
    5. )
    6. BEGIN
    7. SELECT concat(first_name, ' ', last_name) into full_name FROM user_details WHERE id = user_id;
    8. END//
    9. DELIMITER ;

    以下存储过程仅接受一个参数 INOUT,此参数接受输入并提供输出。

    以下过程还会在连接名字和姓氏后返回用户的全名。

    1. DELIMITER //
    2. CREATE DEFINER=`root`@`localhost` PROCEDURE `get_user_full_name_inout`(
    3. INOUT `in_out` VARCHAR(50)
    4. )
    5. BEGIN
    6. SELECT concat(first_name, ' ', last_name) INTO in_out FROm user_details WHERE dob = in_out;
    7. END//
    8. DELIMITER ;

    调用存储过程

    现在,您将了解如何调用存储过程,如何将输入传递给 In 和 INOUT 参数,以及如何从 OUT 和 INOUT 参数接收输出。

    get_users

    此过程列出表user_details 中的所有用户。只需执行 commandon 数据库服务器即可获得以下结果:call get_users()

    现在您将看到如何从 Spring 应用程序调用。

    假设您有以下存储库接口,它扩展了 Spring 的存储库接口:

    1. package spring.data.jpa.stored.procedure.repository;
    2. import java.util.List;
    3. import javax.transaction.Transactional;
    4. import org.springframework.data.jpa.repository.JpaRepository;
    5. import org.springframework.data.jpa.repository.Query;
    6. import org.springframework.data.jpa.repository.query.Procedure;
    7. import org.springframework.data.repository.query.Param;
    8. import spring.data.jpa.stored.procedure.entity.UserDetails;
    9. @Transactional
    10. public interface UserDetailsJpaRepository extends JpaRepository {
    11. @Query(value = "call get_users()", nativeQuery = true)
    12. List findUserDetailsList();
    13. @Procedure(procedureName = "get_user_full_name_in_out", outputParameterName = "full_name")
    14. String findUserFullNameIn_OutUsingName(@Param("user_id") Integer in);
    15. }

    可以使用@Query注释通过以下代码行将过程作为本机查询调用。

    1. @Query(value = "call get_users()", nativeQuery = true)
    2. List findUserDetailsList();

    还可以使用EntityManager以其他方式调用存储过程。假设您有以下存储库类。

    1. package spring.data.jpa.stored.procedure.repository;
    2. import java.util.List;
    3. import javax.persistence.EntityManager;
    4. import javax.persistence.ParameterMode;
    5. import javax.persistence.PersistenceContext;
    6. import javax.persistence.StoredProcedureQuery;
    7. import org.springframework.stereotype.Repository;
    8. import spring.data.jpa.stored.procedure.entity.UserDetails;
    9. @Repository
    10. public class UserDetailsRepository {
    11. @PersistenceContext
    12. private EntityManager entityManager;
    13. public List findUserDetailsListUsingAlias() {
    14. StoredProcedureQuery users = entityManager.createNamedStoredProcedureQuery("getUsers");
    15. return users.getResultList();
    16. }
    17. public List findUserDetailsListUsingName() {
    18. StoredProcedureQuery users = entityManager.createStoredProcedureQuery("get_users");
    19. return users.getResultList();
    20. }
    21. public String findUserFullNameInOutUsingName(String dob) {
    22. StoredProcedureQuery q = entityManager.createStoredProcedureQuery("get_user_full_name_inout");
    23. q.registerStoredProcedureParameter("in_out", String.class, ParameterMode.INOUT);
    24. q.setParameter("in_out", dob);
    25. return q.getOutputParameterValue("in_out").toString();
    26. }
    27. public String findUserFullNameIn_OutUsingName(Integer in) {
    28. StoredProcedureQuery q = entityManager.createStoredProcedureQuery("get_user_full_name_in_out");
    29. q.registerStoredProcedureParameter("user_id", Integer.class, ParameterMode.IN);
    30. q.registerStoredProcedureParameter("full_name", String.class, ParameterMode.OUT);
    31. q.setParameter("user_id", in);
    32. return q.getOutputParameterValue("full_name").toString();
    33. }
    34. }

    现在,可以使用两种不同的方法来调用存储过程:

    1. @PersistenceContext
    2. private EntityManager entityManager;
    3. public List findUserDetailsListUsingAlias() {
    4. StoredProcedureQuery users = entityManager.createNamedStoredProcedureQuery("getUsers");
    5. return users.getResultList();
    6. }
    7. public List findUserDetailsListUsingName() {
    8. StoredProcedureQuery users = entityManager.createStoredProcedureQuery("get_users");
    9. return users.getResultList();
    10. }

    get_user_full_name_in_out

    可以使用以下命令从存储过程中获取结果:

    1. CALL `get_user_full_name_in_out`('7', @full_name);
    2. SELECT @full_name;

    上面的命令将为您提供以下结果:

    现在我将从春季应用程序调用。您可以使用以下代码行从 Spring Data JPA 存储库接口调用:UserDetailsJpaRepository

    1. @Procedure(procedureName = "get_user_full_name_in_out", outputParameterName = "full_name")
    2. String findUserFullNameIn_OutUsingName(@Param("user_id") Integer in);

    您还可以在UserDetailsRepository类中使用以下代码片段:

    1. public String findUserFullNameIn_OutUsingName(Integer in) {
    2. StoredProcedureQuery q = entityManager.createStoredProcedureQuery("get_user_full_name_in_out");
    3. q.registerStoredProcedureParameter("user_id", Integer.class, ParameterMode.IN);
    4. q.registerStoredProcedureParameter("full_name", String.class, ParameterMode.OUT);
    5. q.setParameter("user_id", in);
    6. return q.getOutputParameterValue("full_name").toString();
    7. }

    get_user_full_name_inout

    您可以在 MySQL 服务器中使用以下命令调用此存储过程:

    1. SET @in_out = '30-08-2016';
    2. CALL `get_user_full_name_inout`(@in_out);
    3. SELECT @in_out;

    上述命令将为您提供以下输出:

    以下代码片段写入UserDetailsRepository类:

    1. public String findUserFullNameInOutUsingName(String dob) {
    2. StoredProcedureQuery q = entityManager.createStoredProcedureQuery("get_user_full_name_inout");
    3. q.registerStoredProcedureParameter("in_out", String.class, ParameterMode.INOUT);
    4. q.setParameter("in_out", dob);
    5. return q.getOutputParameterValue("in_out").toString();
    6. }

    实体类

    下面给出了在上述存储库接口和类中使用的相应实体类。

    在类上,我使用@NamedStoredProcedureQueries注释声明了存储过程。

    我使用@Column注释指定了和 Java 属性不同的列名。

    1. @Entity
    2. @Table(name = "user_details")
    3. @NamedStoredProcedureQueries({
    4. @NamedStoredProcedureQuery(name = "getUsers", procedureName = "get_users", resultClasses = {
    5. UserDetails.class }) })
    6. //@NamedStoredProcedureQuery(name = "getUsers", procedureName = "get_users", resultClasses = { UserDetails.class })
    7. public class UserDetails implements Serializable {
    8. private static final long serialVersionUID = 1L;
    9. @Id
    10. @Column
    11. @GeneratedValue(strategy = GenerationType.IDENTITY)
    12. private Integer id;
    13. @Column(name = "first_name")
    14. private String firstName;
    15. @Column(name = "last_name")
    16. private String lastName;
    17. @Column
    18. private String dob;
    19. //getters and setters
    20. }

    服务类

    相应的服务类可以写成:

    1. package spring.data.jpa.stored.procedure.service;
    2. import java.util.List;
    3. import org.springframework.beans.factory.annotation.Autowired;
    4. import org.springframework.stereotype.Service;
    5. import spring.data.jpa.stored.procedure.entity.UserDetails;
    6. import spring.data.jpa.stored.procedure.repository.UserDetailsJpaRepository;
    7. import spring.data.jpa.stored.procedure.repository.UserDetailsRepository;
    8. @Service
    9. public class UserService {
    10. @Autowired
    11. private UserDetailsJpaRepository jpaRepository;
    12. @Autowired
    13. private UserDetailsRepository repository;
    14. public String findUserFullNameIn_OutUsingName(Integer in) {
    15. return jpaRepository.findUserFullNameIn_OutUsingName(in);
    16. }
    17. public List getUserListUsingNativeQuery() {
    18. return jpaRepository.findUserDetailsList();
    19. }
    20. public List getUserDetailsListUsingProcAlias() {
    21. return repository.findUserDetailsListUsingAlias();
    22. }
    23. public List getUserDetailsListUsingProcName() {
    24. return repository.findUserDetailsListUsingAlias();
    25. }
    26. public String getUserFullNameInOutUsingProcName(String dob) {
    27. return repository.findUserFullNameInOutUsingName(dob);
    28. }
    29. public String getUserFullNameIn_OutUsingProcName(int in) {
    30. return repository.findUserFullNameIn_OutUsingName(in);
    31. // return repository.findUserFullNameIn_OutUsingName(in);
    32. }
    33. }

    主类

    一个带有 main 方法的类足以运行我们的 Spring Boot 应用程序。我在这里使用独立应用程序。

    1. package spring.data.jpa.stored.procedure;
    2. import org.springframework.beans.factory.annotation.Autowired;
    3. import org.springframework.boot.CommandLineRunner;
    4. import org.springframework.boot.SpringApplication;
    5. import org.springframework.boot.autoconfigure.SpringBootApplication;
    6. import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
    7. import spring.data.jpa.stored.procedure.service.UserService;
    8. @SpringBootApplication
    9. @EnableJpaRepositories(basePackages = "spring.data.jpa.stored.procedure.repository")
    10. public class SpringDataJpaStoredProcedureApp implements CommandLineRunner {
    11. @Autowired
    12. private UserService service;
    13. public static void main(String[] args) {
    14. SpringApplication.run(SpringDataJpaStoredProcedureApp.class, args);
    15. }
    16. @Override
    17. public void run(String... args) throws Exception {
    18. System.out.println("===========================================");
    19. System.out.println("User List using Native Query");
    20. service.getUserListUsingNativeQuery().stream().forEach(
    21. u -> System.out.println(u.getId() + " " + u.getFirstName() + " " + u.getLastName() + " " + u.getDob()));
    22. System.out.println("===========================================");
    23. System.out.println("User List using Procedure name");
    24. service.getUserDetailsListUsingProcName().stream().forEach(
    25. u -> System.out.println(u.getId() + " " + u.getFirstName() + " " + u.getLastName() + " " + u.getDob()));
    26. System.out.println("===========================================");
    27. System.out.println("User List using Procedure alias");
    28. service.getUserDetailsListUsingProcAlias().stream().forEach(
    29. u -> System.out.println(u.getId() + " " + u.getFirstName() + " " + u.getLastName() + " " + u.getDob()));
    30. System.out.println("===========================================");
    31. System.out.println(
    32. "IN and OUT parameters using Procedure name: " + service.getUserFullNameIn_OutUsingProcName(7));
    33. System.out.println("===========================================");
    34. System.out.println(
    35. "INOUT parameter using Procedure name: " + service.getUserFullNameInOutUsingProcName("30-08-2016"));
    36. System.out.println("===========================================");
    37. System.out.println(service.findUserFullNameIn_OutUsingName(10));
    38. }
    39. }

    数据库配置

    将以下数据库详细信息放入类路径目录src/main/resources下的application.properties文件中,以连接您的 MySQL 服务器。确保根据数据库详细信息进行更改:

    1. spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
    2. spring.datasource.url=jdbc:mysql://localhost:3306/roytuts
    3. spring.datasource.username=root
    4. spring.datasource.password=root

    测试应用程序

    执行上述主类将给出以下输出:

    1. ===========================================
    2. User List using Native Query
    3. 7 Soumitra Roy 30-08-2016
    4. 8 Souvik Sanyal 30-09-1991
    5. 9 Abc Xyz 12-01-1998
    6. 10 Liton Sarkar 30-08-2012
    7. ===========================================
    8. User List using Procedure name
    9. 7 Soumitra Roy 30-08-2016
    10. 8 Souvik Sanyal 30-09-1991
    11. 9 Abc Xyz 12-01-1998
    12. 10 Liton Sarkar 30-08-2012
    13. ===========================================
    14. User List using Procedure alias
    15. 7 Soumitra Roy 30-08-2016
    16. 8 Souvik Sanyal 30-09-1991
    17. 9 Abc Xyz 12-01-1998
    18. 10 Liton Sarkar 30-08-2012
    19. ===========================================
    20. IN and OUT parameters using Procedure name: Soumitra Roy
    21. ===========================================
    22. INOUT parameter using Procedure name: Soumitra Roy
    23. ===========================================
    24. Liton Sarkar

    存储库接口/类和服务层与任何其他层分离,您可以轻松地注入到任何层中。

    这就是如何使用 Spring Data JPA 框架在存储过程中使用 IN、OUT 和 IN OUT 参数的全部内容。

    源代码

    下载

  • 相关阅读:
    前端面试题总结
    将特征转换为正态分布的一种方法示例
    synchronized的锁策略及优化过程
    力扣解法汇总1175-质数排列
    redis部署与管理
    五金机电行业供应商智慧管理平台解决方案:优化供应链管理,带动企业业绩增长
    安卓高通机型的基带移植 修改 编译的相关 增加信号 支持5G等
    PyCharm 中使用文件监视器自动处理 Python 文件
    算法训练day37|贪心算法 part06(LeetCode738.单调递增的数字)
    万字长文 | 泰康人寿基于 Apache Hudi 构建湖仓一体平台的应用实践
  • 原文地址:https://blog.csdn.net/allway2/article/details/127906490