Come si sceglie di utilizzare il database MongoDB non embedded in Spring Boot 2?

di il
10 risposte

Come si sceglie di utilizzare il database MongoDB non embedded in Spring Boot 2?

Sto studiando questa applicazione:
https://github.com/Apress/beg-spring-boot-2/tree/master/chapter-09/springboot-mongodb-demo
che funziona alla perfezione e che ho capito abbastanza bene. Non mi piace molto questa scelta dell'autore:

public interface UserRepository extends MongoRepository<User, String>
{
	@Query("{ 'name' : ?0 }")
	User findByUserName(String name);
}
e avrei preferito questa:

public interface UserRepository extends MongoRepository<User, String>
{
	@Query("{ 'name' : ?0 }")
	List<User> findByUserName(String name);
}
ma si tratta di un esempio che illustra come lavorare con MongoDB e quindi va benissimo così.
Le cose che vorrei chiedere sono essenzialmente due.
Sulla webapp si possono usare due tipi di database MongoDB:
A - MongoDB
B - Embedded MongoDB

Come posso scegliere l'opzione A senza editare il pom.xml nel modo seguente:

		<!--<dependency>
			<groupId>de.flapdoodle.embed</groupId>
			<artifactId>de.flapdoodle.embed.mongo</artifactId>
		</dependency>-->
Mi sono chiesto come mai in questo metodo non ci fosse stato alcun riferimento alla collection e mi sono dato una risposta che mi sono appuntato sopra il metodo:

	// Cerca un utente di tipo User con un id specifico in MongoDB
	// Manca il nome della collection perché non può accadere che due collection diverse abbiano due id identici.
	// La collection è simile alle tabelle dei DB relazionali ma l'id è univoco in tutto il database. La collection è
	// simile alla tabella dei DB relazionali ma si differenzia appunto per avere un id univoco.
	// Due collection diverse non possono avere lo stesso id perché MongoDB è appunto un DB non relazionale.
	public User getUser(Integer id)
	{
		Query query = Query.query(Criteria.where("id").is(id));
		return mongoTemplate.findOne(query , User.class);
	}
E' corretta la mia deduzione?

