I'm trying to setup a webapp with Jersey using a MongoDB as persistence layer. My JPA framework is EclipseLink.
I've run into a problem with persisting objects which classes are extending other (abstract) classes.
For example given the following abstract class:
@Entity
@NoSql(dataFormat=DataFormatType.MAPPED)
public abstract class AbstractClass {
@Id
@Field(name="_id")
protected String id;
public String getId() {
return this.id;
}
}
And this extending 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;
}
}
Here is the persistence.xml
file I'm using:
<?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>
And a webservice that creates and persists an instance of the 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;
}
When calling the webservice with:
http://localhost:8080/webapp/webapi/test/persist/extended-object?name=test-abstraction
I can create a new entry in the mongoDb database that looks like this:
So far so good. However if I try to get a list with all the persisted objects I run into a problem. Here is the example webservice:
@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;
}
This yields the following exception:
javax.resource.ResourceException: org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class org.eclipse.persistence.internal.helper.DatabaseField.
Full stack trace:
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$0(ResourceMethodInvocationHandlerFactory.java:74)
at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.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$2.run(ServerRuntime.java:337)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
at org.glassfish.jersey.internal.Errors$1.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$1.call(FindOperation.java:717)
at com.mongodb.operation.FindOperation$1.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
Does anyone know why this error occurs. According to the documentation of EclipseLink it should be able to handle inheritance just fine when working with NoSQL databases: https://wiki.eclipse.org/EclipseLink/FAQ/NoSQL
Searching for the error message also brought up no clear answer what could be wrong with my implementation.
I've tested also a version without inheritance and there the extraction webservice works just fine.
In addition I tested an implementation where AbstractClass
isn't a abstract class where I got the same error message which indicates to me that it is a general issue with inheritance.
I also tested a variant where the abstract class doesn't have any fields and there the extraction works as expected but the object is not stored as an ABSTRACTCLASS
but as an EXTENDEDCLASS
field in mongoDB which seems to indicate a problem with the inheritance part.
From other webapps I know also that using a SQL database with inheritance is not the issue so I assume it has do do something with mongoDB.
So my question would be why does the issue occur and how to get rid of it.
As always any help is appreciated.
[update:] Frank Caputos answer steered me towards a working solution utilizing the @Inheritance
attribute. This is how my abstract class looks at the moment which seems to be working for my purposes:
@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;
}
}
I used the @Inheritace
attribute to get it working. This is how the abstract class looks know which seems to be a workable solution for me:
@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;
}
}
I hope this is the correct way of doing it. If not please let me know .