在现代的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脚本:
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)
IStemp_count NUMBER := -1;
BEGINSELECT COUNT(*) INTO temp_count FROM PERSON WHERE ID = person_id_in;IF temp_count > 0 THENINSERT 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;ELSEmsg_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
BEGINOPEN history_cursor FOR SELECT * FROM PERSON_HISTORY;
END;
/
2. 实体类定义
接下来,我们定义一个Person
实体类,并使用@NamedStoredProcedureQuery
注解来声明存储过程。
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 Setterspublic 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;}@Overridepublic String toString() {return "Person{" +"id=" + id +", firstName='" + firstName + '\'' +", lastName='" + lastName + '\'' +", address='" + address + '\'' +'}';}@Overridepublic 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);}@Overridepublic int hashCode() {return Objects.hash(id, firstName, lastName, address);}
}
3. 调用存储过程
最后,我们通过EntityManager
调用这些存储过程。以下是主类的代码:
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 idTypedQuery<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 procedureStoredProcedureQuery 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