View Full Version : Polymorphism for spring data?
tdennison
May 5th, 2011, 01:11 PM
Does spring-data support polymorphism? For example, can persist a concrete implementation using a repository declared to manage the interface?
public interface Artifact
{
ObjectId getId();
String getArtifactName();
String getArtifactType();
List<String> getSupportedModels();
Date getDateAdded();
File getArtifactFile();
}
public abstract class AbstractArtifact implements Artifact
{
...
}
public class ConcreteArtifact extends AbstractArtifact
{
....
}
public interface ArtifactRepository extends MongoRepository<Artifact,ObjectId> {}
I seem to be able to instantiate a ConcreteArtifact and save it fine. The mongo shell depicts all attributes using find(). However, when I attempt to use the findOne() method on the ArtifactRepository I receive the following error:
org.springframework.data.mapping.model.MappingInst antiationException: Could not instantiate bean class [com.lexmark.pssd.app.mve.library.Artifact]: Specified class is an interface at
org.springframework.data.mapping.MappingBeanHelper .constructInstance(MappingBeanHelper.java:115) at
org.springframework.data.document.mongodb.convert. MappingMongoConverter.read(MappingMongoConverter.j ava:214) at
org.springframework.data.document.mongodb.convert. MappingMongoConverter.read(MappingMongoConverter.j ava:199) at
org.springframework.data.document.mongodb.MongoTem plate$ReadDbObjectCallback.doWith(MongoTemplate.ja va:1480) at
org.springframework.data.document.mongodb.MongoTem plate.execute(MongoTemplate.java:337) at
org.springframework.data.document.mongodb.MongoTem plate.doFindOne(MongoTemplate.java:978) at
org.springframework.data.document.mongodb.MongoTem plate.findOne(MongoTemplate.java:485) at
org.springframework.data.document.mongodb.MongoTem plate.findOne(MongoTemplate.java:480) at
org.springframework.data.document.mongodb.reposito ry.SimpleMongoRepository.findOne(SimpleMongoReposi tory.java:99) at sun.reflect.NativeMethodAccessorImpl.invoke0(Nativ e Method) at
sun.reflect.NativeMethodAccessorImpl.invoke(Native MethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(De legatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.springframework.data.repository.support.Reposi toryFactorySupport$QueryExecuterMethodInterceptor. executeMethodOn(RepositoryFactorySupport.java:355) at org.springframework.data.repository.support.Reposi toryFactorySupport$QueryExecuterMethodInterceptor. invoke(RepositoryFactorySupport.java:336) at org.springframework.aop.framework.ReflectiveMethod Invocation.proceed(ReflectiveMethodInvocation.java :172) at
org.springframework.aop.framework.JdkDynamicAopPro xy.invoke(JdkDynamicAopProxy.java:202) at $Proxy123.findOne(Unknown Source) at com.lexmark.pssd.app.mve.library.ArtifactRepositor yIntegrationTest.testSave(ArtifactRepositoryIntegr ationTest.java:61) at
Matthias S.
May 6th, 2011, 02:08 AM
Have you tried to pass the Class object of the concrete implementation as fondOne() argument as it is in the documentation?
.findOne(new Query(Criteria.where("name").is("Joe")), Person.class)
tdennison
May 6th, 2011, 08:33 AM
Well...I'm pretty sure that will work. What I really want is to place different concrete implementations into the same collection and be able to query them. For example, if ConcreteA and ConcreteB are both persisted into the same collection, then I want to be able to query for all objects in the collection and know that each is materialized appropriately.
In sum, I want to be able to save a discriminator (maybe the @Document type?) along with the object and have the Repository use that information to instantiate the correct implementation upon query.
Oliver Gierke
May 6th, 2011, 08:34 AM
Currently we unfortunately don't. As Matthias has pointed out you can be specific on the class you're querying for on the template level (and thus map data to various classes by just piping in a different type). Currently the repositories uses the domain type you type the repo to (Account in your case) to map the returned data to it. That's the only way it works right now as we do not transparently store concrete type information for root documents (we already do for nested ones). Unfortunately there won't be no general solution as on storing at the template you simply pipe in an object and we don't know you might use it behind an interface.
However, we could let the repository save infrastructure store the concrete type in the document in case the repository is managing an interface or an abstract class. Could you please open a JIRA for that and assign it to the repository component. I will have a look at it ASAP then.
Cheers,
Ollie
tdennison
May 6th, 2011, 09:00 AM
Thanks for the information Oliver. I will open a JIRA ticket today.
Given this information, it would seem that a good rule of thumb for spring data would be a single domain class per collection. I had also thought that I might be able to query "across" collections as a workaround, but don't see that type of functionality either.
Oliver Gierke
May 6th, 2011, 09:10 AM
We already do entity-per-collection. Or to be more precise, one collection for the entity the repository is typed to. I think this makes much sense as document databases work quite nicely with slightly differently shaped data. Plus we can do polymorphic queries quite easily.
tdennison
May 6th, 2011, 09:21 AM
Not sure what you mean by "polymorphic" queries in the last response. Isn't that what we're suggesting should be implemented by the new JIRA issue?
JIRA issue for this thread: https://jira.springsource.org/browse/DATADOC-128
Thanks!
Oliver Gierke
May 6th, 2011, 10:02 AM
Polymorphic in that context means that you potentially get back objects of different types. Mostly they are in some kind of type hierarchy. If every concrete type would get it's own collection then we'd have to query the collections separately which is not only slow but also rather tricky to handle when using pagination and the like.
sajitmk
May 23rd, 2011, 11:40 AM
Im using spring-data-mongodb.1.0.0.M2. Ive defined my mongorepository as
@Transactional
public interface UserRepository extends MongoRepository<User,String>{
}
----------
And then make hte following call.
List<User> allUsers = userRepository.findAll();
----------------------
I'm getting the following error.
org.springframework.beans.BeanInstantiationExcepti on: Could not instantiate bean class [java.util.List]: Specified class is an interface
at org.springframework.beans.BeanUtils.instantiateCla ss(BeanUtils.java:101)
at org.springframework.data.document.mongodb.convert. SimpleMongoConverter.read(SimpleMongoConverter.jav a:346)
at org.springframework.data.document.mongodb.convert. SimpleMongoConverter.readMap(SimpleMongoConverter. java:456)
at org.springframework.data.document.mongodb.convert. SimpleMongoConverter.readCompoundValue(SimpleMongo Converter.java:427)
at org.springframework.data.document.mongodb.convert. SimpleMongoConverter.read(SimpleMongoConverter.jav a:361)
at org.springframework.data.document.mongodb.MongoTem plate$ReadDbObjectCallback.doWith(MongoTemplate.ja va:1480)
at org.springframework.data.document.mongodb.MongoTem plate.executeEach(MongoTemplate.java:371)
at org.springframework.data.document.mongodb.MongoTem plate.doFind(MongoTemplate.java:1006)
at org.springframework.data.document.mongodb.MongoTem plate.find(MongoTemplate.java:522)
at org.springframework.data.document.mongodb.reposito ry.SimpleMongoRepository.findAll(SimpleMongoReposi tory.java:231)
at org.springframework.data.document.mongodb.reposito ry.SimpleMongoRepository.findAll(SimpleMongoReposi tory.java:173)
at org.springframework.data.document.mongodb.reposito ry.SimpleMongoRepository.findAll(SimpleMongoReposi tory.java:41)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Nativ e Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Native MethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(De legatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.data.repository.support.Reposi toryFactorySupport$QueryExecuterMethodInterceptor. executeMethodOn(RepositoryFactorySupport.java:355)
at org.springframework.data.repository.support.Reposi toryFactorySupport$QueryExecuterMethodInterceptor. invoke(RepositoryFactorySupport.java:336)
at org.springframework.aop.framework.ReflectiveMethod Invocation.proceed(ReflectiveMethodInvocation.java :172)
at org.springframework.aop.framework.JdkDynamicAopPro xy.invoke(JdkDynamicAopProxy.java:202)
----------
What is wrong here?
Thanks in advance.
Matthias S.
May 23rd, 2011, 01:11 PM
1. Why are you adding a @Transactional Annotation? MongoDB does not support Transactions.
Only Atomic Operations are supported. See http://www.mongodb.org/display/DOCS/Developer+FAQ#DeveloperFAQ-HowdoIdotransactions%2Flocking%3F
2. You are still using SimpleMongoConverter, which is Deprecated!
Add
<mongo:mapping-converter id="converter" />
and <constructor-arg ref="converter" />
to the MongoTemplate constructor arguments
to your beans.xml to use the MappingMongoConverter (or instanceiate it by using the bean tag)
sajitmk
Jun 17th, 2011, 10:29 AM
That worked. Thank you.
jacob.metcalf@nomura.com
Jul 13th, 2011, 09:31 AM
Is support for interfaces as a domain type https://jira.springsource.org/browse/DATADOC-131 going to be extended to find methods?
In the attached zipped example I have declared a finder:
public interface PersonRepository extends PagingAndSortingRepository<Person, String> {
List<Person> findByLastName( String lastName );
}
This causes template instantiation to fail with the below:
Caused by: java.lang.IllegalArgumentException: No property last found for type interface org.acme.Person
at org.springframework.data.repository.query.parser.P roperty.<init>(Property.java:66)
at org.springframework.data.repository.query.parser.P roperty.<init>(Property.java:100)
at org.springframework.data.repository.query.parser.P roperty.create(Property.java:300)
at org.springframework.data.repository.query.parser.P roperty.create(Property.java:314)
at org.springframework.data.repository.query.parser.P roperty.create(Property.java:280)
at org.springframework.data.repository.query.parser.P roperty.from(Property.java:239)
at org.springframework.data.repository.query.parser.P roperty.from(Property.java:227)
at org.springframework.data.repository.query.parser.P art.<init>(Part.java:48)
at org.springframework.data.repository.query.parser.P artTree$OrPart.<init>(PartTree.java:242)
at org.springframework.data.repository.query.parser.P artTree.buildTree(PartTree.java:101)
at org.springframework.data.repository.query.parser.P artTree.<init>(PartTree.java:77)
at org.springframework.data.document.mongodb.reposito ry.PartTreeMongoQuery.<init>(PartTreeMongoQuery.java:42)
...
Debugging it appears when Spring MongoDB attempts to "bind" using reflection it uses reflectionUtils.findField() which will calls Class.getDeclaredFields(). Put another way it just looks at fields and does not look at getters and setters which is all the interface has. Could it be changed to be able to bing to getters/setters, or alternatively bind later when it gets the concrete class?
Last question - any target release date for DATADOC-131 ?
Thanks
Jacob
Oliver Gierke
Jul 13th, 2011, 10:35 AM
Commented on the ticket. In short: that's exactly what the ticket is all about. The ticket is scheduled for 1.1-M1. As we're just on the road to 1.0-RC1 and GA so it will probably take a while.
ddurst555808
Jul 13th, 2011, 05:25 PM
Matthias S,
Can you provide a full xml config for you comments above as its not exactly clear how to make the adjustments you are suggesting.
Powered by vBulletin® Version 4.2.1 Copyright © 2013 vBulletin Solutions, Inc. All rights reserved.