coding
Sam Lee的gravatar头像
Sam Lee2016-08-22 16:41:09

Spring Boot1.3.6 Angularjs1.4.0 Mongodb2.3.6 CRUD汽车搜索及增加修改程式

一个实用的编程例子,让读者认真地体会本文的要点。

  • 传统学习编程技术落后,应跟著潮流,要对业务聚焦处理。
  • 要Jar, 不要War;以小为主,以简为宝,集堆而成。
  • 去繁取简 Spring Boot,明日之春。
  • 集堆综合技术如 jHipster 是必然的软件开发途径
  • 前后端分离技术,跨域资源共享( CORS)。

  以一个 Spring Boot + Angular UI-Route + Mongodb CRUD学习程式资料. 笔者要用最简单又有效的编程方法来实践“ 以小为主,以简为宝 ” 的原则。首先什么是小呢?程式可分成前后端分开处理,都是以 MVC 模式安排,加上用跨域资源共享( CORS)来分成前后端的开发。换句话说,前后端各自开发,祇有在程式运作时才有直接数据来往。

后端的开发:Spring Boot + mongoDB + H2

  1. 打开你常用的浏览器, 在 URL 输入https://stat.spring.io
  2. 在页面上输入

               Maven Project: Spring Boot 1.3.7.

          Group:  com.emo.product

          Artifact: product-server

          Selected Dependencies: Web, MongoDb, H2。

Spring Boot1.3.6 Angularjs1.4.0 Mongodb2.3.6 CRUD汽车搜索及增加修改程式

                                                          图1:自动生成 Spring Boot 骨架项目

 

   然后按一下 Generate Project 按钮,就是自动下载一个压缩 zip 档案,即是 product-server.zip。把该 zip 档案放在适当位置,再解压。在解压后的目录里有 src, .setting, .mvn 及 POM.xml. 打开 pom.xml 会见到这里祇用了3个依赖包 (Web, MongoDb, H2)而已,而其他 Jar 包会自动下载的。该网页 (SPRING INITIALIZR) 就是能够提供 Spring Boot 的项目骨架,最重要的 POM.xml 的程式依赖部署档案。本程式采用了记忆体的资料库 H2, 方便数据资料修改及储存到硬盘上的MonogoDB。

  3. 为了容易修改程式文档,当然要输入你所常用的 IDE,例如 Eclipse. 笔者使用的是 STS 3.8.0 RELEASE. 现在你祇有骨架,没有真实的 Java 类,业务,接口等的文档。正是要运用你的 Java 技术了。笔者与人方便,为你提供了汽车搜索及增加修改程式。并附上 MongoDB  资料库档案。 

Spring Boot1.3.6 Angularjs1.4.0 Mongodb2.3.6 CRUD汽车搜索及增加修改程式

                                        图2:Product Server Spring Boot项目Java架构

  你可以按自己的方式来编制你的Java代码及其有关的类,接口和业务的应用。也可以参考本文的代码。

Spring Boot1.3.6 Angularjs1.4.0 Mongodb2.3.6 CRUD汽车搜索及增加修改程式

                                          图3:Product Server Java 所需的程式名称

首先是最重要的程式依赖档

Product-server/POM.xml.

<?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/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>com.emo.product</groupId>

    <artifactId>product-server</artifactId>

    <version>0.0.1-SNAPSHOT</version>

    <packaging>jar</packaging> 

    <name>car-server</name>

    <description>Spring Boot Server</description> 

    <parent>

       <groupId>org.springframework.boot</groupId>

       <artifactId>spring-boot-starter-parent</artifactId>

       <version>1.3.6.RELEASE</version>

       <relativePath/> <!-- lookup parent from repository -->

    </parent>

    <properties>

       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

       <java.version>1.8</java.version>

    </properties>

    <dependencies>

       <dependency>

           <groupId>org.springframework.boot</groupId>

           <artifactId>spring-boot-starter-data-mongodb</artifactId>

       </dependency>

       <dependency>

           <groupId>org.springframework.boot</groupId>

           <artifactId>spring-boot-starter-web</artifactId>

       </dependency>       

       <dependency>

           <groupId>org.springframework.boot</groupId>

           <artifactId>spring-boot-starter-test</artifactId>

           <scope>test</scope>

       </dependency>

    </dependencies>   

    <build>

       <plugins>

           <plugin>

              <groupId>org.springframework.boot</groupId>

              <artifactId>spring-boot-maven-plugin</artifactId>

           </plugin>

       </plugins>

    </build>   

