Ceci est une réimpression du README de ce référentiel.
Ceci est un exemple d'application métier qui utilise Spring Boot côté serveur et AngularJS côté client pour accéder au backend MongoDB.
La base de données contient une collection de marques, de modèles, de voitures et de performances de vente. Il a la capacité d'ajouter, de mettre à jour, de supprimer et de rechercher ces collections, ainsi que de représenter graphiquement les performances des ventes.
Ce document est une note sur les thèmes techniques clés nécessaires pour comprendre l'application. Pour accélérer votre compréhension, c'est une bonne idée de parcourir les didacticiels de la page de guide de Spring (https://spring.io/guides).
*Mise en garde Toutes les fonctionnalités ne fonctionnent pas correctement pour le moment car ce projet n'a pas fini de développer des fonctionnalités. *
Installez MongoDB et démarrez mongod à l'avance. Laissez la fonction d'authentification mongodb non configurée. (Il est dans un état où il est installé et démarré sans rien paramétrer)
Git clone ce projet. Créez un répertoire approprié et exécutez la commande suivante dans ce répertoire.
$ git clone https://github.com/kazz12211/simple-mongo-crud-app.git
Exécutez la commande suivante dans le même répertoire pour démarrer l'application.
$ ./mvnw spring-boot:run
Accédez à l'URL suivante depuis votre navigateur.
http://localhost:8080
Cette application est un projet Spring Boot Maven. Voir pom.xml pour les bibliothèques dépendantes.
RestController
Générez RestController avec l'annotation @RestController. Dans les applications CRUD, il semble courant de mapper respectivement les insertions, les mises à jour, les suppressions et les recherches de données sur POST, PUT, DELETE et GET des requêtes HTTP.
package jp.tsubakicraft.mongocrud.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import jp.tsubakicraft.mongocrud.model.Brand;
import jp.tsubakicraft.mongocrud.service.BrandRepository;
@RestController
public class BrandController {
@Autowired
private BrandRepository repo;
@RequestMapping(value = "/api/brands/listAll", method = RequestMethod.GET)
public List<Brand> listAll() {
Sort sort = new Sort(Sort.Direction.ASC, "name");
return repo.findAll(sort);
}
@RequestMapping(value = "/api/brands", method = RequestMethod.GET)
public Page<?> listBrands(@RequestParam(value = "page", required = true) int page,
@RequestParam(value = "limit", required = true) int limit,
@RequestParam(value = "sortColumn", required = true) String column,
@RequestParam(value = "sortDir", required = true) String dir) {
Sort sort = new Sort(
new Sort.Order("asc".equalsIgnoreCase(dir) ? Sort.Direction.ASC : Sort.Direction.DESC, column));
Pageable pageRequest = new PageRequest(page, limit, sort);
Page<Brand> p = repo.findAll(pageRequest);
return p;
}
@RequestMapping(value = "/api/brands", method = RequestMethod.PUT)
public Brand updateBrand(@RequestBody Brand brand) {
Brand b = repo.findOne(brand.id);
if (b != null) {
b.name = brand.name;
repo.save(b);
}
return b;
}
@RequestMapping(value = "/api/brands", method = RequestMethod.DELETE)
public Brand deleteBrand(@RequestBody Brand brand) {
repo.delete(brand.id);
return brand;
}
@RequestMapping(value = "/api/brands", method = RequestMethod.POST)
public Brand createBrand(@RequestBody Brand brand) {
Brand b = new Brand();
b.name = brand.name;
repo.save(b);
return b;
}
}
Cette application implémente la fonction de nation de page en utilisant ui-bootstrap dans l'interface utilisateur, mais pour rechercher des objets page par page, utilisez PageRequest. Par exemple, pour rechercher les objets de 11e à 20e marque, appelez findAll () de PagingAndSortingRepository avec PageRequest comme argument comme suit.
int page = 1;
int size = 10;
Pageable pageRequest = new PageRequest(page, size);
Page<Brand> page = repo.findAll(pageRequest);
package jp.tsubakicraft.mongocrud.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ErrorHandler {
@RequestMapping(value = "/{[path:[^\\.]*}")
public String redirect() {
return "forward:/";
}
}
in app.js
var app = angular.module("app", ['ngRoute', 'ngDialog', 'ui.bootstrap', 'chart.js']);
app.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when("/", {
controller: 'home_controller',
templateUrl: 'views/home.html'
})
.when("/brand/", {
controller: 'brand_controller',
templateUrl: 'views/brands.html'
})
.when("/newbrand/", {
controller: 'brand_controller',
templateUrl: 'views/newBrand.html'
})
.when("/model/", {
controller: 'model_controller',
templateUrl: 'views/models.html'
})
.when("/newmodel/", {
controller: 'model_controller',
templateUrl: 'views/newModel.html'
})
.when("/car/", {
controller: 'car_controller',
templateUrl: 'views/cars.html'
})
.when("/newcar/", {
controller: 'car_controller',
templateUrl: 'views/newCar.html'
})
.when("/sales/", {
controller: 'sales_controller',
templateUrl: 'views/sales.html'
})
.when("/newsales/", {
controller: 'sales_controller',
templateUrl: 'views/newSales.html'
})
.otherwise({
redirectTo: "/"
});
}]);
app.config(['$locationProvider', function($locationProvider) {
$locationProvider.html5Mode(true);
}]);
Par exemple, pour faire une requête GET à la racine / api / Brands de BrandController (REST Controller), utilisez $ http.get (). (Voir brand_controller.js)
app.controller("brand_controller", function($scope, $http, $location, $q, ngDialog) {
$scope.brands = [];
....
....
$scope.listBrands = function() {
$http.get("/api/brands", {params: {page: $scope.page, limit: $scope.limit, sortColumn: $scope.sortColumn, sortDir: $scope.sortDir}}).then(function(response) {
//J'ai pu recevoir les données normalement
$scope.brands = response.data;
....
....
}, function(error) {
//La requête HTTP a échoué
....
});
};
....
....
$scope.listBrands();
});
La validation peut être effectuée dans le modèle HTML, mais dans cette application, elle est effectuée dans le contrôleur. (Voir brand_controller.js)
....
....
$scope.createBrand = function() {
if(!$scope.validateForm()) {
$http.post("/api/brands", $scope.brand).then(function(response) {
$scope.show = true;
$scope.hide = true;
$scope.hideObj = false;
$scope.showObj = false;
$scope.brandId = "";
$location.path("/brand");
}, function(error) {
$scope.error = error;
});
}
};
....
....
$scope.validateForm = function() {
$scope.validationMessages = [];
if($scope.brand.name == null || $scope.brand.name == null) {
$scope.validationMessages.push("Name is required.");
}
$scope.hasErrors = $scope.validationMessages.length > 0;
return $scope.hasErrors;
};
Du côté du modèle HTML, préparez un bloc à afficher en cas d'erreur (hasErrors of $ scope est true).
<div class="panel panel-default">
<div class="panel-heading">ADD A BRAND</div>
<form name="brand-form">
<div ng-show="hasErrors">
<div class="alert alert-danger" role="alert">
<div ng-repeat="message in validationMessages">
<strong>{{message}}</strong></br/>
</div>
</div>
</div>
<div class="form-group">
<label for="brand-name">Name</label> <input name="brand-name"
type="text" class="form-control" ng-model="brand.name" required>
</div>
<button class="btn btn-primary" type="submit" ng-click="createBrand()">
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
Save
</button>
<button class="btn btn-default" ng-click="linkTo('/brand/')">
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
Cancel
</button>
</form>
</div>
La page ui-bootstrap (https://angular-ui.github.io/bootstrap/) explique comment procéder.
Code côté contrôleur. Spécifiez le nom de la colonne à trier et la méthode de tri. (Pour cette application, il existe deux méthodes de tri, ASC et DESC) La fonction sort () est appelée à partir du modèle HTML avec le nom de la colonne comme argument. Si la colonne passée en argument est la même que la colonne précédente, basculez entre l'ordre croissant et décroissant, et si la colonne est différente de la précédente, définissez-la dans l'ordre croissant et mettez à jour le nom de la colonne. Enfin, recherchez à nouveau la base de données. (Comme il s'agit d'une recherche paginée, le trafic vers la base de données généré par une recherche est faible, nous sommes donc en train de relancer la recherche, mais si le nombre de recherches est important, il peut être préférable de trier en mémoire)
app.controller("model_controller", function($scope, $http, $location, $q, ngDialog) {
....
....
$scope.sortColumn = "name";
$scope.sortDir = "ASC";
....
....
$scope.sort = function(column) {
if($scope.sortColumn == column) {
$scope.sortDir = $scope.sortDir == "ASC" ? "DESC" : "ASC";
} else {
$scope.sortDir = "ASC";
$scope.sortColumn = column;
}
$scope.listModels();
};
});
Du côté du modèle HTML, spécifiez la fonction de contrôleur à exécuter lorsque le clic de souris est effectué sur l'en-tête de colonne.
<th class="col-xs-3 col-ms-3 col-md-3 col-lg-4 sortableTableColumn" ng-click="sort('name')">Name</th>
Si vous utilisez une bibliothèque telle que DataTable.js, qui a des fonctions telles que la pagination et le tri des données affichées dans un tableau, cela peut être fait plus facilement, donc lors du développement d'une application. Veuillez considérer.
Ce n'est pas limité au CSS Bootstrap, mais c'est un moyen de remplacer le CSS. Le code suivant remplace le style de la barre de navigation Bootstrap. Définissez la partie du fichier HTML qui charge le CSS à charger après le CSS Bootstrap.
.navbar {
margin-bottom: 1px;
border-radius: 0px;
}
.navbar-inverse {
background-color: rgb(12, 140, 213);
border-color: rgb(12,140,213);
}
.navbar-inverse .navbar-brand {
color: #fff;
}
.navbar-inverse .navbar-nav>li>a {
color: #fff;
}
MongoRepository est une sous-interface de PagingAndSortingRepository qui a des fonctions d'insertion, de mise à jour, de suppression, de recherche et de pagination de données.
Code qui définit la sélection de base de données MongoDB.
package jp.tsubakicraft.mongocrud.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
import com.mongodb.Mongo;
import com.mongodb.MongoClient;
@Configuration
public class MongoConfiguration extends AbstractMongoConfiguration {
@Override
protected String getDatabaseName() {
return "simplecrud";
}
@Override
public Mongo mongo() throws Exception {
return new MongoClient("127.0.0.1", 27017);
}
}
Exemples d'entités de marque et de modèle. L'entité Model fait référence à l'entité Brand avec l'annotation @DBRef. Dans ce cas, la recherche de Modèle recherchera et combinera également les Marques référencées.
jp.tsubakicraft.mongocrud.model.Brand.java
package jp.tsubakicraft.mongocrud.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection="brand")
public class Brand {
@Id public String id;
public String name;
public Brand(String id) {
this.id = id;
}
public Brand() {
}
}
jp.tsubakicraft.mongocrud.model.Model.java
package jp.tsubakicraft.mongocrud.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection="model")
public class Model {
@Id public String id;
public String name;
@DBRef public Brand brand;
public Model() {
}
public Model(String id) {
this.id = id;
}
}
Comment spécifier les propriétés DBRef dans les critères de recherche.
@Query(value="{brand.$id : ?0}")
public List<Model> findByBrandId(ObjectId brandId);
Comment obtenir le nombre de collections. Définissez count = true comme paramètre de l'annotation @Query.
@Query(value="{brand.$id : ?0}", count=true)
public Long countBrandModel(ObjectId brandId);
Appelez la méthode MongoRepository à l'aide de PageRequest.
@Autowired
private BrandRepository repo;
...
@RequestMapping(value="/api/brands", method=RequestMethod.GET)
public Page<Brand> listBrands(@RequestParam(value="page", required=true) int page, @RequestParam(value="limit", required=true) int limit) {
Pageable pageRequest = new PageRequest(page, limit);
return repo.findAll(pageRequest);
}