EclipseLink:继承 mongoDB 作为持久层

EclipseLink: Inheritance with mongoDB as persistence layer

我正在尝试使用 MongoDB 作为持久层使用 Jersey 设置 Web 应用程序。我的 JPA 框架是 EclipseLink。

我 运行 遇到了持久化对象的问题,classes 正在扩展其他(抽象)classes。

例如给定以下摘要class:

@Entity
@NoSql(dataFormat=DataFormatType.MAPPED)
public abstract class AbstractClass {

    @Id
    @Field(name="_id")
    protected String id;

    public String getId() {

        return this.id;
    }
}

并且这个扩展 class:

@Entity
@NoSql(dataFormat=DataFormatType.MAPPED)
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class ExtendedClass extends AbstractClass {

    @Basic
    private String name;

    public ExtendedClass() {

    }

    public ExtendedClass(String id, String name) {

        this.id = id;
        this.name = name;
    }

    public String getName() {

        return this.name;
    }
}

这是我正在使用的persistence.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="persistence-unit" transaction-type="RESOURCE_LOCAL">
        <class>org.example.webapp.resources.test.AbstractClass</class>
        <class>org.example.webapp.resources.test.ExtendedClass</class>
        <properties>
            <property name="eclipselink.target-database" value="org.eclipse.persistence.nosql.adapters.mongo.MongoPlatform"/>                         
            <property name="eclipselink.nosql.connection-spec" value="org.eclipse.persistence.nosql.adapters.mongo.MongoConnectionSpec"/>
            <property name="eclipselink.nosql.property.mongo.port" value="27017"/>
            <property name="eclipselink.nosql.property.mongo.host" value="ip-address"/>
            <property name="eclipselink.nosql.property.mongo.db" value="database-name"/>
            <property name="eclipselink.logging.level" value="FINEST"/>
        </properties>
    </persistence-unit>
</persistence>

以及创建并保存 ExtendedClass:

实例的网络服务
@PUT
@Path("persist/extended-object")
@Consumes(MediaType.APPLICATION_JSON)
public String addObject(@QueryParam("name") final String name) {

    final String id = UUID.randomUUID().toString();

    ExtendedClass theObject = new ExtendedClass(id, name);

    EntityManagerFactory emf = Persistence.createEntityManagerFactory("persistence-unit");

    EntityManager em = emf.createEntityManager();

    em.getTransaction().begin();

    em.persist(theObject);

    em.getTransaction().commit();
    em.close();

    return "Persisted object with name: " + name;
}

调用网络服务时:

http://localhost:8080/webapp/webapi/test/persist/extended-object?name=test-abstraction

我可以在 mongoDb 数据库中创建一个新条目,如下所示:

到目前为止一切顺利。但是,如果我尝试获取包含所有持久对象的列表,我 运行 就会遇到问题。这是示例网络服务:

@GET
@Path("get/all")
@Produces(MediaType.APPLICATION_JSON)
public List<ExtendedClass> getAllExtendedClasses() {

    EntityManagerFactory emf = Persistence.createEntityManagerFactory("persistence-unit");

    EntityManager em = emf.createEntityManager();

    em.getTransaction().begin();

    List<ExtendedClass> extendedClassList = em.createQuery("SELECT a FROM ExtendedClass a", ExtendedClass.class).getResultList();

    em.getTransaction().commit();
    em.close();

    return extendedClassList;
}   

这会产生以下异常:

javax.resource.ResourceException: org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class org.eclipse.persistence.internal.helper.DatabaseField.

完整堆栈跟踪:

Exception [EclipseLink-90000] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.eclipse.persistence.eis.EISException
Internal Exception: javax.resource.ResourceException: org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class org.eclipse.persistence.internal.helper.DatabaseField.
Error Code: 000
Call: Executing MappedInteraction()
    spec => null
    properties => {mongo.operation=FIND, mongo.collection=ABSTRACTCLASS}
    input => [DatabaseRecord(
    $and => [DatabaseRecord(
    EXTENDEDCLASS._id => ABSTRACTCLASS._id), DatabaseRecord(
    ABSTRACTCLASS.DTYPE => ExtendedClass)])]