</project>

App.java 典型的Spring Boot 起动程式。

package com.emo.product;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication

@EnableAsync

public class app {   

    public static void main(String[] args) {

       SpringApplication.run(app.class, args);

    }

    public void run() {

    }

}

SimpleCORSFilter.java  跨域资源共享的过滤器

package com.emo.product.config;

 import java.io.IOException;

 import javax.servlet.Filter;

import javax.servlet.FilterChain;

import javax.servlet.FilterConfig;

import javax.servlet.ServletException;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.springframework.http.HttpMethod;

import org.springframework.http.HttpStatus;

import org.springframework.stereotype.Service; 

@Service

public class SimpleCORSFilter implements Filter {   

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {     

       HttpServletResponse response = (HttpServletResponse) res;

       HttpServletRequest request = (HttpServletRequest) req;  

       response.setHeader("Access-Control-Allow-Origin", "*");

       response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");

       response.setHeader("Access-Control-Max-Age", "3600");

       response.setHeader("Access-Control-Allow-Headers", "Origin,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization");

       response.setHeader("Access-Control-Allow-Credentials", "true");

       if(request.getMethod().equals(HttpMethod.OPTIONS.name())){

           response.setStatus(HttpStatus.NO_CONTENT.value());

       }else{

           chain.doFilter(req, res);

       }

    }

    public void init(FilterConfig filterConfig) {}

    public void destroy() {} 

}

CarController.java 汽车控制器,留意程式尽量使用现成的mongoRepository的资料库接口,祇有 update 和 search 才做业务上的运作。因为mongoRepository 没有直接提供现成的接口。少一句代码就简化了一度步骤,减少一个机会犯人为错误的。 

package com.emo.product.controllers;

import java.util.List; 

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.ui.Model;

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.RestController; 

import com.emo.product.model.Car;

import com.emo.product.repository.CarSearchRepository;

import com.emo.product.repository.CarRepository;

import com.emo.product.service.CarService;

@RestController

@RequestMapping(value="/api")

public class CarController {  

    @Autowired

    CarRepository carRepository; 

    @Autowired

    CarSearchRepository carSearchRepository;  

    @Autowired

    private CarService carService;  

    /**

     * Save car to database

     */

    @RequestMapping(value="/save",method=RequestMethod.POST)

    public String saveCar(@RequestBody Car car) {

       carRepository.save(car);

       return "success";

    } 

    @RequestMapping(value="/getListOfCar",method=RequestMethod.GET)

    public List<Car> getListOfCar() {

       return carRepository.findAll();

    }   

    @RequestMapping("/home")

    public String home(Model model) {

       model.addAttribute("List", carRepository.findAll());

       return "home";

    }   

    @RequestMapping(value="/update",method=RequestMethod.POST)

    public void updateCar(@RequestBody Car car) {

       carService.update(car);

    }   

    @RequestMapping(value = "/search",method=RequestMethod.POST)

    public Car search(@RequestBody String search) {

       Car car = carService.search(search);

       carService.update(car);

       return car;

    }    

    @RequestMapping(value="/delete", method=RequestMethod.POST)

    public void removeCar(@RequestBody Car car) {

       carRepository.delete(car);

    }

}

CarService.java 十分简单的服务

package com.emo.product.service;

import com.emo.product.model.Car;

public interface CarService {

    //update car

    public void update(Car car);   

    //search Car.

    public Car search(String text); 

CarServiceImpl.java 实验服务

package com.emo.product.service;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import com.emo.product.model.Car;

import com.emo.product.repository.CarRepository;

import com.emo.product.repository.CarSearchRepository;

@Service

public class CarServiceImpl implements CarService {  

    @Autowired

    private CarRepository carRegistory;   

    @Autowired

    private CarSearchRepository searchRegistory;  

    @Override

    public  Car search(String text) {

       return searchRegistory.searchCars(text);

    }

    @Override

    public void update(Car car) {

       carRegistory.save(car);

    }

}

CarRepository.java  mongoDB 的接口

package com.emo.product.repository; 

import java.util.List;

import org.springframework.data.mongodb.repository.MongoRepository;

import org.springframework.stereotype.Repository;

import com.emo.product.model.Car;

@Repository

public interface CarRepository extends MongoRepository<Car, String> {

    public List<Car> findAll();

    public Car findOne(String id);

    @SuppressWarnings("unchecked")

