在现代的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)
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
注解来声明存储过程。
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
调用这些存储过程。以下是主类的代码:
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