Query: ReadAllQuery(referenceClass=ExtendedClass jpql="SELECT a FROM ExtendedClass a")
    at org.eclipse.persistence.eis.EISException.resourceException(EISException.java:65)
    at org.eclipse.persistence.eis.EISException.resourceException(EISException.java:74)
    at org.eclipse.persistence.eis.EISException.resourceException(EISException.java:78)
    at org.eclipse.persistence.eis.EISAccessor.basicExecuteCall(EISAccessor.java:212)
    at org.eclipse.persistence.eis.EISAccessor.executeCall(EISAccessor.java:112)
    at org.eclipse.persistence.internal.sessions.AbstractSession.basicExecuteCall(AbstractSession.java:1995)
    at org.eclipse.persistence.sessions.server.ServerSession.executeCall(ServerSession.java:570)
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:242)
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:228)
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeSelectCall(DatasourceCallQueryMechanism.java:299)
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.selectAllRows(DatasourceCallQueryMechanism.java:694)
    at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.selectAllRowsFromTable(ExpressionQueryMechanism.java:2714)
    at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.selectAllRows(ExpressionQueryMechanism.java:2667)
    at org.eclipse.persistence.queries.ReadAllQuery.executeObjectLevelReadQuery(ReadAllQuery.java:477)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeDatabaseQuery(ObjectLevelReadQuery.java:1155)
    at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:899)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:1114)
    at org.eclipse.persistence.queries.ReadAllQuery.execute(ReadAllQuery.java:402)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeInUnitOfWork(ObjectLevelReadQuery.java:1202)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2894)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1797)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1779)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1744)
    at org.eclipse.persistence.internal.jpa.QueryImpl.executeReadQuery(QueryImpl.java:258)
    at org.eclipse.persistence.internal.jpa.QueryImpl.getResultList(QueryImpl.java:468)
    at org.example.webapp.resources.test.ObjectWebServices.getAllExtendedClasses(ObjectWebServices.java:54)
    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 org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory.lambda$static[=16=](ResourceMethodInvocationHandlerFactory.java:74)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.run(AbstractJavaResourceMethodDispatcher.java:144)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:161)
    at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$TypeOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:247)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:99)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:388)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:346)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:102)
    at org.glassfish.jersey.server.ServerRuntime.run(ServerRuntime.java:337)
    at org.glassfish.jersey.internal.Errors.call(Errors.java:271)
    at org.glassfish.jersey.internal.Errors.call(Errors.java:267)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:280)
    at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:316)
    at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1084)
    at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:418)
    at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:372)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:389)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:342)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:229)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:506)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:610)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:537)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1081)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:658)
    at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:222)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1566)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1523)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: javax.resource.ResourceException: org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class org.eclipse.persistence.internal.helper.DatabaseField.
    at org.eclipse.persistence.internal.nosql.adapters.mongo.MongoDatabaseInteraction.execute(MongoDatabaseInteraction.java:201)
    at org.eclipse.persistence.eis.EISAccessor.basicExecuteCall(EISAccessor.java:163)
    ... 70 more
