Morphia - Class 未在 dbObj 中找到定义

Morphia - Class not found defined in dbObj

我有一个相当有趣的问题。尝试从 Mongo 实例加载模型时,Morphia 抛出以下错误:

[22:17:13 WARN]: Class not found defined in dbObj: 
java.lang.ClassNotFoundException: me.mattrick.test.storage.TestStat
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381) ~[?:1.8.0_101]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[?:1.8.0_101]
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) ~[?:1.8.0_101]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[?:1.8.0_101]
    at java.lang.Class.forName0(Native Method) ~[?:1.8.0_101]
    at java.lang.Class.forName(Class.java:348) ~[?:1.8.0_101]
    at org.mongodb.morphia.mapping.DefaultCreator.getClass(DefaultCreator.java:175) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at org.mongodb.morphia.mapping.DefaultCreator.createInstance(DefaultCreator.java:97) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at org.mongodb.morphia.mapping.EmbeddedMapper.readMapOrCollectionOrEntity(EmbeddedMapper.java:205) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at org.mongodb.morphia.mapping.EmbeddedMapper.readCollection(EmbeddedMapper.java:142) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at org.mongodb.morphia.mapping.EmbeddedMapper.fromDBObject(EmbeddedMapper.java:45) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at org.mongodb.morphia.mapping.Mapper.readMappedField(Mapper.java:772) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at org.mongodb.morphia.mapping.Mapper.fromDb(Mapper.java:230) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at org.mongodb.morphia.mapping.Mapper.fromDBObject(Mapper.java:191) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at org.mongodb.morphia.query.MorphiaIterator.convertItem(MorphiaIterator.java:134) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at org.mongodb.morphia.query.MorphiaIterator.processItem(MorphiaIterator.java:146) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at org.mongodb.morphia.query.MorphiaIterator.next(MorphiaIterator.java:117) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at org.mongodb.morphia.query.QueryImpl.get(QueryImpl.java:220) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at org.mongodb.morphia.dao.BasicDAO.findOne(BasicDAO.java:180) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at me.mattrick.test.storage.UserRepository.getUserFromPlayer(UserRepository.java:22) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at me.mattrick.test.event.JoinEvent.onJoin(JoinEvent.java:37) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_101]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_101]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_101]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_101]
    at org.bukkit.plugin.java.JavaPluginLoader.execute(JavaPluginLoader.java:306) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:62) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at org.bukkit.plugin.SimplePluginManager.fireEvent(SimplePluginManager.java:502) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:487) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at net.minecraft.server.v1_8_R3.PlayerList.onPlayerJoin(PlayerList.java:298) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at net.minecraft.server.v1_8_R3.PlayerList.a(PlayerList.java:157) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at net.minecraft.server.v1_8_R3.LoginListener.b(LoginListener.java:144) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at net.minecraft.server.v1_8_R3.LoginListener.c(LoginListener.java:54) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at net.minecraft.server.v1_8_R3.NetworkManager.a(NetworkManager.java:231) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at net.minecraft.server.v1_8_R3.ServerConnection.c(ServerConnection.java:148) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at net.minecraft.server.v1_8_R3.MinecraftServer.B(MinecraftServer.java:814) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at net.minecraft.server.v1_8_R3.DedicatedServer.B(DedicatedServer.java:374) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at net.minecraft.server.v1_8_R3.MinecraftServer.A(MinecraftServer.java:654) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at net.minecraft.server.v1_8_R3.MinecraftServer.run(MinecraftServer.java:557) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at java.lang.Thread.run(Thread.java:745) [?:1.8.0_101]
