英文:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException Invalid type definition for type `com.app.bankapplet.bank.Bank`
问题
在尝试将对象存储到NOsql对象(NO2,又称Nitrite)数据库时,出现了com.fasterxml.jackson.databind.exc.InvalidDefinitionException
异常。
我已经尝试在bank.java
类中的module-info.java
文件中导出了银行类;我尝试在bank.java
类中添加了一个默认构造函数,但都未解决问题。
感谢您帮助解决此错误。
完整的错误信息如下:
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: 类型 `com.app.bankapplet.bank.Bank` 的类型定义无效:无法为 [简单类型, 类 com.app.bankapplet.bank.Bank] 构造 BeanSerializer:(java.lang.IllegalArgumentException) 在类 `com.app.bankapplet.bank.Bank` 的字段 `id` 上调用 `setAccess()` 失败,由于 `java.lang.reflect.InaccessibleObjectException`,问题是:无法使私有字段 `com.app.bankapplet.bank.Bank.id` 可访问:模块 `com.app.bankapplet` 未对未命名模块打开 `com.app.bankapplet.bank` @6293abcc
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:72)
at com.fasterxml.jackson.databind.SerializerProvider.reportBadTypeDefinition(SerializerProvider.java:1280)
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.constructBeanOrAddOnSerializer(BeanSerializerFactory.java:475)
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.findBeanOrAddOnSerializer(BeanSerializerFactory.java:295)
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory._createSerializer2(BeanSerializerFactory.java:240)
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.createSerializer(BeanSerializerFactory.java:174)
at com.fasterxml.jackson.databind.SerializerProvider._createUntypedSerializer(SerializerProvider.java:1503)
at com.fasterxml.jackson.databind.SerializerProvider._createAndCacheUntypedSerializer(SerializerProvider.java:1451)
at com.fasterxml.jackson.databind.SerializerProvider.findValueSerializer(SerializerProvider.java:556)
at com.fasterxml.jackson.databind.SerializerProvider.findTypedValueSerializer(SerializerProvider.java:834)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:307)
at com.fasterxml.jackson.databind.ObjectMapper._convert(ObjectMapper.java:4522)
... 18 more
Exception running application com.app.bankapplet.Main
我期望的是将对象存储在对象存储库中。
以下是我的代码:
Main.java
// ...
Bank.java
// ...
module-info.java
// ...
pom.xml
// ...
英文:
Trying to create a Java Object and store it in a NOsql Object (NO2 a.k.a Nitrite) database.
Upon trying to store the object in an object repository the com.fasterxml.jackson.databind.exc.InvalidDefinitionException exception is thrown.
I have tried to export the bank.java class in the module-info.java file; I have tried to add a default constructor in the bank.java class. Neither have solved the problem.
Thank you for the help in solving this error.
The full error is displayed as follows:
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Invalid type definition for type `com.app.bankapplet.bank.Bank`: Failed to construct BeanSerializer for [simple type, class com.app.bankapplet.bank.Bank]: (java.lang.IllegalArgumentException) Failed to call `setAccess()` on Field 'id' (of class `com.app.bankapplet.bank.Bank`) due to `java.lang.reflect.InaccessibleObjectException`, problem: Unable to make field private int com.app.bankapplet.bank.Bank.id accessible: module com.app.bankapplet does not "opens com.app.bankapplet.bank" to unnamed module @6293abcc
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:72)
at com.fasterxml.jackson.databind.SerializerProvider.reportBadTypeDefinition(SerializerProvider.java:1280)
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.constructBeanOrAddOnSerializer(BeanSerializerFactory.java:475)
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.findBeanOrAddOnSerializer(BeanSerializerFactory.java:295)
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory._createSerializer2(BeanSerializerFactory.java:240)
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.createSerializer(BeanSerializerFactory.java:174)
at com.fasterxml.jackson.databind.SerializerProvider._createUntypedSerializer(SerializerProvider.java:1503)
at com.fasterxml.jackson.databind.SerializerProvider._createAndCacheUntypedSerializer(SerializerProvider.java:1451)
at com.fasterxml.jackson.databind.SerializerProvider.findValueSerializer(SerializerProvider.java:556)
at com.fasterxml.jackson.databind.SerializerProvider.findTypedValueSerializer(SerializerProvider.java:834)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:307)
at com.fasterxml.jackson.databind.ObjectMapper._convert(ObjectMapper.java:4522)
... 18 more
Exception running application com.app.bankapplet.Main
What I would expect to have happen is the object is stored in the object repository.
Please find below my code:
Main.java
package com.app.bankapplet;
import com.app.bankapplet.bank.Bank;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import javafx.util.Callback;
import org.dizitart.no2.Nitrite;
import org.dizitart.no2.NitriteCollection;
import org.dizitart.no2.objects.ObjectRepository;
import java.io.File;
import java.io.IOException;
import java.util.Optional;
public class Main extends Application {
private static Nitrite db;
private static Bank bank;
@Override
public void start(Stage stage) throws IOException {
// Generates a bank
bank = getBank();
ObjectRepository<Bank> bankObjectStore = db.getRepository(Bank.class);
bankObjectStore.insert(bank);
FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("main.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 800, 600);
stage.setTitle("Bank Manager");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
db = Nitrite.builder()
.filePath(new File("target/appdb.db"))
.compressed()
.openOrCreate();
launch();
}
protected static Bank getBank() {
Dialog<Bank> dialog = new Dialog<>();
dialog.setTitle("New Bank");
dialog.setHeaderText("Enter the name of the bank:");
Label label = new Label("Bank Name: ");
TextField text = new TextField();
GridPane gridPane = new GridPane();
gridPane.add(label, 1, 1);
gridPane.add(text, 2, 1);
dialog.getDialogPane().setContent(gridPane);
ButtonType buttonTypeOK = new ButtonType("OK", ButtonBar.ButtonData.OK_DONE);
dialog.getDialogPane().getButtonTypes().add(buttonTypeOK);
dialog.setResultConverter(new Callback<ButtonType, Bank>() {
@Override
public Bank call(ButtonType b) {
if(b == buttonTypeOK) {
return new Bank(text.getText());
}
return null;
}
});
Optional<Bank> result = dialog.showAndWait();
if(result.isPresent()) {
return result.get();
}
return null;
}
}
Bank.java
package com.app.bankapplet.bank;
import org.dizitart.no2.objects.Id;
public class Bank {
@Id
private int id;
private String name;
public Bank() {
}
public Bank(String name) {
this.id = 0;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private static String toTitleCase(String item) {
if (item == null || item.isEmpty()) {
return item;
}
StringBuilder convertedString = new StringBuilder();
boolean convertNext = true;
for (char ch : item.toCharArray()) {
if (Character.isSpaceChar(ch)) {
convertNext = true;
} else if (convertNext) {
ch = Character.toTitleCase(ch);
convertNext = false;
} else {
ch = Character.toLowerCase(ch);
}
convertedString.append(ch);
}
return convertedString.toString();
}
}
module-info.java
module com.app.bankapplet {
requires javafx.controls;
requires javafx.fxml;
requires nitrite;
opens com.app.bankapplet to javafx.fxml;
exports com.app.bankapplet.bank;
exports com.app.bankapplet;
}
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>
<groupId>com.app</groupId>
<artifactId>BankApplet</artifactId>
<version>1.0-SNAPSHOT</version>
<name>BankApplet</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>5.9.2</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>20</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>20</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.1</version>
</dependency>
<dependency>
<groupId>org.dizitart</groupId>
<artifactId>nitrite</artifactId>
<version>3.4.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>20</source>
<target>20</target>
</configuration>
</plugin>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.8</version>
<executions>
<execution>
<!-- Default configuration for running with: mvn clean javafx:run -->
<id>default-cli</id>
<configuration>
<mainClass>com.app.bankapplet/com.app.bankapplet.Main</mainClass>
<launcher>app</launcher>
<jlinkZipName>app</jlinkZipName>
<jlinkImageName>app</jlinkImageName>
<noManPages>true</noManPages>
<stripDebug>true</stripDebug>
<noHeaderFiles>true</noHeaderFiles>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
答案1
得分: 1
以下是翻译好的内容:
解决方案:更新您的Maven项目和Java模块信息
为了使您的示例运行,我需要进行以下更改。
-
用一个空场景替换FXML场景,因为您的问题中没有FXML。
// FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("main.fxml"));
// Scene scene = new Scene(fxmlLoader.load(), 800, 600);
stage.setScene(new Scene(new Group())); -
根据Jackson Maven指南的建议,向pom.xml添加一个依赖管理部分,并删除jackson-databind的版本,以便从依赖管理部分的“bom”中派生版本(我不确定为什么需要这样做,但是如果没有这个,我的IDE会抱怨依赖的jackson-core jar文件为空)。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-bom</artifactId>
<version>2.15.2</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
. . .
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</>
</dependency> -
更新module-info.java以依赖jackson databind模块。
requires com.fasterxml.jackson.databind;
-
更新module-info.java以向jackson databind模块打开bank包。
opens com.app.bankapplet.bank to com.fasterxml.jackson.databind;
我不需要应用AhmedNabil的回答中提出的添加id setter的修改。这是因为Jackson通过反射进行内省工作,一旦将包打开到模块,即使在id字段上没有公共setter,它也能够内省您的Bank类。不过,您可能还是要考虑使用一个ID setter。
附加评论
您需要:
- 在module-info中要求jackson模块(在它们的module-info或META-INF中查找名称)。
- 在module-info中打开所需的包(您可以在错误消息中看到它),以适当的jackson模块打开它(在您修复第1步后,它将告诉您需要将其打开到哪个jackson模块)。
如果您不明白,请研究理解Java 9模块。
尽管我的答案解决了您问题中标记的问题,但由于您正在使用非模块的nitrate jar作为自动模块,所以将来的工作可能会更加复杂(因此像您的Maven项目中的jlink
之类的东西将不起作用)。
您可能要考虑完全摒弃模块信息,创建一个非模块化的项目,然后要么在运行时拥有JavaFX(例如Azul Zulu JDK FX分发),要么通过命令行参数提供JavaFX模块(有关该配置的更多信息,请参阅openjfx.io入门指南)。
常见问题
> 为什么我需要加载一个空的场景?
您不需要这样做。
这是我需要在我的环境中运行应用所做的。您的应用加载了一个我没有的FXML文件。必要的FXML文件没有包含在您的问题中,所以除非我更改了您的代码,否则它不会运行。
> 什么是jlink
?
jlink
是Java链接器。
它是JDK的一部分。它用于构建Java和模块化Java应用程序的自定义本机运行时。您在问题中提供的Maven项目文件使用了javafx-maven-plugin
的执行,所以我以为您可能正在尝试使用它(对于自动模块的应用程序,例如您的应用,它是不起作用的)。
英文:
Solution: Update your Maven project and Java module-info
To get your example to run, I needed to make the following changes.
-
Replace the FXML scene with an empty scene, as there is no FXML in your question.
// FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("main.fxml")); // Scene scene = new Scene(fxmlLoader.load(), 800, 600); stage.setScene(new Scene(new Group()));
-
Add a dependency management section to the pom.xml as recommended by the Jackson maven guide and replace the remove the version for the jackson-databind so that it could be derived from the bill of materials (bom) in the dependency management section. (I am not sure why this was required, but, without it, my IDE complained that the dependent jackson-core jar file was empty).
<dependencyManagement> <dependencies> <dependency> <groupId>com.fasterxml.jackson</groupId> <artifactId>jackson-bom</artifactId> <version>2.15.2</version> <scope>import</scope> <type>pom</type> </dependency> </dependencies> </dependencyManagement> . . . <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</> </dependency>
-
Update the module-info.java to require the jackson databind module.
requires com.fasterxml.jackson.databind;
-
Update the module-info.java to open the bank package to the jackson databind module.
opens com.app.bankapplet.bank to com.fasterxml.jackson.databind;
I did not need to apply the modification to add an id setter proposed by AhmedNabil's answer. This is because Jackson works on introspection by reflection, and was able to introspect your Bank class once the package opened it to the module, even without a public setter on the id field. You might want to consider using an ID setter regardless.
Additional Comments
You need to:
- Require the jackson modules in your module-info, (look for the names in their module-info or META-INF).
- In your module-info open the required package (you can see it in the error message) to the appropriate jackson module (it will tell you the name of the jackson module you need to open it to in the error it will generate after you fix step 1).
If you don't understand, study Understanding Java 9 modules.
Although my answer solves the issue marked in your question, future work may also be more complicated because of the use of the non-modular nitrate jar which you are using as an automatic module (so stuff like jlink
like you have in your Maven project) won't work with it.
You might want to consider getting rid of the module info altogether and creating a non-modular project, then either having JavaFX in the runtime (e.g. Azul Zulu JDK FX distribution) or providing the JavaFX modules via command line arguments (see openjfx.io getting started for more info about that configuration).
FAQ
> Why would I be required to load an empty Scene?
You don’t need to do that.
It is what I needed to do, to be able to run the app in my environment. Your application loads an fxml file which I don’t have. The required fxml file is not included your question, so unless I changed your code, it would not run.
> What is jlink
?
jlink
is a Java linker.
It is a part of the JDK. It builds custom native runtimes for Java and for modular Java applications.
The Maven project file you provided in your question refers to it using an execution of the javafx-maven-plugin
, so I thought you might be trying to use it (which won't work for an app with automatic modules such as you have).
答案2
得分: 0
添加id
属性的setter方法,以便Jackson可以访问它。
public void setId(int id) {
this.id = id;
}
英文:
> java.lang.reflect.InaccessibleObjectException
, problem: Unable to make field private int com.app.bankapplet.bank.Bank.id accessible
add setter for id property so that jackson can access it
public void setId(int id) {
this.id= id;
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论