Caused by: org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class org.eclipse.persistence.internal.helper.DatabaseField.
    at org.bson.codecs.configuration.CodecCache.getOrThrow(CodecCache.java:46)
    at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:63)
    at org.bson.codecs.configuration.ChildCodecRegistry.get(ChildCodecRegistry.java:51)
    at com.mongodb.DBObjectCodec.writeValue(DBObjectCodec.java:231)
    at com.mongodb.DBObjectCodec.encodeMap(DBObjectCodec.java:241)
    at com.mongodb.DBObjectCodec.writeValue(DBObjectCodec.java:217)
    at com.mongodb.DBObjectCodec.encodeIterable(DBObjectCodec.java:292)
    at com.mongodb.DBObjectCodec.writeValue(DBObjectCodec.java:219)
    at com.mongodb.DBObjectCodec.encode(DBObjectCodec.java:149)
    at com.mongodb.DBObjectCodec.encode(DBObjectCodec.java:65)
    at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:63)
    at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:29)
    at org.bson.codecs.EncoderContext.encodeWithChildContext(EncoderContext.java:91)
    at org.bson.codecs.BsonDocumentCodec.writeValue(BsonDocumentCodec.java:136)
    at org.bson.codecs.BsonDocumentCodec.encode(BsonDocumentCodec.java:115)
    at org.bson.codecs.BsonDocumentCodec.encode(BsonDocumentCodec.java:41)
    at com.mongodb.connection.RequestMessage.addDocument(RequestMessage.java:238)
    at com.mongodb.connection.RequestMessage.addDocument(RequestMessage.java:188)
    at com.mongodb.connection.CommandMessage.encodeMessageBodyWithMetadata(CommandMessage.java:154)
    at com.mongodb.connection.RequestMessage.encode(RequestMessage.java:138)
    at com.mongodb.connection.InternalStreamConnection.sendAndReceive(InternalStreamConnection.java:236)
    at com.mongodb.connection.UsageTrackingInternalConnection.sendAndReceive(UsageTrackingInternalConnection.java:98)
    at com.mongodb.connection.DefaultConnectionPool$PooledConnection.sendAndReceive(DefaultConnectionPool.java:441)
    at com.mongodb.connection.CommandProtocolImpl.execute(CommandProtocolImpl.java:70)
    at com.mongodb.connection.DefaultServer$DefaultServerProtocolExecutor.execute(DefaultServer.java:192)
    at com.mongodb.connection.DefaultServerConnection.executeProtocol(DefaultServerConnection.java:264)
    at com.mongodb.connection.DefaultServerConnection.command(DefaultServerConnection.java:126)
    at com.mongodb.connection.DefaultServerConnection.command(DefaultServerConnection.java:118)
    at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:226)
    at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:217)
    at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:120)
    at com.mongodb.operation.FindOperation.call(FindOperation.java:717)
    at com.mongodb.operation.FindOperation.call(FindOperation.java:711)
    at com.mongodb.operation.OperationHelper.withConnectionSource(OperationHelper.java:471)
    at com.mongodb.operation.OperationHelper.withConnection(OperationHelper.java:415)
    at com.mongodb.operation.FindOperation.execute(FindOperation.java:711)
    at com.mongodb.operation.FindOperation.execute(FindOperation.java:83)
    at com.mongodb.client.internal.MongoClientDelegate$DelegateOperationExecutor.execute(MongoClientDelegate.java:140)
    at com.mongodb.client.internal.MongoIterableImpl.execute(MongoIterableImpl.java:132)
    at com.mongodb.client.internal.MongoIterableImpl.iterator(MongoIterableImpl.java:86)
    at org.eclipse.persistence.internal.nosql.adapters.mongo.MongoDatabaseInteraction.execute(MongoDatabaseInteraction.java:173)
    ... 71 more

有谁知道为什么会出现这个错误。根据 EclipseLink 的文档,在使用 NoSQL 数据库时,它应该能够很好地处理继承:https://wiki.eclipse.org/EclipseLink/FAQ/NoSQL

搜索错误消息也没有得到明确的答案我的实施可能出了什么问题。

我还测试了一个没有继承的版本,提取网络服务工作正常。

此外,我测试了一个 AbstractClass 不是抽象 class 的实现,我收到了相同的错误消息,这表明这是继承的一般问题。

我还测试了一个变体,其中抽象 class 没有任何字段,提取工作按预期进行,但对象没有存储为 ABSTRACTCLASS 而是存储为 EXTENDEDCLASS 字段 mongoDB 这似乎表明继承部分有问题。

从其他 webapps 我也知道使用具有继承的 SQL 数据库不是问题所以我假设它已经对 mongoDB.

做了一些事情

所以我的问题是为什么会出现这个问题以及如何解决这个问题。

一如既往,我们将不胜感激。

[更新:] 回答引导我找到一个利用 @Inheritance 属性的可行解决方案。这就是我的摘要 class 对似乎正在为我的目的工作的时刻的看法:

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@NoSql(dataFormat=DataFormatType.MAPPED)
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public abstract class AbstractClass {

    @Id
    @Field(name="_id")
    protected String id;

    public String getId() {

        return this.id;
    }
}

你需要鉴别器。

@Entity
@NoSql(dataFormat=DataFormatType.MAPPED)
@DiscriminatorColumn(name = "type")
public abstract class AbstractClass {

    @Id
    @Field(name="_id")
    protected String id;

    public String getId() {

        return this.id;
    }
}

@Entity
@NoSql(dataFormat=DataFormatType.MAPPED)
@DiscriminatorValue("extended")
public class ExtendedClass extends AbstractClass {

    @Basic
    private String name;

    public ExtendedClass() {

    }

    public ExtendedClass(String id, String name) {

        this.id = id;
        this.name = name;
    }

    public String getName() {

        return this.name;
    }
}

您可能还需要在@NoSql 中使用 dataType 设置集合名称。

我使用 @Inheritace 属性让它工作。这就是摘要 class 看起来知道哪个对我来说似乎是可行的解决方案:

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@NoSql(dataFormat=DataFormatType.MAPPED)
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public abstract class AbstractClass {

    @Id
    @Field(name="_id")
    protected String id;

    public String getId() {

        return this.id;
    }
}

我希望这是正确的做法。如果没有请告诉我。