Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto-configuration for Spring Data MongoDB ignores spring.data.mongodb.database when spring.data.mongodb.uri has been set #35566

Closed
code-uri opened this issue May 19, 2023 · 17 comments
Assignees
Labels
type: regression A regression from a previous release
Milestone

Comments

@code-uri
Copy link

code-uri commented May 19, 2023

After upgrading to spring-boot 3 mongo auto configuration is not working, when connection string is used.

I think we need to have a fallback option if this.connectionDetails.getConnectionString().getDatabase() is null.

@Bean
@ConditionalOnMissingBean(ReactiveMongoDatabaseFactory.class)
public SimpleReactiveMongoDatabaseFactory reactiveMongoDatabaseFactory(MongoClient mongo) {
	return new SimpleReactiveMongoDatabaseFactory(mongo,
			this.connectionDetails.getConnectionString().getDatabase());
}

Can we change the above code as mentioned below?

@Bean
@ConditionalOnMissingBean(ReactiveMongoDatabaseFactory.class)
public SimpleReactiveMongoDatabaseFactory reactiveMongoDatabaseFactory(MongoProperties properties,         MongoClient mongo) {
        String database = this.connectionDetails.getConnectionString().getDatabase();
        if(database==null)
            database = properties.getDatabase()
		return new SimpleReactiveMongoDatabaseFactory(mongo,
			database);
}
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label May 19, 2023
@wilkinsona
Copy link
Member

Thanks for the report. I don't think we can make the suggested change as the intention is that the connection details contain all of the information that's required to connect to Mongo. Can you please take a step back and describe the problem that you're facing? A sample that works with 3.0.x but does not work with 3.1.0 would be ideal.

@wilkinsona wilkinsona added the status: waiting-for-feedback We need additional information before we can continue label May 19, 2023
@burl21

This comment was marked as off-topic.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels May 19, 2023
@wilkinsona wilkinsona added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels May 19, 2023
@wilkinsona

This comment was marked as resolved.

@code-uri
Copy link
Author

code-uri commented May 19, 2023

@burl21 I'm not sure that this is the same problem. The problem reported here is specific to Spring Boot 3.1, but with your spring.data.mongodb.uri I see a failure with Spring Boot 3.0.x as well:

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.mongodb.core.MongoDatabaseFactorySupport]: Factory method 'mongoDatabaseFactory' threw exception with message: Database name must not be empty
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:171) ~[spring-beans-6.0.8.jar:6.0.8]
	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:655) ~[spring-beans-6.0.8.jar:6.0.8]
	... 61 common frames omitted
Caused by: java.lang.IllegalArgumentException: Database name must not be empty
	at org.springframework.util.Assert.hasText(Assert.java:294) ~[spring-core-6.0.8.jar:6.0.8]
	at org.springframework.data.mongodb.core.MongoDatabaseFactorySupport.<init>(MongoDatabaseFactorySupport.java:68) ~[spring-data-mongodb-4.0.5.jar:4.0.5]
	at org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory.<init>(SimpleMongoClientDatabaseFactory.java:75) ~[spring-data-mongodb-4.0.5.jar:4.0.5]
	at org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory.<init>(SimpleMongoClientDatabaseFactory.java:64) ~[spring-data-mongodb-4.0.5.jar:4.0.5]
	at org.springframework.boot.autoconfigure.data.mongo.MongoDatabaseFactoryConfiguration.mongoDatabaseFactory(MongoDatabaseFactoryConfiguration.java:43) ~[main/:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:139) ~[spring-beans-6.0.8.jar:6.0.8]
	... 62 common frames omitted

If you would like us to investigate this, please open a separate issue as it's difficult to track two potentially different problems in the same issue.

Thanks for checking. yes it is failing starting from 3.0.x . As you have already confirmed the issue, I am just mentioned the logic straight away to the code from spring boot 2.6.6. As you can see the method properties.getMongoClientDatabase() returns the database name from "spring.data.mogodb.uri". if it is not available in uri it falling back to database property "spring.data.mongodb.database". Hence, I am expecting the same behaviour in 3.x.x as well.