10 Risposte

  • Re: Come si sceglie di utilizzare il database MongoDB non embedded in Spring Boot 2?

    giannino1995 ha scritto:


    e avrei preferito questa:
    
    public interface UserRepository extends MongoRepository<User, String>
    {
    	@Query("{ 'name' : ?0 }")
    	List<User> findByUserName(String name);
    }
    
    Si può fare ... anzi, è concettualmente meglio. Un singolo oggetto ha senso quando si fa es. un findFirstBy....... (o un find per "id", chiave primaria, che quindi dà di certo un solo record).

    giannino1995 ha scritto:


    Sulla webapp si possono usare due tipi di database MongoDB:
    A - MongoDB
    B - Embedded MongoDB
    Chiariamo innanzitutto una cosa. Quel de.flapdoodle.embed.mongo non è un vero MongoDB. Nel senso che non contiene un MongoDB "ufficiale" e quindi non è detto che si comporti perfettamente come il vero server MongoDB.

    giannino1995 ha scritto:


    Come posso scegliere l'opzione A senza editare il pom.xml nel modo seguente:
    
    		<!--<dependency>
    			<groupId>de.flapdoodle.embed</groupId>
    			<artifactId>de.flapdoodle.embed.mongo</artifactId>
    		</dependency>-->
    
    Una idea sono i "profili" Maven. In generale, es. si fa il build con un profilo che include la dependency X o si builda con un altro profilo che include la dependency Y.

    P.S. i profili Maven non c'entrano nulla con i profili Spring Framework/Boot.

    giannino1995 ha scritto:


    // Cerca un utente di tipo User con un id specifico in MongoDB
    // Manca il nome della collection perché non può accadere che due collection diverse abbiano due id identici.
    // La collection è simile alle tabelle dei DB relazionali ma l'id è univoco in tutto il database. La collection è
    // simile alla tabella dei DB relazionali ma si differenzia appunto per avere un id univoco.
    // Due collection diverse non possono avere lo stesso id perché MongoDB è appunto un DB non relazionale.
    E' corretta la mia deduzione?
    No, è tutto falso. O perlomeno, è solo vero che una collection di MongoDB è concettualmente similare ad una tabella nei DB relazionali. Ma il discorso che hai fatto sugli ID è completamente campato in aria. Non vuol dire nulla.

    La collection è ben indicata ...

    @Document(collection="users")
    public class User
    {
  • Re: Come si sceglie di utilizzare il database MongoDB non embedded in Spring Boot 2?

    1) Quindi non posso fare come si faceva in precedenza usando un altro file .properties, giusto? Intanto vado a studiarmi i profili Maven, grazie.
    2) Potresti spiegarmi allora perché in getUser() manca un riferimento alla collection? Come fa Hibernate a sapere che deve cercare nella collection 'users'. Record appartenenti a collection diverse possono avere lo stesso id?
    sempre grazie
  • Re: Come si sceglie di utilizzare il database MongoDB non embedded in Spring Boot 2?

    giannino1995 ha scritto:


    1) Quindi non posso fare come si faceva in precedenza usando un altro file .properties, giusto? Intanto vado a studiarmi i profili Maven.
    Un altro properties es. application-prod.properties certo che si può fare. L'unica cosa è che ... non so ora COME dirgli di non usare il flapdoodle.embed.mongo. Non ne sono sicuro ma credo ci sia la solita "auto configuration" di Spring Boot per cui se sente che in classpath c'è il flapdoodle.embed.mongo usa quello.

    Non ho mai usato Spring Data MongoDB (in passato ho solo imparato un po' ad usare il driver Java nativo del MongoDB). Su questi aspetti c'è solo una cosa da fare ... studiare. Sulla documentazione ufficiale e altro ...

    EDIT: alt, aspetta. E' semplice: il de.flapdoodle.embed.mongo si usa principalmente/esclusivamente in ambito di TEST. Quindi basta mettere la dependency con <scope> test e .... lo usa appunto SOLO in fase di test.

    giannino1995 ha scritto:


    Come fa Hibernate
    Che c'entra Hibernate ....

    giannino1995 ha scritto:


    2) Potresti spiegarmi allora perché in getUser() manca un riferimento alla collection?

    [..] a sapere che deve cercare nella collection 'users'.
    Lo "sa" dalla classe User. Viene passato User.class ... e il MongoTemplate verifica: oh toh, guarda, ha un @Document con il nome della collection ...

    giannino1995 ha scritto:


    Record appartenenti a collection diverse possono avere lo stesso id?
    Tecnicamente sì, ovviamente. Ma generalmente il field _id lo si genera con la tecnica di MongoDB del suo ObjectID auto-generato.
    https://docs.mongodb.com/manual/reference/glossary/#term-objectid
  • Re: Come si sceglie di utilizzare il database MongoDB non embedded in Spring Boot 2?

    1) I profili su Maven sono complicati, tanto codice per fare una cosa banalissima, bah... faccio prima a togliere la dipendenza a mano...
    Ho provato a correggere il file pom.xml nel modo seguente ma non ho risolto:
    
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" 
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
    						http://maven.apache.org/maven-v4_0_0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    	<groupId>com.apress</groupId>
    	<artifactId>09-springboot-mongodb-demo</artifactId>
    	<packaging>jar</packaging>
    	<version>1.0-SNAPSHOT</version>
    
    	<parent>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-parent</artifactId>
    		<version>2.0.0.BUILD-SNAPSHOT</version>
    		<relativePath/> <!-- lookup parent from repository -->
    	</parent>
    
    	<properties>
    		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    		<java.version>1.8</java.version>
    		<mongo.version>4.4.0</mongo.version>
    	</properties>
    
        <repositories>
    		<repository>
    			<id>spring-snapshots</id>
    			<name>Spring Snapshots</name>
    			<url>https://repo.spring.io/snapshot</url>
    			<snapshots>
    				<enabled>true</enabled>
    			</snapshots>
    		</repository>
    		<repository>
    			<id>spring-milestones</id>
    			<name>Spring Milestones</name>
    			<url>https://repo.spring.io/milestone</url>
    			<snapshots>
    				<enabled>false</enabled>
    			</snapshots>
    		</repository>
    	</repositories>
    
    	<pluginRepositories>
    		<pluginRepository>
    			<id>spring-snapshots</id>
    			<name>Spring Snapshots</name>
    			<url>https://repo.spring.io/snapshot</url>
    			<snapshots>
    				<enabled>true</enabled>
    			</snapshots>
    		</pluginRepository>
    		<pluginRepository>
    			<id>spring-milestones</id>
    			<name>Spring Milestones</name>
    			<url>https://repo.spring.io/milestone</url>
    			<snapshots>
    				<enabled>false</enabled>
    			</snapshots>
    		</pluginRepository>
    	</pluginRepositories>
        
    	<build>
    		<plugins>
    			<plugin>
    				<groupId>org.springframework.boot</groupId>
    				<artifactId>spring-boot-maven-plugin</artifactId>
    			</plugin>
    		</plugins>
    	</build>
    
    	<dependencies>
    		<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-data-mongodb</artifactId>
    		</dependency>
    	</dependencies>
    
    	<profiles>
    		<profile>
    			<id>EmbeddedMongoDB</id>
    			<activation>
    				<property>
    					<name>spring.profiles.active</name>
    					<value>EmbeddedMongoDB</value>
    				</property>
    			</activation>
    			<dependencies>
    				<dependency>
    					<groupId>de.flapdoodle.embed</groupId>
    					<artifactId>de.flapdoodle.embed.mongo</artifactId>
    				</dependency>
    			</dependencies>
    		</profile>
    		<profile>
    			<id>MongoDB</id>
    			<activation>
    				<property>
    					<name>spring.profiles.active</name>
    					<value>MongoDB</value>
    				</property>
    			</activation>
    		</profile>
    	</profiles>
    
    </project>
    
    Nel properties ho messo questo:
    
    spring.profiles.active=EmbeddedMongoDB
    #spring.profiles.active=MongoDB
    
    oppure questo:
    
    #spring.profiles.active=EmbeddedMongoDB
    spring.profiles.active=MongoDB
    
    2) ok, tutto chiaro
  • Re: Come si sceglie di utilizzare il database MongoDB non embedded in Spring Boot 2?

    giannino1995 ha scritto:


    1) I profili su Maven sono complicati
    No, non sono complicati. Da quello che vedo sotto è che ... non li hai capiti .....

    giannino1995 ha scritto:


    Ho provato a correggere il file pom.xml nel modo seguente ma non ho risolto:
    
    	<profiles>
    		<profile>
    			<id>EmbeddedMongoDB</id>
    			<activation>
    				<property>
    					<name>spring.profiles.active</name>
    					<value>EmbeddedMongoDB</value>
    				</property>
    			</activation>
    			<dependencies>
    				<dependency>
    					<groupId>de.flapdoodle.embed</groupId>
    					<artifactId>de.flapdoodle.embed.mongo</artifactId>
    				</dependency>
    			</dependencies>
    		</profile>
    		<profile>
    			<id>MongoDB</id>
    			<activation>
    				<property>
    					<name>spring.profiles.active</name>
    					<value>MongoDB</value>
    				</property>
    			</activation>
    		</profile>
    	</profiles>
    
    </project>
    
    Nel properties ho messo questo:
    
    spring.profiles.active=EmbeddedMongoDB
    #spring.profiles.active=MongoDB
    
    oppure questo:
    
    #spring.profiles.active=EmbeddedMongoDB
    spring.profiles.active=MongoDB
    
    Ti ho già detto prima che i profili Maven NON c'entrano NULLA direttamente con i profili di Spring !!!

    Quel spring.profiles.active=EmbeddedMongoDB in un properties non c'entra niente con quel <name>spring.profiles.active</name> !


    E comunque prima ho messo un EDIT, perché mi è venuto in mente dopo. Basta usare lo <scope>test</scope>
  • Re: Come si sceglie di utilizzare il database MongoDB non embedded in Spring Boot 2?

    Ho capito quello che hai detto. In pratica mi hai suggerito di scrivere:
    
    		<dependency>
    			<groupId>de.flapdoodle.embed</groupId>
    			<artifactId>de.flapdoodle.embed.mongo</artifactId>
    			<scope>test</scope>
    		</dependency>
    
    però poi non riesco più ad usare MongoDB installato sul mio PC.
    Io vorrei poter decidere in fase di test quale DBMS usare.
    Non credo che esista nulla di più semplice che rimuovere la dipendenza embedded.
    Avrei preferito una cosa di questo tipo:
    #qualcosa=MongoDB
    qualcosa=EmbeddedMongoDB
    nel file .properties in modo da togliere ed aggiungere un solo # ma pazienza, se devo leggere 1200 pagine per fare questo lascio perdere, sono solo ad 1/3 del libro, mi conviene andare avanti.

    Altra cosa odiosa ma veramente odiosa è che su MongoDBCompassCommunity devo digitare sempre "test" per cancellare il database "test" ma se sto a vedere tutto resto arenato sul capitolo 9 a vita.
  • Re: Come si sceglie di utilizzare il database MongoDB non embedded in Spring Boot 2?

    giannino1995 ha scritto:


    però poi non riesco più ad usare MongoDB installato sul mio PC.
    In che senso non riesci? Non ho molto tempo per verificare meglio ....

    giannino1995 ha scritto:


    Altra cosa odiosa ma veramente odiosa è che su MongoDBCompassCommunity devo digitare sempre "test" per cancellare il database "test"
    E' solo una sicurezza, un controllo in più. Lo fanno anche altri servizi/software.
  • Re: Come si sceglie di utilizzare il database MongoDB non embedded in Spring Boot 2?

    Se scrivo:
    
    		<dependency>
    			<groupId>de.flapdoodle.embed</groupId>
    			<artifactId>de.flapdoodle.embed.mongo</artifactId>
    			<scope>test</scope>
    		</dependency>
    
    parte sempre la versione embedded di MongoDB, per usare MongoDB installato sul mio PC devo scrivere questo:
    
    		<!--<dependency>
    			<groupId>de.flapdoodle.embed</groupId>
    			<artifactId>de.flapdoodle.embed.mongo</artifactId>
    			<scope>test</scope>
    		</dependency>-->
    
    I due database che uso sul PC, MongoDB ed Embedded MongoDB, per i quali sono presenti le 2 dipendenze nel pom.xml sono entrambe usate in fase di 'test' per cui non comprendo neppure il tuo suggerimento.
  • Re: Come si sceglie di utilizzare il database MongoDB non embedded in Spring Boot 2?

    giannino1995 ha scritto:


    I due database che uso sul PC, MongoDB ed Embedded MongoDB, per i quali sono presenti le 2 dipendenze nel pom.xml sono entrambe usate in fase di 'test' per cui non comprendo neppure il tuo suggerimento.
    Ecco la mia dimostrazione:

    pom.xml
    Ho usato l'ultimissima versione di Spring Boot, 2.3.3.RELEASE. Le dipendenze sono queste:
            <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-test</artifactId>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>de.flapdoodle.embed</groupId>
                <artifactId>de.flapdoodle.embed.mongo</artifactId>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <optional>true</optional>
            </dependency>
    (il devtools in effetti non serve ma ce l'avevo già lì .... male non fa)

    NOTA che de.flapdoodle.embed.mongo ha scope test


    application.properties che contiene SOLO questa riga:
    spring.data.mongodb.uri = mongodb://localhost:27017/mongodemo
    @SpringBootApplication
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    import org.bson.types.ObjectId;
    import org.springframework.data.mongodb.core.mapping.Document;
    
    @Document(collection = "infos")
    public class Info {
        private ObjectId id;
        private String message;
    
        // ... getter/setter .... (omessi solo per BREVITÀ)
    
        @Override
        public String toString() {
            return "Info[id=" + id + ", message=" + message + "]";
        }
    }
    @Controller
    public class ProvaMongoController {
        private static final Logger logger = LoggerFactory.getLogger(ProvaMongoController.class);
    
        @Autowired
        private MongoTemplate mongoTemplate;
    
        @GetMapping(value = "/infos", produces = "text/plain")
        public @ResponseBody String infos() {
            Document result = mongoTemplate.getDb().runCommand(new Document("buildInfo", 1));
            logger.info("DB INFO -----> {}", result);
    
            Info info = new Info();
            info.setMessage("Messaggio da ProvaMongoController al " + new Date());
            mongoTemplate.save(info);
    
            List<Info> infos = mongoTemplate.findAll(Info.class);
    
            return infos.stream().map(Info::toString).collect(Collectors.joining("\r\n"));
        }
    }
    E infine una classe di test.
    @RunWith(SpringRunner.class)
    @DataMongoTest
    public class MongoTest {
        private static final Logger logger = LoggerFactory.getLogger(MongoTest.class);
    
        @Autowired
        private MongoTemplate mongoTemplate;
    
        @Test
        public void testInfos() {
            Document result = mongoTemplate.getDb().runCommand(new Document("buildInfo", 1));
            logger.info("DB INFO -----> {}", result);
    
            Info info = new Info();
            info.setMessage("Messaggio da MongoTest al " + new Date());
            mongoTemplate.save(info);
    
            List<Info> infos = mongoTemplate.findAll(Info.class);
    
            assertThat(infos).hasSize(1);
            assertThat(infos.get(0).getMessage()).startsWith("Messaggio da MongoTest");
        }
    }

    RUN APPLICAZIONE:

    Se avvio l'applicazione da Eclipse (o da prompt con mvn spring-boot:run), quello che ottengo e vedo è:

    a) ad ogni chiamata a http://localhost:8080/infos la lista di Info aumenta e da browser vedo es.:

    Info[id=5f3d39ff8acf2b0985c1883d, message=Messaggio da ProvaMongoController al Wed Aug 19 16:41:03 CEST 2020]
    Info[id=5f3d3a118acf2b0985c1883e, message=Messaggio da ProvaMongoController al Wed Aug 19 16:41:21 CEST 2020]
    Info[id=5f3d3a5778e0794fad18fcfb, message=Messaggio da ProvaMongoController al Wed Aug 19 16:42:31 CEST 2020]
    Info[id=5f3d3a8d78e0794fad18fcfc, message=Messaggio da ProvaMongoController al Wed Aug 19 16:43:25 CEST 2020]
    Info[id=5f3d3ae865ee9f11766cf557, message=Messaggio da ProvaMongoController al Wed Aug 19 16:44:56 CEST 2020]

    Anche se termino e poi riavvio la applicazione. E i dati li VEDO dal MongoDB Compass nel db "mongodemo", collection "infos".

    b) Nel log vedo il mio info

    DB INFO -----> Document{{version=4.2.0, gitVersion=a4b751dcf51dd249c5865812b390cfd1c0129c30, targetMinOS=Windows 7/Windows Server 2008 R2, modules= blablablabla.......

    Questo MongoDB 4.2.0 è il MIO MongoDB che io avevo a suo tempo INSTALLATO sul mio pc.


    RUN TEST:

    Se avvio i test da Eclipse (o da prompt con mvn test), quello che ottengo e vedo è:

    a) Il test passa con successo (i due assert sono validi). Perché usa il MongoDB "in memory" tirato dentro dal de.flapdoodle. Con quel metodo di test viene inserito un solo documento Info, poi terminato il testing il MongoDB "sparisce". Il test si può quindi eseguire quante volte si vuole (perché i dati NON sono "persistenti"!).

    b) Nel log vedo il mio info

    DB INFO -----> Document{{version=3.5.5, gitVersion=98515c812b6fa893613f063dae568ff8319cbfbd, targetMinOS=Windows Vista/Windows Server 2008, modules= blablablabla.......

    Questo NON è il mio MongoDB che ho installato!!! Questo è il MongoDB "in memory" tirato dentro dal de.flapdoodle!


    ATTENZIONE: se tolgo <scope>test</scope> dalla dependency de.flapdoodle.embed.mongo succede che il MongoDB "in memory" viene usato per i test E per l'avvio normale della applicazione. Potrebbe essere quello che si voleva .. o non si voleva (per me no, NON è quello che intenderei/vorrei io).
    Con il risultato che ad ogni avvio della applicazione, la collection infos è sempre vuota (e non tocca minimamente la infos sul mio "vero" MongoDB).


    CONCLUSIONE: ti chiederei cosa ti risulta chiaro. Ma forse è meglio il contrario: cosa NON ti è chiaro in tutto questo?
  • Re: Come si sceglie di utilizzare il database MongoDB non embedded in Spring Boot 2?

    Ok su tutto ma la mia intenzione era scegliere in fase di test quale DB avviare, tu usi mvn spring-boot:run e mvn test mentre io uso solo mvn test che sul mio IntelliJ si traduce in tasto destro + "Run 'All Tests'".
    Preferisco procedere con la lettura, mi accontento di editare la dipendenza.
    Grazie per tutto, come al solito sei più che disponibile.
    Il successo di questo forum e dovuto alla tua perseveranza.
Devi accedere o registrarti per scrivere nel forum
10 risposte