Reputation: 11
I'm migrating our application from Spring Boot 1.5.9 to version 2.0.0.
In version 1.5.9 we have successfully used mixed Annotations on several Domain Classes e.g:
...
@org.springframework.data.mongodb.core.mapping.Document(collection = "folder")
@org.springframework.data.elasticsearch.annotations.Document(indexName = "folder")
public class Folder {
...
}
The same approach causes probems in Spring Boot 2.0.0. When MongoDB annotatnion @DBRef is used, Spring throws exception while ElasticsearchRepository creation:
java.lang.IllegalStateException: No association found!
Here comes classes and confs
pom.xml
...
<properties>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springfrsamework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
</parent>
...
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
<scope>provided</scope>
</dependency>
</dependencies>
...
Application.java
...
@EnableMongoRepositories("com.hydra.sbmr.repoMongo")
@EnableElasticsearchRepositories("com.hydra.sbmr.repoElastic")
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Folder.java (Note this @DBRef couses exception)
package com.hydra.sbmr.model;
import lombok.Getter;
import lombok.Setter;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.DBRef;
@org.springframework.data.mongodb.core.mapping.Document(collection = "folder")
@org.springframework.data.elasticsearch.annotations.Document(indexName = "folder")
public class Folder {
@Id
@Getter @Setter private String id;
// Why MongoDB core mapping @DBRef causes java.lang.IllegalStateException: No association found! exception
// while ElasticsearchRepository creation???
@DBRef
@Getter @Setter private Profile profile;
@Getter @Setter private String something;
}
Profile.java
package com.hydra.sbmr.model;
import lombok.Getter;
import lombok.Setter;
import org.springframework.data.annotation.Id;
@org.springframework.data.mongodb.core.mapping.Document(collection = "profile")
public class Profile {
@Id
@Getter @Setter private String id;
@Getter @Setter String blah;
}
FolderElasticRepository.java
package com.hydra.sbmr.repoElastic;
import com.hydra.sbmr.model.Folder;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
public interface FolderElasticRepository extends ElasticsearchRepository<Folder, String> {
}
You can find whole mini project on GitHub: https://github.com/hydraesb/sbmr
My question:
Upvotes: 1
Views: 645
Reputation: 1883
I have faced this problem also and I fixed with solution of @ybouraze
@Bean
fun elasticsearchTemplate(client: JestClient, converter: ElasticsearchConverter, builder: Jackson2ObjectMapperBuilder): ElasticsearchOperations {
val entityMapper = CustomEntityMapper(builder.createXmlMapper(false).build())
val mapper = DefaultJestResultsMapper(converter.mappingContext, entityMapper)
return JestElasticsearchTemplate(client, converter, mapper)
}
@Bean
@Primary
fun mappingContext(): SimpleElasticsearchMappingContext {
return MappingContext()
}
@Bean
fun elasticsearchConverter(): ElasticsearchConverter {
return MappingElasticsearchConverter(mappingContext())
}
inner class CustomEntityMapper(private val objectMapper: ObjectMapper) : EntityMapper {
init {
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true)
}
@Throws(IOException::class)
override fun mapToString(`object`: Any): String {
return objectMapper.writeValueAsString(`object`)
}
@Throws(IOException::class)
override fun <T> mapToObject(source: String, clazz: Class<T>): T {
return objectMapper.readValue(source, clazz)
}
}
inner class MappingContext : SimpleElasticsearchMappingContext() {
override fun createPersistentProperty(property: Property, owner: SimpleElasticsearchPersistentEntity<*>, simpleTypeHolder: SimpleTypeHolder): ElasticsearchPersistentProperty {
return PersistentProperty(property, owner, simpleTypeHolder)
}
}
inner class PersistentProperty(property: Property, owner: SimpleElasticsearchPersistentEntity<*>, simpleTypeHolder: SimpleTypeHolder) : SimpleElasticsearchPersistentProperty(property, owner, simpleTypeHolder) {
override fun isAssociation(): Boolean {
return false
}
}
Upvotes: 0
Reputation: 31
I have the same issue and the solution that i found is to extends SimpleElasticsearchMappingContext like this :
package com.mypackage; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.elasticsearch.client.Client; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; import org.springframework.data.elasticsearch.core.EntityMapper; import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import java.io.IOException; @Configuration public class ElasticsearchConfiguration { @Bean public ElasticsearchTemplate elasticsearchTemplate(Client client, Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder) { return new ElasticsearchTemplate(client, new MappingElasticsearchConverter(new CustomElasticsearchMappingContext()), new CustomEntityMapper(jackson2ObjectMapperBuilder.createXmlMapper(false).build())); } public class CustomEntityMapper implements EntityMapper { private ObjectMapper objectMapper; public CustomEntityMapper(ObjectMapper objectMapper) { this.objectMapper = objectMapper; objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); } @Override public String mapToString(Object object) throws IOException { return objectMapper.writeValueAsString(object); } @Override public T mapToObject(String source, Class clazz) throws IOException { return objectMapper.readValue(source, clazz); } } } package com.mypackage; import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty; import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext; import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchPersistentEntity; import org.springframework.data.mapping.model.Property; import org.springframework.data.mapping.model.SimpleTypeHolder; public class CustomElasticsearchMappingContext extends SimpleElasticsearchMappingContext { @Override protected ElasticsearchPersistentProperty createPersistentProperty(Property property, SimpleElasticsearchPersistentEntity owner, SimpleTypeHolder simpleTypeHolder) { return new CustomElasticsearchPersistentProperty(property, owner, simpleTypeHolder); } } package com.mypackage; import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty; import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchPersistentProperty; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.model.Property; import org.springframework.data.mapping.model.SimpleTypeHolder; public class CustomElasticsearchPersistentProperty extends SimpleElasticsearchPersistentProperty { public CustomElasticsearchPersistentProperty(Property property, PersistentEntity owner, SimpleTypeHolder simpleTypeHolder) { super(property, owner, simpleTypeHolder); } @Override public boolean isAssociation() { return false; } }
Upvotes: 3