public class MongoReactiveDataAutoConfiguration {
       ...
	@Bean
	@ConditionalOnMissingBean(ReactiveMongoDatabaseFactory.class)
	public SimpleReactiveMongoDatabaseFactory reactiveMongoDatabaseFactory(MongoProperties properties,
			MongoClient mongo) {
		String database = properties.getMongoClientDatabase();
		return new SimpleReactiveMongoDatabaseFactory(mongo, database);
	}
	...
}

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels May 19, 2023
@wilkinsona
Copy link
Member

yes it is failing starting from 3.0.x

Sorry, I am confused now as the issue title says "after upgrading to 3.1.x". Can you please clarify?

@wilkinsona wilkinsona added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels May 19, 2023
@code-uri code-uri changed the title No fallback option for database name after upgrading to 3.1.x spring-boot-autoconfigure No fallback option for database name after upgrading to 3.0.x spring-boot-autoconfigure May 20, 2023
@code-uri

This comment was marked as outdated.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels May 20, 2023
@code-uri
Copy link
Author

code-uri commented May 20, 2023

I tried reproducing the issue by with spring boot 3.0.7. I don't see this error. I have attached the demo code.here.
spring boot 3.0.7 working fine here

when I change the spring boot version to 3.1.1-SNAPSHOT I see the failure.

spring boot 3.1.1-SNAPSHOT failing here.

@code-uri code-uri changed the title No fallback option for database name after upgrading to 3.0.x spring-boot-autoconfigure No fallback option for database name after upgrading to 3.1.x spring-boot-autoconfigure May 20, 2023
@LauroSilveira
Copy link

LauroSilveira commented May 21, 2023

Good evening,

I am facing the same error after update spring-boot-starter-parent from 3.0.1 to 3.1.0.

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.mongodb.core.MongoDatabaseFactorySupport]: Factory method 'mongoDatabaseFactory' threw exception with message: Database name must not be empty
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:171)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:655)
	... 96 more
Caused by: java.lang.IllegalArgumentException: Database name must not be empty
	at org.springframework.util.Assert.hasText(Assert.java:294)
	at org.springframework.data.mongodb.core.MongoDatabaseFactorySupport.<init>(MongoDatabaseFactorySupport.java:68)
	at org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory.<init>(SimpleMongoClientDatabaseFactory.java:75)
	at org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory.<init>(SimpleMongoClientDatabaseFactory.java:64)
	at org.springframework.boot.autoconfigure.data.mongo.MongoDatabaseFactoryConfiguration.mongoDatabaseFactory(MongoDatabaseFactoryConfiguration.java:47)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:139)
	... 97 more

The link to he repository is https://github.com/LauroSilveira/alura-flix-api.
Please, can you check this error?

Kind regards,
Lauro Correia

@carldini
Copy link

If it adds context, My DB SaaS provider doesn't offer a connection string containing the DB name, it's for the instance instead.
One could fiddle with the connection string, adding the db name... and this gets around the assumption that the connection string always contains a database name.

Overriding PropertiesMongoConnectionDetails seemed to be the easiest place to do that for me.

  @Bean
  PropertiesMongoConnectionDetails mongoProperties(final MongoProperties mongoProperties) {
    final String withDatabaseName =
        mongoProperties.getUri().replace("/?", "/" + mongoProperties.getDatabase() + "?");
    mongoProperties.setUri(withDatabaseName);

    return new PropertiesMongoConnectionDetails(mongoProperties);
  }

@code-uri
Copy link
Author

If it adds context, My DB SaaS provider doesn't offer a connection string containing the DB name, it's for the instance instead. One could fiddle with the connection string, adding the db name... and this gets around the assumption that the connection string always contains a database name.

Overriding PropertiesMongoConnectionDetails seemed to be the easiest place to do that for me.

  @Bean
  PropertiesMongoConnectionDetails mongoProperties(final MongoProperties mongoProperties) {
    final String withDatabaseName =
        mongoProperties.getUri().replace("/?", "/" + mongoProperties.getDatabase() + "?");
    mongoProperties.setUri(withDatabaseName);

    return new PropertiesMongoConnectionDetails(mongoProperties);
  }

