۳۰ شهریور ۱۴۰۳

Techboy

اخبار و اطلاعات روز تکنولوژی

ماندگاری جاوا با JPA و Hibernate: ماندگاری داده ها در پایگاه داده

برنامه‌ای را بنویسید، بسازید و اجرا کنید که داده‌ها را با استفاده از Hibernate، JPA و الگوی مخزن به یک پایگاه داده رابطه‌ای و از آن ادامه می‌دهد.

برنامه‌ای را بنویسید، بسازید و اجرا کنید که داده‌ها را با استفاده از Hibernate، JPA و الگوی مخزن به یک پایگاه داده رابطه‌ای و از آن ادامه می‌دهد.

در این نیمه دوم آموزش دوام جاوا با JPA و Hibernate، مفاهیم گذشته را جابه‌جا می‌کنیم و شروع به نوشتن کدی می‌کنیم که داده‌ها را به و از پایگاه داده رابطه‌ای با استفاده از JPA بنویسیم. با Hibernate ما با پیکربندی یک برنامه مثال برای استفاده از Hibernate به عنوان ارائه‌دهنده JPA شروع می‌کنیم، سپس به سرعت EntityManager را پیکربندی می‌کنیم و دو کلاس را که می‌خواهیم در پایگاه داده باقی بمانند، بنویسیم: Book و نویسنده. در نهایت، یک برنامه کاربردی ساده می نویسیم که همه اجزای برنامه را با هم جمع می کند و دو موجودیت ما را با موفقیت در پایگاه داده باقی می گذارد.

شما یاد خواهید گرفت که چگونه:

  • یک برنامه جاوا را برای استفاده از Hibernate به عنوان ارائه دهنده JPA خود پیکربندی کنید.
  • EntityManager JPA را پیکربندی کنید.
  • یک مدل دامنه JPA ساده که نشان دهنده دو کلاس با رابطه یک به چند است ایجاد کنید.
  • از مخازن برای جدا کردن منطق پایداری از کد برنامه خود استفاده کنید.
  • برای اتصال به پایگاه داده رابطه ای، برنامه مثال را با استفاده از JPA بنویسید، بسازید و اجرا کنید.

همچنین با JPA Query Language (JPQL) شروع می‌کنید و از آن برای اجرای چند عملیات ساده پایگاه داده در برنامه مثال استفاده می‌کنید.

کد منبع کامل برنامه نمونه را می‌توانید در اینجا پیدا کنید. اگر نیمه اول این آموزش را نخوانده اید، توصیه می کنیم قبل از ادامه این کار را انجام دهید.

درباره نویسندگان

این آموزش در اصل توسط استیون هاینز نوشته شده و در JavaWorld منتشر شده است. این توسط متیو تایسون برای Java 21 و Hibernate 6.3 به روز شده است.

پیکربندی Hibernate

برای ساده نگه داشتن کارها، از پایگاه داده H2 تعبیه شده استفاده می کنیم هر دو نمونه توسعه و زمان اجرا. می‌توانید URL JDBC را در EntityManager تغییر دهید تا به هر پایگاه داده‌ای که می‌خواهید اشاره کند.

با بررسی فایل Maven POM برای پروژه، که در فهرست ۱ نشان داده شده است، شروع کنید.


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.infoworld</groupId>
    <artifactId>jpa-example</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>jpa-example</name>
    <url>http://maven.apache.org</url>
    <properties>
        <java.version>21</java.version>
        <hibernate.version>6.3.1.Final</hibernate.version>
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.0.2</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>lib/</classpathPrefix>
                            <mainClass>com.infoworld.jpa.JpaExample</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy</id>
                        <phase>install</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-core</artifactId>
  <version>${hibernate.version}</version>
</dependency>
<dependency>
    <groupId>jakarta.persistence</groupId>
    <artifactId>jakarta.persistence-api</artifactId>
    <version>3.1.0</version>
</dependency>
<dependency>
    <groupId>jakarta.platform</groupId>
    <artifactId>jakarta.jakartaee-api</artifactId>
    <version>9.1.0</version>
</dependency>

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>2.2.224</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>jakarta.json.bind</groupId>
    <artifactId>jakarta.json.bind-api</artifactId>
    <version>3.0.0</version>
</dependency>
<dependency>
    <groupId>org.eclipse</groupId>
    <artifactId>yasson</artifactId>
    <version>3.0.3</version>
    <scope>runtime</scope>
