Maven 和 gradle JavaFX 项目的休眠行为差异
我一直在尝试将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-dialectsimplementation 'org.hibernate.orm:hibernate-community-dialects:6.6.0.Final'// https://mvnrepository.com/artifact/jakarta.persistence/jakarta.persistence-apiimplementation 'jakarta.persistence:jakarta.persistence-api:3.2.0'// https://mvnrepository.com/artifact/org.xerial/sqlite-jdbcimplementation '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
哪个最适合持久化或休眠xml
文件?
解决方案
这不是对您的问题的直接回答,所以如果您不想使用该方法,请忽略它。
推荐
您正在尝试将本地嵌入式数据库技术与JavaFX
结合使用。对我来说(对其他人来说会有所不同),最好使用以下方法:
使用比Hibernate
更高级别的DB API
(例如Spring Data
)。
使用本机Java
嵌入式数据库而不是SQLLite
(例如H2
)。
笔记
在幕后,它仍然是一个使用Hibernate
访问嵌入式数据库的Maven
项目,但 Maven
依赖项和数据库配置任务均由Spring Boot Data
启动器依赖项和Spring Boot
的自动配置处理,这些自动配置来自输入application.yaml
配置和应用程序中的注释驱动配置。因此,您无需定义persistence.xml
之类的内容、单独的Hibernate
属性文件、编写样板DAO
代码、提供hibernate.cfg.xml
等。
缺点是您在应用程序之上添加了几层抽象,这可能会带来进一步的混乱、学习和复杂性。但是,在我看来,如果您要花时间学习使用JavaFX API
进行 JPA
开发,那么您不妨让Spring
完成一些工作,同时学习一些Spring
。只需一步一步来,专注于您当前需要学习的东西来解决您遇到的每个问题,而不是试图学习“所有”Spring
。
使用这样的嵌入式数据库是客户端应用程序的特定(且有效)模型。但是,许多客户端应用程序(例如几乎所有基于HTML
的Web
应用程序,以及许多独立应用程序,如JavaFX
应用程序或移动原生应用程序)不使用嵌入式数据库,而是让客户端与共享Web
服务器通信(通常使用REST API
),数据库集成在服务器端而不是客户端。因此,请考虑您的应用程序是否确实遵循适合您目的的架构模型。
例子
无论如何,这里有一个示例项目,如果您对这种方法感兴趣的话。
本示例使用:
在JavaFX
(JPA Repo
、服务) 中添加Spring
依赖注入
<?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/
资源/应用程序.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:~/demousername: sapassword: passworddriverClassName: org.h2.Driverjpa:defer-datasource-initialization: trueh2:# console is at: http://localhost:8080/h2-console, after app loadedconsole:enabled: truesettings:trace: falseweb-allow-others: false
src/main/
资源/数据.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;@Autowiredprivate CountryRepository countryRepository;@Overridepublic void init() {springContext = SpringApplication.run(SpringApp.class);springContext.getAutowireCapableBeanFactory().autowireBeanProperties(this,AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE,true);}@Overridepublic 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();}@Overridepublic 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> {@Overrideprotected 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@GeneratedValueprivate 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;}@Overridepublic 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);}@Overridepublic int hashCode() {return Objects.hash(id, name);}@Overridepublic 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
<pathtojavafx>
用您机器上的JavaFX SDK
的位置进行替换。
应用程序的功能一旦运行,该应用程序将:
启动Spring/Hibernate/H2
嵌入式数据库。
使用从实体派生的模式初始化数据库(创建所需的“国家/地区”表)。
将提供的种子数据插入数据库。
使用Spring Data
读取数据(Spring Data
又使用Hibernate
通过Hikari
连接池的 H2 JDBC
驱动程序访问数据库)。
在JavaFX
列表视图中显示国家列表。