maven 和 gradle JavaFX 项目的休眠行为差异

问题描述 投票:0回答:1

我一直在尝试将 hibernate 与我的 JavaFX Maven 项目集成。它与 Hibernate 社区包、Jakarta 和 xerial 一起工作得很好。我还将 persistence.xml 文件放置在

src/main/resources/META-INF/persistence.xml
.

我还尝试使用 gradle 创建另一个项目并完成此 Maven 项目的同上,但它导致了错误

No Persistence provider for EntityManager named demo
。对于 gradle javafx 项目,
persistence.xml
与 Maven 项目位于同一位置

pom.xml
供大家参考

<dependency>
      <groupId>org.xerial</groupId>
      <artifactId>sqlite-jdbc</artifactId>
      <version>3.46.1.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.hibernate.orm/hibernate-community-dialects -->
    <dependency>
      <groupId>org.hibernate.orm</groupId>
      <artifactId>hibernate-community-dialects</artifactId>
      <version>6.6.0.Final</version>
    </dependency>


    <!-- https://mvnrepository.com/artifact/jakarta.persistence/jakarta.persistence-api -->
    <dependency>
      <groupId>jakarta.persistence</groupId>
      <artifactId>jakarta.persistence-api</artifactId>
      <version>3.1.0</version>
    </dependency>

persistence.xml
供参考

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
    <persistence-unit name="demo" transaction-type="RESOURCE_LOCAL">


        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <properties>
            <property name="hibernate.connection.driver_class" value="org.sqlite.JDBC"/>
            <property name="hibernate.connection.url" value="jdbc:sqlite:ms.db"/>
            <property name="hibernate.dialect" value="org.hibernate.community.dialect.SQLiteDialect"/>

            <property name="hibernate.connection.username" value=""/>
            <property name="hibernate.connection.password" value=""/>

            <property name="hibernate.temp.use_jdbc_metadata_defaults" value="false"/>

        </properties>

    </persistence-unit>
</persistence>

我的另一个项目的

build.gradle

javafx {
  version = '17.0.6'
  modules = ['javafx.controls', 'javafx.fxml']
}

dependencies {
  // https://mvnrepository.com/artifact/org.hibernate.orm/hibernate-community-dialects
  implementation 'org.hibernate.orm:hibernate-community-dialects:6.6.0.Final'

  // https://mvnrepository.com/artifact/jakarta.persistence/jakarta.persistence-api
  implementation 'jakarta.persistence:jakarta.persistence-api:3.2.0'

  // https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc
  implementation 'org.xerial:sqlite-jdbc:3.46.1.0'

  testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}")
  testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}")
}

虽然持久性单元的拼写也是正确的。

我尝试从 build.gradle 中获取 sourceSets,它显示了有关重复的错误,我得出的结论是包含所有资源文件。我还降级了一些它确实有效的版本。如何解决 maven 和 gradle 之间的这种行为差异?我见过一些用途

hibernate.cfg.xml
这是与持久性或 hibernate xml 文件搭配使用的最佳选择?

java hibernate jpa javafx orm
1个回答
0
投票

这不是您问题的直接答案,所以如果您不想使用该方法,请忽略它。

推荐

您正在做的是尝试将本地嵌入式数据库技术与 JavaFX 结合使用。 对于我来说(这对其他人来说会有所不同),最好使用以下方法来做到这一点:

  1. 使用比 Hibernate 更高级别的 DB API(例如 Spring Data)。
  2. 使用本机 Java 嵌入式数据库而不是 SQLLite(例如 H2)。

注释

在幕后,它仍然是一个使用 Hibernate 访问嵌入式数据库的 Maven 项目,但 Maven 依赖项和数据库配置任务均由 Spring Boot Data starter 依赖项和 Spring Boot 从输入 application.yaml 配置中自动配置和应用程序中的注释驱动配置。 因此,您不需要定义诸如 persistence.xml、单独的 hibernate 属性文件、编写样板 DAO 代码、提供 hibernate.cfg.xml 等内容。

缺点是您在应用程序之上添加了几层抽象,这可能会带来进一步的混乱、学习和复杂性。 但是,IMO,如果您打算投入时间使用 JavaFX API 学习 JPA 开发,不妨让 Spring 做一些工作并学习 Spring。 一次迈出一步,专注于当前要学习的东西,而不是试图学习 Spring 的“全部”。

使用像这样的嵌入式数据库是客户端应用程序的特定(且有效)模型。 然而,许多客户端应用程序(例如,几乎所有基于 HTML 的 Web 应用程序,以及许多独立应用程序,如 JavaFX 应用程序或移动本机应用程序),不使用嵌入式数据库,而是让客户端与共享 Web 服务器通信(通常使用 REST API) )在服务器端而不是客户端进行数据库集成。 因此,请考虑您的应用程序是否实际上遵循适合您目的的架构模型。