</dependency>

    </dependencies>
</project>

این پروژه را با استفاده از جاوا ۲۱ و Hibernate نسخه ۶.۳ خواهیم ساخت. آخرین نسخه تا این لحظه پلاگین ها در گره ساخت، نسخه کامپایل جاوا را تنظیم می کنند، فایل JAR حاصل را قابل اجرا می کنند و اطمینان حاصل می کنند که تمام وابستگی ها در یک پوشه lib کپی می شوند تا JAR اجرایی بتواند اجرا شود. ما وابستگی های زیر را شامل می کنیم:

  • عملکرد اصلی Hibernate: hibernate-core
  • API JPA: hibernate-jpa-2.1-api
  • پایگاه داده H2 تعبیه شده: h2

توجه داشته باشید که محدوده H2 روی runtime تنظیم شده است تا بتوانیم هنگام اجرای کد خود از آن استفاده کنیم.

GitHub Codespaces به صورت رایگان در دسترس همه کاربران GitHub است

هیبرنیت با JDK

Hibernate 6.3 در سپتامبر ۲۰۲۳ منتشر شد و با جاوا ۱۱ فوروارد سازگار است. این به طور خاص برای جاوا LTS نسخه های ۱۱، ۱۷ و ۲۱ طراحی شده است.

پیکربندی EntityManager

به یاد داشته باشید که EntityManager JPA ما توسط فایل persistence.xml هدایت می شود. فهرست ۲ محتویات این فایل را نشان می دهد.


<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_0.xsd"
             version="2.0">
    <persistence-unit name="Books" transaction-type="RESOURCE_LOCAL">
        <!-- Persistence provider -->
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

        <properties>
            <property name="jakarta.persistence.jdbc.driver" value="org.h2.Driver" />
            <property name="jakarta.persistence.jdbc.url"    value="jdbc:h2:mem:bookstore" />
            <property name="jakarta.persistence.jdbc.user" value="sa" />
            <property name="jakarta.persistence.jdbc.password" value="" />

            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
            <property name="hibernate.hbm2ddl.auto" value="update" />
            <property name="show_sql" value="true"/>
            <property name="hibernate.temp.use_jdbc_metadata_defaults" value="false"/>
            <property name="hibernate.format_sql" value="true"/>
            <property name="hibernate.use_sql_comments" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

اگرچه ما از persistence.xml استفاده می‌کنیم، می‌توانید با یک EntityManager به پیکربندی مشابهی دست پیدا کنید. در آن صورت، از حاشیه نویسی @PersistenceContext(unitName = "Books") استفاده می کنید.

فایل persistence.xml با یک گره ماندگاری شروع می شود که می تواند شامل یک یا چند واحد پایداری باشد. یک persistence-unit یک نام دارد که بعداً هنگام ایجاد EntityManager از آن استفاده خواهیم کرد و ویژگی‌های آن واحد را تعریف می‌کند. در این مورد، ما خواص را در این واحد به گونه ای پیکربندی می کنیم که موارد زیر را انجام دهد:

  • HibernatePersistenceProvider را مشخص کنید تا برنامه بداند که ما از Hibernate به عنوان ارائه دهنده JPA خود استفاده می کنیم.
  • پیکربندی پایگاه داده را از طریق JDBC. در این مورد، ما از یک نمونه H2 در حافظه استفاده می‌کنیم.
  • Hibernate را پیکربندی کنید، از جمله تنظیم گویش Hibernate روی H2Dialect، به طوری که Hibernate بداند چگونه با پایگاه داده H2 ارتباط برقرار کند.
نحوه انتخاب یک پلت فرم داده جریانی

توجه داشته باشید که JPA به طور خودکار کلاس های موجودیت مشروح شما را اسکن و کشف می کند.

مدل دامنه

برای این برنامه، ما در حال مدل‌سازی کلاس Book و کلاس Author هستیم. این موجودات یک رابطه یک به چند دارند، به این معنی که یک کتاب را فقط یک نویسنده می‌تواند بنویسد، اما یک نویسنده می‌تواند کتاب‌های زیادی بنویسد. شکل ۱ این مدل دامنه را نشان می دهد.

JPA و Hibernate--نمونه ای از یک مدل دامنه با دو موجودیت.

شکل ۱. مدل دامنه برای یک برنامه JPA/Hibernate با دو موجودیت.

