1.需求(为行政人员发布公告,通知提供遍历)
2.原型设计(系统做完以后是什么样子的,先做一个设计稿)
3.表的设计(公告内容中包含哪些字段)
4.技术分层架构(分而治之-将复杂问题简单化)
5.技术栈的设计(数据库端技术,服务端技术,客服端技术)
目的:以项目为驱动,讲解技术的应用。
系统公告是方便公司行政管理人员向公司全员或部分指定人员发送通知公告的模块。如开会通知、放假通知及其它信息。使用户可以很方便的了解公司动向。
基于系统公告业务,现对公告表进行设计,sql 脚本如下:
drop database if exists db_notice;
create database if not exists db_notice default character set utf8; use db_notice
drop table if exists sys_ notice;
create table sys_ notice (
id int(4) auto_ increment comment'ID',
title varchar(50) not null comment'标题 ',
type char(1) not null comment'类型(1通知 2公告),
content varchar(500) default null comment'公告内容',
status char(1) default '0' comment'状态(0 正常 1关闭)',
createdUser varchar(64) default'' comment'创建者' ,
createTime datetime comment'创建时间',
modifiedUser varchar(64) default'' comment'更新者',
modifiedTime datetime comment'更新时间',
remark varchar( 255) comment'备注',
primary key (id)
) engine=innodb auto_increment=1 comment = '通知公告表';
系统分层设计是一种设计思想(分而治之),是让每层对象都有一个独立职责,再让多层对象协同(耦合)完成一个完整的功能。这样做可以更好提高系统可扩展性,但同时也会增加系统整体运维的难度。
其中,在上图中的箭头表示一种直接依赖关系,开放接口层可以依赖于 Web 层,也可以直接依赖于 Service 层,其它依此类推(具体每层要实现的逻辑可自行查阅阿里巴巴开发手册)。
JAVAEE 应用体系中繁重的配置、低下的开发效率、高难度的三方集成,复杂的部署流程等等一直被开发人员所诟病。
Spring 这样的轻量级的资源整合框架,在实现其相对比较多的资源整合时,依旧需要大量的手动依赖管理,复杂的 XML 配置(还经常没有提示)。
现在的软件生态应用也已经形成一定的规模,系统架构正在从单体架构,分布式架构, 跨越到微服务架构。随着整个架构体系的变化,企业对技术的要求也在变化,现在的企业更注重技术的开箱即用,更注重技术在生态圈中的深度融合,更注重轻量级的运维。由此由此spring boot 诞生。
Spring Boot 是 Java 软件开发框架(很多人现在把它理解为一个脚手架),其设计目的是用来简化 Spring 项目的初始搭建以及开发过程。该框架使用了特定的注解方式来进行配置,从而使开发人员不再需要大量的 xml 配置。不再需要大量的手动依赖管理。Spring Boot 基于快速构建理念,通过约定大于配置,开箱即用的方式,希望能够在蓬勃发展的快速应用开发领域成为其领导者。
SpringBoot 其核心特性包含如下几个方面:
起步依赖(Starter Dependency),
自动配置(Auto Configuration),
健康检查(Actator)-监控。
嵌入式服务(Tomcat,Jetty)等。
SpringBoot 工程中由 SpringBootApplication 注解描述的类为启动入口类,例如:
package com.cy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
//Application.class
public static void main(String[] args) {
//Main Thread
SpringApplication.run(Application.class,args);
}
}
运行启动类检测输出结果,了解其启动过程,如图所示:
其中,项目在启动时要做哪些基础操作.
池化思想是我们项目开发过程中的一种非常重要的思想,如整数池,字符串池,对象池、连接池、线程池等都是池化思想一种应用,都是通过复用对象,以减少因创建和释放对象所带来的资源消耗,进而来提升系统性能。
例如 Integer 对象的内部池应用,代码如下:
package com.cy.java.pool;
public class TestInteger01 {
public static void main(String[] args) {
//演示整数池(-128 ~ 127)
Integer n1=100;//Integer.valueOf(100) 编译时优化
Integer n2=100;
//对整数而言为什么不将所有整数都放到池中,而只是存储了一部分数据呢?
//池设计的目的是? 以空间换时间,这块空间中应该存储一些常用的整数数据
Integer n3=200;
Integer n4=200;// 池 中 没 有 则 new Integer(200)
System.out.println(n1==n2);//true,池存储着-128~+127
System.out.println(n3==n4);//false
//所有的池设计都会用到一种设计
}
}
项目开发过程中应用程序与数据库交互时,“获得连接”或“释放连接”是非常消耗系统资源的两个过程,频繁地进行数据库连接的建立和关闭会极大影响系统的性能,若多线程并发量很大,这样耗时的数据库连接就可能让系统变得卡顿。因为 TCP 连接的创建开支十分昂贵,并且数据库所能承载的 TCP 并发连接数也有限制,针对这种场景,数据库连接池应运而生。如图-1 所示:
Java 官方,为了在应用程序中更好的应用连接池技术,定义了一套数据源规范,例如javax.sql.DataSource 接口,基于这个接口,很多团队或个人创建了不同的连接池对象。然后我们的应用程序中通过耦合与 DataSource 接口,便可以方便的切换不同厂商的连接池。Java 项目中通过连接池获取连接的一个基本过程,如图-2 所示:
在图-2 中,用户通过 DataSource 对象的 getConnection()方法,获取一个连接。假如池中有连接,则直接将连接返回给用户。假如池中没有连接,则会调用 Dirver(驱动, 由数据库厂商进行实现)对象的 connect 方法从数据库获取,拿到连接以后,可以将连接在池中放一份,然后将连接返回给调用方。连接需求方再次需要连接时,可以从池中获取, 用完以后再还给池对象。
数据库连接池的江湖。
数据库连接池在 Java 数据库相关中间件产品群中,应该算是底层最基础的一类产品, 作为企业应用开发必不可少的组件,无数天才们为我们贡献了一个又一个的优秀产品,它们有的随时代发展,功成身退,有的则还在不断迭代,老而弥坚,更有新生代产品,或性能无敌,或功能全面。目前市场上常见的连接池有 DBCP、C3P0,DRUID,HikariCP 等。
数据库连接池的原理分析。
在系统初始化的时候,在内存中开辟一片空间,将一定数量的数据库连接作为对象存储在对象池里,并对外提供数据库连接的获取和归还方法。用户访问数据库时,并不是建立一个新的连接,而是从数据库连接池中取出一个已有的空闲连接对象;使用完毕归还后的连接也不会马上关闭,而是由数据库连接池统一管理回收,为下一次借用做好准备。如果由于高并发请求导致数据库连接池中的连接被借用完毕,其他线程就会等待,直到有连接被归还。整个过程中,连接并不会关闭,而是源源不断地循环使用,有借有还。数据库连接池还可以通过设置其参数来控制连接池中的初始连接数、连接的上下限数,以及每个连接的最大使用次数、最大空闲时间等,也可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。
数据库连接池的构成分析。
数据库连接池以连接池的管理为核心,主要支持连接池的建立和释放这两大核心功能。“麻雀虽小,五脏俱全”,数据库连接池还可以支持其他非常实用的功能。一款商用的数据库连接池、一款能够被开发者广泛使用的数据库连接池、一款能够在开源社区持续活跃发展的数据库连接池还必须再支持一些实用的功能,如并发(锁性能优化乃至无锁)、连接数控制(不同的系统对连接数有不同的需求)、监控(一些自身管理机制来监视连接的数量及使用情况等)、外部配置(各种主流数据库连接池官方文档最核心的部分)、资源重用(数据库连接池的核心思想)、检测及容灾(面对一些网络、时间等问题的自愈)、多库多服务(如不同的数据库、不同的用户名和密码、分库分表等情况)、事务处理(对数据库的操作符合 ALL- ALL-NOTHING 原则)、定时任务(如空闲检查、最小连接数控制)、缓存(如 PSCache 等避免对 SQL 重复解析)、异常处理(对 JDBC 访问的异常统一处理)、组件维护(如连接状态、JDBC 封装的维护)等。
打开 mysql 控制台,然后按如下步骤执行 notice.sql 文件。
链接:https://pan.baidu.com/s/1J8ZOFDoiP1ic4aXWGaW8XQ
提取码:k7xg
第一步:登录 mysql。
mysql –uroot –proot
第二步:设置控制台编码方式。
set names utf8;
第三步:执行 notice.sql 文件(切记不要打开文件复制到 mysql 客户端运行)。
source d:/notice.sql
连接池产品 HikariCP 拥有强劲的性能和稳定性,再加上它自身小巧的身形,在当前的“云时代、微服务”的背景下,HakariCP 得到了越来越多的人青睐。HiKariCP 号称是目
前世界上最快的连接池,有江湖一哥的称号。
添加依赖,其 pom 文件关键元素如下:
<!--链接 mysql 时使用的 mysql 驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--当添加 data-jdbc 依赖时会自动下载 HikariCp 依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
打开Settings -> Plugins ->下载插件
启动快捷键 Alt+insert
选择ok
可以选择需要增加的依赖
打开 application.properties 配置文件,添加如下内容。
# spring datasource
spring.datasource.url=jdbc:mysql:///db_notice?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root
hikariCP 其它额外配置,可通过搜索引擎进行查阅。
package com.cy.pj.sys.dao;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import javax.sql.DataSource;
import java.sql.*;
/**
* 为springboot中的单元测试类
* 说明:
* 1.springboot中的单元测试类必须放在启动类所在包或子包中
* 2.springboot中的单元测试类必须使用@SpringBootTest注解描述
*/
@SpringBootTest
public class DataSourceTests {
/**
* 在项目中添加了数据库相关依赖以后,
* springboot底层会自动帮我们配置一个数据源(DataSource)对象,
* 此对象是连接池的规划。
* @Autowired 注解描述属性时,是告诉spring框架,要基于反射机制为属性赋值(依赖注入)
* 赋值时,首先会基于属性类型从spring容器查找相匹配的对象,假如只有一个则直接注入
* 有多个相同类型的对象时,还会比较属性名(检查属性名是否与bean名字相同),有相同的
* 则直接注入(没有相同的则直接抛出异常)
*
*/
@Autowired
//private javax.sql.DataSource dataSource; //引入包名千万不要引入错了
private DataSource dataSource; //简写
@Test //org.junit.jupiter.api.Test
void testGetConnection() throws SQLException {
//获取链接时,
Connection conn = dataSource.getConnection();
System.out.println("conn="+conn);
//conn=HikariProxyConnection@1269826537 wrapping com.mysql.cj.jdbc.ConnectionImpl@12aa4996
}
}
SpringBoot技术
1.是什么?
基于spring技术实现的一个脚手架。
2.基于此脚手架需要做什么?
快速构建项目开发环境。
3.为什么要使用springboot搭建环境?
提供了开箱即用的特性(依赖),自动装箱…
4.springboot工程的创建,目录结构,启动分析
1)创建(基于http://start.spring.io官方提供的构建结构进行实现)
2)目录结构?
2.1)src/main/java (java业务代码)
2.2)src/main/resoutces (项目配置文件。静态资源)
2.3)src/test/java (单元测试代码)
2.4)pom.xml(服务端依赖,maven插件配置)
3)项目的启动
3.1)启动类(@SpringBootTest)
3.2)启动过程(了解启动过程)
1)我们写的类所在包与启动类所在包要建立什么关系? .
2)启动类在启动时我如何知道加载了哪些类? (-XX: +TraceClassLoading)
1.池化思想 (空间换时间,实现对象复用,进行减少对象创建销毁带来的系统开销)
1)整数池(Integer[])
2)字符串(char[])
3)线程池(ThreadPoolExecutor)
4)对象池(Spr ing中的bean池)
5)…
2.Java中连接池规范(DataSource)
1)为什么要创建连接池?(连接的创建和销毁非常耗时-TCP/IP-3次握手,4次挥手)
2) java中连接池的规范? (javax. sql. DataSource)
3) java中连接池的江湖? (c3p0, dbcp , Druid , hikariCP ,…)
4)连接池设计思考?(数据存储结构,算法,线程安全,参数设计,… )
3.Java中的连接池规范实现-HikariCP
1)特点:日本人->设计小而精,性能高,稳定…
2)SpringBoot默认优先加载? (添加了spring - jdbc依赖会自动配置hikaricp)
3)连接池的测试应用?
3.1)添加依赖? (mysq 1驱动, spring- jdbc依赖)
3.2)连接数据库的配置(url,username,password)
3.3)构建单元测试类及方法对连接池进行单元测试?(获取连接)
3.4)连接获取原理分析?(@Test->DataSource->HikariDataSource->HikariPool->.)
基于 JDBC 技术查询数据库表中的通告信息,并进行单元测试。
# spring datasource
spring.datasource.url=jdbc:mysql:///db_notice?serverTimezone=GMT%2B8&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root
getConnection() 方法,连接MySQL数据库。
statement(sql传送器->负责与将sql发送到数据库端)
setTimestamp 方法,使用 Date 型设置指定列的值(在mysql中 date数据类型 只能存放年月日,所以只能用datetime类型,那在jdbc中,就要调用setTimestamp()方法,取出数据的时候,用结果集调用getTimstamp()方法。)
currentTimeMillis()计算方式与时间的单位转换
package com.cy.pj.sys.dao;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import javax.sql.DataSource;
import java.sql.*;
/**
* 为springboot中的单元测试类
* 说明:
* 1.springboot中的单元测试类必须放在启动类所在包或子包中
* 2.springboot中的单元测试类必须使用@SpringBootTest注解描述
*/
@SpringBootTest
public class DataSourceTests {
/**
* 在项目中添加了数据库相关依赖以后,
* springboot底层会自动帮我们配置一个数据源(DataSource)对象,
* 此对象是连接池的规划。
* @Autowired 注解描述属性时,是告诉spring框架,要基于反射机制为属性赋值(依赖注入)
* 赋值时,首先会基于属性类型从spring容器查找相匹配的对象,假如只有一个则直接注入
* 有多个相同类型的对象时,还会比较属性名(检查属性名是否与bean名字相同),有相同的
* 则直接注入(没有相同的则直接抛出异常)
*
*/
@Autowired
//private javax.sql.DataSource dataSource; //引入包名千万不要引入错了
private DataSource dataSource; //简写
@Test //org.junit.jupiter.api.Test
void testGetConnection() throws SQLException {
//获取链接时,
Connection conn = dataSource.getConnection();
System.out.println("conn="+conn);
//conn=HikariProxyConnection@1269826537 wrapping com.mysql.cj.jdbc.ConnectionImpl@12aa4996
}
@Test
void testSaveNotice01()throws SQLException{
//homework (通过此方法基于jdbc向数据库写入一条数据)
//JDBC (是java中推出的连接数据库的一组API,是规范)
//数据库厂商提供JDBC驱动(jdbc规范的实现)负责实现数据库的操作.
//1.建立连接 (负责与数据库进行通讯)
Connection conn= dataSource.getConnection();
//2.创建statement(sql传送器->负责与将sql发送到数据库端)
String sql="insert into sys_notice " +
" (title,content,type,status,createTime,createdUser,modifiedTime,modifiedUser) " +
" values ('加课通知','本周六加课','1','0',now(),'tony',now(),'tony') ";
Statement stmt=conn.createStatement();
//3.发送sql
stmt.execute(sql);
//4.处理结果
//5.释放资源(后续释放资源要写到finally代码块中)
stmt.close();
conn.close();//将连接返回池中
}
@Test
void testSaveNotice02()throws SQLException{
//homework (通过此方法基于jdbc向数据库写入一条数据)
//JDBC (是java中推出的连接数据库的一组API,是规范)
//数据库厂商提供JDBC驱动(jdbc规范的实现)负责实现数据库的操作.
//1.建立连接 (负责与数据库进行通讯)
Connection conn= dataSource.getConnection();
//2.创建statement(sql传送器->负责与将sql发送到数据库端)
String sql="insert into sys_notice " +
" (title,content,type,status,createTime,createdUser,modifiedTime,modifiedUser) " +
" values (?,?,?,?,?,?,?,?) ";//?表示占位符
PreparedStatement stmt=conn.prepareStatement(sql);//预编译方式创建Statement对象
//3.发送sql
stmt.setString(1,"开学通知");
stmt.setString(2,"2021年2月18号 开学");
stmt.setString(3,"1");
stmt.setString(4,"0");
stmt.setTimestamp(5,new Timestamp(System.currentTimeMillis()));
stmt.setString(6,"jason");
stmt.setTimestamp(7,new Timestamp(System.currentTimeMillis()));
stmt.setString(8,"tony");
stmt.execute();
//4.处理结果
//5.释放资源(后续释放资源要写到finally代码块中)
stmt.close();
conn.close();//将连接返回池中
}
}
1.通过池中的连接借助JDBCAPI向数据库表中写入一 条通告信息.
1.1)JDBC编码步骤?
1.2)建立连接(Connect ion, Da taSource)
1.3)创建Statement (Statement, PreparedS tatement)
1.4)发送SQL
1.5)处理结果(查询操作需要处理结果)
1.6)释放资源
2.JDBC编程过程的问题分析
2. 1)步骤固定(记住了过后,有手就能写,没必要反复写)
2. 2)占位符参数赋值代码简单,但需要重复编写.
2.3)行映射远程代码比较简单,但是需要的代码量比较大,需要反复编写
2.4)SQL语句不够灵活(不支持分支结构,很难基于条件进行动态SQL的定制)
//第三种连接方式(查询)
@Test
void testSelectNotice( )throws SQLException{
//1.建立连接
Connection conn=dataSource.getConnection();
//2.创建查询语句,基于id查询
String sq1="select id,title,content,status, type,createTime "+
" from sys_notice where id >= ?";
PreparedStatement pstmt = conn.prepareStatement(sq1);
//3.发送sql
pstmt.setInt(1, 2);
boolean flag=pstmt.execute();
//4.处理结果
if (flag){
//true表示查询,有结果集
ResultSet rs=pstmt.getResultSet();
List<Map<String,Object>> list=new ArrayList<>();
while (rs.next()){
//一行记录应为一map对象(行映射)
Map<String,Object> map=new HashMap();//将来也可以使用pojo对象
// 将取出类的数据存储到map(key为字段名,值为字段value)
map.put("id",rs.getInt("id"));//取第一列的值
map.put("title",rs.getString("title"));//取第二列的值
map.put("content",rs.getString("content"));//取第二列的值
map.put("status",rs.getString("status"));//取第二列的值
map.put("type",rs.getString("type"));//取第二列的值
map.put("createTime",rs.getTimestamp("createTime"));//取第二列的值
//将每行记录对应的map对象存储到List集合
System.out.println(map);
list.add(map);
}
rs.close();
}
//5.释放资源
pstmt.close();
conn.close();
}
}
//第四种连接方式(优化)
@Test
void testSelectNotice2( )throws SQLException{
//1.建立连接
Connection conn=dataSource.getConnection();
//2.创建查询语句,基于id查询
String sql="select id,title,content,status,type,createTime " +
" from sys_notice where id>=?";
PreparedStatement pstmt=conn.prepareStatement(sql);
//3.发送sql(发送到数据库)
pstmt.setInt(1,2);
boolean flag=pstmt.execute();
//4.处理结果
//获取结果集中的元数据
if (flag){
//true表示查询,有结果集
ResultSet rs=pstmt.getResultSet();
List<Map<String,Object>> list=new ArrayList<>();
//获取结果集中的元shuju (表名,字段名)
ResultSetMetaData rsmd=rs.getMetaData();
while (rs.next()){
//一行记录应为一map对象(行映射)
Map<String,Object> map=new HashMap();//将来也可以使用pojo对象(HashMap底层重写了toString方法)
// 将取出类的数据存储到map(key为字段名,值为字段value)
for (int i = 1; i < rsmd.getColumnCount(); i++) {
map.put(rsmd.getColumnLabel(i), rs.getObject(rsmd.getColumnLabel(i)));
//rsmd.getColumnLabel(i) 获取第i列的名字
}
//下面这种写法更加简单,但是代码量比较大
/* map.put("id",rs.getInt("id"));//取第一列的值
map.put("title",rs.getString("title"));//取第二列的值
map.put("content",rs.getString("content"));//取第二列的值
map.put("status",rs.getString("status"));//取第二列的值
map.put("type",rs.getString("type"));//取第二列的值
map.put("createTime",rs.getTimestamp("createTime"));//取第二列的值*/
//将每行记录对应的map对象存储到List集合
System.out.println(map);
list.add(map);
}
rs.close();
}
//5.释放资源
pstmt.close();
conn.close();
}
}
理解 DataSource 规范及规范的实现。
掌握单元测试类、测试方法编写规范。
Java 中与数据库建立连接需要什么?数据库驱动
这个数据库驱动的设计需要遵守什么规范吗?JDBC
当我们通过 JDBC API 获取到一个连接以后,应用结束我们会将连接返回到池中吗?会
连接池在应用时有什么弊端吗?(会带来一定的内存开销,以空间换时间)
假如现在让你去设计一个连接池,你会考虑哪些问题?(存储,算法,线程,参数)
位置错误,当出现如图-情况时,先检测单元测试类是否写到了src/test/java 目录。如图所示:
类引入错误,DataSource 为javax.sql 包中的类型,如图所示:
连接错误:数据库连接不上,如图所示:
Mybatis 是一个优秀的持久层框架,底层基于 JDBC 实现与数据库的交互。并在 JDBC 操作的基础上做了封装和优化,它借助灵活的 SQL 定制,参数及结果集的映射方式,更好的适应了当前互联网技术的发展。Mybatis 框架的简单应用架构,如图-15 所示:
在当今的互联网应用中项目,mybatis 框架通常会由 spring 框架进行资源整合,作为数据层技术实现数据交互操作。
第一步:添加 mybatis 启动依赖。
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
我们添加了 mybatis 依赖以后,spring 框架启动时会对 mybatis 进行自动配置。例如 SqlSessionFactory 工厂对象的创建。
第二步:Mybatis 简易配置实现。
假如需要对 mybatis 框架进行简易配置,可以打开 application.properties 文件,在此文件中进行基本配置(可选,暂时可以不配置),例如:
mybatis.configuration.default-statement-timeout=30
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.mapper-locations=classpath:/mapper/*/*.xml
配置 mybatis 中的 sql 日志的输出:(com.cy 为我们写的项目的根包)
logging.level.com.cy=DEBUG
在 src/test/java 目录中添加测试类,对 mybatis 框架整合进行基本测试,代码如下:
package com.cy.pj.sys.dao;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.sql.Connection;
@SpringBootTest
public class MyBatisTests {
/**
* sqlsession是mybatis框架中实现与数据库进行绘画的入口对象
* 加入我们可以通过此对象获取与数据库的来凝结,表示可以通过mybatis框架实现与数据库会话
*/
@Autowired
//这里的sqlSession指向的对象是谁?
// (sqlSessionTemplate实现了线程接口,并且线程安全)
//不能随意修改接口,行业规范
private SqlSession sqlSession;
@Test
public void testGetConnection(){
//连接来自哪里?(来源与连接池(底层会自动将连接池注入给mybatis框架))
Connection conn=sqlSession.getConnection();
System.out.println("conn="+conn);
}
}
在 SpringBoot 脚手架工程中,Spring 框架会基于 MyBatis 框架底层配置,创建SqlSessionFactory 对象,然后再通过此工厂对象创建 SqlSession,最后基于 Springku 框架为测试类注入 SqlSession 对象,接下来,我们可以通过 SqlSession 对象实现与数据库的会话了。
基于 SpringBoot 脚手架工程对 MyBatis 框架的整合,实现对通告数据的操作。
创建 SysNotice 类,借助此类对象封装公告(通知)数据。
package com.cy.pj.sys.pojo;
/**
* SysNotice对象用于存储通知数据(例如)
* Java中的对象可以简单分为两大类型:
* 1) 一类是用于执行逻辑(打天下-控制逻辑,业务逻辑,数据持久逻辑)的对象
* 2)一 类是用于存储数据(守天下-pojo)的对象
*/
public class SysNotice {
private static final long serialVersionUID = 1L;
/** 公 告 ID */
private Long id;
/** 公 告 标 题 */
private String title;
/** 公告类型(1 通知 2 公告) */
private String type;
/** 公告内容 */
private String content;
/** 公告状态(0 正常 1 关闭) */
private String status;
/** 创建时间 */
private Date createdTime;
/** 修改时间*/
private Date modifiedTime;
/** 创建用户 */
private String createdUser;
/** 修改用户*/
private String modifiedUser;
/** 备注*/
private String setRemark;
//自己添加 set/get/toString 方法
}
第一步:定义通告业务数据层接口及业务方法。
package com.cy.pj.sys.dao;
@Mapper
public interface NoticeDao {
List<Notice> selectNotices(Notice notice);
int deleteById(Long… id);
@Select("select * from sys_notice where id=#{id}")//注解方式声明sql,复杂sql写入xml中
Notice selectById(Integer id); //基于id去查询
int insertNotice(Notice notice); //往表中写数据
int updateNotice(Notice notice);//修改表数据
}
在 src/test/java 目录中添加测试类,测试,代码如下:
package com.cy.pj.sys.dao;
import com.cy.pj.sys.pojo.SysNotice;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class SysNoticeDaoTests {
@Autowired
private SysNoticeDao sysNoticeDao;
@Test
void testInsertNotice(){
//创建SysNotice对象
SysNotice notice=new SysNotice();
notice.setTitle("CGB2011放假了");
notice.setContent("2021/02/08正式放假");
notice.setCreatedUser("清风");
notice.setModifiedUser("清风");
//将SysNotice对象持久化到数据库
sysNoticeDao.insertNotice(notice);//此方法的实现内部会通过SQLSession向表中写入数据。
}
}
其中:@Mapper 是由 MyBatis 框架中定义的一个描述数据层接口的的注解(所有的注解只起到一个描述性的作用),用于告诉 Spring 框架此接口的实现由 mybatis 创建,并将其实现类对象存储到 spring 容器。
第二步:创建 NoticeDao 接口对应的 SQL 映射文件,名字为 SysNoticeMapper.xml,
存储在 resources/mapper/sys 目录下,同时定义 xml 文件头以及根元素,代码如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cy.pj.sys.dao.NoticeDao">
</mapper>
第三步:在映射文件中添加 insertNotice 方法对应的 Sql 映射,关键代码如下:
<insert id="insertNotice" parameterType="com.cy.pj.sys.pojo.SysNotice">
insert into sys_notice
(title,type,content,status,remark,
createdTime,modifiedTime,createdUser,modifiedUser)
values
(#{
title},#{
type},#{
content},#{
status},#{
remark},
now(),now(),#{
createdUser},#{
modifiedUser})
</insert>
第四步:在映射文件中添加基于条件的 SQL 查询映射,关键代码如下:
<select id="selectNotices"
resultType="com.cy.pj.sys.pojo.Notice"> select *
from sys_notices
<where>
<if test="title!=null and title!=''">
title like concat ("%",#{
title},"%")
</if>
<if test="createdUser!=null and createdUser!=''">
and createdUser like concat ("%",#{
createdUser},"%")
</if>
<if test="type!=null and type!=''">
and type = #{
type}
</if>
</where>
</select>
第五步:在映射文件中定义更新公告对应的 SQL 映射,关键代码如下:
<update id="updateNotice">
update sys_notice
set title=#{
title},type=#{
type},content=#{
content},
status=#{
status},remark=#{
remark},modifiedTime=now(),
modifiedUser=#{
modifiedUser}
where id=#{
id}
</update>
第六步:在映射文件中定义删除操作对应的 SQL 映射,关键代码如下:
<delete id="deleteObjects">
delete from sys_notice
<where>
<if test="ids!=null and ids.length>0">
id in
<foreach collection="ids" open="(" close=")"
separator="," item="id">
#{
id}
</foreach>
</if>
or 1=2
</where>
</delete>
在 src/java/test 目录下定义测试类,对 NoticeDao 对象进行应用测试。
package com.cy.pj.sys.dao; import java.sql.SQLException; import javax.sql.DataSource;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class NoticeDaoTests {
@Autowired
private NoticeDao noticeDao;
….
}
4.5小节总结(Summary)
4.5.1重难点分析
4.5.2FAQ 分析
4.5.3BUG 分析
分层架构设计中通常会借助业务逻辑对象处理请求的核心和拓展业务。
定义通知业务逻辑接口及相关方法,关键代码如下:
package com.cy.pj.sys.service;
public interface SysNoticeService {
int saveNotice(Notice notice);
List<Notice> findNotices(Notice notice);
int deleteById(Integer[] ids);
Notice findById(Integer id);
Int updateNotice(Notice notice);
}
定义通知业务逻辑接口的实现类,并重写业务方法,关键代码如下:
package com.cy.pj.sys.service;
@Service
public class SysNoticeServiceImpl implements SysNoticeService {
@Autowired
private SysNoticeDao sysNoticeDao;
public int saveNotice(Notice notice){
int rows=sysNoticeDao.insertNotice(notice);
return rows;
}
public List<Notice> findNotices(Notice notice){
return sysNoticeDao.selectNotices();
}
public int deleteById(Integer[] ids){
int rows=sysNoticeDao.deleteById(ids);
return rows;
}
public Notice findById(Integer id){
Notice notice=sysNoticeDao.selectById(id);
return notice;
}
public int updateNotice(Notice notice){
int rows=sysNoticeDao.updateNotice(notice);
return rows;
}
package com.cy.pj.sys.service;
@SpringBootTest
public class NoticeServiceTests{
@Autowired
private SysNoticeService noticeService;
…..
}
MybatisSpring MVC 是 Spring 框架中基于 MVC 设计思想,实现的一个用于处理 Web 请求的模块。这个模块封装了对 Servlet 的技术的应用,简化了程序员对请求和响应过程中数据的处理,其简易架构分析如图-37 所示:
图-9 中,核心组件分析:
图-37
DispatcherServlet :前端控制器, 处理请求的入口。
HandlerMapping:映射器对象, 用于管理 url 与对应 controller 的映射关系。
Interceptors:拦截器,实现请求响应的共性处理。
Controller:后端控制器-handler, 负责处理请求的控制逻辑。
备注:假如希望了解 Spring MVC 的详细处理流程可以基于断点调试法进行跟踪。
添加 Spring Web 依赖(提供了 spring mvc 依赖支持),代码如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
其中,这个依赖添加时,会关联下载一个 tomcat 依赖(这个 tomcat 为一个嵌入式tomcat 服务器)
创建 SysNoticeController 类,处理客户端请求。
第一步:创建 SysNoticeController 类,并关联 service 对象,关键代码如下
package com.cy.pj.sys.web.controller;
@RestController
@RequestMapping("/notice/")
public class SysNoticeController {
@Autowired
private SysNoticeService sysNoticeService;
}
第二步:添加处理请求的方法,关键代码如下:
@RequestMapping("doUpdateNotice")
Public String doUpdateNotice(Notice notice){
sysNoticeService.updateNotice(notice);
return "update ok";
}
@RequestMapping("doFindById/{id}")
public Notice doFindById(@PathVariable Integer id){
return sysNoticeService.findById(id);
}
@RequestMapping ("doSaveNotice")
Public String doSaveNotice (Notice notice){
sysNoticeService.saveNotice(notice);
return "save ok";
}
@RequestMapping("doDeleteById")
public Notice doDeleteById(Integer… ids){
return sysNoticeService.deleteById(ids);
}
@RequestMapping("doFindNotices")
public List<Notice> doFindNotices(Notice){
return sysNoticeService.findNotices(Notice);
}
基于 Postman 向服务端发送请求,进行访问测试。