مثال Spring ORM – JPA، Hibernate، Transaction

مرحبًا بك في دليل مثال Spring ORM. اليوم سنلقي نظرة على مثال Spring ORM باستخدام إدارة المعاملات Hibernate JPA. سأريك مثالًا بسيطًا جدًا على تطبيق مستقل من Spring مع الميزات التالية.

  • حقن التبعية (التعليق @Autowired)
  • مدير الكيان JPA (المقدم من Hibernate)
  • الطرق التي تحمل علامة تجارية (@Transactional)

مثال Spring ORM

لقد استخدمت قاعدة البيانات في الذاكرة لمثال Spring ORM ، لذا لا حاجة لإعداد أي قاعدة بيانات (ولكن يمكنك تغييرها إلى أي قاعدة بيانات أخرى في قسم مصدر البيانات في spring.xml). هذا تطبيق مستقل لـ Spring ORM لتقليل جميع التبعيات (لكن يمكنك بسهولة تحويله إلى مشروع ويب من خلال التكوين إذا تعرفت على Spring). NOTE: للحصول على أسلوب حل المشكلة لـ Spring AOP القائم على المعاملات (بدون تعليق @Transactional) يرجى مراجعة هذا البرنامج التعليمي: إدارة المعاملات بواسطة Spring ORM AOP. تظهر الصورة أدناه مشروع مثالنا النهائي لـ Spring ORM. لنلخص كل من مكونات مشروع مثال Spring ORM بشكل فردي.

تبعيات Maven لـ Spring ORM

فيما يلي ملف pom.xml النهائي الذي يحتوي على تبعيات Spring ORM. لقد استخدمنا Spring 4 و Hibernate 4 في مثالنا لـ Spring ORM.

<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>hu.daniel.hari.learn.spring</groupId>
	<artifactId>Tutorial-SpringORMwithTX</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<properties>
		<!-- Generic properties -->
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>1.7</java.version>

		<!-- SPRING & HIBERNATE / JPA -->
		<spring.version>4.0.0.RELEASE</spring.version>
		<hibernate.version>4.1.9.Final</hibernate.version>

	</properties>

	<dependencies>
		<!-- LOG -->
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.17</version>
		</dependency>

		<!-- Spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<!-- JPA Vendor -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>${hibernate.version}</version>
		</dependency>

		<!-- IN MEMORY Database and JDBC Driver -->
		<dependency>
			<groupId>hsqldb</groupId>
			<artifactId>hsqldb</artifactId>
			<version>1.8.0.7</version>
		</dependency>

	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<source>${java.version}</source>
					<target>${java.version}</target>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>
  • نحتاج spring-context و spring-orm كتبعيات لـ Spring.
  • نستخدم hibernate-entitymanager لـ Hibernate كتنفيذ لـ JPA. hibernate-entitymanager معتمد على hibernate-core وهذا السبب في عدم ضرورة وضع hibernate-core في ملف pom.xml بشكل صريح. إنه يتم جلبه إلى مشروعنا من خلال تبعيات Maven.
  • نحتاج أيضًا إلى محرك JDBC كتبعي للوصول إلى قاعدة البيانات. نستخدم HSQLDB التي تحتوي على محرك JDBC وقاعدة بيانات في الذاكرة التشغيلية.

فئة نموذج Spring ORM

يمكننا استخدام تعليقات JPA القياسية لتعيين التصوير في فئات النموذج لدينا لأن Hibernate يوفر تنفيذ JPA.

package hu.daniel.hari.learn.spring.orm.model;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Product {

	@Id
	private Integer id;
	private String name;

	public Product() {
	}

	public Product(Integer id, String name) {
		this.id = id;
		this.name = name;
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "Product [id=" + id + ", name=" + name + "]";
	}

}

نستخدم تعليقات @Entity و @Id JPA لتأهيل كائن POJO ككيان ولتعريف مفتاحه الأساسي.

فئة DAO Spring ORM

نقوم بإنشاء فئة DAO بسيطة جدًا توفر طرق persist و findALL.

package hu.daniel.hari.learn.spring.orm.dao;

import hu.daniel.hari.learn.spring.orm.model.Product;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.springframework.stereotype.Component;

