ברוכים הבאים להדרכת דוגמה של Spring ORM. היום נתחיל להבין דוגמה של Spring ORM באמצעות ניהול עסקאות Hibernate JPA. אנסה להראות לך דוגמה פשוטה מאוד של אפליקציה עצמאית של Spring עם התכונות הבאות.
- הזרקת תלות (@Autowired אנוטציה)
- JPA EntityManager (מסופק על ידי Hibernate)
- שיטות תעבורה מסומנות (אנוטציה @Transactional)
דוגמה ל Spring ORM
שימשתי מסד נתונים בזיכרון עבור דוגמת Spring ORM, אז אין צורך בקונפיגורציה של בסיס נתונים (אך ניתן לשנות את זה לכל מסד נתונים אחר במקטע ה־datasource ב־spring.xml). זוהי יישום עצמאי של Spring ORM כדי למזער את כל התלותיות (אך אפשר לשנות אותו בקלות לפרויקט אינטרנטי על ידי הגדרה אם תתמכרו בסבלנות עם Spring).
הערה: עבור גישת פתרון של שימוש ב־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
כמימוש JPA עבור Hibernate.hibernate-entitymanager
תלוי ב־hibernate-core
, ולכן אנו אינו צריכים לכלול את hibernate-core בקובץ ה־pom.xml באופן בירורי. הוא מתווסף לפרויקט שלנו דרך תלות מייבן. - נדרש גם מנהג 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 שלנו כיישות ולהגדיר את המפתח הראשי שלו.
דגם Spring ORM DAO
אנו יוצרים כיתת 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 שאנו יכולים להשתמש בכיתה זו דרך IoC של Spring (הטמעת תלות).
- אנו משתמשים ב- JPA באמצעות האנוטציה
@PersistenceContext
המציינת הזרמת תלות ל- EntityManager. Spring מביא לתוך התוכנית מופע מתאים של EntityManager על פי התצורה של spring.xml.
מחלקת שירות ORM של Spring
המחלקה הפשוטה שלנו מכילה 2 שיטות כתיבה ושיטה קריאה אחת – add, addAll ו- listAll.
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();
}
}
- אנו משתמשים באנוטציה @Autowired של Spring כדי להזרים את ProductDao במחלקת השירות שלנו.
- אנו רוצים להשתמש בניהול עסקאות, לכן השיטות מודגשות באנוטציה
@Transactional
של Spring. שיטת ה- listAll קוראת בלבד מהמסד נתונים ולכן אנו מגדירים את האנוטציה @Transactional כקריאה בלבד למטרת אופטימיזציה.
דוגמת תצורת חרוזת Spring ORM XML
קבצי ה-Java של דוגמת ORM שלנו עבור Spring מוכנים, בואו נסתכל עכשיו על קובץ התצורה של הפול Bean שלנו. 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>
- ראשית אנו מודיעים ל-Spring שרוצים להשתמש בסריקה של נתיב ה-Classpath עבור רכיבי Spring (שירותים, DAOs) במקום להגדיר אותם אחד-אחד ב- spring xml. כמו כן הפעלנו גילוי של האנוטציות של Spring.
- הוספת מקור נתונים, שכרגע הוא מסד נתונים בזיכרון HSQLDB.
- הגדרנו JPA
EntityManagerFactory
שישמש על ידי האפליקציה לקבלת EntityManager. Spring תומך ב-3 דרכים שונות לעשות זאת, אנו בחרנו ב-LocalContainerEntityManagerFactoryBean
עבור יכולות JPA מלאות. הגדרנו את תכונות ה-LocalContainerEntityManagerFactoryBean
כך:- תכונת packagesToScan שמצביעה על תיקיית המודל שלנו.
- מקור נתונים שהוגדר מראש בקובץ התצורה של Spring.
- jpaVendorAdapter כהגדרת Hibernate והגדרת כמה מאפייני Hibernate.
- יצרנו מופע של Spring PlatformTransactionManager כ- JpaTransactionManager. מנהל העסקאות זה מתאים ליישומים שמשתמשים ב- JPA EntityManagerFactory יחיד לגישה נתונית טרנזקציונלית.
- הפעלנו את ההגדרה של התנהגות טרנזקציונלית בהתבסס על הוראות אנוטציות, והגדרנו את מנהל הטרנזקציות שיצרנו.
דוגמה לתכנית מבחן של 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 לאחר שההקשר של Spring מאותחל. לאחר שקיבלנו את מופע של ProducService, נוכל לבדוק את השיטות שלו, כל קריאת שיטה תהיה transactional עקב מנגנון ה-proxy של Spring. אנו גם בודקים ביטול עסקה בדוגמה זו. אם תריץ את תכנית המבחן של הדוגמה של ORM של Spring שמוצגת למעלה, תקבל את הלוגים הבאים.
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 מהקישור למטה ולשחק עם זה כדי ללמוד עוד.
Source:
https://www.digitalocean.com/community/tutorials/spring-orm-example-jpa-hibernate-transaction