文章目录
10.SSS框架整合概述
10.1 业务需求
-
账户数据电子化管理小程序的升级
-
背景 : 前期项目中我们已经使用SSD框架开发了账户信息管理系统,但是编写复杂度已然没有减少,所以决定对本项目进行二次的开发工作;
-
假设:
- 有springdb数据库一个,账户表一张,表结构如下:
- 账户表(account)
-
要求:
- 完成对账户信息管理系统的二次开发
10.2 技术迭代
-
实现思路
1. 优化掉现有的XML配置 --> 采用注解
2. 轻量化配置Spring --> SpringBoot
3. 采用面向对象思想操作SQL --> SpringData -
技术详解
10.2.1 SpringBoot
概述 :
Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。用我的话来理解,就是 Spring Boot 其实不是什么新的框架,它默认配置了很多框架的使用方式,就像 Maven 整合了所有的 Jar 包,Spring Boot 整合了所有的框架。
好处 :
其实就是简单、快速、方便!平时如果我们需要搭建一个 Spring Web 项目的时候需要怎么做呢?
1)配置 web.xml,加载 Spring 和 Spring mvc
2)配置数据库连接、配置 Spring 事务
3)配置加载配置文件的读取,开启注解
4)配置日志文件
…
配置完成之后部署 Tomcat 调试
…
现在非常流行微服务,如果我这个项目仅仅只是需要发送一个邮件,如果我的项目仅仅是生产一个积分;我都需要这样折腾一遍!
但是如果使用 Spring Boot 呢?
很简单,我仅仅只需要非常少的几个配置就可以迅速方便的搭建起来一套 Web 项目或者是构建一个微服务!
使用 Spring Boot 到底有多爽,用下面这幅图来表达
官方解释
Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”.
We take an opinionated view of the Spring platform and third-party libraries so you can get started with minimum fuss. Most Spring Boot applications need very little Spring configuration.
SpringBoot使创建独立的、生产级的、基于Spring的应用程序变得容易,您可以“只运行”。
我们对Spring平台和第三方图书馆有一个独到的观点,这样你就可以以最少的麻烦开始了。大多数Spring引导应用程序只需要很少的Spring配置。
Features
Create stand-alone Spring applications
Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)
Provide opinionated ‘starter’ dependencies to simplify your build configuration
Automatically configure Spring and 3rd party libraries whenever possible
Provide production-ready features such as metrics, health checks and externalized configuration
Absolutely no code generation and no requirement for XML configuration
- 创建独立的Spring应用程序
- 直接嵌入Tomcat、Jetty或Underow(无需部署战争文件)
- 提供固定的“starter”依赖项以简化构建配置
- 尽可能自动配置Spring和第三方库
- 提供生产就绪的特性,如度量、运行状况检查和外部化配置
- 绝对没有代码生成,也不需要XML配置
10.2.2 SpringData jpa
Spring-data-jpa的基本介绍:
JPA诞生的缘由是为了整合第三方ORM框架,建立一种标准的方式,百度百科说是JDK为了实现ORM的天下归一,目前也是在按照这个方向发展,但是还没能完全实现。
在ORM框架中,Hibernate是一支很大的部队,使用很广泛,也很方便,能力也很强,同时Hibernate也是和JPA整合的比较良好,我们可以认为JPA是标准,事实上也是,JPA几乎都是接口,实现都是Hibernate在做,宏观上面看,在JPA的统一之下Hibernate很良好的运行。
上面阐述了JPA和Hibernate的关系,那么Spring-data-jpa又是个什么东西呢?这地方需要稍微解释一下,我们做Java开发的都知道Spring的强大,到目前为止,企业级应用Spring几乎是无所不能,无所不在,已经是事实上的标准了,企业级应用不使用Spring的几乎没有,这样说没错吧。而Spring整合第三方框架的能力又很强,他要做的不仅仅是个最早的IOC容器这么简单一回事,现在Spring涉及的方面太广,主要是体现在和第三方工具的整合上。
而在与第三方整合这方面,Spring做了持久化这一块的工作,我个人的感觉是Spring希望把持久化这块内容也拿下。于是就有了Spring-data-**这一系列包。
包括,Spring-data-jpa,Spring-data-template,Spring-data-mongodb,Spring-data-redis,还有个民间产品,mybatis-spring,和前面类似,这是和mybatis整合的第三方包,这些都是干的持久化工具干的事儿。
这里介绍Spring-data-jpa,表示与jpa的整合。
我们都知道,在使用持久化工具的时候,一般都有一个对象来操作数据库,在原生的Hibernate中叫做Session,在JPA中叫做EntityManager,在MyBatis中叫做SqlSession,通过这个对象来操作数据库。我们一般按照三层结构来看的话,Service层做业务逻辑处理,Dao层和数据库打交道,在Dao中,就存在着上面的对象。那么ORM框架本身提供的功能有什么呢?
答案是基本的CRUD,所有的基础CRUD框架都提供,我们使用起来感觉很方便,很给力,业务逻辑层面的处理ORM是没有提供的,如果使用原生的框架,业务逻辑代码我们一般会自定义,会自己去写SQL语句,然后执行。
在这个时候,Spring-data-jpa的威力就体现出来了,ORM提供的能力他都提供,ORM框架没有提供的业务逻辑功能Spring-data-jpa也提供,全方位的解决用户的需求。使用Spring-data-jpa进行开发的过程中,常用的功能,我们几乎不需要写一条sql语句,至少在我看来,企业级应用基本上可以不用写任何一条sql,当然spring-data-jpa也提供自己写sql的方式,这个就看个人怎么选择,都可以。我觉得都行。
官方解释
Spring Data JPA, part of the larger Spring Data family, makes it easy to easily implement JPA based repositories. This module deals with enhanced support for JPA based data access layers. It makes it easier to build Spring-powered applications that use data access technologies.
Implementing a data access layer of an application has been cumbersome for quite a while. Too much boilerplate code has to be written to execute simple queries as well as perform pagination, and auditing. Spring Data JPA aims to significantly improve the implementation of data access layers by reducing the effort to the amount that’s actually needed. As a developer you write your repository interfaces, including custom finder methods, and Spring will provide the implementation automatically.
SpringDataJPA是更大的Spring数据系列的一部分,它使得很容易实现基于JPA的存储库。此模块处理对基于JPA的数据访问层的增强支持。它使得构建使用数据访问技术的Spring驱动的应用程序更加容易。
实现应用程序的数据访问层有一段时间很麻烦。为了执行简单的查询以及执行分页和审计,必须编写太多样板代码。SpringDataJPA的目标是通过将工作减少到实际需要的数量来显著改进数据访问层的实现。作为开发人员,您可以编写存储库接口,包括定制的finder方法,Spring将自动提供实现。
- 对基于Spring和JPA构建存储库的复杂支持
- 支持querydsl谓词,因此键入安全的jpa查询
- 域类的透明审核
- 分页支持,动态查询执行,能够集成自定义数据访问代码
- 启动时验证@query注释查询
- 支持基于XML的实体映射
- 通过引入@enablejparepositories,基于javaconfig的存储库配置。.
10.3 SSS框架整合实现
10.3.1 项目初始化工作 :
涉及技术
- Spring Boot 2.0.4 RELEASE --> 整合了spring springMVC
- Spring Data 2.0.4 RELEASE --> 数据库的CURD操作
- bootstrap 3.3.5 --> 页面提供CSS支持
- jquery 1.9.1 --> 对页面的JS支持
创建项目 :
创建 maven java项目即可
首先:导入jar包依赖 :
<!-- springboot父级项目,使用它可以直接锁定引入jar包的版本解决版本冲突 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath />
</parent>
<!-- 项目的初始化 -->
<properties>
<!-- maven项目整体编码 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 项目编译的时候,源码(.java)使用哪个版本JDK -->
<maven.compiler.source>1.8</maven.compiler.source>
<!-- 项目编译的时候,可执行文件(.class)使用哪个版本JDK -->
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- springBoot核心 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- springBoot热部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<!-- WEB支持主要包括springMVC 和 tomcat插件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<!-- jsp相关 -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<!-- JSTL相关 -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<!-- spring data -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.9</version>
</dependency>
<!-- mysqljar -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
<!-- 加载springBoot的maven插件 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
</configuration>
</plugin>
</plugins>
</build>
在添加了该插件之后,当运行“mvn package”进行打包时,会打包成一个可以直接运行的JAR文件,使用"java -jar"命令就可以直接运行
如果未进行上述配置,应用本地可以正常启动,但是发布到测试机器就无法启动
随后:更新项目
更新完之后项目不再报错,完成jar包及项目初始化配置;
10.3.2 实体类
实体类 : 需要注意字段与属性不对称的问题
以后 实体类的包 会有以下几种命名规则
com.xxx.domain --> 域;定义域
com.xxx.pojo --> POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBeans,是为了避免和EJB混淆所创造的简称。
com.xxx.entity --> 实体; 实际存在物; 本质;
在SpringBoot中我们使用全新的JPA注解:
JPA和Hibernate的关系
JPA(Java Persistence API),是Java EE 5的标准ORM接口,也是ejb3规范的一部分。
Hibernate是之前很流行的ORM框架,也是JPA的一个实现,其它还有Toplink之类的ROM框架。
JPA和Hibernate之间的关系,可以简单的理解为JPA是标准接口,Hibernate是实现。
Hibernate主要是通过三个组件来实现的:
hibernate-core:Hibernate的核心实现,提供了Hibernate所有的核心功能。
hibernate-entitymanager:Hibernate实现了标准的JPA,可以把它看成hibernate-core和JPA之间的适配器,它并不直接提供ORM的功能,而是对hibernate-core进行封装,使得Hibernate符合JPA的规范。
hibernate-annotation:Hibernate支持annotation方式配置的基础,它包括了标准的JPA annotation以及Hibernate自身特殊功能的annotation。
我们引入的spring-data的依赖中已经对hibernate本身进行了高度的封装,所以我们使用起来就很简单了;
我们在此处进行注解映射的意义就是将实体类与表进行映射,然后我们在程序中操作类对象就相当于在数据库中操作表示一个道理的;
10.3.2.1 @Entity
@Entity说明这个class是实体类,并且使用默认的orm规则,即class名对应数据库表中表名,class字段名即表中的字段名。
(如果想改变这种默认的orm规则,就要使用@Table来改变class名与数据库中表名的映射规则,@Column来改变class中字段名与db中表的字段名的映射规则)
元数据属性说明:
• name: 表名
@Entity
@Table(name="ACCOUNT")
public class Account {
10.3.2.2 @Table
Table用来定义entity主表的name,catalog,schema等属性。
元数据属性说明:
• name: 表名
• catalog: 对应关系数据库中的catalog
• schema:对应关系数据库中的schema
• UniqueConstraints:定义一个UniqueConstraint数组,指定需要建唯一约束的列
@Entity
@Table(name="ACCOUNT")
public class Account {
10.3.2.3 @Id
声明当前field为映射表中的主键列。
id值的获取方式有五种:TABLE(主键表), SEQUENCE(序列), IDENTITY(自增), AUTO(自动), NONE(手动指定)。
Oracle和DB2支持SEQUENCE,SQL Server和Sybase支持IDENTITY,MySQL支持AUTO。
所有的数据库都可以指定为AUTO,我们会根据不同数据库做转换。
NONE (默认)需要用户自己指定Id的值。
元数据属性说明:
• generate():主键值的获取类型
• generator():TableGenerator的名字(当generate=GeneratorType.TABLE才需要指定该属性)
此处我们是通过使用 @GeneratedValue 的性质来指定主键自增策略;
其中 strategy 代表 主键策略 GenerationType.IDENTITY 代表使用主键自增策略
@Id
@Column(name="A_ID")
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer aid; // 主键
10.3.2.4 @Column
Column元数据定义了映射到数据库的列的所有属性:列名,是否唯一,是否允许为空,是否允许更新等。
元数据属性说明:
• name:列名。
• unique: 是否唯一
• nullable: 是否允许为空
• insertable: 是否允许插入
• updatable: 是否允许更新
• columnDefinition: 定义建表时创建此列的DDL
• secondaryTable: 从表名。如果此列不建在主表上(默认建在主表),该属性定义该列所在从表的名字。
此处我们使用@column 注解用来指定 属性名 和 列名的映射
@Column(name="A_NAME")
private String aname; // 客户姓名
@Column(name="A_MONEY")
private Double money; // 客户存款
@Column(name="A_CREATEDATE")
private Date createDate; // 开户时间
运用起来如此:
实体类代码 :
package com.hnxy.entity;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import org.springframework.format.annotation.DateTimeFormat;
/**
* 账户信息表
* 表对应类 字段对应属性
* 避开关键字命名
* @author My
*
*/
@Entity
@Table(name="ACCOUNT")
public class Account {
// 私有属性
@Id
@Column(name="A_ID")
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer aid; // 主键
@Column(name="A_NAME")
private String aname; // 客户姓名
@Column(name="A_MONEY")
private Double money; // 客户存款
@Column(name="A_CREATEDATE")
private Date createDate; // 开户时间
// 对外方法
public Integer getAid() {
return aid;
}
public void setAid(Integer aid) {
this.aid = aid;
}
public String getAname() {
return aname;
}
public void setAname(String aname) {
this.aname = aname;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
@Override
public String toString() {
return "Account [aid=" + aid + ", aname=" + aname + ", money=" + money + ", createDate=" + createDate + "]";
}
}
10.3.2 DAO Service
10.3.2.1 DAO
package com.hnxy.dao;
import org.springframework.data.jpa.repository.JpaRepository;
import com.hnxy.entity.Account;
public interface AccountDAO extends JpaRepository<Account,Integer> {
}
实现类说明
JpaRepository 是springData 帮助我们封装的带有简单CURD的数据操作接口,该接口由spring-data自己的实现类SimpleJpaRepository来实现CURD功能
简单方法说明
添加或者更新 --> save(对象)
删除 --> delete(对象)
查询全部 --> findAll()
分页排序查询 --> findAll(Sort)
获取单个对象 --> getOne(ID)
10.3.2.2 Service
package com.hnxy.service;
import java.util.List;
import java.util.Map;
import com.hnxy.entity.Account;
public interface AccountService {
/**
* 添加或者更新
* TODO(这里用一句话描述这个方法的作用)
* <p>
* TODO(这里描述这个方法详情– 可选)
* @param account
* @return
* @throws Exception TODO(这里描述每个参数,如果有返回值描述返回值,如果有异常描述异常)
*/
public int saveOrUpdateAccount(Account account)throws Exception;
/**
* 删除
* @param account
* @return
* @throws Exception
*/
public int deleteAccount(Account account)throws Exception;
/**
* 根据ID查询
* @param id
* @return
* @throws Exception
*/
public Account findAccountByID(Integer id)throws Exception;
/**
* 分页
* @param pageIndex 页码 第N页
* @param pageSize 页容量 每页展示N条数据
* @return map 封装 页码 页容量 总条数 总页数
* @throws Exception
*/
public Map<String,Object> findAccountsByPage(Integer pageIndex,Integer pageSize)throws Exception;
}
实现类
package com.hnxy.service.impl;
import java.util.HashMap;
import java.util.Map;
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.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.stereotype.Service;
import com.hnxy.dao.AccountDAO;
import com.hnxy.entity.Account;
import com.hnxy.service.AccountService;
@Service
public class AccountServiceImpl implements AccountService {
// 创建DAO层对象
@Autowired
private AccountDAO accountDAO;
@Override
public int saveOrUpdateAccount(Account account) throws Exception {
// 创建方法的返回值
int count = 0;
try {
// 执行添加或者更新操作 他们的区别就在于 添加传入参数没有主键ID 更新传入参数是有主键ID的
accountDAO.save(account);
// 如果不报错修改方法的返回值
count = 1;
} catch (Exception e) {
// 报错抛出异常
throw e;
}
// 返回
return count;
}
@Override
public int deleteAccount(Account account) throws Exception {
// 创建方法的返回值
int count = 0;
try {
// 执行添加或者更新操作 他们的区别就在于 添加传入参数没有主键ID 更新传入参数是有主键ID的
accountDAO.delete(account);
// 如果不报错修改方法的返回值
count = 1;
} catch (Exception e) {
// 报错抛出异常
throw e;
}
// 返回
return count;
}
@Override
public Account findAccountByID(Integer id) throws Exception {
return accountDAO.getOne(id);
}
@Override
public Map<String, Object> findAccountsByPage(Integer pageIndex, Integer pageSize) throws Exception {
// 创建方法的返回值
Map<String, Object> result = new HashMap<String, Object>();
// 设定排序 其中 构造函数中 第一个是排序规则 Direction.DESC 降序排序 Direction.ASC 升序排序
// "aid" 设定实体类中的排序属性 切记 这个是实体类中的属性 不是表的字段
// org.springframework.data.domain 所有的类都是这个包的
Order order = new Order(Direction.DESC, "aid");
// 封装对象
Sort sort = Sort.by(order);
// 业务判断
if (pageIndex < 1) {
pageIndex = 1;
}
// 获取数据总条数
long totalCount = accountDAO.count();
int totalPage = (int) (totalCount % pageSize == 0 ? totalCount / pageSize : totalCount / pageSize + 1);
if (pageIndex > totalPage) {
pageIndex = totalPage;
}
// 设定分页
// PageRequest 用于设置分页 其中构造函数的参数有三个
// 第一个 页码索引(从0开始代表第一页) 所以我们这里来了一个pageIndex-1
// 第二个 每页展示数据条数
// 第三个 排序规则
Pageable pageable = new PageRequest.of(pageIndex - 1, pageSize, sort);
// 获取分页数据
Page<Account> pas = accountDAO.findAll(pageable);
// 设定我们需要的对象
result.put("pageIndex", pageIndex);
result.put("pageSize", pageSize);
result.put("totalCount", pas.getTotalElements()); // 数据总条数 long类型的
result.put("totalPage", pas.getTotalPages()); // 数据总页数 int 类型的
result.put("pageList", pas.getContent()); // 分页数据 List类型的
// 返回
return result;
}
}
10.4 配置文件编写
编写springBoot配置文件 --> application.properties
导入context命名空间
# Spring Data DataSource
spring.datasource.type = com.alibaba.druid.pool.DruidDataSource
spring.datasource.driverClassName = com.mysql.jdbc.Driver
spring.datasource.url = jdbc:mysql:///spring_db?useUnicode=true&characterEncoding=UTF-8
spring.datasource.username = root
spring.datasource.password = root
spring.datasource.filters = stat,wall,logback
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
spring.datasource.useGlobalDataSourceStat=true
# Spring Data Hibernate
spring.jpa.show-sql = true
spring.jpa.properties.hibernate.enable_lazy_load_no_trans = true
# tomcat config
server.port=8080
server.tomcat.uri-encoding=UTF-8
# project name
server.servlet.context-path=/sys_am
# spring HD
spring.devtools.restart.enabled=true
spring.devtools.restart.additional-paths= src/main/java
因为最好不要在配置文件里写中文,所以尽量注释也不要用中文的,然后我们通过下面的内容详解一下
# Spring Data DataSource 配置
# 设置数据源类型
spring.datasource.type = com.alibaba.druid.pool.DruidDataSource
# 设置驱动类
spring.datasource.driverClassName = com.mysql.jdbc.Driver
# 设置URL
spring.datasource.url = jdbc:mysql:///spring_db?useUnicode=true&characterEncoding=UTF-8
# 设置用户名
spring.datasource.username = root
# 设置密码
spring.datasource.password = root
# 开启druid web 的SQL监控功能 stat 页面SQL统计 wall 防火墙 logback 和log4j一样 日志技术
spring.datasource.filters = stat,wall,logback
# 记录执行缓慢的SQL
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 使用全局数据源SQL记录功能
spring.datasource.useGlobalDataSourceStat=true
# Spring Data Hibernate 显示SQL语句
spring.jpa.show-sql = true
# 解决 getOne() 方法的延迟加载问题 如果不设置这个属性 update的时候会报 no session 错误
spring.jpa.properties.hibernate.enable_lazy_load_no_trans = true
# tomcat config
# 配置tomcat端口
server.port=8080
# 指定tomcat编码
server.tomcat.uri-encoding=UTF-8
# 项目的名称
server.servlet.context-path=/sys_am
# spring boot 的热部署配置
# 开启热部署
spring.devtools.restart.enabled=true
# 热部署扫描包 也就是说 src/main/java 下只要有文件的内容发生变化都会触发springboot重新加载
spring.devtools.restart.additional-paths= src/main/java
编写springBoot的核心配置类加载springBoot的配置文件
因为我们现在的包结构是
根据上图可知因为springboot约定要大于配置所以它的核心配置类的位置一定要在其他包之上,所以我们需要再com.hnxy包中创建这个核心类
package com.hnxy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringCoreApp {
/**
* 主要执行类
* TODO(这里用一句话描述这个方法的作用)
* <p>
* TODO(这里描述这个方法详情– 可选)
* @param args TODO(这里描述每个参数,如果有返回值描述返回值,如果有异常描述异常)
*/
public static void main(String[] args) {
// 设置springboot的微服务运行类
SpringApplication.run(SpringCoreApp.class, args);
}
}
@SpringBootApplication,进入源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
....
}
发现@SpringBootApplication是一个复合注解,包括@ComponentScan,和@SpringBootConfiguration,@EnableAutoConfiguration。
@SpringBootConfiguration继承自@Configuration,二者功能也一致,标注当前类是配置类,并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到srping容器中,并且实例名就是方法名。
@EnableAutoConfiguration的作用启动自动的配置,@EnableAutoConfiguration注解的意思就是Springboot根据你添加的jar包来配置你项目的默认配置,比如根据spring-boot-starter-web ,来判断你的项目是否需要添加了webmvc和tomcat,就会自动的帮你配置web项目中所需要的默认配置。在下面博客会具体分析这个注解,快速入门的demo实际没有用到该注解。
@ComponentScan,扫描当前包及其子包下被@Component,@Controller,@Service,@Repository注解标记的类并纳入到spring容器中进行管理。是以前的context:component-scan(以前使用在xml中使用的标签,用来扫描包配置的平行支持)。所以本demo中的User为何会被spring容器管理。
10.5 测试单元
package com.hnxy.test;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.hnxy.entity.Account;
import com.hnxy.service.AccountService;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = com.hnxy.SpringCoreApp.class)
public class AccountTest {
@Autowired
private AccountService service;
// 添加测试
@Test
public void addTest()throws Exception{
Account account = new Account();
account.setAname("测试添加");
account.setCreateDate(new Date());
account.setMoney(-100D);
int count = service.saveOrUpdateAccount(account);
String msg = count > 0 ? "添加成功" : "添加失败";
System.out.println(msg);
}
// 更新测试
@Test
public void updateTest()throws Exception{
// 修改ID为 1 的客户的名称为 测试更新
Account account = service.findAccountByID(1);
if(null != account){
account.setAname("测试更新");
int count = service.saveOrUpdateAccount(account);
String msg = count > 0 ? "更新成功" : "更新失败";
System.out.println(msg);
}else{
System.out.println("没有找到要更新的数据");
}
}
// 删除测试
@Test
public void deleteTest()throws Exception{
// 删除 id为 10002 的账户信息
Account account = service.findAccountByID(10002);
if(null != account){
account.setAname("测试更新");
int count = service.deleteAccount(account);
String msg = count > 0 ? "删除成功" : "删除失败";
System.out.println(msg);
}else{
System.out.println("没有找到要删除的数据");
}
}
// 分页测试
@Test
public void selectTest()throws Exception{
// 获取第一页数据
Map<String,Object> result = service.findAccountsByPage(1, 10);
int pageIndex = (Integer)result.get("pageIndex");
int pageSize = (Integer)result.get("pageSize");
long totalCount = (Long)result.get("totalCount");
int totalPage = (Integer)result.get("totalPage");
List<Account> accounts = (List<Account>)result.get("pageList");
System.out.println("一共查询出"+totalCount+"条数据,每页展示"+pageSize+"条,一共展示"+totalPage+"页,当前为第"+pageIndex+"页");
for (Account account : accounts) {
System.out.println(account);
}
}
}
10.6 SSS框架功能实现
10.6.1 系统首页+分页功能
加入静态资源
其中
webapp --> web项目根目录
css --> 样式目录
fonts --> 字体图标
img --> 图片
js --> 脚本文件
add.jsp --> 添加页面
comm_header.jsp --> 公共的头部页面
error.jsp --> 错误页面
index.jsp --> 系统首页
list.jsp --> 数据展示页面
update.jsp --> 更新页面
创建com.hnxy.web 包,创建DefaultAction 类做首页跳转与异常处理跳转
package com.hnxy.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
@ControllerAdvice
public class DefaultAction {
// 系统首页跳转
@RequestMapping("/")
public String home(){
return "forward:/index.jsp";
}
// 异常处理
@ExceptionHandler(Exception.class)
public ModelAndView showErrorMsg(Exception e){
ModelAndView mv = new ModelAndView();
mv.addObject("msg", e);
mv.setViewName("forward:/error.jsp");
return mv;
}
}
编写系统首页
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>系统首页</title>
<jsp:include page="/comm_header.jsp" />
</head>
<body>
<div class="jumbotron">
<div class="container">
<h1>欢迎使用账户信息管理系统!</h1>
<p>请选择一个功能吧 <span class="glyphicon glyphicon-qrcode"></span></p>
<p>
<a onclick="showDatas();" class="btn btn-primary btn-lg" role="button"><span class="glyphicon glyphicon-search"></span> 查看账户信息</a>
</p>
<p>
<a onclick="goAdd();" class="btn btn-success btn-lg" role="button"><span class="glyphicon glyphicon-plus"></span> 添加账户信息</a>
</p>
</div>
</div>
</body>
</html>
运行SpringCoreApp,看下页面效果
首页效果完成之后我们根据首页的显示账户信息的URL请求编写后端控制器代码
控制器实现 :
package com.hnxy.web;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import com.hnxy.service.AccountService;
@Controller
public class AccountViewAction {
// 创建service对象
@Autowired
private AccountService service;
private static final Integer pageSize = 10;
@RequestMapping("/findAccountsByPageList")
public ModelAndView findAccountsByPageList(
@RequestParam(value="pi",required=false,defaultValue="1") Integer pageIndex)throws Exception{
// 创建方法的返回值
ModelAndView mv = new ModelAndView();
// 获取数据
Map<String,Object> accounts = service.findAccountsByPage(pageIndex, pageSize);
// 保存数据
mv.addObject("pages",accounts);
mv.setViewName("forward:/list.jsp");
// 返回
return mv;
}
}
分页页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>数据展示</title>
<jsp:include page="/comm_header.jsp" />
</head>
<body>
<div>
<h1>数据展示</h1>
<br />
<button type="button" class="btn btn-primary" onclick="javascript:window.location.href='${pageContext.request.contextPath}/index.jsp'">返回首页</button>
<br />
<br />
<table class="table table-hover">
<tr class="default">
<td colspan="5" align="left">
一共查询出<span class="sp">${pages.totalCount}</span>条数据,
每页展示<span class="sp">${pages.pageSize}</span>条,
一共可以展示<span class="sp">${pages.totalPage}</span>页,
当前为第<span class="sp">${pages.pageIndex}</span>页;
</td>
</tr>
<tr class="info">
<th>客户编号</th>
<th>客户姓名</th>
<th>开户时间</th>
<th>账户余额</th>
<th>操作</th>
</tr>
<c:forEach items="${pages.pageList}" var="a" varStatus="sta">
<tr align="center">
<td>${sta.index+1+(pages.pageIndex-1)*pages.pageSize}</td>
<td>${a.aname}</td>
<td>
<fmt:formatDate value="${a.createDate}" pattern="yyyy年MM月dd日"/>
</td>
<td>
<fmt:formatNumber type="currency" value="${a.money}" />
</td>
<td>
<button type="button" class="btn btn-warning" onclick="goUpdate(${a.aid});"><span class="glyphicon glyphicon-edit"></span> 更新</button>
<button type="button" class="btn btn-danger" onclick="goDelete(${a.aid});"><span class="glyphicon glyphicon-trash"></span> 删除</button>
</td>
</tr>
</c:forEach>
<tr>
<th colspan="6" class="default">
<button type="button" class="btn btn-default" onclick="goPage(1);"><span class="glyphicon glyphicon-fast-backward"></span> 首页</button>
<!-- 上一页 和 下一页的业务 第一页不能点上一页 最后一页 不能点下一页 -->
<c:choose>
<c:when test="${pages.pageIndex > 1}">
<button type="button" class="btn btn-default" onclick="goPage(${pages.pageIndex-1});"><span class="glyphicon glyphicon-backward"></span> 上一页</button>
</c:when>
<c:otherwise>
<button type="button" class="btn btn-default" disabled="disabled"><span class="glyphicon glyphicon-backward"></span> 上一页</button>
</c:otherwise>
</c:choose>
<c:choose>
<c:when test="${pages.totalPage > pages.pageIndex}">
<button type="button" class="btn btn-default" onclick="goPage(${pages.pageIndex+1});"><span class="glyphicon glyphicon-forward"></span> 下一页</button>
</c:when>
<c:otherwise>
<button type="button" class="btn btn-default" disabled="disabled"><span class="glyphicon glyphicon-forward"></span> 下一页</button>
</c:otherwise>
</c:choose>
<button type="button" class="btn btn-default" onclick="goPage(${pages.totalPage});"><span class="glyphicon glyphicon-fast-forward"></span> 末页</button>
</th>
</tr>
</table>
</div>
</body>
</html>
分页JS
/* 系统分页函数 */
function goPage(pageIndex){
window.location.href = "findAccountsByPageList?pi="+pageIndex;
}
10.7 页面添加
添加页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>数据添加</title>
<jsp:include page="/comm_header.jsp" />
</head>
<body>
<div>
<h1>添加账户数据</h1>
<br />
<form action="saveOrUpdateAccount" method="post">
<fieldset>
<legend>客户数据收集表</legend>
<table class="table table-hover">
<tr class="default">
<td>客户姓名 : </td>
<td>
<input type="text" class="form-control" required="required" name="aname" placeholder="请输入客户的真实姓名☺" maxlength="10"/>
</td>
</tr>
<tr class="default">
<td>开户时间 : </td>
<td>
<input type="date" class="form-control" required="required" name="createDate" />
</td>
</tr>
<tr class="default">
<td>账户金额 : </td>
<td>
<input class="form-control" step="0.01" type="number" min="10" max="100000" required="required" name="money" placeholder="请填写10-100000之间的账户余额 ☺"/>
</td>
</tr>
<tr class="default">
<td colspan="2">
<button type="submit" class="btn btn-success"><span class="glyphicon glyphicon-ok"></span> 确定</button>
<button class="btn btn-danger" type="reset" onclick="javascript:history.go(-1);"><span class="glyphicon glyphicon-remove"></span> 取消</button>
</td>
</tr>
</table>
</fieldset>
</form>
</div>
</body>
</html>
后端控制器
package com.hnxy.web;
import java.text.SimpleDateFormat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.validation.DataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.hnxy.entity.Account;
import com.hnxy.service.AccountService;
@Controller
public class AccountOperateAction {
// 创建业务层对象
@Autowired
private AccountService service;
// 时间处理
@InitBinder
public void initDate(DataBinder binder)throws Exception{
binder.registerCustomEditor(java.util.Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), false));
}
@RequestMapping("/saveOrUpdateAccount")
public ModelAndView saveOrUpdateAccount(Account account)throws Exception{
// 创建方法的返回值
ModelAndView mv = new ModelAndView();
// 执行
int count = service.saveOrUpdateAccount(account);
// 判断返回
if(count > 0){
mv.setViewName("redirect:/findAccountsByPageList");
}else{
mv.addObject("msg","很抱歉,添加失败!");
mv.setViewName("forward:/error.jsp");
}
// 返回
return mv;
}
}
时间类型转换
// 时间处理
@InitBinder
public void initDate(DataBinder binder)throws Exception{
binder.registerCustomEditor(java.util.Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), false));
}
编写错误提示页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>系统通知</title>
<jsp:include page="/comm_header.jsp" />
</head>
<body>
<div class="jumbotron">
<div class="container">
<h1 style="color: red;">哎呀~~系统不小心出现了一个小错误!</h1>
<p>错误的原因可能是 : ${msg} <span class="glyphicon glyphicon-qrcode"></span></p>
<p>
<a onclick="${pageContext.request.contextPath}/index.jsp" class="btn btn-primary btn-lg" role="button"><span class="glyphicon glyphicon-home"></span> 返回系统首页</a>
</p>
</div>
</div>
</body>
</html>
10.8 页面删除
页面的JS
/* 系统删除方法 */
function goDelete(aid){
// 询问
var con = confirm("确定要删除这条数据么?");
if(con == true){
// 确定删除
window.location.href = "deleteAccount?aid="+aid;
}
}
执行的方法
@RequestMapping("/deleteAccount")
public ModelAndView deleteAccount(Integer aid) throws Exception {
// 创建方法的返回值
ModelAndView mv = new ModelAndView();
// 查找
Account account = service.findAccountByID(aid);
// 判断
if (null != account) {
// 执行
int count = service.deleteAccount(account);
// 判断返回
if (count > 0) {
mv.setViewName("redirect:/findAccountsByPageList");
} else {
mv.addObject("msg", "很抱歉,删除失败!");
mv.setViewName("forward:/error.jsp");
}
}else{
mv.addObject("msg", "很抱歉,删除失败!");
mv.setViewName("forward:/error.jsp");
}
// 返回
return mv;
}
10.9 页面更新
更新JS
/* 系统准备更新方法(更新反显方法) */
function goUpdate(aid){
window.location.href = "findAccountById?aid="+aid;
}
后端控制器
@RequestMapping("/findAccountById")
public ModelAndView findAccountByID(Integer aid)throws Exception{
// 创建方法的返回值
ModelAndView mv = new ModelAndView();
// 获取数据
Account account = service.findAccountByID(aid);
// 保存数据
mv.addObject("acc", account);
// 设置转发地址
mv.setViewName("forward:/update.jsp");
// 返回
return mv;
}
更新页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>更新添加</title>
<jsp:include page="/comm_header.jsp" />
</head>
<body>
<div>
<h1>更新账户数据</h1>
<br />
<form action="saveOrUpdateAccount" method="post">
<input type="hidden" name="aid" value="${acc.aid}" />
<fieldset>
<legend>客户数据收集表</legend>
<table class="table table-hover">
<tr class="default">
<td>客户姓名 : </td>
<td>
<input value="${acc.aname}" type="text" class="form-control" required="required" name="aname" placeholder="请输入客户的真实姓名☺" maxlength="10"/>
</td>
</tr>
<tr class="default">
<td>开户时间 : </td>
<td>
<input value="<fmt:formatDate value="${acc.createDate}" pattern="yyyy-MM-dd"/>" type="date" class="form-control" required="required" name="createDate" />
</td>
</tr>
<tr class="default">
<td>账户金额 : </td>
<td>
<input value="${acc.money}" class="form-control" step="0.01" type="number" min="10" max="100000" required="required" name="money" placeholder="请填写10-100000之间的账户余额 ☺"/>
</td>
</tr>
<tr class="default">
<td colspan="2">
<button type="submit" class="btn btn-success"><span class="glyphicon glyphicon-ok"></span> 确定</button>
<button class="btn btn-danger" type="reset" onclick="javascript:history.go(-1);"><span class="glyphicon glyphicon-remove"></span> 取消</button>
</td>
</tr>
</table>
</fieldset>
</form>
</div>
</body>
</html>
执行方法(这个方法在做添加的时候就已经写了,这里只是拿出来看一下,不用重复编写)
@RequestMapping("/saveOrUpdateAccount")
public ModelAndView saveOrUpdateAccount(Account account) throws Exception {
// 创建方法的返回值
ModelAndView mv = new ModelAndView();
// 执行
int count = service.saveOrUpdateAccount(account);
// 判断返回
if (count > 0) {
mv.setViewName("redirect:/findAccountsByPageList");
} else {
mv.addObject("msg", "很抱歉,添加失败!");
mv.setViewName("forward:/error.jsp");
}
// 返回
return mv;
}
编写druid的web监控功能类
创建 com.hnxy.util 包 创建 DruidWebStatInit 类
package com.hnxy.util;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
@Configuration
public class DruidWebStatInit {
// 配置过滤器
@Bean
public FilterRegistrationBean webStatFilter(){
FilterRegistrationBean beanFilter = new FilterRegistrationBean();
beanFilter.setFilter(new WebStatFilter());
Map<String,String> initParams = new HashMap<>();
// 需要排除拦截的url可以在此处配置
initParams.put("exclusions","*.js,*.css,/druid/*");
beanFilter.setInitParameters(initParams);
beanFilter.setUrlPatterns(Arrays.asList("/*"));
return beanFilter;
}
// 配置servlet
@Bean
public ServletRegistrationBean setStatViewServlet(){
ServletRegistrationBean beanServlet = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
Map<String,String> initParams = new HashMap<>();
initParams.put("loginUsername","admin");
initParams.put("loginPassword","admin123");
initParams.put("allow",""); // 允许访问
// initParams.put("deny","127.0.0.1"); // 不许许
initParams.put("resetEnable","false");
beanServlet.setInitParameters(initParams);
return beanServlet;
}
}
验证 :