@Component
public class ProductDao {

	@PersistenceContext
	private EntityManager em;

	public void persist(Product product) {
		em.persist(product);
	}

	public List<Product> findAll() {
		return em.createQuery("SELECT p FROM Product p").getResultList();
	}

}
  • @Component هو تعليق Spring الذي يخبر حاوية Spring أنه يمكننا استخدام هذه الفئة من خلال حقن الاعتماديات في Spring (IoC).
  • نستخدم تعليق JPA @PersistenceContext للإشارة إلى حقن التبعية في EntityManager. يقوم Spring بحقن نسخة مناسبة من EntityManager وفقًا لتكوين spring.xml.

فئة خدمة Spring ORM

تحتوي فئتنا البسيطة للخدمة على 2 وظيفة للكتابة وواحدة للقراءة – إضافة، إضافة الكل، وقائمة الكل.

package hu.daniel.hari.learn.spring.orm.service;

import hu.daniel.hari.learn.spring.orm.dao.ProductDao;
import hu.daniel.hari.learn.spring.orm.model.Product;

import java.util.Collection;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
public class ProductService {

	@Autowired
	private ProductDao productDao;

	@Transactional
	public void add(Product product) {
		productDao.persist(product);
	}
	
	@Transactional
	public void addAll(Collection<Product> products) {
		for (Product product : products) {
			productDao.persist(product);
		}
	}

	@Transactional(readOnly = true)
	public List<Product> listAll() {
		return productDao.findAll();

	}

}
  • نستخدم تعليق Spring @Autowired لحقن ProductDao في فئة الخدمة الخاصة بنا.
  • نرغب في استخدام إدارة المعاملات، لذا الوسائل محددة بتعليق @Transactional من Spring. الوسيلة listAll تقوم فقط بقراءة قاعدة البيانات لذا نحدد تعليق @Transactional كقراءة فقط لتحسين الأداء.

مثال تكوين بيانات Spring ORM

تم إعداد فئات المشروع المثال Spring ORM الخاص بنا، دعنا نلقي نظرة على ملف تكوين بيناتنا bean الخاص بـ spring الآن. spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans" 
	xmlns:p="https://www.springframework.org/schema/p"
	xmlns:context="https://www.springframework.org/schema/context" 
	xmlns:tx="https://www.springframework.org/schema/tx" 
	xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
		https://www.springframework.org/schema/beans
		https://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		https://www.springframework.org/schema/context
		https://www.springframework.org/schema/context/spring-context-3.0.xsd
		https://www.springframework.org/schema/tx
		https://www.springframework.org/schema/tx/spring-tx.xsd
		">
	
	<!-- Scans the classpath for annotated components that will be auto-registered as Spring beans -->
	<context:component-scan base-package="hu.daniel.hari.learn.spring" />
	<!-- Activates various annotations to be detected in bean classes e.g: @Autowired -->
	<context:annotation-config />

	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
		<property name="url" value="jdbc:hsqldb:mem://productDb" />
		<property name="username" value="sa" />
		<property name="password" value="" />
	</bean>
	
	<bean id="entityManagerFactory" 
			class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
			p:packagesToScan="hu.daniel.hari.learn.spring.orm.model"
            p:dataSource-ref="dataSource"
			>
		<property name="jpaVendorAdapter">
			<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
				<property name="generateDdl" value="true" />
				<property name="showSql" value="true" />
			</bean>
		</property>
	</bean>

	<!-- Transactions -->
	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>
	<!-- enable the configuration of transactional behavior based on annotations -->
	<tx:annotation-driven transaction-manager="transactionManager" />

