JPA与存储过程的完美结合

在现代的Java开发中,JPA(Java Persistence API)已经成为ORM(对象关系映射)的主流选择之一。它不仅简化了数据库操作,还提供了强大的功能来与数据库交互。今天,我们将深入探讨如何通过@NamedStoredProcedureQuery注解在JPA中使用数据库存储过程,并通过一个完整的示例来展示其强大的功能。

一、@NamedStoredProcedureQuery简介

@NamedStoredProcedureQuery是JPA提供的一个注解,用于在实体类中声明可复用的数据库存储过程。通过这个注解,我们可以将存储过程与实体类关联起来,并在代码中方便地调用这些存储过程。

以下是@NamedStoredProcedureQuery注解的主要属性:

  • name:用于引用的存储过程名称。
  • procedureName:数据库中存储过程的实际名称。
  • parameters:存储过程的参数信息,包括参数名、类型和模式(如IN、OUT、INOUT、REF_CURSOR)。
  • resultClasses:存储过程返回的结果集对应的Java类。
  • resultSetMappings:存储过程返回的结果集映射。
  • hints:查询的属性和提示。

二、示例:使用Oracle存储过程

为了更好地理解@NamedStoredProcedureQuery的使用,我们将通过一个完整的示例来展示如何在JPA中调用Oracle数据库的存储过程。这个示例包括两个存储过程:一个用于将记录从主表移动到历史表,另一个用于查询历史表中的记录。

1. 数据库准备

首先,我们需要在Oracle数据库中创建表和存储过程。以下是SQL脚本:

sql 复制代码
DROP TABLE PERSON;
DROP SEQUENCE SQ_PERSON;
CREATE TABLE PERSON (
    ID NUMBER(19),
    FIRST_NAME VARCHAR(255),
    LAST_NAME VARCHAR(255),
    ADDRESS VARCHAR(255),
    PRIMARY KEY (ID)
);
CREATE SEQUENCE SQ_PERSON MINVALUE 1 START WITH 1 INCREMENT BY 1 CACHE 10;

DROP TABLE PERSON_HISTORY;
CREATE TABLE PERSON_HISTORY AS SELECT * FROM PERSON WHERE 1=0;

CREATE OR REPLACE PROCEDURE MOVE_TO_HISTORY(person_id_in IN NUMBER, msg_out OUT VARCHAR2)
IS
    temp_count NUMBER := -1;
BEGIN
    SELECT COUNT(*) INTO temp_count FROM PERSON WHERE ID = person_id_in;
    IF temp_count > 0 THEN
        INSERT INTO PERSON_HISTORY SELECT * FROM PERSON WHERE ID = person_id_in;
        msg_out := 'Person with id: ' || person_id_in || ' moved to History table. Update count: ' || SQL%ROWCOUNT;
        DELETE FROM PERSON WHERE ID = person_id_in;
    ELSE
        msg_out := 'No Person Exists with id: ' || person_id_in;
    END IF;
END;
/

CREATE OR REPLACE PROCEDURE FETCH_PERSON_HISTORY(history_cursor OUT SYS_REFCURSOR)
IS
BEGIN
    OPEN history_cursor FOR SELECT * FROM PERSON_HISTORY;
END;
/
2. 实体类定义

接下来,我们定义一个Person实体类,并使用@NamedStoredProcedureQuery注解来声明存储过程。

java 复制代码
import javax.persistence.*;
import java.util.Objects;

@Entity
@NamedStoredProcedureQuery(
        name = Person.NamedQuery_MoveToHistory,
        procedureName = "MOVE_TO_HISTORY",
        parameters = {
                @StoredProcedureParameter(name = "person_id_in", type = Long.class, mode = ParameterMode.IN),
                @StoredProcedureParameter(name = "msg_out", type = String.class, mode = ParameterMode.OUT)
        }
)
@NamedStoredProcedureQuery(
        name = Person.NamedQuery_FetchFromHistory,
        procedureName = "FETCH_PERSON_HISTORY",
        resultClasses = {Person.class},
        parameters = {
                @StoredProcedureParameter(name = "history_cursor", type = void.class, mode = ParameterMode.REF_CURSOR)
        }
)
public class Person {
    public static final String NamedQuery_MoveToHistory = "moveToHistory";
    public static final String NamedQuery_FetchFromHistory = "fetchFromHistory";

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SQ_PERSON")
    @SequenceGenerator(sequenceName = "SQ_PERSON", allocationSize = 1, name = "SQ_PERSON")
    private long id;

    @Column(name = "FIRST_NAME")
    private String firstName;

    @Column(name = "LAST_NAME")
    private String lastName;

    private String address;