[22:17:13 WARN]: Class not found defined in dbObj: 
java.lang.ClassNotFoundException: me.mattrick.test.storage.TestStat
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381) ~[?:1.8.0_101]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[?:1.8.0_101]
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) ~[?:1.8.0_101]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[?:1.8.0_101]
    at java.lang.Class.forName0(Native Method) ~[?:1.8.0_101]
    at java.lang.Class.forName(Class.java:348) ~[?:1.8.0_101]
    at org.mongodb.morphia.mapping.DefaultCreator.getClass(DefaultCreator.java:175) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at org.mongodb.morphia.mapping.DefaultCreator.createInstance(DefaultCreator.java:87) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at org.mongodb.morphia.mapping.DefaultCreator.createInstance(DefaultCreator.java:102) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at org.mongodb.morphia.mapping.EmbeddedMapper.readMapOrCollectionOrEntity(EmbeddedMapper.java:205) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at org.mongodb.morphia.mapping.EmbeddedMapper.readCollection(EmbeddedMapper.java:142) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at org.mongodb.morphia.mapping.EmbeddedMapper.fromDBObject(EmbeddedMapper.java:45) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at org.mongodb.morphia.mapping.Mapper.readMappedField(Mapper.java:772) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at org.mongodb.morphia.mapping.Mapper.fromDb(Mapper.java:230) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at org.mongodb.morphia.mapping.Mapper.fromDBObject(Mapper.java:191) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at org.mongodb.morphia.query.MorphiaIterator.convertItem(MorphiaIterator.java:134) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at org.mongodb.morphia.query.MorphiaIterator.processItem(MorphiaIterator.java:146) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at org.mongodb.morphia.query.MorphiaIterator.next(MorphiaIterator.java:117) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at org.mongodb.morphia.query.QueryImpl.get(QueryImpl.java:220) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at org.mongodb.morphia.dao.BasicDAO.findOne(BasicDAO.java:180) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at me.mattrick.test.storage.UserRepository.getUserFromPlayer(UserRepository.java:22) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at me.mattrick.test.event.JoinEvent.onJoin(JoinEvent.java:37) [Test-1.5-SNAPSHOT-shaded.jar:?]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_101]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_101]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_101]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_101]
    at org.bukkit.plugin.java.JavaPluginLoader.execute(JavaPluginLoader.java:306) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:62) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at org.bukkit.plugin.SimplePluginManager.fireEvent(SimplePluginManager.java:502) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:487) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at net.minecraft.server.v1_8_R3.PlayerList.onPlayerJoin(PlayerList.java:298) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at net.minecraft.server.v1_8_R3.PlayerList.a(PlayerList.java:157) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at net.minecraft.server.v1_8_R3.LoginListener.b(LoginListener.java:144) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at net.minecraft.server.v1_8_R3.LoginListener.c(LoginListener.java:54) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at net.minecraft.server.v1_8_R3.NetworkManager.a(NetworkManager.java:231) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at net.minecraft.server.v1_8_R3.ServerConnection.c(ServerConnection.java:148) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at net.minecraft.server.v1_8_R3.MinecraftServer.B(MinecraftServer.java:814) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at net.minecraft.server.v1_8_R3.DedicatedServer.B(DedicatedServer.java:374) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at net.minecraft.server.v1_8_R3.MinecraftServer.A(MinecraftServer.java:654) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at net.minecraft.server.v1_8_R3.MinecraftServer.run(MinecraftServer.java:557) [spigot-1.8.8.jar:git-Spigot-e4d4710-e1ebe52]
    at java.lang.Thread.run(Thread.java:745) [?:1.8.0_101]

如您所见,我 运行 它在一个 Spigot 服务器中(本质上是一个具有适当 API 实现的 Minecraft 服务器)。我不确定这是否非常相关,但 Spigot 服务器以与 Morphia 类似的方式加载插件,所以我认为这可能是导致此问题的原因,因为之前有记录的 Morphia 实例与其他实例冲突使用 class 加载程序的库。

向@Entity 注释添加"noClassnameStored = true" 似乎可以防止此错误,但是我在数据库中存储了一个自定义对象列表,这也会导致此错误。

我使用的模型如下:

Stat.java:

package me.mattrick.test.storage;

public class Stat {
}

测试Stat.java:

package me.mattrick.test.storage;

