140. Spring Boot con Mongodb¶
Algunos puntos a tener en cuenta con MongoDB
- Dado que MongoDB es una base de datos basada en documentos sin relaciones integradas como las que encontrarías en una base de datos relacional, debes gestionar manualmente estas relaciones en tu código.
140.1 Configuración del enlace : application.yml¶
Esta configuración sirve de ejemplo:
spring:
data:
mongodb:
authentication-database: admin
username: root
password: example
database: transporte
port: 27017
host: localhost
140.2 Entidades para el ejemplo¶
@Document("restaurants")
data class Restaurant(
@Id
val id: ObjectId = ObjectId(),
val address: Address = Address(),
val borough: String = "",
val cuisine: String = "",
val grades: List<Grade> = emptyList(),
val name: String = "",
@Field("restaurant_id")
val restaurantId: String = ""
)
data class Address(
val building: String = "",
val street: String = "",
val zipcode: String = "",
@Field("coord")
val coordinate: List<Double> = emptyList()
)
data class Grade(
val date: Date = Date(),
@Field("grade")
val rating: String = "",
val score: Int = 0
)
@Document
: This declares that this data class represents a document in Atlas.
* @Field: This is used to define an alias name for a property in the document, like coord for coordinate in Address model.
140.3 Operación de lectura¶
@RestController
@RequestMapping("/restaurants")
class Controller(@Autowired val repo: Repo) {
@GetMapping
fun getCount(): Int {
return repo.findAll().count()
}
}
@Id
( es un campo más)
Podemos declarar la búsqueda por este campo
interface Repo : MongoRepository<Restaurant, String> {
fun findByRestaurantId(restaurantId: String): Restaurant?
}
(Obervacion: el campo se llama restauranId, que se añade como se muestra en la sentencia anterior)
140.4 Operación de escritura:¶
val restaurant = Restaurant().copy(name = "sample", restaurantId = "33332")
repo.insert(restaurant)
140.5 Operacion de actualización¶
MongoDB no tiene una operación Update
por lo que se hace una lectura y escritura
@PatchMapping("/{id}")
fun updateRestaurant(@PathVariable("id") id: String): Restaurant? {
return repo.findByRestaurantId(restaurantId = id)?.let {
repo.save(it.copy(name = "Update"))
}
}
140.6 Borrado¶
@DeleteMapping("/{id}")
fun deleteRestaurant(@PathVariable("id") id: String) {
repo.findByRestaurantId(id)?.let {
repo.delete(it)
}
}
140.7 Modelo de relaciones entre documentos¶
140.7.1 Relación one-to-one¶
Usamos un modelo de datos embebido en otro. Por ejemplo:
// patron document
{
_id: "joe",
name: "Joe Bookreader"
}
// address document
{
street: "123 Fake Street",
city: "Faketon",
state: "MA",
zip: "12345"
}
// Lo usamos :
{
_id: "joe",
name: "Joe Bookreader",
address: {
street: "123 Fake Street",
city: "Faketon",
state: "MA",
zip: "12345"
}
}
140.7.2 Relación One-to-Many¶
// patron document
{
_id: "joe",
name: "Joe Bookreader"
}
// address one
{
street: "123 Fake Street",
city: "Faketon",
state: "MA",
zip: "12345"
}
// address two
{
street: "1 Some Other Street",
city: "Boston",
state: "MA",
zip: "12345"
}
// de forma embebida:
{
"_id": "joe",
"name": "Joe Bookreader",
"addresses": [
{
"street": "123 Fake Street",
"city": "Faketon",
"state": "MA",
"zip": "12345"
},
{
"street": "1 Some Other Street",
"city": "Boston",
"state": "MA",
"zip": "12345"
}
]
}
140.7.3 Modelo One-to-Manu con referencias en documentos.¶
{
name: "O'Reilly Media",
founded: 1980,
location: "CA",
books: [123456789, 234567890, ...]
}
{
_id: 123456789,
title: "MongoDB: The Definitive Guide",
author: [ "Kristina Chodorow", "Mike Dirolf" ],
published_date: ISODate("2010-09-24"),
pages: 216,
language: "English"
}
{
_id: 234567890,
title: "50 Tips and Tricks for MongoDB Developer",
author: "Kristina Chodorow",
published_date: ISODate("2011-05-06"),
pages: 68,
language: "English"
}
140.8 Modelo de relacion spring-data-mongodb¶
Usando @DocumentReference
class Publisher {
// …
@DocumentReference
List<Book> books;
}
{
…
"books" : ["617cfb", … ]
}
La relación inversa entre Book y Plublisher se puede modelar de la misma forma.
class Book {
// …
@DocumentReference(lazy=true)
private Publisher publisher;
}
Observación: Cuando se añade un nuevo Book se debe añadir también a la lista de books en Publisher para establecer el enlace. Y para asegurar la atomizidad de la operación debemos usar Transaciones
140.9 One-To-Many¶
Es posible guardar la relación solamente en el documento Book sin insertar la invera en Publisher.
Para esto necesitamos dos cosas: indicar a la capa de mapping que omita guardar el enlace en Publisher y actualizar la consulta de busqueda cada vez que se recupere Books.
La primera parte se consigue con la anotación en lapropiedad books de Publisher: @ReadOnlyPorperty
y la segunda añadiendo el atributo lookup
en la anotación @DocumentReference
con una consulta personalizada:
class Publisher {
// …
@ReadOnlyProperty
@DocumentReference(lookup="{'publisher':?#{#self._id} }")
List<Book> books;
}
-
lazy = true: Este atributo indica que la carga de la referencia es perezosa (lazy loading). Es decir, el documento referenciado (Customer en este caso) no se cargará desde la base de datos hasta que sea explícitamente accedido. Esto puede mejorar el rendimiento al evitar cargas innecesarias de datos, especialmente cuando no se necesitan todos los detalles del documento referenciado de inmediato.
-
lookup = "{ 'primaryAddress' : ?#{#self._id} }": La sintaxis de lookup utiliza la notación de consulta de MongoDB. Por ejemplo, lookup="{'fieldName': ?#{#self.fieldName}}" define una consulta donde fieldName en el documento referenciado debe coincidir con el valor de fieldName en el documento actual (#self hace referencia al documento actual). Esto significa que para cada Publisher, la lista books se llenará con documentos Book cuyo campo publisher coincida con el _id del Publisher en cuestión. La consulta dentro de lookup especifica este comportamiento, permitiendo una forma flexible de referenciar documentos relacionados.
-
@ReadOnlyProperty: Esta anotación indica que la propiedad customer no debe ser considerada en las operaciones de guardado o actualización del documento Address. En otras palabras, aunque tengas una referencia al Customer en la clase Address, cualquier cambio hecho a customer no se reflejará en la base de datos cuando guardes un objeto Address. Esto asegura la integridad de los datos al mantener la relación de referencia unidireccional desde Address hacia Customer sin modificar el documento Customer al guardar Address.
¿Como sería el código de actualización?
-
Paso 1: Crear o Actualizar Publisher
Al crear o actualizar un Publisher, después de guardar el Publisher en la base de datos, necesitarás asegurarte de que todos los Book relevantes estén correctamente asociados con este Publisher. -
Paso 2: Actualizar la Relación Inversa en Book
Para cada libro que deba estar asociado con el Publisher, debes actualizar su referencia a publisher para asegurarte de que apunte al Publisher correcto. Esto podría implicar buscar todos los libros que deberían estar asociados con este Publisher y actualizarlos individualmente.
Primero creamos publisher, puede que no tengamos aun libros.
A continuación insertamos book con su relación al publisherid
val publisher = PublisherWithoutBooks(name = "Editorial XYZ")
val savedPublisher = publisherRepository.save(publisher)
val book = Book(title = "El Gran Libro de Kotlin", publisherId = savedPublisher.id!!)
val savedBook = bookRepository.save(book)
140.10 Many-to-Many¶
Se crean colecciones en cada entidad implicada sin necesidad de tablas intermedias
140.11 Kotlin y MongoDB¶
ver tutorial
140.12 Fundamentos de MongoDB¶
140.12.1 BASES DE datos y colecciones¶
Los datos se organizan en MongoDB:
- Databaes: raíz en la organización
- Colecciones: las bases de datos se organican en colecciones que contenen Documentos
- Documentos: Contienen datos (enteros, string, etc)
140.13 NOTAS¶
- id en Mongo es un ObjectId que podemos representar por un string de 12 caracteres
- Para operaciones financieras, mejor "Bigdecimal" que Double
140.14 Apendice¶
Enlaces * Directo de Mongodb *