@carldini This should work in your case. but, I think we should not change the value of a configurationProperty bean programmatically. Maybe, you could create a new object of MongoProperties and pass the new obj instead.

@wilkinsona
Copy link
Member

wilkinsona commented May 22, 2023

Thanks, all. I now understand the problem.

The spring.data.mongodb.uri property has always been documented as overriding the database:

Mongo database URI. Overrides host, port, username, password, and database.

Prior to 3.1, the implementation did not align with that which is why it used to work. The behavior is now aligned with the documentation so spring.data.mongodb.database is ignored if you've set spring.data.mongodb.uri. We may need to roll this back for now and spend some more time on straightening things out in Spring Boot 3.2.

In the meantime, it may be easiest to define your own SimpleReactiveMongoDatabaseFactory or SimpleMongoClientDatabaseFactory bean. Something like this:

@Bean
MongoDatabaseFactorySupport<?> mongoDatabaseFactory(MongoClient mongoClient, MongoProperties properties) {
	return new SimpleMongoClientDatabaseFactory(mongoClient, properties.getDatabase());
}

@wilkinsona
Copy link
Member

I don't think we can make the suggested change as the intention is that the connection details contain all of the information that's required to connect to Mongo.

I think I'm changing my mind about this. The connection details are already sufficient to connect. It's only Spring Data Mongo that insists on a database which it ultimately uses to make a call to com.mongodb.client.MongoClient.getDatabase(String). This is, potentially, completely separate to the database in the connection string that's used for authentication.

@wilkinsona wilkinsona changed the title No fallback option for database name after upgrading to 3.1.x spring-boot-autoconfigure Auto-configuration for Spring Data MongoDB ignores spring.data.mongodb.database when spring.data.mongodb.uri has been set May 22, 2023
@wilkinsona wilkinsona added type: regression A regression from a previous release and removed status: waiting-for-triage An issue we've not yet triaged status: feedback-provided Feedback has been provided labels May 22, 2023
@wilkinsona wilkinsona added this to the 3.1.x milestone May 22, 2023
@scottfrederick scottfrederick self-assigned this May 22, 2023
@Umigatsu
Copy link

Hi,

If it can help anyone who also face this issue. On a project after an update from 3.0.x to 3.1.x, I resolved the issue by a using properties overriding :
Replacing :

spring.data.mongodb.database=demo-db
spring.data.mongodb.uri=mongodb://localhost:27018/?readPreference=primaryPreferred&directConnection=true

with this :

spring.data.mongodb.database=demo-db
spring.data.mongodb.uri=mongodb://localhost:27018/${spring.data.mongodb.database}?readPreference=primaryPreferred&directConnection=true

In case you need authentication in the URI, you also need to add authentication database in the URI :

spring.data.mongodb.authentication-database=admin
spring.data.mongodb.database=demo-db
spring.data.mongodb.uri=mongodb://username:password@localhost:27018/${spring.data.mongodb.database}?authSource=${spring.data.mongodb.authentication-database}&readPreference=primaryPreferred&directConnection=true

My projects uses Yaml files instead of properties files but I expect the behavior to be the same regardless.

Best regards

@ctonsing
Copy link

Just a quick question: what I am experiencing on 3.1.0 is that the property spring.data.mongodb.authentication-database is ignored, even if spring.data.mongodb.uri is not set. Is this also covered by the fix?

@wilkinsona
Copy link
Member

@ctonsing It's hard to say without knowing more. Please try 3.1.1-SNAPSHOT (available from https://repo.spring.io/snapshot) and see if your problem is fixed. If it isn't, please open a new issue with a minimal sample that reproduces the problem.

@ctonsing
Copy link

Thank you @wilkinsona, I did as suggested and the problem is fixed in current 3.1.1-SNAPSHOT. Thank you to all the devs for all your hard work!

@wilkinsona
Copy link
Member

Thanks for trying the snapshot, @ctonsing. Much appreciated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: regression A regression from a previous release
Projects
None yet
Development

No branches or pull requests

9 participants