6.1 简介
Spring Boot的设计目的是用来简化Spring应用的初始搭建以及开发过程,使用Spring Boot可以创建独立的Spring应用,简化了Maven配置,而且内嵌了Tomcat可以直接部署jar文件。
6.2 STS
在项目开发初期先在本地开发,测试或上线时再部署到服务器,首先确保本地环境安装了Java,然后到 https://spring.io/tools 下载开发Spring Boot项目的IDE,或者读者可以选择自己熟悉的IDE,本书采用STS讲解。
STS下载解压之后可以直接运行而且会自动使用本地的Java运行时环境,如果没有匹配好,可以在STS的Window > Preferences > Installed JREs里添加本地JDK,前提时本地环境已经安装Java环境。
6.3 创建项目
在STS中 File > New > Spring Starter Project来创建一个新项目。
选择项目所需的依赖,Web
、 JPA
和 MySQL
Finish后STS会自动拉取项目依赖的jar包,稍微耗时耐心等待。
6.4 配置项目
此时项目仍然是无法启动的,因为创建项目时选择了JPA依赖,它是一个ORM框架,使用它必须要有可连接的数据库。找到配置文件src/main/resources/application.properties,添加下面的数据库配置
spring.datasource.url=jdbc:mysql://rm-2zenrqfr9iy0vbw17uo.mysql.rds.aliyuncs.com/test?useSSL=false
spring.datasource.username=username
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
右键选中项目,Run AS > Spring Boot App就可以启动项目,因为Spring Boot内置了Tomcat所以不需要配置Server即可让项目运行起来,并可一个通过8080端口访问。
6.5 响应请求
在 src/main/java下创建新的包cn.mx.starter.controller
,并在cn.mx.starter.controller
下创建新的Class:IndexController.java
。
package cn.mx.starter.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class IndexController {
@GetMapping("/")
private String index() {
return "Spring Boot";
}
}
打开浏览器在地址栏输入 http://127.0.0.1:8080,即可收到到服务器返回的字符串:Spring Boot。
6.6 读写数据库
JPA
Java持久化API是一个将对象映射为关系数据库的标准技术,包含了Hibernate、Spring Data和Spring ORM等依赖,本书不想在此涉及太多JPA的细节,直接讲解如何使用。
Entity
在第4章创建了2个表: user
和 address
,分别创建这两个表对应的两个实体,并放在包cn.mx.starter.model
下:
User
package cn.mx.starter.model;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Table(name = "user")
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private Integer age;
@OneToMany()
private List addresses;
// Setter Getter
}
Address
package cn.mx.starter.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Table(name = "address")
@Entity
public class Address {
@Id
@GeneratedValue
private Long id;
@Column(nullable = false)
private String addr;
@ManyToOne()
private User user;
// 防止序列化JSON时循环引用
@JsonIgnore
public User getUser() {
return user;
}
// Setter Getter
}
Address实体里的getUser()
方法多了注解@JsonIgnore
,该注解是为了解除User和Address序列化时循环引用,即序列化User时调用getAddresses()
,而每一个Address序列化又调用getUser()
,互相循环调用直至内存异常。读者可以尝试去掉该注解体会一下。
Repository
JPA提供了非常便捷的接口访问数据库,而且默认内置了很多方法直接读取数据库,比如findById。这里创建一个 UserRepository
放在包cn.mx.starter.repository
下:
package cn.mx.starter.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import cn.mx.starter.model.User;
public interface UserRepository extends JpaRepository {
}
Controller
IndexController
增加新的访问路径/users
返回数据库查询数据,使用注解@Autowired
注入UserRepository
:
package cn.mx.starter.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import cn.mx.starter.model.User;
import cn.mx.starter.repository.UserRepository;
@RestController
public class IndexController {
@Autowired
UserRepository userRepository;
@GetMapping("/")
private String index() {
return "Spring Boot";
}
@GetMapping("/users")
private List users() {
return userRepository.findAll();
}
}
浏览器访问http://127.0.0.1:8080/users会返回查询数据的JSON格式:
[
{
id: 1,
name: "john",
age: 0,
addresses: [
{
id: 1,
addr: "朝阳区亚运村路"
},
{
id: 2,
addr: "学院路北京大学"
}
]
}
]
内置查询方法
调用http://127.0.0.1:8080/users/1可以查找id=1的用户。
//IndexController
@GetMapping("/users/{id}")
private User user(@PathVariable(name="id") Long id) {
return userRepository.findById(id).orElse(null);
}
自定义查找方法
调用http://127.0.0.1:8080/users/?name=john可以查找name=join的用户。
//IndexController
@GetMapping("/users/")
private List usersByName(@RequestParam(name="name") String name) {
return userRepository.findByName("john");
}
因为findByName
不是JPA内置方法,所以需要在UserRepository
添加这个方法:
//UserRepository
public interface UserRepository extends JpaRepository {
List findByName(String name);
}
插入数据
终端里调用命令curl -X POST http://127.0.0.1:8080/users
插入一个新的用户。
//IndexController
@PostMapping("/users")
private User postUser() {
Address addr = new Address();
addr.setAddr("学院路中关村");
User user = new User();
user.setName("Jack");
user.setAge(10);
user.setAddresses(Arrays.asList(addr));
addr.setUser(user);
userRepository.save(user);
return user;
}
更新数据
//IndexController
@PutMapping("/users/{id}")
private User putUser(@PathVariable(name="id") Long id) {
User user = userRepository.findById(id).orElse(null);
if (user != null) {
user.setName("Sara");
userRepository.save(user);
}
return user;
}
打印SQL
使用JPA可以方便的用操作Java对象的方式操作数据库,如果想核对数据库SQL尤其是复杂查询对应的的SQL,可以在控制台打印出每次数据操作的的SQL语句,在配置文件添加如下配置:
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.type=trace
至此,一个简单的Spring Boot项目已经完成,连接数据库并对数据进行增删改查。JPA查询有一套内置的查找规则映射到对应的SQL查询,详情可以查阅 https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation。
该项目中我们在Controller层直接调用了Repository层访问数据,实际项目业务逻辑会比较复杂,可以在中间添加Service层专门处理业务逻辑。实际项目的复杂之处在于,业务需求复杂,从而对应的数据库表之间的关联关系就复杂,处理好表之间的关联关系,捋清业务逻辑,剩下的技术层面无非是对数据库表的增删改查。
6.7 部署项目
项目完成之后,需要将项目打包放到服务器上测试。STS已经内置了Maven所以打包非常方便,右键项目 Run AS > Maven install,会将整个项目打成jar包并放在target目录下,默认是starter-0.0.1-SNAPSHOT.jar。
因为Spring Boot内置了Tomcat并一起打包到jar文件里,所以可以直接在终端运行它:
java -jar target/starter-0.0.1-SNAPSHOT.jar
把打包好的jar文件传到服务器上/root目录下,scp是SSH附带的命令,通过SSH协议拷贝本地文件到远程服务器:
scp -i ~/.ssh/mx.pem target/starter-0.0.1-SNAPSHOT.jar [email protected]:/root
登录远程服务器,并且保证该服务器的网络安全组协议里开通了8080端口
ssh -i ~/.ssh/mx.pem [email protected]
运行项目,此时便可以通过IPhttp://59.110.173.162访问项目了:
java -jar starter-0.0.1-SNAPSHOT.jar
6.8 后台运行
在服务器上java -jar starter-0.0.1-SNAPSHOT.jar
可以让项目运行起来,比可以通过地址正常访问,但是一旦退出远程登录则该java进程的服务就停止了,因为该服务是运行在前台。使用CentOS的Systemd来让项目可以运行在系统后台。
-
添加一个Systemd服务文件
vim /etc/systemd/system/starter.service
写入如下内容,保存后退出
[Unit]
Description=starter
After=syslog.target
[Service]
ExecStart= /usr/bin/java -jar /root/starter-0.0.1-SNAPSHOT.jar
[Install]
WantedBy=multi-user.target
之后就可以通过systemctl启动、关闭和重启项目了
启动服务:
systemctl start starter.service
停止服务:
systemctl stop starter.service
服务状态:
systemctl status starter.service
开机启动:
systemctl enable starter.service
查看项目日志:
journalctl -u starter.service