وقتی در مورد جداول پایگاه داده صحبت می کنیم، معمولاً در مورد یک “مدل داده” صحبت می کنیم، اما وقتی در مورد موجودیت های جاوا و روابط آنها صحبت می کنیم، معمولاً از آن به عنوان “مدل دامنه” یاد می کنیم.

مدل سازی کلاس کتاب

بیایید با موجودیت‌های خود شروع کنیم. فهرست ۳ کد منبع کلاس Book را نشان می دهد.


package com.infoworld.jpa.model;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.NamedQueries;
import jakarta.persistence.NamedQuery;
import jakarta.persistence.Table;

@Entity
@Table(name = "BOOK")
@NamedQueries({
        @NamedQuery(name = "Book.findByName",
                query = "SELECT b FROM Book b WHERE b.name = :name"),
        @NamedQuery(name = "Book.findAll",
                query = "SELECT b FROM Book b")
})
public class Book {
    @Id
    @GeneratedValue
    private Integer id;
    private String name;

    @ManyToOne
    @JoinColumn(name="AUTHOR_ID")
    private Author author;

    public Book() {
    }

    public Book(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Book(String name) {
        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;
    }

    public Author getAuthor() {
        return author;
    }

    public void setAuthor(Author author) {
        this.author = author;
    }

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", author=" + author.getName() +
                '}';
    }
}

این کتاب یک POJO ساده (شیء قدیمی جاوا) است که سه ویژگی را مدیریت می کند:

  • id: کلید اصلی یا شناسه کتاب.
  • name: نام یا عنوان کتاب.
  • نویسنده: نویسنده ای که کتاب را نوشته است.

خود کلاس با سه حاشیه نویسی شده است:

  • @Entity: Book را به عنوان یک موجودیت JPA شناسایی می‌کند.
  • @Table: نام جدولی را که این موجودیت در آن باقی خواهد ماند، لغو می‌کند. در این مورد ما نام جدول را به عنوان BOOK تعریف می کنیم.
  • @NamedQueries: به شما امکان می‌دهد جستارهای زبان پرس و جو JPA را تعریف کنید که بعداً می‌توانند توسط EntityManager بازیابی و اجرا شوند.
امنیت سخت است و آسان تر نخواهد شد

درباره زبان جستجوی JPA (JPQL)

JPQL شبیه SQL است، اما به جای اینکه روی نام ستون های پایگاه داده عمل کند، روی موجودیت ها، فیلدها و روابط آنها عمل می کند. JPA به این پرس‌و‌جوها به‌عنوان در حال اجرا در بالای یک «طرح انتزاعی» اشاره می‌کند که به یک طرح پایگاه داده مناسب ترجمه می‌شود. موجودیت ها به آن طرح پایگاه داده نگاشت می شوند. فرمت یک جستار ساده JPQL در اینجا آمده است:


SELECT returnedEntity FROM entityName var WHERE whereClause

پرسمان Book.findAll به صورت SELECT b FROM Book b تعریف می شود که در آن “Book” نام موجودیت و “b” نام متغیر است. به “کتاب” اختصاص داده شده است. این معادل SQL SELECT * FROM BOOK است.

Book.findByName از یک پارامتر با نام “name” استفاده می کند، مانند:


SELECT b FROM Book b WHERE b.name = :name

پارامترها را می توان با نام یا موقعیت ارجاع داد. هنگام ارجاع به یک پارامتر با نام، نام مورد انتظار درخواست خود را مشخص می کنید، مانند “name” یا “bookName،” که با یک “:". در عوض، اگر می‌خواهید نام را بر اساس موقعیت ارجاع دهید، می‌توانید «:name» را با «» جایگزین کنید. هنگام اجرای پرس و جو، پارامتر را با موقعیت "۱" تنظیم می کنید. اتصال نام کتاب به "MyBook" معادل SQL زیر است:


SELECT * FROM BOOK WHERE name='MyBook'

ویژگی id Book با @Id و @GeneratedValue حاشیه نویسی شده است. حاشیه نویسی @Id id را به عنوان کلید اصلی Book شناسایی می‌کند که به کلید اصلی پایگاه داده زیرین باز می‌گردد. حاشیه نویسی @GeneratedValue به JPA می گوید که پایگاه داده باید کلید اصلی را زمانی که موجودیت در پایگاه داده باقی می ماند تولید کند. از آنجایی که ما حاشیه‌نویسی @Column را مشخص نکرده‌ایم، id به همان نام ستون، "id."

نگاشت می‌شود.