前言
两周的软件开发综合能力训练刚刚结束了。这两周可以说是完全被大佬带飞了,虽然自己没有起到至关重要的作用,但是学到的相当多的东西,并且开始对Web APP的开发有了自己的理解。一个Web APP其实就是将数据取出并作相关的处理,同时加以展示,最后将需要保存的数据通过持久化技术将其持久化,如此循环的一个过程。Spring Boot分布式层次结构提供了相当清晰的开发结构和框架。对于数据的访问,Spring Data JPA分为持久化类、数据访问层、业务层和控制器类四个大的层次,同时对应于bean、repository、service、controller四个包。
准备过程
在开始之前需要将Maven工程的pom.xml文件内容进行修改:
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.test1</groupId>
<artifactId>ddv</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>ddv Maven Webapp</name>
<url>http://maven.apache.org</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
</parent>
<properties> //一些全局设置
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies> //依赖包
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency> //web app依赖包
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency> //thymeleaf依赖包
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency> //postgresql的依赖包实现与数据库连接的重要依赖
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.6</version><!--$NO-MVN-MAN-VER$-->
</dependency>
<dependency> //jpa依赖包,也是重要的依赖包
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
<build>
<finalName>ddv</finalName>
</build>
</project>
除此之外还需要在resources包下新建一个全局配置文件application.properties,在该配置文件中配置数据源和JPA相关的属性。如下:
#数据库地址
spring.datasource.url = jdbc:postgresql://localhost:5432/ddv
#数据库登录用户名
spring.datasource.username = postgres
#数据库登录密码
spring.datasource.password = 123456
#postgreSQL数据库的驱动包
spring.datasource.driverClassName = org.postgresql.Driver
#暂不知道是啥
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
#解决“org.postgresql.jdbc.PgConnection.createClob() 方法尚未被实作”的报错
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false
#如果数据库中存在持久化类对应的的表就不创建,不存在就创建对应的表
spring.jpa.hibernate.ddl-auto = update
#在日志中打印对应的sql语句
spring.jpa.show-sql = true
持久化类
在持久化层我们需要根据项目的数据库设计,应用ORM的思想和方法实现相应的实体类的定义。这其中涉及到了几个注释的使用。例如一个Student类这样编写:
package ddv.bean;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity //表明是一个实体类
@Table(name="tb_student") //对应于数据库中的关系表的表名
public class Student implements Serializable{ //实现序列化接口(目前还不知道是为什么)
@Id //对象的唯一标识对应于关系表的主键
@GeneratedValue(strategy = GenerationType.IDENTITY) //设置主键为自增长模式
private int id;
private String name;
private String address;
private int age;
private char sex;
public int getId() { //以下为一些set,get方法
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
}
数据访问层
数据访问层其实就是将我们整个项目中所需要的对数据库操作的一些SQL语句和对应的函数联系起来,在后续的开发中我们只需要调用相应的函数便可以执行对数据库的SQL操作,体现了ORM的思想并将其实现。例如一些对于以上学生表的操作如下实现:
package ddv.repository;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import ddv.bean.Student;
public interface StudentRepository extends JpaRepository<Student, Integer> { //暂时不知道为什么实现该接口
Student findByName(String name); //通过名字查找学生
List<Student> findByNameAndAddress(String name, String address); //通过名字和地址查询学生
List<Student> findByNameLike(String name); //通过名字的模糊查询查询学生
}
Repository已将一些基本的SQL语句封装成了对应的函数,包括单独属性查询、两个属性结合查询、单个属性模糊查询等等。
业务层
在业务层我们需要利用数据访问层的方法得到想要的数据并提供相应的代码逻辑来实现相应的功能。比如对于以上学生的一些功能,保存、查询如下:
package ddv.service;
import java.util.List;
import javax.annotation.Resource;
import javax.transaction.Transactional;
import org.springframework.stereotype.Service;
import ddv.bean.Student;
import ddv.repository.StudentRepository;
@Service //表明是个业务
public class StudentService {
@Resource //引入相应的数据访问层资源
private StudentRepository studentRepository;
@Transactional //事务注解保证事务要么全部完成要么啥也不做
public void saveALL(List<Student> students) {
studentRepository.saveAll(students);
}
public Student getStuByName(String name) {
return studentRepository.findByName(name);
}
public List<Student> getStusByNameAndAddress(String name, String address){
return studentRepository.findByNameAndAddress(name, address);
}
public List<Student> getStusByNameLike(String name){
return studentRepository.findByNameLike(name);
}
}
该例中仅仅是实现了数据访问层中的函数,其实在该处应该完成相应的逻辑。例如,要实现用户的登录功能,则需要根据用户名在数据库中查询相应用户,再校验密码。这其中便要考虑用户不存在,密码不正确,密码正确等多种情况。
Controller层
在该层需要利用业务层的相关接口完成对相应url的映射和响应。例如同样对上述学生类的一些操作实现如下:
package ddv.controller;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import ddv.bean.Student;
import ddv.service.StudentService;
/**
*@RestController是@Controller和@ResponseBody两个的结合
*如果需要返回html或者jsp界面则使用@Controller当有返回字符串和JSON数据的时候需要加@ResponseBody注解所**以直接使用@RestController
**/
@RestController
@RequestMapping("/student") //完成url映射
public class StudentController {
@Resource //引入业务类
private StudentService studentService;
@RequestMapping("/save") //http://localhost:8080/student/save
public String save() {
Student swk = new Student(); //初始化几个学生类
swk.setAddress("山西");
swk.setAge(700);
swk.setName("孙悟空");
swk.setSex('男');
Student zzj = new Student();
zzj.setAddress("山西");
zzj.setAge(700);
zzj.setName("蜘蛛精");
zzj.setSex('女');
Student nmw = new Student();
nmw.setAddress("山西");
nmw.setAge(500);
nmw.setName("牛魔王");
nmw.setSex('男');
List<Student> students = new ArrayList<>();
students.add(swk);
students.add(zzj);
students.add(nmw);
studentService.saveALL(students); //调用保存学生接口
return "保存学生对象成功!";
}
@RequestMapping("/name") //http://localhost:8080/student/name?name=牛魔王
public Student getByName(String name) {
return studentService.getStuByName(name);
}
@RequestMapping("/nameAndAddress") //http://localhost:8080/student/nameAndAddress?name=蜘蛛精 //&address=山西
public List<Student> getByNameAndAddress(String name, String address){
return studentService.getStusByNameAndAddress(name, address);
}
@RequestMapping("/nameLike") //http://localhost:8080/student/nameLike?name=魔王
public List<Student> getByNameLike(String name){
return studentService.getStusByNameLike("%"+name+"%");
}
}
注意遇到一个问题
SpringBoot中service注入失败
A component required a bean of type 'XXService' that could
本人的错误原因是:bean,service,repository,controller等这些包和Application.java(SpringBoot程序的入口)不在同一个包且不在Application.java的子包中。原因是,SpringBoot运行时所加载的包是Application.java本包及其子包的代码。所以根本扫描不到其他包。