    public Car save(Car car);

    public void delete(Car car);

}

 

CarSearchRepository.java 利用mongoTemplate 来查询资料库

package com.emo.product.repository; 

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.data.mongodb.core.MongoTemplate;

import org.springframework.data.mongodb.core.query.Criteria;

import org.springframework.data.mongodb.core.query.Query;

import org.springframework.stereotype.Repository; 

import com.emo.product.model.Car;

@Repository

public class CarSearchRepository {

    @Autowired

    MongoTemplate mongoTemplate;

    public Car searchCars(String text) {

       Car car = mongoTemplate.findOne(Query.query(new Criteria()

                     .orOperator(Criteria.where("description").regex(text, "i"),

                                Criteria.where("make").regex(text, "i"),

                                Criteria.where("model").regex(text, "i"))

                     ), Car.class);

        return car;     

    }

}

application.properties最后是设置mongoDB 的 Host 和 Port.

spring.data.mongodb.database=car_dealer

spring.data.mongodb.host=localhost

spring.data.mongodb.port=27017 

  上面就是汽车搜索及增加修改资料程式,十分简洁。不过,Spring Boot 版本 1.4.0在 Windows 10会出错的; 而在 Mac 机上相安无事。读者请留意:本服务程式没有使用 DAO,又没有 JSP ! 制作风险都是与网络顺畅有关的,有时因本机的 jar 包出现问题,要重新下载的,可惜网络不一定给面子, 无奈! 

前端的开发: Angular + UI-Route

  开始处理所有有关  Java 的程式都是要考虑需要那些依赖包及其版本,AngularJS 也不例外。根据前端程式需要,它多是存在 Bower.json 档的。该档案是记录著该 AngularJS 程序所需要的各类 Jar 包。如果丢失了某个包,可以重新下载的。功能有点像服务程式中的 POM.xml. 

Bower.json 

{

  "name": "angular-auth",

  "version": "0.1",

  "authors": [

    "sam8881"

  ],

  "moduleType": [

    "amd"

  ],

  "license": "MIT",

  "dependencies": {

    "angular-ui-router": "~0.2.15",

    "angular-ui": "~0.4.0",

    "angular": "~1.4.0",

    "jQuery": "~2.1.4",

    "angular-cookies": "~1.4.0"

  }

}

注意: 不要随便修改该档,很容易出错的。必须用 Json 工具。 

静态程式如 AngularJS 不方便在 Eclipse 或 STS 内操作(可以处理的,不过是烦了一点),所以笔者喜欢用 Intelli J Idea 直接打开目录就可以看到全部的档案了。

Spring Boot1.3.6 Angularjs1.4.0 Mongodb2.3.6 CRUD汽车搜索及增加修改程式

                                      图4:Car-Client 所需的AngularJS 架构及其程式名称

 

