Announcement Announcement Module
Collapse
No announcement yet.
Polymorphism for spring data? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Polymorphism for spring data?

    Does spring-data support polymorphism? For example, can persist a concrete implementation using a repository declared to manage the interface?

    Code:
    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:

    Code:
    org.springframework.data.mapping.model.MappingInstantiationException: 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.java:214) at
     org.springframework.data.document.mongodb.convert.MappingMongoConverter.read(MappingMongoConverter.java:199) at
     org.springframework.data.document.mongodb.MongoTemplate$ReadDbObjectCallback.doWith(MongoTemplate.java:1480) at
     org.springframework.data.document.mongodb.MongoTemplate.execute(MongoTemplate.java:337) at 
    org.springframework.data.document.mongodb.MongoTemplate.doFindOne(MongoTemplate.java:978) at
     org.springframework.data.document.mongodb.MongoTemplate.findOne(MongoTemplate.java:485) at
     org.springframework.data.document.mongodb.MongoTemplate.findOne(MongoTemplate.java:480) at
     org.springframework.data.document.mongodb.repository.SimpleMongoRepository.findOne(SimpleMongoRepository.java:99) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at
     sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.springframework.data.repository.support.RepositoryFactorySupport$QueryExecuterMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:355) at org.springframework.data.repository.support.RepositoryFactorySupport$QueryExecuterMethodInterceptor.invoke(RepositoryFactorySupport.java:336) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at
     org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202) at $Proxy123.findOne(Unknown Source) at com.lexmark.pssd.app.mve.library.ArtifactRepositoryIntegrationTest.testSave(ArtifactRepositoryIntegrationTest.java:61) at

  • #2
    Have you tried to pass the Class object of the concrete implementation as fondOne() argument as it is in the documentation?

    Code:
    .findOne(new Query(Criteria.where("name").is("Joe")), Person.class)

    Comment


    • #3
      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.

      Comment


      • #4
        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

        Comment


        • #5
          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.

          Comment


          • #6
            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.

            Comment


            • #7
              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!

              Comment


              • #8
                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.

                Comment


                • #9
                  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.

                  Comment


                  • #10
                    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/...s%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)

                    Comment


                    • #11
                      That worked. Thank you.

                      Comment


                      • #12
                        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.ja va: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

                        Comment


                        • #13
                          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.

                          Comment


                          • #14
                            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.

                            Comment

                            Working...
                            X