MongoDB Projection on embedded document field with mongoTemplate

霸气de小男生 提交于 2019-12-11 15:22:57

问题


In my java application i want retrieve a field of an embedded document. This is my pojo:

My User.class

public class User implements Comparable<User> {

    @Id
    private String username;

    private String ownerFirstname;

    private String ownerLastname;

    @DBRef
    @CascadeSave
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private Role role;
}

Role.class

@Document
@JsonDeserialize(using = RoleDeserializer.class)
public interface Role {

    String getId();

    void setId(String id);

}

Society.class

@Document(collection = "role")
public class Society implements Role {

    @Id
    private String id;

    private String name;

    @JsonInclude(JsonInclude.Include.NON_NULL)
    private Address societyAddress;

    @JsonInclude(JsonInclude.Include.NON_NULL)
    private String vatNumber;

    @JsonInclude(JsonInclude.Include.NON_NULL)
    private long birthday;

    private List<ProductResearch> productResearches;

    @DBRef
    private List<Advertisement> advertisementOwnedList;

    @DBRef
    private List<OrderRequest> orderRequests;

    private UserType type;
}

I'm interestet to the advertisementOwnedList field but i cannot get it. Using MongoTemplate i have tried to do a projection:

Query query = Query.query(Criteria.where("_id").is(user.getUsername()));
query.fields().include("role.advertisementOwnedList;");
User user1 = mongoTemplate.findOne(query, User.class);

Or using MongoRepository:

@Query(value = "{ '_id' : ?0}", fields = "{ '_id': 0 , 'role' : 1 ,
'role.advertisementOwnedList' : 1 }") 
User getRoleAdvertisementOwnedList(String username);

However Role is an interface and i always get the exception:

org.springframework.data.mapping.model.MappingInstantiationException: Failed to instantiate it.bmv.backend.model.Role using constructor NO_CONSTRUCTOR with arguments 

    at org.springframework.data.convert.ReflectionEntityInstantiator.createInstance(ReflectionEntityInstantiator.java:64)
    at org.springframework.data.convert.ClassGeneratingEntityInstantiator.createInstance(ClassGeneratingEntityInstantiator.java:83)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:259)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:239)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readValue(MappingMongoConverter.java:1222)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.access$200(MappingMongoConverter.java:85)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter$MongoDbPropertyValueProvider.getPropertyValue(MappingMongoConverter.java:1170)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.getValueInternal(MappingMongoConverter.java:880)
    at org.springframework.data.mongodb.core.convert.DefaultDbRefResolverCallback.resolve(DefaultDbRefResolverCallback.java:59)
    at org.springframework.data.mongodb.core.convert.DefaultDbRefResolver.resolveDbRef(DefaultDbRefResolver.java:98)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter$2.doWithAssociation(MappingMongoConverter.java:313)
    at org.springframework.data.mapping.model.BasicPersistentEntity.doWithAssociations(BasicPersistentEntity.java:360)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:296)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:239)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:199)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:195)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:85)
    at org.springframework.data.mongodb.core.MongoTemplate$ReadDbObjectCallback.doWith(MongoTemplate.java:2324)
    at org.springframework.data.mongodb.core.MongoTemplate.executeFindOneInternal(MongoTemplate.java:1925)
    at org.springframework.data.mongodb.core.MongoTemplate.doFindOne(MongoTemplate.java:1736)
    at org.springframework.data.mongodb.core.MongoTemplate.findOne(MongoTemplate.java:606)
    at org.springframework.data.mongodb.core.MongoTemplate.findOne(MongoTemplate.java:601)
    at it.bmv.backend.UserTest.getAdvTest(UserTest.java:93)
    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.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:237)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [it.bmv.backend.model.Role]: Specified class is an interface
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:99)
    at org.springframework.data.convert.ReflectionEntityInstantiator.createInstance(ReflectionEntityInstantiator.java:61)
    ... 50 more

How can i get the field of the role nested document?


回答1:


As my comment wasn't probably clear enough I'll try to give a bit more exhaustive answer.

For an internal testtool I'm writing if I have time to spare I came up with the idea of tasks that contain one or multiple different scenarios. A scenario is just a certain predefined test that needs to be executed. A task also contains a list of test-cases that contain the current status of a respective test.

Our model simplified looks something like this:

@Getter
@Setter
@Document(collection = "task")
public class TaskEntity implements Serializable, NamedResource {

  @Id
  private String id;
  private String name;
  private String testerName;
  ...
  @DBRef
  private List<ScenarioEntity> testsToExecute = new ArrayList<>();
  ...
  private List<TestCase> performedTests = new ArrayList<>();
  ...
}

The scenario entities are maintained in a different collection and only available as reference within the task entry. The test case, however, is an embedded document within the task.

Spring Data allows to define views on entities or sub-documents by introducing a separate interface that contains a subset of the entities getter methods you are interested in.

In example the TaskView contains the following content:

public interface TaskView extends NamedResource {

  String getTesterName();

  @Value("#{target.testsToExecute}")
  List<ScenarioView> getTestsToExecute();
}

where a ScenarioView looks like this:

public interface ScenarioView extends NamedResource {

}

as it only inherits the methods from NamedResource:

public interface NamedResource {
  String getId();
  String getName();
}

The TestCaseView on the other hand is a bit more complex as it also retrieves properties from a contained entry:

public interface TestCaseView {

  String getId();
  String getTaskId();
  Date getStart();
  Date getEnd();
  @Value("#{target.scenario.id}")
  String getScenarioId();
  @Value("#{target.secnario.name}")
  String getScenarioName();
  @Value("#{target.result.result.name()}")
  String getJobResult();
}

In order to make use of all those interfaces the TaskRepository does define the following queries:

public interface TaskRepository extends MongoRepository<TaskEntity, String> {

  @Query("{ '_id': ?0 }")
  TaskEntity findById(String id);

  @Query("{ }")
  Page<TaskView> findPageable(Pageable pageable);

  @Query("{ '_id': ?0, 'performedTests': { 'id': ?1 } }")
  TestCase findTestCaseByIdViaTaskId(String id, String uuid);

  @Query("{ '_id': ?0, 'performedTests': { } }")
  List<TestCase> findTestsCasesForTask(String id);

  @Query("{ '_id': ?0, 'performedTests': { } }")
  Page<TestCaseView> findTestCaseViews(String id, Pageable pageable);

  ...
}

As can hopefully be seen from the code, Spring Data allows to define view interfaces directly within the repository interface instead of the entity classes and thus retrieve only a subset or an aggregation of the data.



来源:https://stackoverflow.com/questions/49545155/mongodb-projection-on-embedded-document-field-with-mongotemplate

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!