Compare commits

...

7 Commits

Author SHA1 Message Date
Christoph Strobl
569f9838d2 switch to commons branch 2020-10-16 17:47:58 +02:00
Christoph Strobl
e4f2085861 some movement 2020-10-15 13:47:16 +02:00
Christoph Strobl
326a10f1bb extract some interfaces 2020-10-15 09:52:21 +02:00
Christoph Strobl
b61c1abd7b hick hack - annotation support for properties 2020-10-12 14:13:33 +02:00
Christoph Strobl
6d5d9776c9 make it see the constructor 2020-10-12 13:00:01 +02:00
Christoph Strobl
755f65299d Move and modify 2020-10-12 12:24:18 +02:00
Christoph Strobl
0b507c342f prepare issue branch. 2020-10-12 12:23:59 +02:00
10 changed files with 636 additions and 5 deletions

View File

@@ -5,7 +5,7 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.1.0-STATIC-METADATA-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Spring Data MongoDB</name>
@@ -26,7 +26,7 @@
<properties>
<project.type>multi</project.type>
<dist.id>spring-data-mongodb</dist.id>
<springdata.commons>2.4.0-SNAPSHOT</springdata.commons>
<springdata.commons>2.4.0-BUILD-TIME-DOMAIN-TYPE-METADATA-SNAPSHOT</springdata.commons>
<mongo>4.1.0</mongo>
<mongo.reactivestreams>${mongo}</mongo.reactivestreams>
<jmh.version>1.19</jmh.version>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.1.0-STATIC-METADATA-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.1.0-STATIC-METADATA-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -11,7 +11,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.1.0-STATIC-METADATA-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.buildtimetypeinfo;
import org.springframework.util.ObjectUtils;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public class Address {
String city;
String street;
public Address(String city, String street) {
this.city = city;
this.street = street;
}
public String getCity() {
return city;
}
public String getStreet() {
return street;
}
@Override
public String toString() {
return "Address{" + "city='" + city + '\'' + ", street='" + street + '\'' + '}';
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Address address = (Address) o;
if (!ObjectUtils.nullSafeEquals(city, address.city)) {
return false;
}
return ObjectUtils.nullSafeEquals(street, address.street);
}
@Override
public int hashCode() {
int result = ObjectUtils.nullSafeHashCode(city);
result = 31 * result + ObjectUtils.nullSafeHashCode(street);
return result;
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.buildtimetypeinfo;
import org.springframework.data.mapping.model.DomainTypeConstructor;
import org.springframework.data.mapping.model.DomainTypeInformation;
import org.springframework.data.mapping.model.Field;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public class AddressTypeInformation extends DomainTypeInformation<Address> {
private static final AddressTypeInformation INSTANCE = new AddressTypeInformation();
private AddressTypeInformation() {
super(Address.class);
// CONSTRUCTOR
setConstructor(computePreferredConstructor());
// FIELDS
addField(Field.<Address> string("city").getter(Address::getCity));
addField(Field.<Address> string("street").getter(Address::getStreet));
}
public static AddressTypeInformation instance() {
return INSTANCE;
}
private DomainTypeConstructor<Address> computePreferredConstructor() {
return DomainTypeConstructor.<Address> builder().args("city", "street")
.newInstanceFunction(args -> new Address((String) args[0], (String) args[1]));
}
}

View File

@@ -0,0 +1,139 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.buildtimetypeinfo;
import java.util.List;
import org.springframework.util.ObjectUtils;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public class Person {
private long id;
private String firstname, lastname; // TODO: we need a persistence constructor to resolve this here.
private int age;
private Address address;
private List<String> nicknames;
public Person(String firstname, String lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
private Person(long id, String firstname, String lastname, int age, Address address, List<String> nicknames) {
this.id = id;
this.firstname = firstname;
this.lastname = lastname;
this.age = age;
this.address = address;
this.nicknames = nicknames;
}
public String getFirstname() {
return firstname;
}
public String getLastname() {
return lastname;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public long getId() {
return id;
}
public Person withId(long id) {
return new Person(id, firstname, lastname, age, address, nicknames);
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public List<String> getNicknames() {
return nicknames;
}
public void setNicknames(List<String> nicknames) {
this.nicknames = nicknames;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
@Override
public String toString() {
return "Person{" + "id=" + id + ", firstname='" + firstname + '\'' + ", lastname='" + lastname + '\'' + ", age="
+ age + ", address=" + address + ", nicknames=" + nicknames + '}';
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Person person = (Person) o;
if (id != person.id)
return false;
if (age != person.age)
return false;
if (!ObjectUtils.nullSafeEquals(firstname, person.firstname)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(lastname, person.lastname)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(address, person.address)) {
return false;
}
return ObjectUtils.nullSafeEquals(nicknames, person.nicknames);
}
@Override
public int hashCode() {
int result = (int) (id ^ (id >>> 32));
result = 31 * result + ObjectUtils.nullSafeHashCode(firstname);
result = 31 * result + ObjectUtils.nullSafeHashCode(lastname);
result = 31 * result + age;
result = 31 * result + ObjectUtils.nullSafeHashCode(address);
result = 31 * result + ObjectUtils.nullSafeHashCode(nicknames);
return result;
}
}

View File

@@ -0,0 +1,128 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.buildtimetypeinfo;
import java.lang.annotation.Annotation;
import java.util.List;
import org.springframework.data.mapping.model.DomainTypeConstructor;
import org.springframework.data.mapping.model.DomainTypeInformation;
import org.springframework.data.mapping.model.Field;
import org.springframework.data.mapping.model.ListTypeInformation;
import org.springframework.data.mapping.model.StringTypeInformation;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.FieldType;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public class PersonTypeInformation extends DomainTypeInformation<Person> {
private static final PersonTypeInformation INSTANCE = new PersonTypeInformation();
private PersonTypeInformation() {
super(Person.class);
// CONSTRUCTOR
setConstructor(computePreferredConstructor());
// ANNOTATIONS
addAnnotation(computeAtDocumentAnnotation());
// FIELDS
addField(
Field.<Person> int64("id").annotatedWithAtId().getter(Person::getId).wither((bean, id) -> bean.withId(id)));
addField(Field.<Person> string("firstname").getter(Person::getFirstname).annotation(atFieldOnFirstname()));
addField(Field.<Person> string("lastname").getter(Person::getLastname));
addField(Field.<Person> int32("age").getter(Person::getAge).setter(Person::setAge));
addField(Field.<Person, Address> type("address", AddressTypeInformation.instance()).getter(Person::getAddress)
.setter(Person::setAddress));
addField(Field.<Person, List<String>> type("nicknames", new ListTypeInformation<>(StringTypeInformation.instance()))
.getter(Person::getNicknames).setter(Person::setNicknames));
}
public static PersonTypeInformation instance() {
return INSTANCE;
}
private DomainTypeConstructor<Person> computePreferredConstructor() {
return DomainTypeConstructor.<Person> builder().args("firstname", "lastname")
.newInstanceFunction((args) -> new Person((String) args[0], (String) args[1]));
}
private Document computeAtDocumentAnnotation() {
return new Document() {
@Override
public Class<? extends Annotation> annotationType() {
return Document.class;
}
@Override
public String value() {
return collection();
}
@Override
public String collection() {
return "star-wars";
}
@Override
public String language() {
return "";
}
@Override
public String collation() {
return "";
}
};
}
private Annotation atFieldOnFirstname() {
return new org.springframework.data.mongodb.core.mapping.Field() {
@Override
public Class<? extends Annotation> annotationType() {
return org.springframework.data.mongodb.core.mapping.Field.class;
}
@Override
public String value() {
return "first-name";
}
@Override
public String name() {
return value();
}
@Override
public int order() {
return 0;
}
@Override
public FieldType targetType() {
return FieldType.IMPLICIT;
}
};
}
}

View File

@@ -23,6 +23,7 @@ import static org.springframework.data.mongodb.core.DocumentTestUtils.*;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import java.lang.annotation.Annotation;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URL;
@@ -70,6 +71,7 @@ import org.springframework.data.mongodb.core.convert.DocumentAccessorUnitTests.N
import org.springframework.data.mongodb.core.convert.DocumentAccessorUnitTests.ProjectingType;
import org.springframework.data.mongodb.core.convert.MappingMongoConverterUnitTests.ClassWithMapUsingEnumAsKey.FooBarEnum;
import org.springframework.data.mongodb.core.geo.Sphere;
import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.FieldType;
@@ -78,7 +80,9 @@ import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.mapping.PersonPojoStringId;
import org.springframework.data.mongodb.core.mapping.TextScore;
import org.springframework.data.mongodb.core.mapping.event.AfterConvertCallback;
import org.springframework.data.mongodb.buildtimetypeinfo.AddressTypeInformation;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.mongodb.buildtimetypeinfo.PersonTypeInformation;
import org.springframework.test.util.ReflectionTestUtils;
import com.mongodb.BasicDBList;
@@ -2179,6 +2183,108 @@ public class MappingMongoConverterUnitTests {
assertThat(((LinkedHashMap) result.get("cluster")).get("_id")).isEqualTo(100L);
}
// @Test
// public void perf1() {
//
// ClassTypeInformation.warmCache(PersonTypeInformation.instance(), AddressTypeInformation.instance());
//
// MongoMappingContext mappingContext = new MongoMappingContext();
// mappingContext.setInitialEntitySet(new LinkedHashSet<>(
// Arrays.asList(org.springframework.data.mongodb.xxx.Person.class, org.springframework.data.mongodb.xxx.Address.class)));
// mappingContext.initialize();
//
// MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext);
//
// org.springframework.data.mongodb.xxx.Person source = new org.springframework.data.mongodb.xxx.Person("spring", "data");
// source.setAddress(new org.springframework.data.mongodb.xxx.Address("the city", "never sleeps"));
// source.setAge(10);
// source = source.withId(9876);
// source.setNicknames(Arrays.asList("tick", "trick", "track"));
//
// StopWatch stopWatch = new StopWatch();
//
// List<org.bson.Document> sources = new ArrayList<>();
// stopWatch.start("write");
// for (int i = 0; i < 10000; i++) {
//
// org.bson.Document targetDocument = new org.bson.Document();
// converter.write(source, targetDocument);
//
// sources.add(targetDocument);
// }
// stopWatch.stop();
//
// stopWatch.start("read");
// for (org.bson.Document sourceDoc : sources) {
// assertThat(converter.read(org.springframework.data.mongodb.xxx.Person.class, sourceDoc)).isEqualTo(source);
// }
// stopWatch.stop();
//
// System.out.println(stopWatch.prettyPrint());
//
// }
// public void perf2() {
//
// ClassTypeInformation.warmCache(new PersonTypeInformation(), new AddressTypeInformation());
//
// MongoMappingContext mappingContext = new MongoMappingContext();
// mappingContext.setInitialEntitySet(new LinkedHashSet<>(Arrays.asList(org.springframework.data.mongodb.xxx.Person.class,
// org.springframework.data.mongodb.xxx.Address.class)));
// mappingContext.initialize();
//
// MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext);
//
// org.springframework.data.mongodb.xxx.Person source = new org.springframework.data.mongodb.xxx.Person("spring", "data");
// source.setAddress(new org.springframework.data.mongodb.xxx.Address("the city", "never sleeps"));
// source.setAge(10);
// source.setId(9876);
// source.setNicknames(Arrays.asList("tick", "trick", "track"));
//
// }
@Test
public void staticEntityMetadata() {
ClassTypeInformation.warmCache(PersonTypeInformation.instance(), AddressTypeInformation.instance());
MongoMappingContext mappingContext = new MongoMappingContext();
mappingContext.setInitialEntitySet(new LinkedHashSet<>(
Arrays.asList(org.springframework.data.mongodb.buildtimetypeinfo.Person.class, org.springframework.data.mongodb.buildtimetypeinfo.Address.class)));
mappingContext.initialize();
org.springframework.data.mongodb.buildtimetypeinfo.Person source = new org.springframework.data.mongodb.buildtimetypeinfo.Person("spring", "data");
source.setAddress(new org.springframework.data.mongodb.buildtimetypeinfo.Address("the city", "never sleeps"));
source.setAge(10);
source = source.withId(9876);
source.setNicknames(Arrays.asList("tick", "trick", "track"));
MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext);
org.bson.Document targetDocument = new org.bson.Document();
System.out.println();
System.out.println("------ WRITE -------");
converter.write(source, targetDocument);
System.out.println();
System.out.println("targetDocument: " + targetDocument);
System.out.println();
System.out.println("------ READ -------");
assertThat(targetDocument).containsEntry("_id", 9876L);
assertThat(targetDocument).containsEntry("first-name", "spring");
assertThat(targetDocument).containsEntry("address",
new org.bson.Document("city", "the city").append("street", "never sleeps"));
assertThat(targetDocument).containsEntry("nicknames", Arrays.asList("tick", "trick", "track"));
org.springframework.data.mongodb.buildtimetypeinfo.Person targetEntity = converter.read(org.springframework.data.mongodb.buildtimetypeinfo.Person.class,
targetDocument);
System.out.println();
System.out.println("targetEntity: " + targetEntity);
assertThat(targetEntity).isEqualTo(source);
BasicMongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(org.springframework.data.mongodb.buildtimetypeinfo.Person.class);
assertThat(entity.getCollection()).isEqualTo("star-wars");
}
static class GenericType<T> {
T content;
}
@@ -2649,4 +2755,34 @@ public class MappingMongoConverterUnitTests {
return entity;
}
}
void xxx2() {
new Field() {
@Override
public Class<? extends Annotation> annotationType() {
return null;
}
@Override
public String value() {
return null;
}
@Override
public String name() {
return null;
}
@Override
public int order() {
return 0;
}
@Override
public FieldType targetType() {
return null;
}
};
}
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright 2020. the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.staticmetadata;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import static org.springframework.data.mongodb.core.query.Query.*;
import java.util.Arrays;
import java.util.LinkedHashSet;
import org.bson.Document;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.buildtimetypeinfo.Address;
import org.springframework.data.mongodb.buildtimetypeinfo.AddressTypeInformation;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.mongodb.buildtimetypeinfo.Person;
import org.springframework.data.mongodb.buildtimetypeinfo.PersonTypeInformation;
import com.mongodb.client.MongoClients;
/**
* @author Christoph Strobl
* @since 2020/10
*/
public class StaticMetadataTests {
MongoMappingContext mappingContext;
MappingMongoConverter mongoConverter;
MongoTemplate template;
Person luke;
@BeforeAll
static void beforeAll() {
ClassTypeInformation.warmCache(PersonTypeInformation.instance(), AddressTypeInformation.instance());
}
@BeforeEach
void beforeEach() {
mappingContext = new MongoMappingContext();
mappingContext.setInitialEntitySet(new LinkedHashSet<>(
Arrays.asList(Person.class, Address.class)));
mappingContext.initialize();
mongoConverter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext);
mongoConverter.afterPropertiesSet();
template = new MongoTemplate(new SimpleMongoClientDatabaseFactory(MongoClients.create(), "sem"), mongoConverter);
luke = new Person("luke", "skywalker");
luke.setAddress(new Address("Mos Eisley", "WB154"));
luke.setAge(22);
luke = luke.withId(9876);
luke.setNicknames(Arrays.asList("jedi", "wormie"));
}
@Test
void readWrite() {
template.save(luke);
Document savedDocument = template.execute("star-wars",
collection -> collection.find(new Document("_id", luke.getId())).first());
System.out.println("savedDocument.toJson(): " + savedDocument.toJson());
Person savedEntity = template.findOne(query(where("id").is(luke.getId())), Person.class);
System.out.println("savedEntity: " + savedEntity);
assertThat(savedEntity).isEqualTo(luke);
}
}