  前端程式也很简单,重要的代码文档祇有4个,其他一般读者都会明白的。以下就解释一下这几个档案。

Index.hml  起动档案,部署有关的附属工具/包/界面等来保证程式运作正常。

<!DOCTYPE html>
<html>
<head lang="en">
    <title>Angularjs Spring-boot example</title>
    <script src="plugin/angular/angular.js"></script>
    <script src="plugin/angular-ui-router/angular-ui-router.min.js"></script>
    <script src="app/js/app.js"></script>
    <script src="app/js/ProductController.js"></script>
    <style>
        .username.ng-valid {
            background-colorlightgreen;
        }
        .username.ng-dirty.ng-invalid-required {
            background-colorred;
        }
        .username.ng-dirty.ng-invalid-minlength {
            background-coloryellow;
        }
        .email.ng-valid {
            background-colorlightgreen;
        }
        .email.ng-dirty.ng-invalid-required {
            background-colorred;
        }
        .email.ng-dirty.ng-invalid-email {
            background-coloryellow;
        }
    </style>
    <!-- <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css"> -->
    
<link rel="stylesheet" href="plugin/bootstrap-3.3.4-css/bootstrap.min.css">
    <link rel="stylesheet" href="app/css/app.css">
</head>
<body ng-app="springApp">
    <div ui-view></div>
</body>
</html

app.js  - AngularJS 设定有关状况及指令

var springApp angular.module('springApp',['ui.router'])

springApp.config(function($stateProvider, $urlRouterProvider) {

    $urlRouterProvider.otherwise('/home');
    $stateProvider

       .state('home', {

          url:'/home',

            templateUrl:'app/html/present.html',

           controller:'productController'

        });

});

springApp.directive('ngConfirmClick', [function() {

   return{

        link :function(scope, element, attr) {

           varmsg = attr.ngConfirmClick ||"Are you sure?";

           varclickAction = attr.confirmedClick;
 

            element.bind('click',function(event) {

               if(window.confirm(msg)) {

                   scope.$eval(clickAction)

                }

            });

        }

    };

} ])

present.html - 这是界面与用户互动画面。/*** Created by sam8881 on 2016/8/22*<div class="inner"

<div class="generic-container">

         <div class="panel panel-default">

             <div class="panel-heading"><span class="lead">Car Entry Form </span></div>

                <div class="formcontainer">

                <table>

                <div class="row">
                    <div class="form-group col-md-12">

                 <label class="col-md-2 control-lable" for="Car Make">Car Make</label>

                        <div class="col-md-7">

        <input type="text"  ng-model="car.make" class="form-control input-sm" placeholder="Enter the make of the car."/>

                        </div>

                    </div>

                </div>

                <div class="row">

                        <div class="form-group col-md-12">
               <label class="col-md-2 control-lable" for="Car Model">Car Model</label>

                        <div class="col-md-7">

      <input type="text"   ng-model="car.model" class="form-control input-sm" placeholder="Enter the model of the car. "/>

                        </div>

                        </div>

                </div>
             <div class="row">

                   <div class="form-group col-md-12">

  <label class="col-md-2 control-lable" for="Car Description">Car Description</label>

                            <div class="col-md-7">
 <input type="text"  ng-model="car.description" class="form-control input-sm" placeholder="Enter the description of the car. "/>

                        </div>

                       </div>

                </div>
              <div class="row">

                    <div class="form-group col-md-12">

           <label class="col-md-2 control-lable" for="Year of Car">Year of Car</label>

                        <div class="col-md-7">

                    <input type="text"  value="" ng-model="car.year" class="form-control input-sm" placeholder="Enter the year of the car. "/>

                       </div>

                     </div>

                </div>

                <div class="row">

                    <div class="floatRight">
     <input type="submit" name="submit" class="btn btn-primary btn-sm custom-width" value="Submit" ng-click="save();"/>

                           <input type="submit" name="clear" class="btn btn-warning btn-sm custom-width" value="Clear" ng-click="car = null"/>

                    </div>

                </div>
                </table>

            </div>

        </div>

               <div class="panel panel-default">  

                <div class="formcontainer">
             <form ng-init="acar=null" ng-submit="search(acar)" method="POST">
             <div class="row">

             <div class="form-group col-md-12">
             <label class="col-md-2 control-lable" for="Info">Search Information</label>

             <div class="col-md-7">

      <input type="text"  ng-model="acar" class="form-control input-sm" placeholder="Enter search of the car"/>

          </div>

            </div>

            <div class="floatRight"><input type="submit" name="search" class="btn btn-primary btn-sm custom-width" value="Search"/>

 

            <input type="submit" name="clear" class="btn btn-warning btn-sm custom-width" value="Clear" ng-click="acar = null"/>
            </div>

            </div>

            </form>
            </div>

        </div>

            <div class="panel panel-default">

               <!-- Default panel contents -->

                <div class="panel-heading"><span class="lead">List of Cars </span></div>

                <td class="tablecontainer">

                <table class="table table-hover">

                    <thead>

                        <tr>

                            <th>No</th>

                            <th>Car Make</th>
 

                            <th>Car Model</th>

                            <th>Description</th>

                            <th>Car of Year</th>

                        </tr>

                    </thead>

                    <tbody>

                        <tr ng-repeat="car in cars">

                            <td>{{$index+ 1}}</td>

                            <td>{{car.make}}</td>

                            <td>{{car.model}}</td>
                           <td>{{car.description}}</td>

                            <td>{{car.year}}</td>

                            <td>

                                <div class="floatRight">

                            <button type="button" ng-click="updateCar(car);" class="btn btn-success custom-width">Edit</button>

                            <button type="button" confirmed-click="deleteCar(car);" ng-confirm-click=" Do you want to delete this car?" class="btn btn-danger custom-width">Delete</button>

                            </div>

                            </td>

                        </tr>

                    </tbody>

                </table>

                </td>

            </div>

            </div>

            </div>

</div>

CarController.js这是前端程式核心机能,通过运用 $http api,负责和后台接收及输送资讯,这样才可以和服务器完全分离开发。前端工作者可以自由处理画面上的图示和表格,只要数据是正确,其他因素和后端没有什么关系。 /** * Created by sam8881 on 2016/8/22 */(function() {

  varproductController =function($scope,$http) {
        vargetListOfCar =function() {

         varresponse = $http({

                method:'GET',

                url:"http://localhost:8080/api/getListOfCar"

            }).success(function(data, status, headers, config) {

                $scope.cars= data;

           }).error(function(err, status, headers, config) {
                console.log(err);

            });

       }

     $scope.save =function() {

            varresponse = $http({

                method:'POST',

                url:"http://localhost:8080/api/save",

                data: $scope.car

            }).success(function(data, status, headers, config) {

                $scope.car=null;

                getListOfCar();

            }).error(function(err, status, headers, config) {

                console.log(err);

            });

        }

        $scope.search =function(acar) {

            varresponse = $http({

                method:'POST',

                url:"http://localhost:8080/api/search",

                data:$params=acar,

   }).success(function(data) {

                $scope.car= data;

                getListOfCar();

                console.log(data);

                console.log("success!");

            }).error(function(err, status, headers, config) {

                console.log(err);

            });

        }

        $scope.updateCar =function(car) {

            $scope.car= car;

            getListOfCar();

        }

      $scope.deleteCar =function(car) {

            varresponse = $http({

 

               method:'POST',
                url:"http://localhost:8080/api/delete",

                data: car

            }).success(function(data, status, headers, config) {

                getListOfCar();

            }).error(function(err, status, headers, config) {

                console.log(err);

            });

        }

      getListOfCar();
    };
 productController.$inject= ['$scope','$http'];
 angular.module('springApp').controller('productController', productController);
}());

