我一直在尝试将 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 文件搭配使用的最佳选择?
这不是您问题的直接答案,所以如果您不想使用该方法,请忽略它。
您正在做的是尝试将本地嵌入式数据库技术与 JavaFX 结合使用。 对于我来说(这对其他人来说会有所不同),最好使用以下方法来做到这一点:
在幕后,它仍然是一个使用 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运行时组件.
关键部分是VM参数(不是程序参数):
--module-path <pathtojavafx>/javafx-sdk-22.0.2/lib --add-modules javafx.controls
替换为JavaFX SDK的位置。
运行后,应用程序将: