无法序列化到二级缓存

Cannot serialize into L2 cache

我在独立 Java 网络应用程序(使用 Jetty、Servlets [是的,架构有点旧] 和 spring).

我们目前正在开发新版本;原项目近3年没有任何依赖更新;所以我们希望进行安全、性能和功能方面的升级,但正如预期的那样,升级并不那么容易。

我们已经解决了大部分问题,但一个变得非常乏味的问题还没有解决:

我们正在使用datanucleus + JDO访问底层数据库;主要是因为它已经存在并且因为大部分系统 确实 与它相关联。到目前为止,我们可以确认 datanucleus 可以读取数据库(调试器显示来自数据库的非空结果正确地代表默认用户;以及来自活动端点的其他一些虚拟结果)。但是当datanucleus试图缓存结果时,它就进入了死胡同。

错误堆栈如下:

GRAVE: exception catched
javax.jdo.JDOException: Failed to set object CachedPC : cls=mobi.allshoppings.model.User version=null loadedFlags=[YYYYYYYYYYYYYYYYY] with id com.inodes.datanucleus.model.Key:User("admin") into Redis cache
    at org.datanucleus.api.jdo.NucleusJDOHelper.getJDOExceptionForNucleusException(NucleusJDOHelper.java:680)
    at org.datanucleus.api.jdo.JDOPersistenceManager.getObjectById(JDOPersistenceManager.java:1747)
    at mobi.allshoppings.dao.spi.GenericDAOJDO.get(GenericDAOJDO.java:144)
    at mobi.allshoppings.dao.spi.GenericDAOJDO.get(GenericDAOJDO.java:108)
    at mobi.allshoppings.bdb.tools.BasicDataBuilder.warmUp(BasicDataBuilder.java:49)
    at mobi.allshoppings.bdb.tools.InitAppServlet.init(InitAppServlet.java:21)
    at org.eclipse.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:616)
    at org.eclipse.jetty.servlet.ServletHolder.initialize(ServletHolder.java:396)
    at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:871)
    at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:298)
    at org.eclipse.jetty.webapp.WebAppContext.startWebapp(WebAppContext.java:1349)
    at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1342)
    at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:741)
    at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:505)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
    at org.eclipse.jetty.server.handler.ContextHandlerCollection.doStart(ContextHandlerCollection.java:163)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
    at org.eclipse.jetty.server.Server.start(Server.java:387)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
    at org.eclipse.jetty.server.Server.doStart(Server.java:354)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at mobi.allshoppings.jetty.JettyServer.startup(JettyServer.java:62)
    at mobi.allshoppings.cli.StartServer.main(StartServer.java:44)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at mobi.allshoppings.cli.CLI.main(CLI.java:100)
NestedThrowablesStackTrace:
Failed to set object CachedPC : cls=mobi.allshoppings.model.User version=null loadedFlags=[YYYYYYYYYYYYYYYYY] with id com.inodes.datanucleus.model.Key:User("admin") into Redis cache
org.datanucleus.exceptions.NucleusException: Failed to set object CachedPC : cls=mobi.allshoppings.model.User version=null loadedFlags=[YYYYYYYYYYYYYYYYY] with id com.inodes.datanucleus.model.Key:User("admin") into Redis cache
    at org.datanucleus.cache.redis.RedisLevel2Cache.put(RedisLevel2Cache.java:280)
    at org.datanucleus.ExecutionContextImpl.putObjectIntoLevel2CacheInternal(ExecutionContextImpl.java:4914)
    at org.datanucleus.ExecutionContextImpl.putObjectIntoLevel2Cache(ExecutionContextImpl.java:4741)
    at org.datanucleus.ExecutionContextImpl.findObject(ExecutionContextImpl.java:3579)
    at org.datanucleus.ExecutionContextImpl.findObject(ExecutionContextImpl.java:3016)
    at org.datanucleus.api.jdo.JDOPersistenceManager.getObjectById(JDOPersistenceManager.java:1742)
    at mobi.allshoppings.dao.spi.GenericDAOJDO.get(GenericDAOJDO.java:144)
    at mobi.allshoppings.dao.spi.GenericDAOJDO.get(GenericDAOJDO.java:108)
    at mobi.allshoppings.bdb.tools.BasicDataBuilder.warmUp(BasicDataBuilder.java:49)
    at mobi.allshoppings.bdb.tools.InitAppServlet.init(InitAppServlet.java:21)
    at org.eclipse.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:616)
    at org.eclipse.jetty.servlet.ServletHolder.initialize(ServletHolder.java:396)
    at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:871)
    at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:298)
    at org.eclipse.jetty.webapp.WebAppContext.startWebapp(WebAppContext.java:1349)
    at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1342)
    at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:741)
    at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:505)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
    at org.eclipse.jetty.server.handler.ContextHandlerCollection.doStart(ContextHandlerCollection.java:163)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
    at org.eclipse.jetty.server.Server.start(Server.java:387)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
    at org.eclipse.jetty.server.Server.doStart(Server.java:354)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at mobi.allshoppings.jetty.JettyServer.startup(JettyServer.java:62)
    at mobi.allshoppings.cli.StartServer.main(StartServer.java:44)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at mobi.allshoppings.cli.CLI.main(CLI.java:100)

如堆栈所示,异常在序列化过程中被触发。我知道任何将使用 datanucleus 存储的 class 必须扩展 Serializable 并且必须使用一些特殊的注释,比如 @PersistenceCapable、@NotPersistant 或 @EmbebedOnly

问题是,当前的生产版本具有完全相同的配置,唯一可能破坏任何 datanucleus 配置的更改是升级本身和两个新 classes 的声明,这: 已经被注释了(在文档中和其他工作中的 classes)并且那些 classes 根本不参与 statup 过程,所以即使它们的配置不好,服务器应该开始就好了;但它拒绝。 persitence.xmlpackage-mongodb.orm 看起来很好(同样,在文档和其他工作 classes).

datanucleus 增强过程和依赖添加到 classpath 是;据我们所知,正确。

在序列化过程中出现的不可序列化异常是由(格式错误的?)CachePC 对象(由 datanucleus 构建以包装实际的 User 对象)引起的,它有一个字段,该字段是 StateManagerImpl 的一个实例,并且似乎没有实现 Serializable,因此导致了异常。


注意:上面的堆栈跟踪与项目的当前配置相匹配,它没有直接显示 NoSerializableException,但在调试时我确认这是第一个引发的异常。当 ObjectOutputStream 确定 StateManagerImpl 不是要序列化的有效对象时,它会被引发。当前版本使用XMemcached,如果我们将这个版本切换到Memcached,我们可以清楚地看到一个"Non-Serializable Exception"。 (我们想改用 Redis,因为原来的服务器是设计成分布式系统的;但是你会惊讶于它有多少瓶颈)


所以;我们不知道它为什么要尝试序列化 StateManagerImpl;我们认为这是上一个 datanucleus 版本的错误,所以我们切换到 版本 5.1.0-release 但没有成功。应用程序、Jetty 服务器和 servlet 启动正常,我们可以确认 datanucleus 成功解析 datanucleus.configuration 文件,我们可以获得 PersistanceManager 实例并读取 mongo 数据库。但是当它写入缓存时(这是自动完成的); Redis 或 XMemcached,我们总是会得到一个不可序列化的异常,因为 datanucleus 构建了一个带有不可序列化的 StateManagerImpl 的 CachedPC。

我们应该从哪里重新开始?有什么问题吗?我们是否应该切换 datanucleus 以获得更受支持的数据访问层?

谢谢你的建议。


更新

以下是使用 Memcached 而不是 Redis 时产生的堆栈跟踪(行为相同,再次序列化到 L2 缓存时中断)

GRAVE: exception catched
javax.jdo.JDOException: Exception thrown in persistence to xmemcached
    at org.datanucleus.api.jdo.NucleusJDOHelper.getJDOExceptionForNucleusException(NucleusJDOHelper.java:676)
    at org.datanucleus.api.jdo.JDOPersistenceManager.getObjectById(JDOPersistenceManager.java:1747)
    at mobi.allshoppings.dao.spi.GenericDAOJDO.get(GenericDAOJDO.java:144)
    at mobi.allshoppings.dao.spi.GenericDAOJDO.get(GenericDAOJDO.java:108)
    at mobi.allshoppings.bdb.tools.BasicDataBuilder.warmUp(BasicDataBuilder.java:49)
    at mobi.allshoppings.bdb.tools.InitAppServlet.init(InitAppServlet.java:21)
    at org.eclipse.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:616)
    at org.eclipse.jetty.servlet.ServletHolder.initialize(ServletHolder.java:396)
    at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:871)
    at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:298)
    at org.eclipse.jetty.webapp.WebAppContext.startWebapp(WebAppContext.java:1349)
    at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1342)
    at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:741)
    at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:505)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
    at org.eclipse.jetty.server.handler.ContextHandlerCollection.doStart(ContextHandlerCollection.java:163)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
    at org.eclipse.jetty.server.Server.start(Server.java:387)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
    at org.eclipse.jetty.server.Server.doStart(Server.java:354)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at mobi.allshoppings.jetty.JettyServer.startup(JettyServer.java:62)
    at mobi.allshoppings.cli.StartServer.main(StartServer.java:44)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at mobi.allshoppings.cli.CLI.main(CLI.java:100)
NestedThrowablesStackTrace:
java.lang.IllegalArgumentException: Non-serializable object
    at net.rubyeye.xmemcached.transcoders.BaseSerializingTranscoder.serialize(BaseSerializingTranscoder.java:99)
    at net.rubyeye.xmemcached.transcoders.SerializingTranscoder.encode(SerializingTranscoder.java:211)
    at net.rubyeye.xmemcached.command.text.TextStoreCommand.encodeValue(TextStoreCommand.java:199)
    at net.rubyeye.xmemcached.command.text.TextStoreCommand.encode(TextStoreCommand.java:155)
    at net.rubyeye.xmemcached.impl.MemcachedTCPSession.wrapMessage(MemcachedTCPSession.java:189)
    at com.google.code.yanf4j.core.impl.AbstractSession.write(AbstractSession.java:383)
    at net.rubyeye.xmemcached.impl.MemcachedConnector.send(MemcachedConnector.java:556)
    at net.rubyeye.xmemcached.XMemcachedClient.sendCommand(XMemcachedClient.java:322)
    at net.rubyeye.xmemcached.XMemcachedClient.sendStoreCommand(XMemcachedClient.java:2537)
    at net.rubyeye.xmemcached.XMemcachedClient.set(XMemcachedClient.java:1369)
    at net.rubyeye.xmemcached.XMemcachedClient.set(XMemcachedClient.java:1428)
    at net.rubyeye.xmemcached.XMemcachedClient.set(XMemcachedClient.java:1415)
    at org.datanucleus.cache.xmemcached.XmemcachedLevel2Cache.put(XmemcachedLevel2Cache.java:177)
    at org.datanucleus.ExecutionContextImpl.putObjectIntoLevel2CacheInternal(ExecutionContextImpl.java:4914)
    at org.datanucleus.ExecutionContextImpl.putObjectIntoLevel2Cache(ExecutionContextImpl.java:4741)
    at org.datanucleus.ExecutionContextImpl.findObject(ExecutionContextImpl.java:3579)
    at org.datanucleus.ExecutionContextImpl.findObject(ExecutionContextImpl.java:3016)
    at org.datanucleus.api.jdo.JDOPersistenceManager.getObjectById(JDOPersistenceManager.java:1742)
    at mobi.allshoppings.dao.spi.GenericDAOJDO.get(GenericDAOJDO.java:144)
    at mobi.allshoppings.dao.spi.GenericDAOJDO.get(GenericDAOJDO.java:108)
    at mobi.allshoppings.bdb.tools.BasicDataBuilder.warmUp(BasicDataBuilder.java:49)
    at mobi.allshoppings.bdb.tools.InitAppServlet.init(InitAppServlet.java:21)
    at org.eclipse.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:616)
    at org.eclipse.jetty.servlet.ServletHolder.initialize(ServletHolder.java:396)
    at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:871)
    at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:298)
    at org.eclipse.jetty.webapp.WebAppContext.startWebapp(WebAppContext.java:1349)
    at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1342)
    at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:741)
    at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:505)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
    at org.eclipse.jetty.server.handler.ContextHandlerCollection.doStart(ContextHandlerCollection.java:163)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
    at org.eclipse.jetty.server.Server.start(Server.java:387)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
    at org.eclipse.jetty.server.Server.doStart(Server.java:354)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at mobi.allshoppings.jetty.JettyServer.startup(JettyServer.java:62)
    at mobi.allshoppings.cli.StartServer.main(StartServer.java:44)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at mobi.allshoppings.cli.CLI.main(CLI.java:100)