    // Getters and Setters
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                ", address='" + address + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return id == person.id &&
                Objects.equals(firstName, person.firstName) &&
                Objects.equals(lastName, person.lastName) &&
                Objects.equals(address, person.address);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, firstName, lastName, address);
    }
}
3. 调用存储过程

最后,我们通过EntityManager调用这些存储过程。以下是主类的代码:

java 复制代码
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.StoredProcedureQuery;
import java.util.List;

public class ExampleMain {
    private static EntityManagerFactory entityManagerFactory =
            Persistence.createEntityManagerFactory("example-unit");

    public static void main(String[] args) {
        try {
            reset();
            persistEntities();
            findAllEmployeeEntities();
            movePersonToHistoryByName("Dana");
            movePersonToHistoryByName("Mike");
            fetchPersonHistory();
        } finally {
            entityManagerFactory.close();
        }
    }

    private static void fetchPersonHistory() {
        System.out.println("-- Fetching person History --");
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        StoredProcedureQuery procedureQuery =
                entityManager.createNamedStoredProcedureQuery(Person.NamedQuery_FetchFromHistory);
        procedureQuery.execute();
        @SuppressWarnings("unchecked")
        List<Person> resultList = procedureQuery.getResultList();
        resultList.forEach(System.out::println);
    }

    private static void movePersonToHistoryByName(String name) {
        System.out.printf("-- Moving person to history table name: %s --%n", name);
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        // get person id
        TypedQuery<Long> query = entityManager
                .createQuery("SELECT p.id FROM Person p WHERE p.firstName = :firstName", Long.class);
        query.setParameter("firstName", name);
        Long personId = query.getSingleResult();
        // stored procedure
        StoredProcedureQuery procedureQuery = entityManager
                .createNamedStoredProcedureQuery(Person.NamedQuery_MoveToHistory);
        entityManager.getTransaction().begin();
        procedureQuery.setParameter("person_id_in", personId);
        procedureQuery.execute();
        Object msg_out = procedureQuery.getOutputParameterValue("msg_out");
        System.out.println("Out msg= " + msg_out);
        entityManager.getTransaction().commit();
    }

    private static void findAllEmployeeEntities() {
        System.out.println("-- all Person entities -  --");
        EntityManager em = entityManagerFactory.createEntityManager();
        TypedQuery<Person> query = em.createQuery("SELECT p from Person p", Person.class);
        List<Person> resultList = query.getResultList();
        resultList.forEach(System.out::println);
        em.close();
    }

    private static void persistEntities() {
        Person person1 = new Person();
        person1.setFirstName("Dana");
        person1.setLastName("Whitley");
        person1.setAddress("464 Gorsuch Drive");

        Person person2 = new Person();
        person2.setFirstName("Robin");
        person2.setLastName("Cash");
        person2.setAddress("64 Zella Park");

        Person person3 = new Person();
        person3.setFirstName("Chary");
        person3.setLastName("Mess");
        person3.setAddress("112 Yellow Hill");

        Person person4 = new Person();
        person4.setFirstName("Rose");
        person4.setLastName("Kantata");
        person4.setAddress("2736 Kooter Lane");

        Person person5 = new Person();
        person5.setFirstName("Mike");
        person5.setLastName("Togglie");
        person5.setAddress("111 Cool Dr");

        EntityManager em = entityManagerFactory.createEntityManager();
        em.getTransaction().begin();
        em.persist(person1);
        em.persist(person2);
        em.persist(person3);
        em.persist(person4);
        em.persist(person5);
        em.getTransaction().commit();
        em.close();
    }

    private static void reset() {
        EntityManager em = entityManagerFactory.createEntityManager();
        em.getTransaction().begin
相关推荐
NCIN EXPE2 小时前
redis 使用
数据库·redis·缓存
MongoDB 数据平台2 小时前
为编码代理引入 MongoDB 代理技能和插件
数据库·mongodb
极客on之路2 小时前
mysql explain type 各个字段解释
数据库·mysql
代码雕刻家3 小时前
MySQL与SQL Server的基本指令
数据库·mysql·sqlserver
lThE ANDE3 小时前
开启mysql的binlog日志
数据库·mysql
yejqvow123 小时前
CSS如何控制placeholder文字的颜色_使用--placeholder伪元素
jvm·数据库·python
oLLI PILO3 小时前
nacos2.3.0 接入pgsql或其他数据库
数据库
m0_743623923 小时前
HTML怎么创建多语言切换器_HTML语言选择下拉结构【指南】
jvm·数据库·python
pele3 小时前
Angular 表单中基于下拉选择动态启用字段必填校验的完整实现
jvm·数据库·python