import lombok.Getter;
import lombok.Setter;

public class TestStat extends Stat {

    @Getter @Setter
    private int test = 100;

}

User.java:

package me.mattrick.test.storage;

import lombok.Getter;
import lombok.Setter;
import me.mattrick.test.Test;
import me.mattrick.test.api.storage.mongo.MongoEntity;
import me.mattrick.test.cosmetics.particles.EnumParticleEffect;
import me.mattrick.test.player.PlayerRank;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.mongodb.morphia.annotations.Entity;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@Entity(value = "users", noClassnameStored = true)
public class User extends MongoEntity {

    @Getter @Setter
    private UUID uuid;

    @Getter @Setter
    private PlayerRank rank = PlayerRank.DEFAULT;

    @Getter @Setter
    private EnumParticleEffect effect = EnumParticleEffect.NONE;

    @Getter @Setter
    private int money = 0;

    @Getter @Setter
    private int level = 0;

    @Getter @Setter
    private long experience = 0;

    @Getter @Setter
    private LocalDateTime firstJoined;

    @Getter @Setter
    private LocalDateTime lastPlayed;

    @Getter @Setter
    private List<Stat> stats = new ArrayList<>();

    public Player getPlayer() {
        return Bukkit.getPlayer(uuid);
    }

    public void save() {
        Test.getInstance().getMongoBridge().repositoryBy(User.class).save(this);
    }

    public <T extends Stat> T getStat(Class<T> statClass) {
        for (Stat stat : stats) {
            if(stat.getClass() == statClass) {
                return statClass.cast(stat);
            }
        }
        return null;
    }

    public void addStat(Stat stat) {
        stats.add(stat);
    }

}

MongoEntity.java:

package me.mattrick.test.api.storage.mongo;

import lombok.Getter;
import lombok.Setter;
import org.bson.types.ObjectId;
import org.mongodb.morphia.annotations.Id;

public class MongoEntity {

    @Id
    @Getter @Setter
    private ObjectId id;

}

应该注意的是,由于在@Entity 注释上设置了 "noClassnameStored = true" 参数,User class 可以正常保存和加载,但 Stat class 不会,因为这不可行。

有没有人碰巧知道这个问题的解决方案?这是一个相当奇怪的错误,而且我对 Morphia 不太了解。感谢您的帮助,很抱歉这么长post。

该错误通常源于损坏的类路径。看起来你的 spigot 服务器在启动时没有在类路径上包含你的实体的 jar。

我已经弄明白了。您需要用您的插件自己覆盖 Morphia 的 ClassLoader。如果有人使用 Spigot 或 Bukkit 正在寻找答案:

morphia.getMapper().getOptions().setObjectFactory(new DefaultCreator() {
    @Override
    protected ClassLoader getClassLoaderForClass() {
        return JavaPlugin.getClassLoader();
    }
});

对于在 2020 年遇到此问题(就像我一样)并使用 PaperSpigot 的任何人,以下是我在求助于 Morphia 的文档后设法做到的。我正在使用 Morphia v1.6.0 and PaperSpigot 1.16.2.

morphia.getMapper().setOptions(
    MapperOptions.legacy().classLoader(<MainClass>.class.getClassLoader())
        .build()
);

这里有一个解释:

  1. 已接受的答案表明您可以使用 .setObjectFactory to achieve this change. In my IDE (IntelliJ), it wasn't only marked as deprecated, but using that along with the DefaultCreator wasn't doing the job. I don't think it's accepted practice in the new version(s) of Morphia? Anyways, I ended up using the setOptions method using the legacy builder for setting MapperOptions。此方法也未弃用。
  2. 在接受的答案中,显示了JavaPlugin.getClassLoader()的用法。该方法受到保护。因此您不能直接访问它。但是,如果您使用插件的主要 class,例如,您可以毫无问题地访问 getClassLoader() 方法。除非您尝试做一些 weird/hacky 事情,否则您不会想要直接访问这些方法。