</beans>
  1. أولاً نخبر Spring أننا نريد استخدام المسح الضوئي لمكونات Spring (الخدمات، DAOs) بدلاً من تعريفها واحدة تلو الأخرى في spring xml. لقد قمنا أيضًا بتمكين كشف التعليقات Spring.
  2. إضافة مصدر البيانات، وهو قاعدة بيانات HSQLDB في الذاكرة حاليًا.
  3. قمنا بإعداد JPA EntityManagerFactory التي ستُستخدم من قبل التطبيق للحصول على EntityManager. يدعم Spring 3 طرق مختلفة للقيام بذلك، وقد استخدمنا LocalContainerEntityManagerFactoryBean للحصول على جميع إمكانيات JPA. قمنا بتعيين سمات LocalContainerEntityManagerFactoryBean كالتالي:
    1. سمة packagesToScan التي تشير إلى حزمة فئات النموذج الخاصة بنا.
    2. مصدر البيانات المعرف سابقًا في ملف تكوين Spring.
    3. jpaVendorAdapter كـ Hibernate وتعيين بعض خصائص Hibernate.
  4. نقوم بإنشاء مثيل Spring PlatformTransactionManager كـ JpaTransactionManager. يعتبر هذا المدير مناسبًا لتطبيقات تستخدم JPA EntityManagerFactory واحدة للوصول البيانات التحويلية.
  5. نمكّن تكوين السلوك التحويلي استنادًا إلى التعليقات، ونقوم بتعيين transactionManager الذي قمنا بإنشائه.

برنامج اختبار مثال Spring ORM Hibernate JPA

مشروع مثالنا Spring ORM JPA Hibernate جاهز، لنكتب برنامج اختبار لتطبيقنا.

public class SpringOrmMain {
	
	public static void main(String[] args) {
		
		//إنشاء سياق تطبيق Spring
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/spring.xml");
		
		//الحصول على الخدمة من السياق. (تم حقن تبعية الخدمة (ProductDAO) في ProductService)
		ProductService productService = ctx.getBean(ProductService.class);
		
		//القيام ببعض العمليات على البيانات
		
		productService.add(new Product(1, "Bulb"));
		productService.add(new Product(2, "Dijone mustard"));
		
		System.out.println("listAll: " + productService.listAll());
		
		//اختبار إلغاء المعاملة (مفتاح مكرر)
		
		try {
			productService.addAll(Arrays.asList(new Product(3, "Book"), new Product(4, "Soap"), new Product(1, "Computer")));
		} catch (DataAccessException dataAccessException) {
		}
		
		//اختبار قائمة العناصر بعد التراجع
		System.out.println("listAll: " + productService.listAll());
		
		ctx.close();
		
	}
}

يمكنك أن ترى بسهولة كيف يمكننا بدء حاوية Spring من طريقة main. نحصل على نقطة الدخول المحقنة التابعة لدينا الأولى، فصيلة الخدمة. تم حقن مرجع الفئة ProductDao في فئة ProductService بعد تهيئة سياق الربيع. بعد الحصول على مثيل ProducService، يمكننا اختبار طرقه، وستكون جميع استدعاءات الطريقة تراكمية بسبب آلية الوكيل في Spring. نختبر أيضًا التراجع في هذا المثال. إذا قمت بتشغيل برنامج الاختبار أعلاه لمثال ORM الربيع، فستحصل على السجلات أدناه.

Hibernate: insert into Product (name, id) values (?, ?)
Hibernate: insert into Product (name, id) values (?, ?)
Hibernate: select product0_.id as id0_, product0_.name as name0_ from Product product0_
listAll: [Product [id=1, name=Bulb], Product [id=2, name=Dijone mustard]]
Hibernate: insert into Product (name, id) values (?, ?)
Hibernate: insert into Product (name, id) values (?, ?)
Hibernate: insert into Product (name, id) values (?, ?)
Hibernate: select product0_.id as id0_, product0_.name as name0_ from Product product0_
listAll: [Product [id=1, name=Bulb], Product [id=2, name=Dijone mustard]]

يرجى ملاحظة أن المعاملة الثانية تم التراجع عنها، وهذا هو السبب في عدم تغيير قائمة المنتجات. إذا استخدمت ملف log4j.properties من المصدر المرفق، يمكنك رؤية ما يحدث تحت الغطاء. المراجع: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/orm.html يمكنك تنزيل مشروع مثال Spring ORM JPA Hibernate النهائي من الرابط أدناه واللعب به للتعلم أكثر.

تنزيل مشروع Spring ORM مع المعاملة

Source:
https://www.digitalocean.com/community/tutorials/spring-orm-example-jpa-hibernate-transaction