Перейти к основному содержимому

Руководство по хранимым процедурам с JPA

· 4 мин. чтения

1. Введение

В этом кратком руководстве мы рассмотрим использование хранимых процедур в Java Persistence API (JPA).

2. Настройка проекта

2.1. Настройка Maven

Сначала нам нужно определить следующие зависимости в нашем pom.xml :

  • javax.javaee-api — так как включает JPA API
  • реализация JPA API — в этом примере мы будем использовать Hibernate , но EclipseLink также будет хорошей альтернативой
  • база данных MySQL
<properties>
<jee.version>7.0</jee.version>
<mysql.version>11.2.0.4</mysql.version>
<hibernate.version>5.1.38</hibernate.version>
</properties>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>${jee.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
</dependencies>

2.2. Определение единицы сохраняемости

Второй шаг — создание файла src/main/resources/META-INF/persistence.xml , который содержит определения единиц сохраняемости:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_1.xsd"
version="2.1">

<persistence-unit name="jpa-db">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<class>com.foreach.jpa.model.Car</class>
<properties>
<property name="javax.persistence.jdbc.driver"
value="com.mysql.jdbc.Driver" />
<property name="javax.persistence.jdbc.url"
value="jdbc:mysql://127.0.0.1:3306/foreach" />
<property name="javax.persistence.jdbc.user"
value="foreach" />
<property name="javax.persistence.jdbc.password"
value="YourPassword" />
<property name="hibernate.dialect"
value="org.hibernate.dialect.MySQLDialect" />
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>

</persistence>

Все определенные свойства Hibernate не нужны, если вы ссылаетесь на JNDI DataSource (среды JEE):

<jta-data-source>java:jboss/datasources/JpaStoredProcedure</jta-data-source>

2.3. Скрипт создания таблицы

Теперь создадим таблицу (CAR) с тремя атрибутами: ID, MODEL и YEAR :

CREATE TABLE `car` (
`ID` int(10) NOT NULL AUTO_INCREMENT,
`MODEL` varchar(50) NOT NULL,
`YEAR` int(4) NOT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

Предполагалось, конечно, что схема БД и разрешения уже на месте.

2.4. Создание хранимой процедуры в БД

Самый последний шаг перед переходом к коду Java — это создание хранимой процедуры в нашей базе данных MySQL:

DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `FIND_CAR_BY_YEAR`(in p_year int)
begin
SELECT ID, MODEL, YEAR
FROM CAR
WHERE YEAR = p_year;
end
$$
DELIMITER ;

3. Хранимая процедура JPA

Теперь мы готовы использовать JPA для связи с базой данных и выполнения определенной нами хранимой процедуры.

Как только мы это сделаем, мы также сможем перебирать результаты в виде списка автомобилей .

3.1. Автомобильная сущность _

Под объектом Car , который хорошо сопоставляется с таблицей базы данных CAR с помощью Entity Manager.

Обратите внимание, что мы также определяем нашу хранимую процедуру непосредственно в сущности, используя аннотацию @NamedStoredProcedureQueries :

@Entity
@Table(name = "CAR")
@NamedStoredProcedureQueries({
@NamedStoredProcedureQuery(
name = "findByYearProcedure",
procedureName = "FIND_CAR_BY_YEAR",
resultClasses = { Car.class },
parameters = {
@StoredProcedureParameter(
name = "p_year",
type = Integer.class,
mode = ParameterMode.IN) })
})
public class Car {

private long id;
private String model;
private Integer year;

public Car(String model, Integer year) {
this.model = model;
this.year = year;
}

public Car() {
}

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID", unique = true, nullable = false, scale = 0)
public long getId() {
return id;
}

@Column(name = "MODEL")
public String getModel() {
return model;
}

@Column(name = "YEAR")
public Integer getYear() {
return year;
}

// standard setter methods
}

3.2. Доступ к базе данных

Теперь, когда все определено и установлено, давайте напишем пару тестов, фактически использующих JPA для выполнения процедуры.

Мы собираемся получить все автомобили в данном году : ``

public class StoredProcedureTest {

private static EntityManagerFactory factory = null;
private static EntityManager entityManager = null;

@BeforeClass
public static void init() {
factory = Persistence.createEntityManagerFactory("jpa-db");
entityManager = factory.createEntityManager();
}

@Test
public void findCarsByYearWithNamedStored() {
StoredProcedureQuery findByYearProcedure =
entityManager.createNamedStoredProcedureQuery("findByYearProcedure");

StoredProcedureQuery storedProcedure =
findByYearProcedure.setParameter("p_year", 2015);

storedProcedure.getResultList()
.forEach(c -> Assert.assertEquals(new Integer(2015), ((Car) c).getYear()));
}

@Test
public void findCarsByYearNoNamedStored() {
StoredProcedureQuery storedProcedure =
entityManager
.createStoredProcedureQuery("FIND_CAR_BY_YEAR",Car.class)
.registerStoredProcedureParameter(1, Integer.class, ParameterMode.IN)
.setParameter(1, 2015);

storedProcedure.getResultList()
.forEach(c -> Assert.assertEquals(new Integer(2015), ((Car) c).getYear()));
}

}

Обратите внимание, что во втором тесте мы больше не используем хранимую процедуру, которую мы определили для сущности . Вместо этого мы определяем процедуру с нуля.

Это может быть очень полезно, когда вам нужно использовать хранимые процедуры, но у вас нет возможности изменять свои объекты и перекомпилировать их.

4. Вывод

В этом руководстве мы обсудили использование хранимой процедуры с Java Persistence API.

Пример, использованный в этой статье, доступен в качестве образца проекта на GitHub .