 注:比较花时间还是有关Css,笔者有点懒,偷偷用了人家的Css,加以改良一下。

测试

  首先要启动资料库 MongoDB,建议使用 RoboMongo,容易查询资料。

打开一个 terminal 或 cmd,cd 到那有cars.dat的目录里,准备输入所提供的资料到MongoDB:

  mongoimport -d car_dealer -c cars cars.dat

 然后打开MongoDB,确定这些汽车资源在库存。

  有了汽车资料,就可以开始测试了。首先是打开 STS,在右边选择了该服务器程式,按右键操作:

product-server =>Run As => Spring Boot App 。如果你可以在 console 里看到com.emo.product.app 在等候,该服务器正在运作了。有读者可能问,画面呢?去了那里?他可能不记得前后端分离处理的,前台都没有开始,那有画面呢!注: 如果不想在 STS/Eclipse 上运行,可以在 terminal/cmd 进入该 product-server的目录内,输入: mvn spring-boot:run 也同样可以启动服务器。

  启动前端更容易,打开那个 car-client 的视窗或 Mac的 finder,在 car-cleint的目录内(不用在 terminal 内开启的)选择了该index.html程式,按右键选取浏览器(Firefox)操作,如无意外,画面就出现在你的眼前。

Spring Boot1.3.6 Angularjs1.4.0 Mongodb2.3.6 CRUD汽车搜索及增加修改程式                             图5:Spring Boot + Angular UI-Route + Mongodb CRUD学习程式

Spring Boot1.3.6 Angularjs1.4.0 Mongodb2.3.6 CRUD汽车搜索及增加修改程式                                             图6:输入S4按 search 键

Spring Boot1.3.6 Angularjs1.4.0 Mongodb2.3.6 CRUD汽车搜索及增加修改程式                                       图7:输入Honda 按 Submit 键来增加

Spring Boot1.3.6 Angularjs1.4.0 Mongodb2.3.6 CRUD汽车搜索及增加修改程式                                               图8: Honda 汽车资料已保存

  一个简易又清楚的程式开发过程全部显示了。也可能作为一个模版作将来的 Web 的开发。所以要处理当然是页数及搜索的速度。因为现在是全方位的资料库搜查, MongoDB 是十分成任的。

原创,如有转载,请注明出处!

笔者以往分享的记录:

jHipster 3.4  创建最流行Java Web应用项目最简单的入门基本教程

PHP5.2.9+MySQL5.6.26+Easyui 1.41开发易贸达出入口公司产品查询浏览表


打赏

文件名:car.zip,文件大小:358.792K下载
顶部客服微信二维码底部
>扫描二维码关注最代码为好友扫描二维码关注最代码为好友