Issue
I am getting "Couldn't find PersistentEntity for type class" error when I am using @EnableMongoAuditing
features along with MongoRepository.
This happens when I save a document when collection isn't already present in database.
I tried whatever is mentioned in:
- https://github.com/spring-projects/spring-boot/issues/12023
- https://jira.spring.io/browse/DATAMONGO-1999
- Spring boot mongodb auditing error
but nothing is working.
Mentioned things are:
Extend MongoConfig by AbstractMongoConfiguration and override all methods.
Here is my code which reproduced the same error:
MongoConfig class
@Configuration
public class MongoConfig extends AbstractMongoConfiguration {
@Value("${spring.data.mongodb.host}")
private String mongoHost;
@Value("${spring.data.mongodb.port}")
private String mongoPort;
@Value("${spring.data.mongodb.database}")
private String mongoDB;
@Override
public MongoDbFactory mongoDbFactory() {
return new SimpleMongoDbFactory(new MongoClient(mongoHost + ":" + mongoPort), mongoDB);
}
@Override
public MongoClient mongoClient() {
return new MongoClient(mongoHost, Integer.parseInt(mongoPort));
}
@Override
public MongoTemplate mongoTemplate() {
return new MongoTemplate(mongoDbFactory());
}
@Override
public MappingMongoConverter mappingMongoConverter() {
return new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory()), new MongoMappingContext());
}
@Override
protected String getDatabaseName() {
return mongoDB;
}
}
Person Collection class
@Document
public class Person {
@Id
private String id;
private String name;
@CreatedDate
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime lastModified;
// Getter Setters Constructors omitted for brevity
}
Main Application class
@EnableMongoAuditing
@EnableMongoRepositories ({"com.example.*", "org.apache.*"})
@SpringBootApplication
@ComponentScan({"com.example.*", "org.apache.*"})
public class DemoApplication implements CommandLineRunner {
@Autowired
PersonRepository personRepository;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
Person p1 = new Person("1", "prakhar");
personRepository.save(p1);
}
}
Expected Result is Person entity should be saved in database. Actual Result is "Couldn't find PersistentEntity for type class Person" error
Solution
Looks like you ran into https://github.com/spring-projects/spring-boot/issues/12023
Extending AbstractMongoConfiguration will switch off Spring Boot's auto-configuration of various Mongo components and also customises the base packages that are used to scan for mappings. I would recommend that you don't use it in Spring Boot.
Update
I managed to get the example running with the configuration as simple as
@Configuration
public class MongoConfig {
@Value("${spring.data.mongodb.host}")
private String mongoHost;
@Value("${spring.data.mongodb.port}")
private String mongoPort;
@Value("${spring.data.mongodb.database}")
private String mongoDB;
@Bean
public MongoDbFactory mongoDbFactory() {
return new SimpleMongoDbFactory(new MongoClient(mongoHost + ":" + mongoPort), mongoDB);
}
@Bean
public MongoClient mongoClient() {
return new MongoClient(mongoHost, Integer.parseInt(mongoPort));
}
}
and the app class
@EnableMongoAuditing
@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
@Autowired
PersonRepository personRepository;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
Thread.sleep(2000);
Person p1 = new Person("1", "prakhar");
personRepository.save(p1);
}
}
Notice that I followed my own advice and did't inherit from AbstractMongoConfiguration
Explanation
The problem lies in the initialization of
@Bean
public MappingMongoConverter mappingMongoConverter() {
return new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory()), new MongoMappingContext());
}
You simply call MongoMappingContext
constructor, without calling setInitialEntitySet
. Compare that with MongoDataConfiguration
auto-configuration class.
@Bean
@ConditionalOnMissingBean
public MongoMappingContext mongoMappingContext(MongoCustomConversions conversions)
throws ClassNotFoundException {
MongoMappingContext context = new MongoMappingContext();
context.setInitialEntitySet(new EntityScanner(this.applicationContext)
.scan(Document.class, Persistent.class));
Class<?> strategyClass = this.properties.getFieldNamingStrategy();
if (strategyClass != null) {
context.setFieldNamingStrategy(
(FieldNamingStrategy) BeanUtils.instantiateClass(strategyClass));
}
context.setSimpleTypeHolder(conversions.getSimpleTypeHolder());
return context;
}
Even worse, you don't register MongoMappingContext
as a managed bean.
Due to this fact, auto-configuration class is still created. This leads to a race condition, I tried to run the original code and could easily reproduce the error, but with a breakpoint in AbstractMappingContext.addPersistentEntity
the test always passed.
Answered By - Lesiak
Answer Checked By - Robin (JavaFixing Admin)