打算就”用Spring Boot & Cloud和Angular2快速搭建微服务web应用“这个题目写一系列文章,作为自己学习的一个记录。在参加完读脉组织的一个Java培训活动后,发现自己在这方面的知识已经落后好多年了。讲师Josh的激情演讲,也让我看到了自己和顶级程序员的差距。在此感谢读脉组织的这次活动,感谢Josh的激情演讲,给了我写这个系列文章的动力。
读脉:http://readmore.cc/
Josh:https://github.com/joshlong
<?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.healtrav</groupId> <artifactId>user-service</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>user-service</name> <description>user management service for healtrav</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.0.RELEASE</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> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-rest</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </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>
同时注意到以下几个依赖:
package com.healtrav; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class, args); } }现在我们只需要添加一些业务代码,就可以直接实现一个Restful,支持CRUD的web应用。
package com.healtrav.domain; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.validation.constraints.NotNull; @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; @NotNull private String username; @NotNull private String password; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
package com.healtrav.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.repository.query.Param; import org.springframework.data.rest.core.annotation.RepositoryRestResource; import com.healtrav.domain.User; @RepositoryRestResource(collectionResourceRel = "user", path = "user") public interface UserRepository extends JpaRepository<User, Long> { User findByUsername(@Param("username") String username); }
# MySQL data source settings spring.datasource.url=jdbc:mysql://localhost:3306/healtrav spring.datasource.username=root spring.datasource.password= spring.datasource.initial-size=20 spring.datasource.max-idle=60 spring.datasource.max-wait=10000 spring.datasource.min-idle=10 spring.datasource.max-active=200 # auto create tables and data for database healtrav spring.jpa.generate-ddl=true spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect spring.datasource.schema=..\..\..\db\schema.sql spring.datasource.data=..\..\..\db\data.sql # show each sql for debug spring.jpa.show-sql = true
-- MySQL Script generated by MySQL Workbench -- 09/20/16 18:12:37 -- Model: New Model Version: 1.0 -- MySQL Workbench Forward Engineering SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0; SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0; SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES'; -- ----------------------------------------------------- -- Schema healtrav -- ----------------------------------------------------- DROP SCHEMA IF EXISTS `healtrav` ; -- ----------------------------------------------------- -- Schema healtrav -- ----------------------------------------------------- CREATE SCHEMA IF NOT EXISTS `healtrav` DEFAULT CHARACTER SET utf8 ; USE `healtrav` ; -- ----------------------------------------------------- -- Table `healtrav`.`user` -- ----------------------------------------------------- DROP TABLE IF EXISTS `healtrav`.`user` ; CREATE TABLE IF NOT EXISTS `healtrav`.`user` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `username` VARCHAR(64) NOT NULL, `password` VARCHAR(128) NOT NULL, PRIMARY KEY (`id`), UNIQUE INDEX `id_UNIQUE` (`id` ASC), UNIQUE INDEX `username_UNIQUE` (`username` ASC)) ENGINE = InnoDB; SET SQL_MODE=@OLD_SQL_MODE; SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
$ curl http://localhost:8080/会得到当前的Restful URI列表。其中一个是"href" : "http://localhost:8080/user{?page,size,sort}",
$ curl http://localhost:8080/user会得到user路径下面的URI列表,及一个user数组。但是现在这个user数组是空的,因为我们还没有添加用户信息。
$ curl -i -X POST -H "Content-Type:application/json" -d '{ "username" : "cuiwader", "password" : "123" }' http://localhost:8080/user会添加数据到数据库,并且会返回新添加的用户的信息。
$ curl http://localhost:8080/user/search/findByUsername?username=cuiwader会调用UserRepository.java中,接口UserRepository的findByUsername方法。而且该方法的实现也是Spring Data JPA根据方法的名字自动生成的。
$ curl http://localhost:8080/user会得到user路径下面的URI列表,及一个user数组。这次这个数组多了一个元素,即刚才添加的那个元素,但是请大家仔细看下面的输出,数组元素没有id字段,因为默认id是不导出的。可以通过继承RepositoryRestMvcConfiguration,重写configureRepositoryRestConfiguration(RepositoryRestConfiguration config)方法,并且在方法里面调用
$ curl http://localhost:8080/user-service/user % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 726 0 726 0 0 15782 0 --:--:-- --:--:-- --:--:-- 15782{ "_embedded" : { "user" : [ { "username" : "cuiwader", "password" : "123", "_links" : { "self" : { "href" : "http://localhost:8080/user-service/user/1" }, "user" : { "href" : "http://localhost:8080/user-service/user/1" } } } ] }, "_links" : { "self" : { "href" : "http://localhost:8080/user-service/user" }, "profile" : { "href" : "http://localhost:8080/user-service/profile/user" }, "search" : { "href" : "http://localhost:8080/user-service/user/search" } }, "page" : { "size" : 20, "totalElements" : 1, "totalPages" : 1, "number" : 0 } }
@Query("select u from user u where u.username = :username") User findByUsername(@Param("username") String username);
@Entity @NamedStoredProcedureQuery(name = "User.findByUsername", procedureName = "findByUsername", parameters = { @StoredProcedureParameter(mode = ParameterMode.IN, name = "username", type = String.class), @StoredProcedureParameter(mode = ParameterMode.OUT, name = "user", type = User.class) }) public class User { ...
@Procedure("findByUsername") User findByUserName(String username);
package com.healtrav.repository; import com.healtrav.domain.User; public interface UserRepositoryCustom { public User getByUsername(String username); }
package com.healtrav.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.repository.query.Param; import org.springframework.data.rest.core.annotation.RepositoryRestResource; import com.healtrav.domain.User; @RepositoryRestResource(collectionResourceRel = "user", path = "user") public interface UserRepository extends JpaRepository<User, Long>, UserRepositoryCustom { User findByUsername(@Param("username") String username); }
package com.healtrav.repository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.rest.webmvc.RepositorySearchesResource; import org.springframework.hateoas.Link; import org.springframework.hateoas.ResourceProcessor; 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 com.healtrav.domain.User; @RestController @RequestMapping("/user") public class UserRepositoryImpl implements UserRepositoryCustom, ResourceProcessor<RepositorySearchesResource> { @Autowired UserRepository userRepo; @Override @RequestMapping(value = "/search/getUserByUsername", method = RequestMethod.GET) public User getByUsername( @RequestParam(value = "username", required = true) String username) { return userRepo.findByUsername(username); } @Override public RepositorySearchesResource process( RepositorySearchesResource resource) { String href = resource.getId().getHref(); resource.add(new Link(href + "/getByUsername{?username}") .withRel("getByUsername")); return resource; } }
"getByUsername" : { "href" : "http://localhost:8081/user/search/getByUsernamee{?username}", "templated" : true }