Caused by: java.io.NotSerializableException: org.datanucleus.state.StateManagerImpl
    - field (class "org.datanucleus.identity.IdentityReference", name: "client", type: "class java.lang.Object")
    - object (class "org.datanucleus.identity.IdentityReference", org.datanucleus.identity.IdentityReference@44fd7ba4)
    - field (class "org.datanucleus.cache.CachedPC", name: "id", type: "class java.lang.Object")
    - object (class "org.datanucleus.cache.CachedPC", CachedPC : cls=mobi.allshoppings.model.Address version=null loadedFlags=[YYYYYYYYY])
    - custom writeObject data (class "java.util.HashMap")
    - object (class "java.util.HashMap", {0=1, 1=CachedPC : cls=mobi.allshoppings.model.Address version=null loadedFlags=[YYYYYYYYY], 2=null, 3=null, 4=CachedPC : cls=mobi.allshoppings.model.ContactInfo version=null loadedFlags=[YYYYYYYYYYYY], 5=Sun Oct 29 04:57:34 CST 2017, 6=null, 7=true, 8=User("admin"), 9=null, 10=Sun Oct 29 05:03:40 CST 2017, 11=0, 12=true, 13=CachedPC : cls=mobi.allshoppings.model.UserSecurity version=null loadedFlags=[YYYYYYYYYYYYYYYYY], 14=null, 15=00000000880E0D76, 16=CachedPC : cls=mobi.allshoppings.model.tools.ViewLocation version=null loadedFlags=[Y]})
    - field (class "org.datanucleus.cache.CachedPC", name: "fieldValues", type: "interface java.util.Map")
    - root object (class "org.datanucleus.cache.CachedPC", CachedPC : cls=mobi.allshoppings.model.User version=null loadedFlags=[YYYYYYYYYYYYYYYYY])
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1182)
    at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
    at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
    at java.util.HashMap.internalWriteEntries(HashMap.java:1790)
    at java.util.HashMap.writeObject(HashMap.java:1363)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1128)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
    at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
    at net.rubyeye.xmemcached.transcoders.BaseSerializingTranscoder.serialize(BaseSerializingTranscoder.java:94)
    ... 48 more

此外,以下是 ObjectOutputStream 的 debugInfoStack 在序列化过程中实际抛出异常之前的状态:

这是异常创建之前的状态:

- object (class "java.lang.StackTraceElement", java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1182))
- element of array (index: 0)
- array (class "[Ljava.lang.StackTraceElement;", size: 74)
- field (class "java.lang.Throwable", name: "stackTrace", type: "class [Ljava.lang.StackTraceElement;")
- custom writeObject data (class "java.lang.Throwable")
- root object (class "java.io.NotSerializableException", java.io.NotSerializableException: org.datanucleus.state.StateManagerImpl
- field (class "org.datanucleus.identity.IdentityReference", name: "client", type: "class java.lang.Object")
- object (class "org.datanucleus.identity.IdentityReference", org.datanucleus.identity.IdentityReference@44fd7ba4)
- field (class "org.datanucleus.cache.CachedPC", name: "id", type: "class java.lang.Object")
- object (class "org.datanucleus.cache.CachedPC", CachedPC : cls=mobi.allshoppings.model.Address version=null loadedFlags=[YYYYYYYYY])
- custom writeObject data (class "java.util.HashMap")
- object (class "java.util.HashMap", {0=1, 1=CachedPC : cls=mobi.allshoppings.model.Address version=null loadedFlags=[YYYYYYYYY], 2=null, 3=null, 4=CachedPC : cls=mobi.allshoppings.model.ContactInfo version=null loadedFlags=[YYYYYYYYYYYY], 5=Sun Oct 29 04:57:34 CST 2017, 6=null, 7=true, 8=User("admin"), 9=null, 10=Sun Oct 29 05:03:40 CST 2017, 11=0, 12=true, 13=CachedPC : cls=mobi.allshoppings.model.UserSecurity version=null loadedFlags=[YYYYYYYYYYYYYYYYY], 14=null, 15=00000000880E0D76, 16=CachedPC : cls=mobi.allshoppings.model.tools.ViewLocation version=null loadedFlags=[Y]})
- field (class "org.datanucleus.cache.CachedPC", name: "fieldValues", type: "interface java.util.Map")
- root object (class "org.datanucleus.cache.CachedPC", CachedPC : cls=mobi.allshoppings.model.User version=null loadedFlags=[YYYYYYYYYYYYYYYYY]))