示例

无论如何,这是一个示例项目,以防您对这种方法感兴趣。

此示例使用:

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>org.example</groupId>
    <artifactId>FXSpringData</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>FXSpringData</name>

    <properties>
        <java.version>22</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>22.0.2</version>
        </dependency>

        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

src/main/resources/application.yaml

spring:
  datasource:
    url: jdbc:h2:mem:mydb
    # use this url for a file db named demo stored in the user home dir.
    # url: jdbc:h2:file:~/demo
    username: sa
    password: password
    driverClassName: org.h2.Driver
  jpa:
    defer-datasource-initialization: true
  h2:
    # console is at: http://localhost:8080/h2-console, after app loaded
    console:
      enabled: true
      settings:
        trace: false
        web-allow-others: false

src/main/resources/data.sql

INSERT INTO countries (id, name) VALUES (1, 'USA');
INSERT INTO countries (id, name) VALUES (2, 'France');
INSERT INTO countries (id, name) VALUES (3, 'Brazil');
INSERT INTO countries (id, name) VALUES (4, 'Italy');
INSERT INTO countries (id, name) VALUES (5, 'Canada');

src/main/java/org/esample/fxspringdata/SpringApp.java

package org.example.fxspringdata;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class FXApp {    
    public static void main(String[] args) {
        SpringApplication.run(SpringApp.class, args);
    }   
}

src/main/java/org/esample/fxspringdata/FXApp.java

package org.example.fxspringdata;

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.example.fxspringdata.dao.CountryRepository;
import org.example.fxspringdata.model.Country;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;

public class FXApp extends Application {
    private ConfigurableApplicationContext springContext;

    @Autowired
    private CountryRepository countryRepository;

    @Override
    public void init() {
        springContext = SpringApplication.run(SpringApp.class);
        springContext
                .getAutowireCapableBeanFactory()
                .autowireBeanProperties(
                        this,
                        AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE,
                        true
                );
    }

    @Override
    public void start(Stage stage) {
        ListView<Country> countriesListView = new ListView<>(
                FXCollections.observableArrayList(
                        countryRepository.findAll()
                )
        );
        countriesListView.setCellFactory(_ -> new CountryListCell());
        countriesListView.setPrefSize(100, 100);

        VBox layout = new VBox(10,
                new Label("Countries from DB"),
                countriesListView
        );
        layout.setPadding(new Insets(10));

        stage.setScene(new Scene(layout));
        stage.show();
    }

    @Override
    public void stop() {
        springContext.stop();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

src/main/java/org/esample/fxspringdata/CountryListCell.java

package org.example.fxspringdata;

import javafx.scene.control.ListCell;
import org.example.fxspringdata.model.Country;

public final class CountryListCell extends ListCell<Country> {
    @Override
    protected void updateItem(Country item, boolean empty) {
        super.updateItem(item, empty);

        if (empty || item == null) {
            setText(null);
            return;
        }

        setText(item.getName());
    }
}

src/main/java/org/esample/fxspringdata/model/Country.java

package org.example.fxspringdata.model;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.util.Objects;

@Table(name = "countries")
@Entity
public class Country {

    @Id
    @GeneratedValue
    private int id;

    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;
        Country country = (Country) o;
        return id == country.id && name.equals(country.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }

    @Override
    public String toString() {
        return "Country{" +
          "id=" + id +
          ", name='" + name + '\'' +
          '}';
    }
}

src/main/java/org/esample/fxspringdata/dao/CountryRepository.java

package org.example.fxspringdata.dao;

import org.example.fxspringdata.model.Country;
import org.springframework.data.jpa.repository.JpaRepository;

public interface CountryRepository extends JpaRepository<Country, Integer> {}

执行

我使用Idea,将项目配置为作为非模块化项目执行(未定义module-info.java),单独下载与Maven中定义的版本匹配的JavaFX SDK,以便可以轻松配置模块路径和JavaFX运行时组件.

idea

关键部分是VM参数(不是程序参数):

--module-path <pathtojavafx>/javafx-sdk-22.0.2/lib --add-modules javafx.controls

替换为JavaFX SDK的位置。

运行后,应用程序将:

  • 启动Spring/Hibernate/H2嵌入式数据库。
  • 使用从实体派生的模式初始化数据库(创建所需的国家/地区表)。
  • 将提供的种子数据插入数据库。
  • 使用 Spring Data 读取数据(Spring Data 又使用 Hibernate 使用 Hikari 连接池通过 H2 JDBC 驱动程序访问数据库)。
  • 在 JavaFX 列表视图中显示国家/地区列表。

screenshot

© www.soinside.com 2019 - 2024. All rights reserved.