这个状态显示了一个我之前没有注意到的 suppressedException(也是触发异常之前的最后状态之一):

- object (class "java.util.ArrayList", [])
- field (class "java.util.Collections$UnmodifiableCollection", name: "c", type: "interface java.util.Collection")
- object (class "java.util.Collections$UnmodifiableList", [])
- field (class "java.lang.Throwable", name: "suppressedExceptions", type: "interface java.util.List")
- custom writeObject data (class "java.lang.Throwable")
- root object (class "java.io.NotSerializableException", java.io.NotSerializableException: org.datanucleus.state.StateManagerImpl
- field (class "org.datanucleus.identity.IdentityReference", name: "client", type: "class java.lang.Object")
- object (class "org.datanucleus.identity.IdentityReference", org.datanucleus.identity.IdentityReference@44fd7ba4)
- field (class "org.datanucleus.cache.CachedPC", name: "id", type: "class java.lang.Object")
- object (class "org.datanucleus.cache.CachedPC", CachedPC : cls=mobi.allshoppings.model.Address version=null loadedFlags=[YYYYYYYYY])
- custom writeObject data (class "java.util.HashMap")
- object (class "java.util.HashMap", {0=1, 1=CachedPC : cls=mobi.allshoppings.model.Address version=null loadedFlags=[YYYYYYYYY], 2=null, 3=null, 4=CachedPC : cls=mobi.allshoppings.model.ContactInfo version=null loadedFlags=[YYYYYYYYYYYY], 5=Sun Oct 29 04:57:34 CST 2017, 6=null, 7=true, 8=User("admin"), 9=null, 10=Sun Oct 29 05:03:40 CST 2017, 11=0, 12=true, 13=CachedPC : cls=mobi.allshoppings.model.UserSecurity version=null loadedFlags=[YYYYYYYYYYYYYYYYY], 14=null, 15=00000000880E0D76, 16=CachedPC : cls=mobi.allshoppings.model.tools.ViewLocation version=null loadedFlags=[Y]})
- field (class "org.datanucleus.cache.CachedPC", name: "fieldValues", type: "interface java.util.Map")
- root object (class "org.datanucleus.cache.CachedPC", CachedPC : cls=mobi.allshoppings.model.User version=null loadedFlags=[YYYYYYYYYYYYYYYYY]))

唯一的 class 持有列表作为属性,是用户 class 的属性,它具有以下声明:

@PersistenceCapable(detachable="true")
@EmbeddedOnly
public class UserSecurity implements Serializable {

而且我没有看到它在缓存过程中被调用,仅在 mongodb 读取期间被调用。再次说明:对 mongodb 的讲座效果很好,但 datanucleus 会尝试缓存刚刚读取的对象;它失败。所以,我不知道那个不可修改的列表是从哪里来的。

所以,也许我有点太咸了;但事实证明,实际上我们的问题是由 Datanucleus 错误引起的:https://github.com/datanucleus/datanucleus-core/issues/283

文档说嵌入式对象不应该有 ID,因为它们直接喜欢有 ID 的容器对象(在 "same table" 方案下)http://www.datanucleus.org/products/datanucleus/jdo/mapping.html#embedded_pc 但是我们的嵌入式对象被赋予了不应该存在的 IdentityReference。

我注意到在序列化嵌入式对象时,StateManagerImpl是导致异常的原因,但我并没有过多关注导致StateManagerImpl序列化的IdentityReference对象;导致缓存 L2 写入错误。这就是错误的全部内容。

因此,解决方案是克隆 datanucleus-core 项目,在本地构建和安装最新版本,re-build 我们的项目将 datanuclues-core 依赖项更改为本地文件。效果很好。

在深入研究了 datanucleus 及其代码本身的文档之后,我更加相信 datanucleus 实际上是一个非常好的数据存储抽象,我们将在我们的项目中保留它(但我仍然相信支持和错误信息含糊)