数据上链的文件上传系统WeBase上链的数据user_Name Stringfile_Name Stringfile_Size Floatfile_Type Stringfile_Hash Stringcreate_Time Dateupdate_Time Date表user表IDuser_Name Stringuser_Account Stringuser_Password Stringuser_Role integer三字段create table user ( ID int auto_increment comment 'ID' primary key, user_Name char(15) not null comment '名称', user_Account char(10) not null comment '账号', user_Password char(20) default '123456' null comment '密码', user_Role tinyint(1) default 1 not null comment '角色' );file表ID Stringuser_Id Stringfile_Name Stringfile_Size Floatfile_Type Stringfile_Hash String三字段create table file ( ID int auto_increment comment 'ID' primary key, user_ID int null comment 'user_ID', file_Name char(20) not null comment '文件名称', file_Size float not null comment '文件大小', file_Type char(10) default '未知' null invisible comment '文件类型', file_Hash char not null comment 'Hash', create_Time datetime not null comment '创建时间', update_Time datetime not null comment '更新时间', is_Delete tinyint(1) default 1 null comment '是否删除', constraint file_user_ID_fk foreign key (user_ID) references user (ID) );链表看返回什么,到时候说
一个随时随地通过FTP上传文件系统基于SpringBoot的FilePD文件系统pom文件<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.8</version> <relativePath/> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>demo</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.2</version> </dependency> <!-- <dependency>--> <!-- <groupId>com.baomidou</groupId>--> <!-- <artifactId>mybatis-plus-generator</artifactId>--> <!-- <version>3.1.0</version>--> <!-- </dependency>--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.20</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.76</version> </dependency> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.23</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot</artifactId> <version>2.7.8</version> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.9</version> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.10.3</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.2</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.2</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-gson</artifactId> <version>0.11.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>io.swagger.core.v3</groupId> <artifactId>swagger-annotations</artifactId> <version>2.2.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> <version>3.0.5</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-test</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.10</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.4.5</version> </plugin> </plugins> </build> </project> apache-maven-3.8.5.zipgit 下载 :https://registry.npmmirror.com/binary.html?path=git-for-windows/v2.42.0.windows.1/
我的面试宝典AIJava框架篇打乱顺序解析 : B D当发现有多种类型的Bean时，@Primary注解会通知IoC容器优先使用它所标注的Bean进行注入；@Quelifier注解可以与@AutoWired注解组合使用，达到通过类型和名称一起筛选Bean的效果。解析 : CSpring MVC 拦截器包含三个方法：preHandle()、postHandle()、afterCompletion()。​ preHandle() 方法：该方方法在控制器方法前执行，其返回值表示是否中断后续操作。当其返回值为true时，表示继续向下执行；当其返回值为false时，会中断后续的所有操作（包括调用下一个拦截器和控制器类中的方法执行等）。 ​ postHandle()方法：该方法在控制器方法调用之后，且解析视图之前执行。可以通过此方法对请求域中的模型和视图做出进一步的修改。 ​ afterCompletion()方法：该方法在整个请求完成，即视图渲染结束之后执行。可以通过此方法实现一些资源清理、记录日志信息等工作。解析 : Cmethod属性代表指定请求的method的类型，value属性指请求的实际地址，params是指定request中一定要有的参数值。required是@RequestParam注解的属性，是指该参数是否为必传项，默认为true，表示请求中一定要传入对应的参数。解析 : C解析 : A拦截器需实现 HandlerInterceptor 接口，而 WebMvcConfigurer 接口是 MVC 配置类要实现的接口解析 : C 目标方法允许结束后，若发生异常，会先执行异常通知，再执行后置通知。返回通知仅在无异常时执行@Before:Before 通知，用于方法执行前增强 @AfterReturning ：After Returning 通知，方法正常执行返回后增强 @AfterThrowing：After Throwing 通知，方法执行通过抛出异常退出时 @After：After (Finally) 通知，方法执行退出时执行增强，不管是正常返回，还是抛出异常退出，相当于try{}catch{}finally{}中的finally的语句。 @Around：Around 通知，最强大的通知，环绕在目标方法前后执行。它有机会在方法运行之前和之后进行工作，并确定该方法何时、如何以及是否真正开始运行@EnableAutoConfiguration注解解析 : A B C D@EnableAutoConfiguration由@SpringBootApplication引入，它的主要功能是启动Spring应用程序上下文时进行自动配置，它会尝试猜测并配置项目可能需要的Bean。@Import是@EnableAutoConfiguration注解的组成部分，也是自动配置功能的核心实现者。@SpringBootApplication @SpringBootConfiguration @EnableAutoConfiguration @AutoConfigurationPackage @Import(AutoConfigurationPackages.Registrar.class) @Import(AutoConfigurationImportSelector.class) @ComponentScanSpringBoot的核心注解 : @SpringBootApplication注解包含三个注解@SpringBootConfiguration​@EnableAutoConfiguration​ 实现自动配置的关键 , 此注解又包含两个关键注解@AutoConfigurationPackage将主启动类所在包以及子包里面的所有组件扫描并加载到spring的容器中，所以我们开发过程中Controller层、Service层、Dao层的代码总是和主启动类在同一级目录或者次级目录之下。@Import(AutoConfigurationImportSelector.class)来作自动配置至于哪些才是需要自动配置的类呢，AutoConfigurationImportSelector类里面有一个方法selectImports()，该方法通过层层调用，最终会调用一个方法loadSpringFactories()，此方加载所有的spring.factories文件，而spring.factories文件中的数据以（key-value）的形式存储，key就是EnableAutoConfiguration，value自然就是那些需要自动配置的类的全类名了，解析这个文件然后返回需要做自动配置的类的全类名@ComponentScanSpring常用容器解析 : A C​ A，B考察BeanFactory与FactoryBean。BeanFactory是Spring容器的接口，FactoryBean是Spring中用于实现“工厂Bean”的接口。​ C 是ApplicationContext，是BeanFactory的子接口。​ D应该是干扰选项，暂时没有找到相关解释。 另外，BeacFactory和ApplicationContext其实是有继承关系的两个接口。个人觉得题目所说的，“属于Spring容器的类”，存在问题。Spring AOP解析 : D 在运行时织入 , 需要为目标生成代理对象MySQL数据库篇MySQL技能树MySQLl触发器 : http://t.csdn.cn/2UGaQMySQL视图 : http://t.csdn.cn/0qUVaMySQL存储过程 : http://t.csdn.cn/B062N一 . 基础篇相关概念 、三大范式 、基本操作 、约束 、事务 、索引在MySQL中，"@@"和"@"符号用于引用系统和用户定义的变量，其区别在于：@@var_name：引用系统预定义的变量，如@@version、@@global.tx_isolation、@@autocommit等。这些变量的值是数据库服务器针对会话级别或全局级别的设置。@var_name：引用自定义会话级别变量，在当前会话中定义该变量，并将值赋给变量。可以使用SET 或 SELECT语句来声明和赋值变量，例如：SET @my_var := 10; SELECT @my_var;。1 . SQL分类1. 1 数据定义语言(DDL) Definition用来操作数据库、表、列等； 常用语句：create 、 alter 、drop1. 2 数据操作语言(DML) Manipulation用来操作数据库中表里的数据；常用语句：insert 、 update 、 deleteinsert-- 用法 insert into 'student' ('name') values ('At'); insert into 'student' ('name','pwd') values ('At','123'); -- 省略字段(值必须一一对应) , isdelete等公共字段可以不填 INSERT INTO `student` values ('xxx','xxx','xxx','xxx','xxx','xxx'); -- 语法 INSERT INTO 表名([字段1,字段2..])VALUES('值1','值2'..),[('值1','值2'..)..]; update-- 语法 UPDATE 表名 SET 字段1=值1,[字段2=值2...] WHERE 条件[]; -- 修改名字,指定条件 UPDATE `student` SET `name`='zsr204' WHERE id=1; -- 不指定条件的情况,会改动所有表 UPDATE `student` SET `name`='zsr204'; -- 修改多个属性 UPDATE `student` SET `name`='zsr',`address`='湖北' WHERE id=1; -- 通过多个条件定位数据 UPDATE `student` SET `name`='zsr204' WHERE `name`='zsr' AND `pwd`='200024';delete-- 删除数据(避免这样写,会全部删除) DELETE FROM `student`; -- 删除指定数据 DELETE FROM `student` WHERE id=1;关于DELETE删除的问题，重启数据库现象：INNODB 自增列会从1开始（存在内存当中，断电即失）MYISAM 继续从上一个子增量开始（存在内存当中，不会丢失）truncate ：完全清空一个数据库表，表的结构和索引约束不会变！ 不支持数据回滚1. 3 数据库控制语言(DCL) Control用来操作访问权限和安全级别； 常用语句：grant 、deny1. 4 数据查询语言(DQL) Query用来查询数据 常用语句：select-- 基本数据 -- 创建student表 CREATE TABLE student ( sid CHAR(6), sname VARCHAR(50), age INT, gender VARCHAR(50) DEFAULT 'male' ); -- 向student表插入数据 INSERT INTO student (sid,sname,age,gender) VALUES ('S_1001', 'lili', 14, 'male'); INSERT INTO student (sid,sname,age,gender) VALUES ('S_1002', 'wang', 15, 'female'); INSERT INTO student (sid,sname,age,gender) VALUES ('S_1003', 'tywd', 16, 'male'); INSERT INTO student (sid,sname,age,gender) VALUES ('S_1004', 'hfgs', 17, 'female'); INSERT INTO student (sid,sname,age,gender) VALUES ('S_1005', 'qwer', 18, 'male'); INSERT INTO student (sid,sname,age,gender) VALUES ('S_1006', 'zxsd', 19, 'female'); INSERT INTO student (sid,sname,age,gender) VALUES ('S_1007', 'hjop', 16, 'male'); INSERT INTO student (sid,sname,age,gender) VALUES ('S_1008', 'tyop', 15, 'female'); INSERT INTO student (sid,sname,age,gender) VALUES ('S_1009', 'nhmk', 13, 'male'); INSERT INTO student (sid,sname,age,gender) VALUES ('S_1010', 'xdfv', 17, 'female');-- 查询所有 select * from student; -- 查询指定字段 select sid,sname from student; -- 数据过滤 select distinct gender from student; -- 查询出来的所有age全部加10 select sname,age+10 from student;distinct作用 : 从查询结果中过滤重复数据在select查询语句中distinct关键字只能用在第一个所查列名之前。查询中的算术运算符在select查询语句中还可以使用加减乘除运算符。2 . 三大范式第一范式每列的值具有原子性，不可再分割第二范式在第一范式的基础上建立 , 如果表是单主键，那么主键以外的列必须完全依赖于主键；如果表是复合主键，那么主键以外的列必须完全依赖于主键，不能仅依赖主键的一部分。第三范式也是在第二范式的基础撒花姑娘建立的 , 意思是 : 非主键列之间不能相关依赖。3 . 基本操作完整的流程3 .1 库的操作0-- 如果存在先删除 drop database if exists 数据库名称; -- 创建数据库 create database 数据库名称; -- 创建数据库后查看该数据库基本信息MySQL命令 show create database 数据库名称; -- 删除命令 drop database 数据库名称; -- 展示所有数据 show databases; -- 更改字符集 alter database 数据库名称 character set gbk; -- 切换数据库 use 数据库名称; -- 查看当前使用的数据库 select database();3 . 2 表的操作创建数据表一个完整的用户表-- 用户表 create table if not exists user ( id bigint auto_increment comment 'id' primary key, userAccount varchar(256) not null comment '账号', userPassword varchar(512) not null comment '密码', userName varchar(256) null comment '用户昵称', userAvatar varchar(1024) null comment '用户头像', userProfile varchar(512) null comment '用户简介', userRole varchar(256) default 'user' not null comment '用户角色：user/admin/ban', createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间', updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间', isDelete tinyint default 0 not null comment '是否删除' ) comment '用户' collate = utf8mb4_unicode_ci;展示库中所有的表和查看数据表-- 查看所有表 show tables; -- 查询基本信息 show create table user; -- 查看字段信息 desc user;修改数据表修改表名、修改字段名、修改字段 数据类型…等等-- 修改表名 (下文还是user) alter table user rename to use; -- 修改字段 , 修改userName 为Uername (下文还是userName) alter table user change userName username varchar(10); -- 修改字段名 alter table user modify userName int; -- 增加字段 (新增otherID) alter table user add otherID int; -- 删除字段 alter table user drop otherID;删除数据表drop table 表名;4 . 约束约束分为 : 主键约束 、非空约束、默认值约束、外键约束、唯一性约束4 . 1 主键约束用于唯一的标识表中的每一行。被标识为主键的数据在表中是唯一的且其值不能为空-- 方式一 create table student( id int primary key, name varchar(20) ); -- 方式二 create table student( id int, name varchar(20), primary key(id) );4 . 2 非空约束即 NOT NULL指的是字段的值不能为空create table student02( id int name varchar(20) not null );4 . 2 默认值约束即 default 用于给数据表中的字段指定默认值create table student( id int, name varchar(20), gender varchar(10) default 'male' );4 . 4 唯一性约束即UNIQUE用于保证数据表中字段的唯一性 , 可以设置多个create table student( id int, name varchar(20) unique );4 . 5 外键约束常用于多张表之间的约束 , 主从关系-- 在创建数据表时语法如下： constraint 外键名 foreign key (从表外键字段) references 主表 (主键字段) -- 将创建数据表创号后语法如下： alter table 从表名 add constraint 外键名 foreign key (从表外键字段) references 主表 (主键字段);示例 :-- 创建student表 create table student( id int primary key, name varchar(20) ); -- 创建班级表 create table class( classid int primary key, studentid int ); -- 将班级表中的studentid设置为外键 叫fk_class_studentid alter table class add constraint fk_class_studentid foreign key(studentid) references student(id); 数据一致性建立外键是为了保证数据的完整和统一性 , 如果主表中的数据被删除或修改从表中对应的数据 , 从表对应的数据也应该被删除删除外键-- 以上述为例 alter table class drop foreign key fk_class_studentid;5 . 事务四个特性原子性（Atomicity）：指事务是一个不可分割的最小工作单位，事务中的操作只有都发生和都不发生两种情况一致性（Consistency）：事务必须使数据库从一个一致状态变换到另外一个一致状态，举一个栗子，李二给王五转账50元，其事务就是让李二账户上减去50元，王五账户上加上50元；一致性是指其他事务看到的情况是要么李二还没有给王五转账的状态，要么王五已经成功接收到李二的50元转账。而对于李二少了50元，王五还没加上50元这个中间状态是不可见的。隔离性（Isolation）：一个事务的执行不能被其他事务干扰，即一个事务内部的操作及使用的数据对并发的其他事务是隔离的，并发执行的各个事务之间不能互相干扰。持久性（Durability）：一个事务一旦提交成功，它对数据库中数据的改变将是永久性的，接下来的其他操作或故障不应对其有任何影响。事务分类事务分为隐式事务和显式事务两种。DML语句（insert、update、delete）就是隐式事务。显示事务：该事务具有明显的开启和结束标记；也是本文重点要讲的东西。使用显式事务的前提是你得先把自动提交事务的功能给禁用。禁用自动提交功能就是设置autocommit变量值为0（0:禁用 1:开启）隐式事务：该事务没有明显的开启和结束标记，它们都具有自动提交事务的功能；不妨思考一下，update语句修改数据时，是不是对表中数据进行改变了，它的本质其实就相当于一个事务。二 . 进阶篇此篇包含聚合函数及其他函数的运用 、条件查询的各种关键字 、关联查询 、子查询 、连接查询2. 1 函数聚合函数count()统计表中数据的行数或者统计指定列其值不为NULL的数据个数select count(*) from student;max()计算指定列的最大值，如果指定列是字符串类型则使用字符串排序运算select max(age) from student;min()计算指定列的最小值，如果指定列是字符串类型则使用字符串排序运算select sname,min(age) from student;sum()计算指定列的数值和，如果指定列类型不是数值类型则计算结果为0select sum(age) from student;avg()计算指定列的平均值，如果指定列类型不是数值类型则计算结果为select avg(age) from student;其他函数-- 时间函数 SELECT NOW(); SELECT DAY (NOW()); SELECT DATE (NOW()); SELECT TIME (NOW()); SELECT YEAR (NOW()); SELECT MONTH (NOW()); SELECT CURRENT_DATE(); SELECT CURRENT_TIME(); SELECT CURRENT_TIMESTAMP(); SELECT ADDTIME('14:23:12','01:02:01'); SELECT DATE_ADD(NOW(),INTERVAL 1 DAY); SELECT DATE_ADD(NOW(),INTERVAL 1 MONTH); SELECT DATE_SUB(NOW(),INTERVAL 1 DAY); SELECT DATE_SUB(NOW(),INTERVAL 1 MONTH); SELECT DATEDIFF('2019-07-22','2019-05-05'); -- 字符串函数 -- 连接函数 SELECT CONCAT () -- 用于在字符串中查找指定字符串或子字符串的位置（索引） SELECT INSTR (); -- 统计长度 SELECT LENGTH(); -- 数学函数 -- 绝对值 SELECT ABS(-136); -- 向下取整 SELECT FLOOR(3.14); -- 向上取整 SELECT CEILING(3.14);2. 2 条件查询关系运算符关系运算符说明=等于<>不等于!=不等于<小于<=小于等于>大于>=大于等于in关键字查询in关键字用于判断某个字段的值是否在指定集合中。如果字段的值恰好在指定的集合中，则将字段所在的记录将査询出来。-- 查询这俩 select * from student where sid in ('S_1002','S_1003'); -- 查询除过s_1001以外的 select * from student where sid not in ('S_1001'); between and关键字查询between and 用于判断某个字段的值是否在指定的范围之内。如果字段的值在指定范围内，则将所在的记录将查询出来-- 查询15到18的学生 select * from student where age between 15 and 18; -- 查询不在这个区间的 select * from student where age not between 15 and 18;空值查询在MySQL中，使用 IS NULL关键字判断字段的值是否为空值。请注意：空值NULL不同于0，也不同于空字符串-- 查询不为空 select * from student where sname is not null;and关键字查询在MySQL中可使用AND关键字可以连接两个或者多个查询条件。select * from student where age>15 and gender='male';or 关键字查询在使用SELECT语句查询数据时可使用OR关键字连接多个査询条件。在使用OR关键字时，只要记录满足其中任意一个条件就会被查询出来select * from student where age>15 or gender='male';like关键字查询MySQL中可使用LIKE关键字可以判断两个字符串是否相匹配-- 查询sname中与wang匹配的学生信息 select * from student where sname like 'wang';含有%通配的字符串-- 查询学生姓名以li开始的 select * from student where sname like 'li%'; -- 查询学生姓名以g结尾的 select * from student where sname like '%g'; -- 查询学生姓名包含s的 select * from student where sname like '%s%';含有__通配的字符串_-- 查询学生姓名以zx开头且长度为4的 select * from student where sname like 'zx__'; -- 查询学生姓名以g结尾且长度为4的 select * from student where sname like '___g';limit限制查询当执行查询数据时可能会返回很多条记录，而用户需要的数据可能只是其中的一条或者几条select * from student order by age asc limit 3;group by分组查询俗地理解为：通过GROUP BY将原来的表拆分成了几张小表。order by 排序查询2. 3 关联查询基本数据DROP TABLE IF EXISTS student; DROP TABLE IF EXISTS class; -- 创建班级表 CREATE TABLE class( cid int(4) NOT NULL PRIMARY KEY, cname varchar(30) ); -- 创建学生表 CREATE TABLE student( sid int(8) NOT NULL PRIMARY KEY, sname varchar(30), classid int(8) NOT NULL ); -- 为学生表添加外键约束 ALTER TABLE student ADD CONSTRAINT fk_student_classid FOREIGN KEY(classid) REFERENCES class(cid); -- 向班级表插入数据 INSERT INTO class(cid,cname)VALUES(1,'Java'); INSERT INTO class(cid,cname)VALUES(2,'Python'); -- 向学生表插入数据 INSERT INTO student(sid,sname,classid)VALUES(1,'tome',1); INSERT INTO student(sid,sname,classid)VALUES(2,'lucy',1); INSERT INTO student(sid,sname,classid)VALUES(3,'lili',2); INSERT INTO student(sid,sname,classid)VALUES(4,'domi',2); 查询-- 查询Java班的所有学生 select * from student where classid=(select cid from class where cname='Java');2. 4 连接查询举例student表学号姓名性别001Alice女002Bob男003Charlie男004David男005Eve女course表课程号课程名学分001数据结构4002操作系统5003计算机网络3004编译原理4005数据库系统原理5SC 表学号课程号成绩001001900010028500100392002002880020038900200495003001800030038700300596004001850040028200400389005001900050028800500393出几道子查询不用join的题目(分组并having过滤啥的各种函数),并给出答案问题 : 1> 查询学生姓名、课程名和成绩SELECT student.姓名, course.课程名, sc.成绩 FROM student JOIN sc ON student.学号 = sc.学号 JOIN course ON course.课程号 = sc.课程号; 2> 查询每个学生的平均成绩，并按照平均成绩从高到低排序。SELECT s.学号, s.姓名, AVG(sc.成绩) AS 平均成绩 FROM 学生 s INNER JOIN SC sc ON s.学号 = sc.学号 GROUP BY s.学号, s.姓名 ORDER BY 平均成绩 DESC;3> 查询每门课程的平均成绩，并按照平均成绩从高到低排序。SELECT c.课程号, c.课程名, AVG(sc.成绩) AS 平均成绩 FROM course c INNER JOIN SC sc ON c.课程号 = sc.课程号 GROUP BY c.课程号, c.课程名 ORDER BY 平均成绩 DESC;4> 查询选修了数据结构课程的男生中，成绩最高的学生的学号、姓名和成绩。SELECT s.学号, s.姓名, MAX(sc.成绩) AS 成绩 FROM 学生 s INNER JOIN SC sc ON s.学号 = sc.学号 WHERE s.性别 = '男' AND sc.课程号 = '001' GROUP BY s.学号, s.姓名 ORDER BY 成绩 DESC LIMIT 1;内连接查询内连接(Inner Join)又称简单连接或自然连接，是一种非常常见的连接查询。内连接使用比较运算符对两个表中的数据进行比较并列出与连接条件匹配的数据行，组合成新的 记录。也就是说在内连接查询中只有满足条件的记录才能出现在查询结果中。select student.name , sc.grade from sc inner join student on sc.sno = student.sno; -- 其他举例 -- 查询学员所属的年级（学号，学生姓名，年级名称） SELECT `StudentNo`,`StudentName`,`GradeName` FROM student s INNER JOIN grade g ON s.GradeID=g.GradeID; -- 查询科目所属的年级 SELECT `SubjectName`,`GradeName` FROM `subject` s INNER JOIN `grade` g ON s.GradeID=g.GradeID; -- 查询列参加程序设计考试的同学信息（学号，姓名，科目名，分数） SELECT s.`StudentNo`,`StudentName`,`SubjectName`,`StudentResult` FROM student s INNER JOIN result r on s.StudentNo=r.StudentNo INNER JOIN `subject` sub on r.SubjectNo=sub.SubjectNo where SubjectName='课程设计';外连接查询在使用内连接查询时我们发现：返回的结果只包含符合查询条件和连接条件的数据。但是，有时还需要在返回查询结果中不仅包含符合条件的数据，而且还包括左表、右表或两个表中的所有数据，此时我们就需要使用外连接查询。左外连接查询右外连接查询2. 5 子查询举例1> 查询每个学生的平均分SELECT s.学号, s.姓名, AVG(sc.成绩) AS 平均分 FROM student AS s, sc WHERE s.学号 = sc.学号 GROUP BY s.学号, s.姓名;2> 查询成绩优秀的前三名学生SELECT s.学号, s.姓名, SUM(sc.成绩) AS 总成绩 FROM student AS s, sc WHERE s.学号 = sc.学号 GROUP BY s.学号, s.姓名 ORDER BY 总成绩 DESC LIMIT 3;3>查询选修了所有课程的学生SELECT s.学号, s.姓名 FROM student AS s WHERE NOT EXISTS( SELECT c.课程号 FROM course AS c WHERE NOT EXISTS( SELECT sc.学号 FROM sc WHERE sc.课程号 = c.课程号 AND sc.学号 = s.学号 ) )查询分数最高的学生及其所修的课程和成绩-- join on SELECT student.姓名, course.课程名, sc.成绩 FROM student JOIN sc ON student.学号 = sc.学号 JOIN course ON course.课程号 = sc.课程号 WHERE sc.成绩 = ( SELECT MAX(成绩) FROM sc WHERE 学号 = student.学号 ); -- and SELECT S.姓名, C.课程名, SC.成绩 FROM student S, course C, sc SC WHERE S.学号 = SC.学号 AND C.课程号 = SC.课程号 AND SC.成绩 = ( SELECT MAX(成绩) FROM sc WHERE 学号 = S.学号 );带比较运算符比较运算符前面我们提到过得，就是>、<、=、>=、<=、!=等select * from class where cid=(select classid from student where sname='张三'); select * from class where cid>(select classid from student where sname='张三');带exists关键字EXISTS关键字后面的参数可以是任意一个子查询， 它不产生任何数据只返回TRUE或FALSE。当返回值为TRUE时外层查询才会 执行select * from class where exists (select * from student where sname='王五');带any关键字ANY关键字表示满足其中任意一个条件就返回一个结果作为外层查询条件。select * from class where cid > any (select classid from student);带all关键字ALL关键字与ANY有点类似，只不过带ALL关键字的子査询返回的结果需同时满足所有内层査询条件。select * from class where cid > all (select classid from student);重要（从关键字分析）：查询语句的书写顺序和执行顺序select ==> from ==> where ==> group by ==> having ==> order by ==> limit查询语句的执行顺序from ==> where ==> group by ==> having ==> select ==> order by ==> limit2 . 6 视图CSDN视图不是表，不保存数据，只是一张虚拟表；示例: ​ 数据库视图是在关系数据库中定义的虚拟表格。它是一个基于一个或多个数据库表的查询结果集，并且与查询结果集相关联的视图是动态的。与实际的数据库表格不同，视图并不具有自己的数据，而是从一个或多个实际数据库表格中提取出数据，并根据某种规则呈现出来。视图的作用在于提供一种通过表的连结机制而过滤的数据表视图机制。一般情况下，视图只存储视图定义，而不存储数据。在访问视图时，查询解析器会使用视图定义来生成查询语句，并将其发送给关系数据库 引擎执行。因此，使用视图可以在不修改基础表结构的情况下，实现数据的不同组合、排序、分组和筛选等处理方式。同时，视图还可以提供一个对基础表的安全性和完整性进行控制的手段，以保护敏感数据的访问。-- 以下是一个简单的视图示例，假设我们有一个名为“students”的表格，其中包含学生的成绩和基本信息，我们可以通过创建一个“优秀学生”的视图来筛选出所有分数大于等于80分（即“优秀”的学生）的学生信息： CREATE VIEW excellent_students AS SELECT name, score FROM students WHERE score >= 80; -- 这样一来，我们就可以通过访问excellent_students视图来查询包含所有“优秀”的学生信息了： SELECT * FROM excellent_students;总之，数据库视图是一种虚拟的、动态的表格，可以根据自定义的规则从一个或多个实际数据库表格中提取数据，并提供一种安全、灵活、高效的操作方式。它在关系数据库实现中扮演着重要的角色。相关的MySQL指令操作指令代码创建视图CREATE VIEW 视图名(列1，列2...) AS SELECT (列1，列2...) FROM ...;使用视图当成表使用修改视图CREATE OR REPLACE VIEW 视图名 AS SELECT [...] FROM [...];查看数据库已有视图SHOW TABLES [like...];（可以使用模糊查找）查看视图详情DESC 视图名或者SHOW FIELDS FROM 视图名视图条件限制[WITH CHECK OPTION]​创建视图创建视图和查询相比，增加了前面的create view 视图名 ascreate view 视图名(列1，列2...) as select (列1，列2...) from ...;查询视图正常当一张表来使用就行select * from 视图名称 [可跟条件];修改视图create or replace view 视图名 as select [...] from [...];查看视图show tables; -- 查看视图详情 , 俩都行 desc 视图名; show fields from 视图名;2 . 7 存储过程就是数据库 SQL 语言层面的代码封装与重用。存储过程：（PROCEDURE）是事先经过编译并存储在数据库中的一段SQL语句的集合。调用存储过程可以简化应用开发人员的很多工作，减少数据在数据库和应用服务器之间的传输，对于提高数据处理的效率是很有好处的。存储过程就类似于Java中的方法，需要先定义，使用时需要调用。存储过程可以定义参数，参数分为IN、OUT、INOUT类型三种类型。in代表输入参数（默认情况下为in参数），表示该参数的值必须由调用程序指定。out代表输出参数，表示该参数的值经存储过程计算后，将out参数的计算结果返回给调用程序。inout代表即时输入参数，又是输出参数，表示该参数的值即可有调用程序制定，又可以将inout参数的计算结果返回给调用程序。格式-- 调用 CALL xxx(); -- 定义 DELIMITER // CREATE /*[DEFINER = { user | CURRENT_USER }]*/ PROCEDURE 数据库名.存储过程名([in变量名 类型,out 参数 2，...]) /*LANGUAGE SQL | [NOT] DETERMINISTIC | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA } | SQL SECURITY { DEFINER | INVOKER } | COMMENT 'string'*/ BEGIN [DECLARE 变量名 类型 [DEFAULT 值];] 存储过程的语句块; END// -- DELIMITER ; 存储过程中的语句必须包含在BEGIN和END之间。DECLARE中用来声明变量，变量默认赋值使用的DEFAULT，语句块中改变变量值，使用SET 变量=值；示例查询student表中的sex为男的有多少个人。DELIMITER // CREATE PROCEDURE `demo`.`demo2`(IN s_sex CHAR(1),OUT s_count INT) -- 存储过程体 BEGIN -- 把SQL中查询的结果通过INTO赋给变量 SELECT COUNT(*) INTO s_count FROM student WHERE sex= s_sex; SELECT s_count; END// -- DELIMITER ; -- @s_count表示测试出输出的参数 CALL demo2 ('男',@s_count); 管理显示存储过程SHOW PROCEDURE STATUS显示存储过程的源码SHOW CREATE PROCEDURE 存储过程名;删除存储过程DROP PROCEDURE 存储过程名;Mybatis与存储过程<parameterMap type="savemap" id=“usermap"> <parameter property="name" jdbcType="VARCHAR" mode="IN"/> <parameter property="sex" jdbcType="CHAR" mode="IN"/> <parameter property="result" jdbcType="VARCHAR" mode="OUT"/> </parameterMap> <insert id="saveUserDemo" parameterMap="savemap" statementType="CALLABLE"> {call saveuser(?, ?, ?)} </insert >HashMap<String, Object> map = new HashMap<String, Object>(); map.put("name", "Jim"); map.put("sex","男"); //调用 userDao.saveUserDemo(map); map.get(“result”);//获得输出参数2 . 8 触发器触发器是一种特殊的存储过程，它在试图更改触发器所保护的数据时自动执行。在MySQL中，只有执行insert,delete,update操作时才能触发触发器的执行；触发器的这种特性可以协助应用在数据库端确保数据的完整性，日志记录，数据校验等操作；使用别名OLD和NEW来引用触发器中发生变化的记录内容，这与其他的数据库是相似的。现在触发器还只支持行级触发，不支持语句级触发；创建触发器1 . 创建只有一个执行语句的触发器create trigger 触发器名 before|after 事件 on 表名 for each row 语句;eg: 当给user表中插入一条数据时 , user_logs添加日志create trigger trigger_test1 after insert on user for each row insert into user_logs values(NULL,now(),'new');2 . 创建多个执行语句的触发器create trigger 触发器名 before|after 事件 on 表名 foreach row begin 语句 end;三 . 应用篇JDBC 、Mybatis 、Mybatis-PlusJava篇Linux篇理论篇1 . Linux的基本使用1 . 常用命令命令格式 :command [-options] [parameter]command ：命令名，相应功能的英文单词或单词的缩写[-options] ：选项，可用来对命令进行控制，也可以省略parameter ：传给命令的参数，可以是 零个、一个 或者 多个帮助命令-help 显示对应命令的帮助信息-man 查阅对应命令的使用手册操作键功能空格键显示手册页的下一屏回车键一次滚动手册页的一行b回滚一屏f前滚一屏q退出常用命令序号命令英文作用1lslist 2pwd 3cd 4touch 5mkdir 6rm 7clear 2 . 其他命令3 . 文件和目录的常用命令4 . 系统信息相关命令5 . 用户权限相关命令6 . 远程管理常用命令2 . 实战篇windows平台下 , 做一个类似cmd的网站Git篇
记一次Jwt生成 tokenThe signing key’s algorithm ‘AES’ does not equal a valid HmacSHA* algorithm name and cannot be used with HS256.The signing key’s size is 16 bits which is not secure enough for the HS256 algorithm. The JWT JWA Specification (RFC 7518, Section 3.2) states that keys used with HS256 MUST have a size >= 256 bits (the key size must be greater than or equal to the hash output size). Consider using the io.jsonwebtoken.security.Keys class’s ‘secretKeyFor(SignatureAlgorithm.HS256)’ method to create a key guaranteed to be secure enough for HS256. See https://tools.ietf.org/html/rfc7518#section-3.2 for more information.Unable to find an implementation for interface io.jsonwebtoken.io.Serializer using java.util.ServiceLoader. Ensure you include a backing implementation .jar in the classpath, for example jjwt-impl.jar, or your own .jar for custom implementations.三个错误 , jjwt的版本(0.9.0升级到0.11.2)问题一 AES 改为 HmacSHA256// jjwt 0.9.0版本 private static SecretKey generateKey(String secret) { byte[] encodedKey = Base64.decodeBase64(secret); return new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES"); } // jjwt 0.11.2版本 private static SecretKey generateKey(String secret) { byte[] encodedKey = Base64.decodeBase64(secret); return new SecretKeySpec(encodedKey, 0, encodedKey.length, "HmacSHA256"); }问题二密钥位数不够，必须大于256位，一个字符按照8位算，至少32个字符。// jjwt 0.9版本 String compactJws = Jwts.builder() .setClaims(claims) .signWith(SignatureAlgorithm.HS256, generateKey("jinan_20220511")) .compact(); // jjwt 0.11.2版本 String compactJws = Jwts.builder() .setClaims(claims) .signWith(generateKey("AtanycostscodecodecodecodeAllbushpullcommit"), SignatureAlgorithm.HS256) .serializeToJsonWith(new GsonSerializer<>(new Gson())) .compact();问题三没找到序列化的实现，添加序列化相关依赖和代码。<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-gson</artifactId> <version>0.11.2</version> </dependency>String compactJws = Jwts.builder() .setClaims(claims) .signWith(generateKey("AtanycostscodecodecodecodeAllbushpullcommit"), SignatureAlgorithm.HS256) // 添加序列化相关 .serializeToJsonWith(new GsonSerializer<>(new Gson())) .compact();
膳食小搭项目-后端主要技术栈SpringBoot+SSM+MybatisX+Lombox插件接口文档采用ApiFox生成的文档 , 文档地址: 膳食小搭暂定的功能基本的注册登录饮食的搭配一个简易的聊天(看情况)帖子功能(看情况)接口数量预计20+打底 第一次一个人做后端 , 从需求分析 --> 数据库表的设计 --> 业务逻辑 --> Api文档的编写不定时更新1 . 接口设计​ 基本的登录 , 也可以用于注册注册退出用户用户信息查询修改用户信息查询指定ID食物食物查询新增食物​ 选择分类后,根据提交的分类ID来调用不同的子表修改食物数据删除食物(逻辑删除)平衡控制方案(预置)增加方案删除方案修改方案查询方案个性化方案增加个性化删除个性化修改个性化查询个性化用户分析前后端实现都行增加分析更新分析(用户修改信息后自动更新 , 或者不满意申请重新分析等)查询分析2 . 数据库表的设计用户表食物表​ 食物表中 , 包含水果/蔬菜/肉类吗? , 数据多会不会影响速度​ 包含的话就0/1/2​ 不包含的话会多出好多接口如何进行联调 , 需要在实体类中
SpringMvc学习一、SpringMVC简介1 . 入门案例报错的解决办法关于编码问题如果出现了就加,没出现不管<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties>关于启动Tomcat后自动关闭在 pom.xml 开头加上<packaging>war</packaging>遇到其他情况百度1.1 实现步骤1 创建web工程（Maven结构） 2 设置tomcat服务器，加载web工程(tomcat插件） 3 导入坐标（SpringMVC+Servlet） 4 定义处理请求的功能类（UserController） 5 编写SpringMVC配置类，加载处理请求的Bean。 6 加载SpringMVC配置，并设置SpringMVC请求拦截的路径1.2 代码实现1.2.1 . 创建web工程（Maven结构）1.2.2 . 设置tomcat服务器，加载web工程(tomcat插件）<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> 1.8 <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <port>8080</port> <path>/</path> <uriEncoding>UTF-8</uriEncoding> </configuration> </plugin> </plugins> </build>然后在配置中挂上//命令 tomcat7:run //工作目录 F:/Coding/java/atanycosts/学习/SSMLearn/SpringMvc1.2.3 . 导入坐标（SpringMVC+Servlet）<dependencies> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.10.RELEASE</version> </dependency> </dependencies>1.2.4 . 定义处理请求的功能类（UserController）package com.controller; //定义表现层控制器bean @Controller public class UserController { //设置映射路径为/save，即外部访问路径 @RequestMapping("/save") //设置当前操作返回结果为指定json数据（本质上是一个字符串信息） @ResponseBody public String save(){ System.out.println("user save ..."); return "{'info':'springmvc'}"; } }注意事项：对于SpringMVC而言，Controller方法返回值默认表示要跳转的页面，没有对应的页面就会报错。如果不想跳转页面而是响应数据，那么就需要在方法上使用@ResponseBody注解。1.2.5 . 编写SpringMVC配置类，加载处理请求的Bean。//springmvc配置类，本质上还是一个spring配置类 @Configuration @ComponentScan("com.controller") public class SpringMvcConfig { }1.2.6 . 加载SpringMVC配置，并设置SpringMVC请求拦截的路径//web容器配置类 public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer { //加载springmvc配置类，产生springmvc容器（本质还是spring容器） protected WebApplicationContext createServletApplicationContext() { //初始化WebApplicationContext对象 AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); //加载指定配置类 ctx.register(SpringMvcConfig.class); return ctx; } //设置由springmvc控制器处理的请求映射路径 protected String[] getServletMappings() { return new String[]{"/"}; } //加载spring配置类 protected WebApplicationContext createRootApplicationContext() { return null; } }1.3 案例注解和类解析2.3.1 . @Controller注解名称：@Controller类型：类注解位置：SpringMVC控制器类定义上方作用：设定SpringMVC的核心控制器bean范例@Controller public class UserController { }2.3.2 . @RequestMapping注解名称：@RequestMapping类型：方法注解位置：SpringMVC控制器方法定义上方作用：设置当前控制器方法请求访问路径范例@RequestMapping("/save") public void save(){ System.out.println("user save ..."); }注意：其实@RequestMapping注解还可以写到类上面，笔记后面会介绍到。2.3.3 . @ResponseBody注解名称：@ResponseBody类型：方法注解位置：SpringMVC控制器方法定义上方作用：设置当前控制器方法响应内容为当前返回值，无需解析范例@RequestMapping("/save") @ResponseBody public String save(){ System.out.println("user save ..."); return "{'info':'springmvc'}"; }2.3.4 . AbstractDispatcherServletInitializer类AbstractDispatcherServletInitializer类是SpringMVC提供的快速初始化Web3.0容器的抽象类AbstractDispatcherServletInitializer提供三个接口方法供用户实现createServletApplicationContext()方法，创建Servlet容器时，加载SpringMVC对应的bean并放入WebApplicationContext对象范围中，而WebApplicationContext的作用范围为ServletContext范围，即整个web容器范围。//加载springmvc配置类，产生springmvc容器（本质还是spring容器） protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(SpringMvcConfig.class); return ctx; }getServletMappings()方法，设定SpringMVC对应的请求映射路径，设置为/表示拦截所有请求，任意请求都将转入到SpringMVC进行处理。//设置由springmvc控制器处理的请求映射路径 protected String[] getServletMappings() { return new String[]{"/"}; }createRootApplicationContext()方法，如果创建Servlet容器时需要加载非SpringMVC对应的bean，使用当前方法进行，使用方式同createServletApplicationContext()//加载spring配置类 protected WebApplicationContext createRootApplicationContext() { return null; }1.5 入门程序开发总结一次性工作创建工程，设置服务器，加载工程导入坐标创建web容器启动类，加载SpringMVC配置，并设置SpringMVC请求拦截路径SpringMVC核心配置类（设置配置类，扫描controller包，加载Controller控制器bean）多次工作定义处理请求的控制器类定义处理请求的控制器方法，并配置映射路径（@RequestMapping）与返回json数据（@ResponseBody）
Spring学习一 . 核心概念IOC（Inversion of Control）控制反转使用对象时，由主动new产生对象转换为由 外部 提供对象，此过程中对象创建控制权由程序转移到外部，此思想称为控制反转。通俗的讲就是 “将new对象的权利交给Spring，我们从Spring中获取对象使用即可”Spring技术对IOC思想进行了实现Spring提供了一个容器，称为IOC容器，用来充当IOC思想中的 外部IOC容器负责对象的创建、初始化等一系列工作，被创建或被管理的对象在IOC容器中统称为BeanDI（Dependency Injection）依赖注入在容器中建立bean与bean之间的依赖关系的整个过程，称为依赖注入。目标：充分解耦使用IoC容器管理bean（IOC)在IoC容器内将有依赖关系的bean进行关系绑定（DI）最终效果使用对象时不仅可以直接从IOC容器中获取，并且获取到的bean已经绑定了所有的依赖关系二 . 初步实现将被管理的对象交给IOC容器 , 然后在容器中建立bean与bean之间的依赖关系(DI)1 . 创建接口和实现类UserDao 的接口和实现类public interface UserDaoApi { public void save(); } public class UserDao implements UserDaoApi { public void save(){ System.out.println("dao save"); } } USerService 的接口和实现类public interface UserServiceApi { public void save(); } public class UserService implements UserServiceApi { private UserDao dao; public void save(){ System.out.println("service save"); dao.save(); } public void setDao(UserDao dao) { this.dao = dao; } } 2 . 创建配置文件applicationContext.xml , 在 resources 文件夹下<bean id="userService" class="com.service.UserService"> <property name="dao" ref="userDao"></property> </bean> <bean id="userDao" class="com.dao.UserDao"></bean>三 . Bean基础1 . 别名配置name来配置别名可以用 " 空格 , ; "来分隔<bean id="userDao" name="test 111,a;b" class="com.dao.UserDao"></bean>2 . 作用范围因为 bean 的默认为单例UserService Service1= (UserService)ctx.getBean("userService"); UserService Service2= (UserService)ctx.getBean("userService"); System.out.println(service1); System.out.println(service2); //如此打印出来的 service1 service2 这两个对象的地址是一样的当给 bean 添加 scope 属性后 <bean id="userDao" class="com.dao.UserDao" scope="prototype"></bean>scope 还有request、session、application、 websocket ，表示创建出的对象放置在web容器(tomcat)对应的位置。比如：request表示保存到request域中。四、Bean的实例化1 . Bean是如何创建的bean本质上就是对象，创建bean使用构造方法完成2 . 实例化Bean的三种方式2.1 . 构造方法方式 (重要)在创建 bean 时,调用的是对应类的无参构造方法 , 当被有参构造方法重写后会抛出异常 BeanCreationException2.2 . 使用静态工厂造bean处理早期遗留问题 , 知道就好2.3 . 实例工厂方式处理早期遗留问题 , 知道就好五 . Bean生命周期控制1 . 自定义提供生命周期控制方法 , 在 Dao 层设置 //表示bean初始化对应的操作 public void init(){ System.out.println("init..."); } //表示bean销毁前对应的操作 public void destory(){ System.out.println("destory..."); }applicationContext.xml声明 <bean id="Dao" class="xxx" init-method="init" destroy-method="destory"/>容器运行在虚拟机中 , 程序执行完 , 虚拟机会直接关闭 , 并不会主动关闭容器 , 需要手动关闭public static void main( String[] args ) { //此处需要使用实现类类型，接口类型没有close方法 ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); BookDao bookDao = (BookDao) ctx.getBean("bookDao"); bookDao.save(); //关闭容器，执行销毁的方法 ctx.close(); //或者钩子来关闭容器 ctx.registerShutdownHook(); }ClassPathXmlApplicationContext 这个类继承了 ApplicationContext 的 close 方法ctx.close() 和 ctx.registerShutdownHook() , 前者暴力,只能放在程序尾部 , 后者任意2 . Spring内置在 service 层实现 InitializingBean , DisposableBean 接口 , 并实现对应的方法public class UserService implements UserServiceApi , InitializingBean, DisposableBean { private UserDao dao; public void save(){ System.out.println("service save"); dao.save(); } public void setDao(UserDao dao) { this.dao = dao; } @Override public void destroy() throws Exception { } @Override public void afterPropertiesSet() throws Exception { } }ctx.close() 或 ctx.registerShutdownHook()六 . 依赖注入(DI)1 . 依赖注入的两种方式1.1 .setter注入前文用的都是setter注入 , 但前文都是用的引用类型简单类型注入的方式 , 还是一样创建对应的 set 方法在对应的 Bean 下 <property name="connectNum" value="10"></property>1.2 .构造器注入在类中创建有参构造器 , 构造器注入,传递的是形参public UserService(UserDao dao, int connectNum) { this.dao = dao; this.connectNum = connectNum; }<bean id="userService" class="com.service.UserService"> <constructor-arg name="connectNum" value="10"></constructor-arg> <constructor-arg name="dao" ref="userDao"></constructor-arg> </bean> <bean id="userDao" name="test 111,a;b" class="com.dao.UserDao"></bean>1.3 . 注入方式选择强制依赖使用构造器进行，使用setter注入有概率不进行注入导致null对象出现如果有必要可以两者同时使用，使用构造器注入完成强制依赖的注入，使用setter注入完成可选依赖的注入2 . 依赖自动装配IOC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配自动装配方式 :按类型按名称按构造方法2.1 . 依赖自动装配特征自动装配用于引用类型依赖注入，不能对简单类型进行操作使用按类型装配时（byType）必须保障容器中相同类型的bean唯一，推荐使用使用按名称装配时（byName）必须保障容器中具有指定名称的bean，因变量名与配置耦合，不推荐使用自动装配优先级低于setter注入与构造器注入，同时出现时自动装配配置失效2.2 . 实现简单类型用构造器方法或者setter方法引用类型自动装配 : autowire="byType"<bean id="userService" class="com.service.UserService" autowire="byType"> <constructor-arg name="connectNum" value="10"></constructor-arg> </bean> <bean id="userDao" name="test 111,a;b" class="com.dao.UserDao"></bean>3 . 集合注入public class UserDao implements UserDaoApi { private int[] array; private List<String> list; private Set<String> set; private Map<String,String> map; private Properties properties; public void setArray(int[] array) { this.array = array; } public void setList(List<String> list) { this.list = list; } public void setSet(Set<String> set) { this.set = set; } public void setMap(Map<String, String> map) { this.map = map; } public void setProperties(Properties properties) { this.properties = properties; } public void save(){ System.out.println("dao save"); } @Override public String toString() { return "UserDao{" + "array=" + Arrays.toString(array) + ", list=" + list + ", set=" + set + ", map=" + map + ", properties=" + properties + '}'; } } 3.1 . 注入数组类型数据<property name="array"> <array> <value>100</value> <value>200</value> <value>300</value> </array> </property>3.2 . 注入List类型数据<property name="list"> <value>itcast</value> <value>itheima</value> <value>boxuegu</value> <value>chuanzhihui</value> </property>3.3 . 注入Set类型数据<property name="set"> <set> <value>itcast</value> <value>itheima</value> <value>boxuegu</value> <value>boxuegu</value> </set> </property>3.4 . 注入Map类型数据<property name="map"> <entry key="country" value="china"/> <entry key="province" value="henan"/> <entry key="city" value="kaifeng"/> </property>3.5 . 注入Properties类型数据<property name="properties"> <props> <prop key="country">china</prop> <prop key="province">henan</prop> <prop key="city">kaifeng</prop> </props> </property>七 . Spring注解开发1 . 注解定义Bean对象xml配置Bean对象有些繁琐，使用注解简化Bean对象的定义1.1 . 基本使用在 applicationContext.xml 中开启 Spring 注解包扫描<context:component-scan base-package="com.dao"/>在类上使用 @Component 注解定义 Beanpackage com.dao; @Component("dao") public class UserDao implements UserDaoApi { public void save(){ System.out.println("dao save"); } }1.2 . @Component三个衍生注解加粗的注解为常用注解Spring提供@Component注解的三个衍生注解@Controller：用于控制层bean定义@Service：用于业务层bean定义@Repository：用于数据层bean定义2 . 纯注解开发模式Java类代替Spring核心配置文件@Configuration注解用于设定当前类为配置类@ComponentScan注解用于设定扫描路径，此注解只能添加一次，多个数据请用数组格式@ComponentScan({"com.itheima.service","com.itheima.dao"})读取Spring核心配置文件初始化容器对象切换为读取Java配置类初始化容器对象//加载 配置文件 初始化容器 ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); //加载 配置类 初始化容器 ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);2.1 . 代码定义配置类代替配置文件在 com 包下创建 config 包 再创建 SpringConfig 类//声明当前类为Spring配置类 @Configuration //Spring注解扫描，相当于<context:component-scan base-package="com"/> @ComponentScan("com") //设置bean扫描路径，多个路径书写为字符串数组格式 //@ComponentScan({"com.service","com.dao"}) public class SpringConfig { }在测试类中加载配置类，获取Bean对象并使用public static void main(String[] args) { //AnnotationConfigApplicationContext加载Spring配置类初始化Spring容器 ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); BookDao bookDao = (BookDao) ctx.getBean("UserDao"); System.out.println(bookDao); //按类型获取bean UserService userService = ctx.getBean(UserService.class); System.out.println(userService); } 3 . 注解开发Bean作用范围和生命周期管理3.1 . bean作用范围注解配置使用@Scope定义bean作用范围@Repository("dao") @Scope("prototype") public class UserDao implements UserDaoApi { public void save(){ System.out.println("dao save "); } }3.2 . bean生命周期注解配置使用 @PostConstruct 、@PreDestroy 定义bean生命周期@Repository("dao") @Scope("prototype") public class UserDao implements UserDaoApi { public void save(){ System.out.println("dao save "); } @PreDestroy //销毁前 public void destroy(){ System.out.println("destroy service"); } /** * 执行后 */ @PostConstruct public void afterPropertiesSet(){ System.out.println("init service"); } }注意：@PostConstruct和@PreDestroy注解是jdk中提供的注解，从jdk9开始，jdk中的javax.annotation包被移除了，需要导依赖解决这个问题。<dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency>4 . 注解开发依赖注入4.1 . 使用@Autowired注解开启自动装配模式不管是使用配置文件还是配置类，都必须进行对应的 Spring 注解包扫描才可以使用。 @Autowired 默认按照类型自动装配，如果IOC容器中同类的 Bean 有多个，那么默认按照变量名和Bean的名称匹配，建议使用 @Qualifier 注解指定要装配的 Bean 名称package com.service; //业务逻辑层 @Service public class UserService implements UserServiceApi{ //@Autowired：注入引用类型，自动装配模式，默认按类型装配 @Autowired private UserDao dao; public void save(){ System.out.println("service save "+UserServiceName); dao.save(); } @PreDestroy //销毁前 public void destroy(){ System.out.println("destroy service"); } /** * 执行后 */ @PostConstruct public void afterPropertiesSet(){ System.out.println("init service"); } }自动装配基于反射设计创建对象并暴力反射对应属性为私有属性初始化数据，因此无需提供setter方法4.2 . 使用@Qualifier注解指定要装配的bean名称目的：解决IOC容器中同类型Bean有多个装配哪一个的问题package com.service; //业务逻辑层 @Service public class UserService implements UserServiceApi{ @Autowired //@Qualifier注解无法单独使用，必须配合@Autowired注解使用 @Qualifier("dao") private UserDao dao; public void save(){ System.out.println("service save "); dao.save(); } @PreDestroy //销毁前 public void destroy(){ System.out.println("destroy service"); } /** * 执行后 */ @PostConstruct public void afterPropertiesSet(){ System.out.println("init service"); } } 4.3 . 使用@Value实现简单类型注入package com.service; //业务逻辑层 @Service public class UserService implements UserServiceApi{ @Autowired //@Qualifier注解无法单独使用，必须配合@Autowired注解使用 @Qualifier("dao") private UserDao dao; @Value("${name}") private String UserServiceName; public void save(){ System.out.println("service save "+UserServiceName); dao.save(); } @PreDestroy //销毁前 public void destroy(){ System.out.println("destroy service"); } /** * 执行后 */ @PostConstruct public void afterPropertiesSet(){ System.out.println("init service"); } } 以上@Value注解中使用${name}从属性文件中读取name值，那么就需要在配置类或者配置文件中加载属性文件。在 resources 下 --> 创建 jdbc.propertiesname=enterpackage com.config; //声明当前类为Spring配置类 @Configuration //Spring注解扫描，相当于<context:component-scan base-package="com"/> @ComponentScan("com") @PropertySource("classpath:jdbc.properties") //{}当有多个时 public class SpringConfig { }八 . 注解开发管理第三方Bean【重点】需要导入依赖 Druid <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency>1 . 单独定义配置类public class JdbcConfig { //@Bean：表示当前方法的返回值是一个bean对象，添加到IOC容器中 @Bean public DataSource dataSource(){ DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUrl("jdbc:mysql://localhost:3306/spring_db"); ds.setUsername("root"); ds.setPassword("root"); return ds; } }2 . 将独立的配置类加入核心配置2.1 . @Import注解导入式@Configuration @ComponentScan("com") //@Import:导入配置信息 @Import({JdbcConfig.class}) public class SpringConfig { }2.2 . @ComponentScan扫描式@Configuration @ComponentScan("com") //只要com.config包扫到了就行，三个包可以合并写成com.itheima public class SpringConfig { }九 . 注解开发为第三方Bean注入资源1 . 简单类型依赖注入public class JdbcConfig { //1.定义一个方法获得要管理的对象 @Value("com.mysql.jdbc.Driver") private String driver; @Value("jdbc:mysql://localhost:3306/spring_db") private String url; @Value("root") private String userName; @Value("root") private String password; //2.@Bean：表示当前方法的返回值是一个bean对象，添加到IOC容器中 @Bean public DataSource dataSource(){ DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName(driver); ds.setUrl(url); ds.setUsername(userName); ds.setPassword(password); return ds; } }说明：如果@Value()中使用了EL表达式读取properties属性文件中的内容，那么就需要加载properties属性文件。2 . 引用类型依赖注入//Spring会自动从IOC容器中找到BookDao对象赋值给参数bookDao变量，如果没有就会报错。 @Bean public DataSource dataSource(UserDao userDao){ System.out.println(userDao); DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName(driver); ds.setUrl(url); ds.setUsername(userName); ds.setPassword(password); return ds; }说明：引用类型注入只需要为bean定义方法设置形参即可，容器会根据类型自动装配对象十 . Spring整合Mybatis和Juint​ 引入的依赖 , 看着删<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.10.RELEASE</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.10.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.10.RELEASE</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.7</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.18</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.1</version> <scope>test</scope> </dependency> </dependencies>1 . 创建config配置类层1.1 . config类下JdbcConfig配置DataSource数据源package com.config; import com.alibaba.druid.pool.DruidDataSource; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import javax.sql.DataSource; public class JdbcConfig { @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String userName; @Value("${jdbc.password}") private String password; @Bean public DataSource dataSource(){ DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setDriverClassName(driver); druidDataSource.setUrl(url); druidDataSource.setUsername(userName); druidDataSource.setPassword(password); return druidDataSource; } }1.2 . config类下MybatisConfigMybatisConfig整合mybatispackage com.config; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.mapper.MapperScannerConfigurer; import org.springframework.context.annotation.Bean; import javax.sql.DataSource; public class MybatisConfig { @Bean public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){ SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setTypeAliasesPackage("com.entity.User"); sqlSessionFactoryBean.setDataSource(dataSource); return sqlSessionFactoryBean; } //定义bean，返回MapperScannerConfigurer对象 @Bean public MapperScannerConfigurer mapperScannerConfigurer(){ MapperScannerConfigurer msc = new MapperScannerConfigurer(); msc.setBasePackage("com.dao"); return msc; } } 1.3 . config类下SpringConfig创建SpringConfig主配置类进行包扫描和加载其他配置类package com.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.PropertySource; @Configuration @ComponentScan("com") @PropertySource("classpath:jdbc.properties") @Import({JdbcConfig.class,MybatisConfig.class}) public class SpringConfig { } 2 . 创建Dao数据访问层dao基础代码创建一个接口 , 利用注解省略 mapper.xml 文件package com.dao; import com.entity.User; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import java.util.List; public interface UserDaoApi { @Insert("insert into test(id,userName,userPwd)values(#{id},#{userName},#{userPwd})") void Insert(User user); @Delete("delete from test where id = #{id} ") void delete(Integer id); @Update("update test set userName = #{userName} , userPwd = #{userPwd} where id = #{id} ") void update(User user); @Select("select * from test") List<User> findAll(); @Select("select * from test where id = #{id} ") User findById(Integer id); } 3 . 创建entity实体类层定义User对象 , 变量与字段一致package com.entity; public class User { private Integer id; private String userName; private String UserPwd; public User(Integer id, String userName, String userPwd) { this.id = id; this.userName = userName; UserPwd = userPwd; } public User(Integer id) { this.id = id; } public Integer getUserId() { return id; } public void setUserId(Integer id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserPwd() { return UserPwd; } public void setUserPwd(String userPwd) { UserPwd = userPwd; } @Override public String toString() { return "User{" + "userId=" + id + ", userName='" + userName + '\'' + ", UserPwd='" + UserPwd + '\'' + '}'; } }4 . 创建service业务逻辑层service基础代码4.1 . UserServicepackage com.service; import com.dao.UserDaoApi; import com.entity.User; import com.service.Api.UserServiceApi; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service public class UserService implements UserServiceApi { @Autowired private UserDaoApi userDao; public void insert(User user) { userDao.Insert(user); } public void update(User user){ userDao.update(user); } public void delete(Integer id) { userDao.delete(id); } public User findById(Integer id) { return userDao.findById(id); } public List<User> findAll() { return userDao.findAll(); } }4.2 . UserServiceApi创建 API 包 --> UserServiceApipackage com.service.Api; import com.entity.User; import java.util.List; public interface UserServiceApi { void insert(User user); void delete(Integer id); void update(User user); List<User> findAll(); User findById(Integer id); }5 . 创建JDBC的properties文件在 resources 包下jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/atanycosts?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC jdbc.username=root jdbc.password=root指定时区 , 不然会报时区错误?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC6 . Test类Java整合Juint的两个注解@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfig.class)依赖 <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.10.RELEASE</version> </dependency>import com.config.SpringConfig; import com.entity.User; import com.service.Api.UserServiceApi; import com.service.UserService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; //使用Spring整合Junit专用的类加载器 @RunWith(SpringJUnit4ClassRunner.class) //加载配置文件或者配置类 @ContextConfiguration(classes = SpringConfig.class) public class test { //支持自动装配注入bean @Autowired private UserServiceApi userServiceApi; @Test public void getId(){ System.out.println(userServiceApi.findAll()); } @Test public void test(){ ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); UserService accountService = ctx.getBean(UserService.class); User user = new User(null,"111","11"); accountService.insert(user); User ac = accountService.findById(1); System.out.println(ac+"\n"+user); } } 十一 . AOP面向切面编程1 AOP简介1.1 . AOP简介和作用AOP(Aspect Oriented Programming)面向切面编程，一种编程范式，指导开发者如何组织程序结构OOP(Object Oriented Programming)面向对象编程作用：在不惊动原始设计的基础上为其进行功能增强。简单的说就是在不改变方法源代码的基础上对方法进行功能增强。Spring理念：无入侵式/无侵入式1.2 . AOP中的核心概念连接点（JoinPoint）：正在执行的方法，例如：update()、delete()、select()等都是连接点 随便一个方法切入点（Pointcut）：进行功能增强了的方法，例如:update()、delete()方法，select()方法没有被增强所以不是切入点，但是是连接点。在SpringAOP中，一个切入点可以只描述一个具体方法，也可以匹配多个方法一个具体方法：com.itheima.dao包下的BookDao接口中的无形参无返回值的save方法匹配多个方法：所有的save方法，所有的get开头的方法，所有以Dao结尾的接口中的任意方法，所有带有一个参数的方法通知类：通知方法所在的类叫做通知类通知（Advice）：在切入点前后执行的操作，也就是增强的共性功能在SpringAOP中，功能最终以方法的形式呈现切面（Aspect）：描述通知与切入点的对应关系，也就是哪些通知方法对应哪些切入点方法。1.2 . AOP入门案例思路分析在接口执行前输出当前系统时间注解思路分析：导入坐标（pom.xml）制作连接点方法（原始操作，dao接口与实现类）制作共性功能（通知类与通知）定义切入点绑定切入点与通知关系（切面）花费一小时解决一个问题 , 还是无意试试在开启AOP功能时,需要设置 proxyTargetClass = true , 不然会导致Bean找不到@EnableAspectJAutoProxy(proxyTargetClass = true)1.2.1 . 导入坐标<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.10.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency> </dependencies>1.2.2 . 定义接口和实现类service层和dao层都行package com.service; @Service public class UserService implements UserServiceApi { @Autowired private UserDaoApi userDao; //原有的打印时间方法 public void save() { System.out.println(System.currentTimeMillis()); System.out.println("AOP测试 save ..."); } //要连接的方法 public void update1(){ System.out.println("AOP测试 update ..."); } } 1.2.3 . 定义通知类，制作通知方法//通知类必须配置成Spring管理的bean @Component //设置当前类为切面类类 @Aspectpackage com.aop; //通知类必须配置成Spring管理的bean @Component //设置当前类为切面类类 @Aspect public class aopTest { public void method(){ System.out.println(System.currentTimeMillis()); } }1.2.4 . 定义切入点表达式、配置切面(绑定切入点与通知关系)相关注解//通知类必须配置成Spring管理的bean @Component //设置当前类为切面类类 @Aspect //设置切入点，@Pointcut注解要求配置在方法上方 @Pointcut("execution(void com.service.Api.UserServiceApi.update1())") public void pt(){} //设置在切入点pt()的前面运行当前操作(前置通知) @Before("pt()") public void method(){}package com.aop; //通知类必须配置成Spring管理的bean @Component //设置当前类为切面类类 @Aspect public class aopTest { //设置切入点，@Pointcut注解要求配置在方法上方 @Pointcut("execution(void com.service.Api.UserServiceApi.update1())") public void pt(){} //设置在切入点pt()的前面运行当前操作(前置通知) @Before("pt()") public void method(){ System.out.println(System.currentTimeMillis()); } } 1.2.5 . 在配置类中开启AOP功能//开启注解开发AOP功能 @EnableAspectJAutoProxy(proxyTargetClass = true)1.2.6 . Test类@Test public void test(){ ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); UserService userService = ctx.getBean(UserService.class); userService.update1(); }2 . AOP工作流程2.1 . AOP工作流程Spring容器启动读取所有切面配置中的切入点初始化bean，判定bean对应的类中的方法是否匹配到任意切入点匹配失败，创建原始对象匹配成功，创建原始对象（目标对象）的代理对象获取bean执行方法获取的bean是原始对象时，调用方法并执行，完成操作获取的bean是代理对象时，根据代理对象的运行模式运行原始方法与增强的内容，完成操作2.2 . AOP核心概念目标对象（Target）：被代理的对象，也叫原始对象，该对象中的方法没有任何功能增强。代理对象（Proxy）：代理后生成的对象，由Spring帮我们创建代理对象。3 . AOP切入点表达式3.1 . 语法格式切入点：要进行增强的方法切入点表达式：要进行增强的方法的描述方式描述方式一：执行com.itheima.dao包下的BookDao接口中的无参数update方法@Pointcut("execution(void com.dao.UserDao.update())")描述方式二：执行com.dao包下的UserDao类中的无参数update方法@Pointcut("execution(void com.dao.UserDao.update())")切入点表达式标准格式：动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数) 异常名）@Pointcut("execution(public User com.dao.UserDao.findById(int))")动作关键字：描述切入点的行为动作，例如execution表示执行到指定切入点访问修饰符：public，private等，可以省略返回值：写返回值类型包名：多级包使用点连接类/接口名：方法名：参数：直接写参数的类型，多个类型用逗号隔开异常名：方法定义中抛出指定异常，可以省略3.2 . 通配符目的：可以使用通配符描述切入点，快速描述。：单个独立的任意符号，可以独立出现，也可以作为前缀或者后缀的匹配符出现匹配com.itheima包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法execution（public * com.*.UserService.find*(*)).. ：多个连续的任意符号，可以独立出现，常用于简化包名与参数的书写匹配com包下的任意包中的UserService类或接口中所有名称为findById的方法execution（public User com..UserService.findById(..))+：专用于匹配子类类型execution(* *..*Service+.*(..))4 . AOP通知类型 (重点)4.1 . AOP通知分类AOP通知描述了抽取的共性功能，根据共性功能抽取的位置不同，最终运行代码时要将其加入到合理的位置AOP通知共分为5种类型前置通知 ：在切入点方法执行之前执行后置通知 ：在切入点方法执行之后执行，无论切入点方法内部是否出现异常，后置通知都会执行。环绕通知 : 手动调用切入点方法并对其进行增强的通知方式。返回后通知 ：在切入点方法执行之后执行，如果切入点方法内部出现异常将不会执行。抛出异常后通知 ：在切入点方法执行之后执行，只有当切入点方法内部出现异常之后才执行。4.2 . AOP通知详解4.2.1 . 前置通知名称：@Before作用：设置当前通知方法与切入点之间的绑定关系，当前通知方法在原始切入点方法前运行//设置切入点，@Pointcut注解要求配置在方法上方 @Pointcut("execution(void com.service.Api.UserServiceApi.update1())") public void pt(){} @Before("pt()") public void before() { System.out.println("before advice ..."); }4.2.2 . 后置通知名称：@After作用：设置当前通知方法与切入点之间的绑定关系，当前通知方法在原始切入点方法后运行//设置切入点，@Pointcut注解要求配置在方法上方 @Pointcut("execution(void com.service.Api.UserServiceApi.update1())") public void pt(){} @After("pt()") public void after() { System.out.println("after advice ..."); }4.2.3 . 返回后通知名称：@AfterReturning作用：设置当前通知方法与切入点之间的绑定关系，当前通知方法在原始切入点方法正常执行完毕后运行//设置切入点，@Pointcut注解要求配置在方法上方 @Pointcut("execution(void com.service.Api.UserServiceApi.update1())") public void pt(){} @AfterReturning("pt()") public void afterReturning() { System.out.println("afterReturning advice ..."); }4.2.4 . 抛出异常后通知名称：@AfterThrowing作用：设置当前通知方法与切入点之间的绑定关系，当前通知方法在原始切入点方法运行抛出异常后执行//设置切入点，@Pointcut注解要求配置在方法上方 @Pointcut("execution(void com.service.Api.UserServiceApi.update1())") public void pt(){} @AfterThrowing("pt()") public void afterThrowing() { System.out.println("afterThrowing advice ..."); }4.2.5 . 环绕通知名称：@Around（重点，常用）作用：设置当前通知方法与切入点之间的绑定关系，当前通知方法在原始切入点方法前后运行public Object around(ProceedingJoinPoint pjp) throws Throwable { //调用pjp.proceed() , 不然连接点的业务会消失 Object ret = pjp.proceed(); }//设置切入点，@Pointcut注解要求配置在方法上方 @Pointcut("execution(void com.service.Api.UserServiceApi.update1())") public void pt(){} @Around("pt()") public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("around before advice ..."); Object ret = pjp.proceed(); System.out.println("around after advice ..."); return ret; }环绕通知注意事项环绕通知方法形参必须是ProceedingJoinPoint，表示正在执行的连接点，使用该对象的proceed()方法表示对原始对象方法进行调用，返回值为原始对象方法的返回值。环绕通知方法的返回值建议写成Object类型，用于将原始对象方法的返回值进行返回，哪里使用代理对象就返回到哪里。5 . 通过AOP测试接口耗时在原本整合Mybatis和Juint的基础上测试5.1 . Test类 /** * 测试获取所有效率 */ @Test public void getAll(){ userServiceApi.findAll(); } /** * 测试获取指定效率 */ @Test public void getById(){ userServiceApi.findById(1); } //打印结果 万次执行：com.service.UserService.findAll---->3763ms 万次执行：com.service.UserService.findById---->1555ms5.2 . 通知类通知所有接口 , 但在Test类中只调用查询接口@Pointcut("execution(* com.service.Api.UserServiceApi.*(..))") public void pt(){} //设置环绕通知，在原始操作的运行前后记录执行时间 @Around("pt()") //本类类名可以省略不写 public void runSpeed(ProceedingJoinPoint pjp) throws Throwable { //获取执行的签名对象 Signature signature = pjp.getSignature(); //获取接口/类全限定名 String className = signature.getDeclaringTypeName(); //获取方法名 String methodName = signature.getName(); //记录开始时间 long start = System.currentTimeMillis(); //执行万次操作 for (int i = 0; i < 10000; i++) { pjp.proceed(); } //记录结束时间 long end = System.currentTimeMillis(); //打印执行结果 System.out.println("万次执行："+ className+"."+methodName+"---->" +(end-start) + "ms"); }6 . AOP切入点数据获取6.1 . 获取参数说明：在前置通知和环绕通知中都可以获取到连接点方法的参数们JoinPoint 对象描述了连接点方法的运行状态，可以获取到原始方法的调用参数@Before("pt()") public void before(JoinPoint jp) { Object[] args = jp.getArgs(); //获取连接点方法的参数们 System.out.println(Arrays.toString(args)); }ProccedJointPoint 是 JoinPoint 的子类@Around("pt()") public Object around(ProceedingJoinPoint pjp) throws Throwable { Object[] args = pjp.getArgs(); //获取连接点方法的参数们 System.out.println(Arrays.toString(args)); Object ret = pjp.proceed(); return ret; }6.2 . 获取返回值说明：在返回后通知和环绕通知中都可以获取到连接点方法的返回值抛出异常后通知可以获取切入点方法中出现的异常信息，使用形参可以接收对应的异常对象@AfterReturning(value = "pt()",returning = "ret") public void afterReturning(String ret) { //变量名要和returning="ret"的属性值一致 System.out.println("afterReturning advice ..."+ret); }环绕通知中可以手工书写对原始方法的调用，得到的结果即为原始方法的返回值@Around("pt()") public Object around(ProceedingJoinPoint pjp) throws Throwable { // 手动调用连接点方法，返回值就是连接点方法的返回值 Object ret = pjp.proceed(); return ret; }6.3 . 获取异常说明：在抛出异常后通知和环绕通知中都可以获取到连接点方法中出现的异常抛出异常后通知可以获取切入点方法中出现的异常信息，使用形参可以接收对应的异常对象@AfterThrowing(value = "pt()",throwing = "t") public void afterThrowing(Throwable t) {//变量名要和throwing = "t"的属性值一致 System.out.println("afterThrowing advice ..."+ t); }抛出异常后通知可以获取切入点方法运行的异常信息，使用形参可以接收运行时抛出的异常对象@Around("pt()") public Object around(ProceedingJoinPoint pjp) { Object ret = null; //此处需要try...catch处理，catch中捕获到的异常就是连接点方法中抛出的异常 try { ret = pjp.proceed(); } catch (Throwable t) { t.printStackTrace(); } return ret; }7 . 案例-百度网盘密码数据兼容处理7.1 . 需求和分析需求：对百度网盘分享链接输入密码时尾部多输入的空格做兼容处理分析：①：在业务方法执行之前对所有的输入参数进行格式处理——trim()②：使用处理后的参数调用原始方法——环绕通知中存在对原始方法的调用7.2 . 代码实现7.2.1 . 环境准备//-------------service层代码----------------------- public interface ResourcesService { public boolean openURL(String url ,String password); } @Service public class ResourcesServiceImpl implements ResourcesService { @Autowired private ResourcesDao resourcesDao; public boolean openURL(String url, String password) { return resourcesDao.readResources(url,password); } } //-------------dao层代码----------------------- public interface ResourcesDao { boolean readResources(String url, String password); } @Repository public class ResourcesDaoImpl implements ResourcesDao { public boolean readResources(String url, String password) { System.out.println(password.length()); //模拟校验 return password.equals("root"); } }7.2.2 . 编写通知类@Component @Aspect public class DataAdvice { @Pointcut("execution(boolean com.service.*Service.*(*,*))") private void servicePt(){} @Around("DataAdvice.servicePt()") public Object trimStr(ProceedingJoinPoint pjp) throws Throwable { Object[] args = pjp.getArgs(); for (int i = 0; i < args.length; i++) { //判断参数是不是字符串 if(args[i].getClass().equals(String.class)){ args[i] = args[i].toString().trim(); } } Object ret = pjp.proceed(args); return ret; } }7.2.3 . 在SpringConfig配置类上开启AOP注解功能@Configuration @ComponentScan("com") @EnableAspectJAutoProxy(proxyTargetClass = true) public class SpringConfig { }7.2.4 . 运行测试类，查看结果public class App { public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); ResourcesService resourcesService = ctx.getBean(ResourcesService.class); boolean flag = resourcesService.openURL("http://pan.baidu.com/haha", "root "); System.out.println(flag); } }通过 AOP 中 getArgs() 来得到数据 调用 trim() 方法来解决空格等字符 , 再 通过proceed() 将处理后的值传回去 , 打印 flag十二 . Spring事务1 Spring事务简介【重点】1.1 Spring事务作用事务作用：在数据层保障一系列的数据库操作同成功同失败Spring事务作用：在数据层或 业务层 保障一系列的数据库操作 同成功同失败1.2 需求和分析需求：实现任意两个账户间转账操作需求微缩：A账户减钱，B账户加钱分析：①：数据层提供基础操作，指定账户减钱（outMoney），指定账户加钱（inMoney）②：业务层提供转账操作（transfer），调用减钱与加钱的操作③：提供2个账号和操作金额执行转账操作④：基于Spring整合MyBatis环境搭建上述操作结果分析：①：程序正常执行时，账户金额A减B加，没有问题②：程序出现异常后，转账失败，但是异常之前操作成功，异常之后操作失败，整体业务失败1.3 代码实现1.3.1 . 环境准备Spring整合Mybatis相关代码(依赖、JdbcConfig、MybatisConfig、SpringConfig)省略。public interface AccountDao { @Update("update tbl_account set money = money + #{money} where name = #{name}") void inMoney(@Param("name") String name, @Param("money") Double money); @Update("update tbl_account set money = money - #{money} where name = #{name}") void outMoney(@Param("name") String name, @Param("money") Double money); } public interface AccountService { /** * 转账操作 * @param out 传出方 * @param in 转入方 * @param money 金额 */ public void transfer(String out,String in ,Double money) ; } @Service public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; public void transfer(String out,String in ,Double money) { accountDao.outMoney(out,money); int i = 1/0; accountDao.inMoney(in,money); } }1.3.2 . 在业务层接口上添加Spring事务管理public interface AccountService { //配置当前接口方法具有事务 @Transactional public void transfer(String out,String in ,Double money) ; }注意事项Spring注解式事务通常添加在业务层接口中而不会添加到业务层实现类中，降低耦合注解式事务可以添加到业务方法上表示当前方法开启事务，也可以添加到接口上表示当前接口所有方法开启事务1.3.3 . 设置事务管理器(将事务管理器添加到IOC容器中)说明：可以在JdbcConfig中配置事务管理器//配置事务管理器，mybatis使用的是jdbc事务 @Bean public PlatformTransactionManager transactionManager(DataSource dataSource){ DataSourceTransactionManager dtm = new DataSourceTransactionManager(); transactionManager.setDataSource(dataSource); return transactionManager; }注意事项事务管理器要根据实现技术进行选择MyBatis框架使用的是JDBC事务1.3.4 . 开启注解式事务驱动@Configuration @ComponentScan("com.itheima") @PropertySource("classpath:jdbc.properties") @Import({JdbcConfig.class,MybatisConfig.class}) //开启注解式事务驱动 @EnableTransactionManagement public class SpringConfig { }1.3.5 . 运行测试类，查看结果@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfig.class) public class AccountServiceTest { @Autowired private AccountService accountService; @Test public void testTransfer() throws IOException { accountService.transfer("Tom","Jerry",100D); } }2 Spring事务角色2.1 Spring事务角色事务管理员：发起事务方，在Spring中通常指代业务层开启事务的方法事务协调员：加入事务方，在Spring中通常指代数据层方法，也可以是业务层方法3 Spring事务相关配置3.1 事务配置说明：对于RuntimeException类型异常或者Error错误，Spring事务能够进行回滚操作。但是对于编译器异常，Spring事务是不进行回滚的，所以需要使用rollbackFor来设置要回滚的异常。3.2 案例：转账业务追加日志需求和分析需求：实现任意两个账户间转账操作，并对每次转账操作在数据库进行留痕需求微缩：A账户减钱，B账户加钱，数据库记录日志分析：①：基于转账操作案例添加日志模块，实现数据库中记录日志②：业务层转账操作（transfer），调用减钱、加钱与记录日志功能实现效果预期：无论转账操作是否成功，均进行转账操作的日志留痕存在的问题：日志的记录与转账操作隶属同一个事务，同成功同失败实现效果预期改进：无论转账操作是否成功，日志必须保留事务传播行为：事务协调员对事务管理员所携带事务的处理态度3.2.1 . 环境整备USE spring_db; CREATE TABLE tbl_log( id INT PRIMARY KEY AUTO_INCREMENT, info VARCHAR(255), createDate DATE );public interface LogService { //propagation设置事务属性：传播行为设置为当前操作需要新事务 @Transactional void log(String out, String in, Double money); } @Service public class LogServiceImpl implements LogService { @Autowired private LogDao logDao; public void log(String out,String in,Double money ) { logDao.log("转账操作由"+out+"到"+in+",金额："+money); } } public interface LogDao { @Insert("insert into tbl_log (info,createDate) values(#{info},now())") void log(String info); }3.2.2 . 在AccountServiceImpl中调用logService中添加日志的方法@Service public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; @Autowired private LogService logService; public void transfer(String out,String in ,Double money) { try{ accountDao.outMoney(out,money); int i = 1/0; accountDao.inMoney(in,money); }finally { logService.log(out,in,money); } } }3.2.3 . 在LogService的log()方法上设置事务的传播行为public interface LogService { //propagation设置事务属性：传播行为设置为当前操作需要新事务 @Transactional(propagation = Propagation.REQUIRES_NEW) void log(String out, String in, Double money); }3.2.4 . 运行测试类，查看结果@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfig.class) public class AccountServiceTest { @Autowired private AccountService accountService; @Test public void testTransfer() throws IOException { accountService.transfer("Tom","Jerry",50D); } }
Mybatis学习正式进入框架学习一 . 环境搭建及测试1 . 导入依赖在IDEA中创建一个maven项目在根目录的 pom.xml 文件夹下导入依赖 mybatis mysql junit <dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.7</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.18</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.1</version> <scope>test</scope> </dependency> </dependencies>2 . mapper文件在 src --> main --> resources 下创建 mybatis-config.xml 文件此文件是连接数据库 和 让Mybatis扫描对应的mapper文件<?xml version="1.0" encoding="UTF8" ?> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/atanycosts?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mapper/userMapper.xml"/> </mappers> </configuration>再 在src --> main --> resources 下创建 mapper 文件夹,里创建 userMapper.xml 文件此文件里写的是 SQL语句<?xml version="1.0" encoding="UTF-8" ?> <mapper namespace="UserEntity"> select * from user </mapper>3 . 实体类src --> main --> com --> atanycosts --> entity写入对应数据库的字段package com.atanycosts.entity; //实体类 public class UserEntity { private Long id; private String name; private String address; public Long getId() { return id; } public void setId(Long 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; } @Override public String toString() { return "UserEntity{" + "id=" + id + ", name='" + name + '\'' + ", address='" + address + '\'' + '}'; } } 4 . Test类package atanycosts.dao; import com.atanycosts.entity.UserEntity; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.List; public class UserdaoTest { @Test public void test() throws IOException { // 1.读取加载mybatis-config.xml String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 2.获取到获取到 SqlSession sqlSession = sqlSessionFactory.openSession(); // 3.根据 mapper id=getByUsers 执行该s ql 语句 通过 sql语句得到我们的对象 orm List<UserEntity> userEntitys = sqlSession.selectList("getByUsers", UserEntity.class); System.out.println(userEntitys); sqlSession.close(); } } 二 . Mapper 代理开发模式1 . 在实体类层创建 UserFlightpackage com.atanycosts.entity; import java.util.Date; //UserFlight实体类 public class UserFlight { private Integer id; //id private String flight_id ; //航号 private String company ; //航空公司 private String departure_airport ; //出发机场 private String arrive_airport ; //达到机场 private Date departure_time; //出发时间 private Date arrive_time; //到达时间 private String model; //机型 private Integer is_delete; //是否隐藏0显示 1隐藏 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getFlight_id() { return flight_id; } public void setFlight_id(String flight_id) { this.flight_id = flight_id; } public String getCompany() { return company; } public void setCompany(String company) { this.company = company; } public String getDeparture_airport() { return departure_airport; } public void setDeparture_airport(String departure_airport) { this.departure_airport = departure_airport; } public String getArrive_airport() { return arrive_airport; } public void setArrive_airport(String arrive_airport) { this.arrive_airport = arrive_airport; } public Date getDeparture_time() { return departure_time; } public void setDeparture_time(Date departure_time) { this.departure_time = departure_time; } public Date getArrive_time() { return arrive_time; } public void setArrive_time(Date arrive_time) { this.arrive_time = arrive_time; } public String getModel() { return model; } public void setModel(String model) { this.model = model; } public Integer getIs_delete() { return is_delete; } public void setIs_delete(Integer is_delete) { this.is_delete = is_delete; } } 2 . 创建mapper层并创建 getByFlightAll getByFlightID 接口src --> main --> com --> atanycosts --> mapper -->UserMapperpackage com.atanycosts.mapper; import com.atanycosts.entity.UserEntity; import com.atanycosts.entity.UserFlight; import java.util.List; public interface UserMapper { List<UserEntity> getByUsers(); //环境测试接口 List<UserFlight> getByFlightAll(); //航班接口 UserFlight getByFlightID(Integer id); //获取对应ID } 3 . 创建service层src --> main --> com --> atanycosts --> service -->FlightServicepackage com.atanycosts.service; import com.atanycosts.entity.UserFlight; import com.atanycosts.mapper.UserMapper; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; import java.util.List; //flight业务逻辑层 public class FlightService { private UserMapper flightMapper; private SqlSession sqlSession; public FlightService() throws IOException { // 1.读取加载mybatis-config.xml String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 2.获取到获取到 sqlSession = sqlSessionFactory.openSession(); flightMapper = sqlSession.getMapper(UserMapper.class); // sqlSession.close(); } /** * 获取所有 * @return */ public List<UserFlight> getByFlightAll(){ return flightMapper.getByFlightAll(); } /** * 获取对应ID * @param id * @return */ public UserFlight getByFlightID(Integer id){ return flightMapper.getByFlightID(id); } } 4 . 在测试类中创建对应方法package atanycosts.dao; import com.atanycosts.entity.UserEntity; import com.atanycosts.entity.UserFlight; import com.atanycosts.mapper.UserMapper; import com.atanycosts.service.FlightService; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.Date; import java.util.List; public class UserdaoTest { /** * 环境测试 * * @throws IOException */ @Test public void test01() throws IOException { // 1.读取加载mybatis-config.xml String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); /** * 接口 * getByUsers */ UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<UserEntity> byUsers = mapper.getByUsers(); System.out.println(byUsers); sqlSession.close(); } /** * flight测试 * 获取所有 * * @throws IOException */ @Test public void test02() throws IOException { FlightService flightService = new FlightService(); List<UserFlight> byFlightAll = flightService.getByFlightAll(); System.out.println(byFlightAll); // sqlSession.close(); } /** * flight测试 * 获取对应ID * * @throws IOException */ @Test public void getByUsersID() throws IOException { FlightService flightService = new FlightService(); UserFlight byFlightID = flightService.getByFlightID(1); System.out.println(byFlightID); } } 5 . 对应的SQL<?xml version="1.0" encoding="UTF-8" ?> <mapper namespace="com.atanycosts.mapper.UserMapper"> select * from flight select * from flight where id = #{id} </mapper>当数据库的字段与Java的属性对应不上时在userMapper.xml下写入如下代码column 数据库字段 property Java属性id 用 id 包裹其他用 result<resultMap id="test" type="com.atanycosts.entity.UserEntity"> <id column="id" property="id"></id> <result column="Database Field" property="Java property"></result> </resultMap>三 . 增删改查1 . 实体类实体类中构造增删改所需要的方法/** * 插入数据 * @param flight_id * @param company * @param departure_airport * @param arrive_airport * @param departure_time * @param arrive_time * @param model * @param is_delete */ public UserFlight(String flight_id, String company, String departure_airport, String arrive_airport, Date departure_time, Date arrive_time, String model, Integer is_delete) { this.flight_id = flight_id; this.company = company; this.departure_airport = departure_airport; this.arrive_airport = arrive_airport; this.departure_time = departure_time; this.arrive_time = arrive_time; this.model = model; this.is_delete = is_delete; } /** * 更新数据 * @param id * @param flight_id * @param company * @param departure_airport * @param arrive_airport * @param departure_time * @param arrive_time * @param model * @param is_delete */ public UserFlight(Integer id, String flight_id, String company, String departure_airport, String arrive_airport, Date departure_time, Date arrive_time, String model, Integer is_delete) { this.id = id; this.flight_id = flight_id; this.company = company; this.departure_airport = departure_airport; this.arrive_airport = arrive_airport; this.departure_time = departure_time; this.arrive_time = arrive_time; this.model = model; this.is_delete = is_delete; } /** * 删除数据 * @param id */ public UserFlight(Integer id) { this.id = id; }2 . 在接口文件中写入/** * 插入数据 * @param userFlight * @return */ boolean insertFlight(UserFlight userFlight); /** * 修改数据 * @param userFlight * @return */ boolean updateFlight(UserFlight userFlight); /** * 删除数据 * @param userFlight * @return */ boolean deleteByIdFlight(UserFlight userFlight);3 . 在Service层写入/** * 插入数据 * @param userFlight * @return */ public boolean insertFlight(UserFlight userFlight){ // 艹了 , 插入时 , 要提交事务 boolean result = flightMapper.insertFlight(userFlight); sqlSession.commit(); sqlSession.close(); return result; } /** * 更新数据 * @param userFlight * @return */ public boolean updateFlight(UserFlight userFlight){ boolean result = flightMapper.updateFlight(userFlight); sqlSession.commit(); sqlSession.close(); return result; } /** * 删除数据 * @param userFlight * @return */ public boolean deleteByIdFlight(UserFlight userFlight){ boolean result = flightMapper.deleteByIdFlight(userFlight); sqlSession.commit(); sqlSession.close(); return result; }4 . 测试类/** * flight测试 * 插入数据 * * @throws IOException */ @Test public void insertFlight() throws IOException { FlightService flightService = new FlightService(); UserFlight userFlight = new UserFlight("1", "1", "1", "1", new Date(), new Date(), "1", 0); boolean b = flightService.insertFlight(userFlight); System.out.println(b); } /** * flight测试 * 更新数据 * * @throws IOException */ @Test public void updateFlight() throws IOException { FlightService flightService = new FlightService(); UserFlight userFlight = new UserFlight(12, "2", "2", "2", "2", new Date(), new Date(), "更改成功", 0); boolean b = flightService.updateFlight(userFlight); System.out.println(b); } /** * 删除数据 * * @throws IOException */ @Test public void deleteByIdFlight() throws IOException { FlightService flightService = new FlightService(); UserFlight userFlight = new UserFlight(13); boolean b = flightService.deleteByIdFlight(userFlight); System.out.println(b); }5 . 对应的SQL <insert id="insertFlight" parameterType="com.atanycosts.entity.UserFlight" useGeneratedKeys="true" keyProperty="id"> INSERT INTO atanycosts.flight (`id`, `flight_id`, `company`, `departure_airport`, `arrive_airport`,`departure_time`, `arrive_time`, `model`, `is_delete`) VALUES (null, #{flight_id}, #{company}, #{departure_airport}, #{arrive_airport}, #{departure_time},#{arrive_time}, #{model}, #{is_delete}); </insert> <update id="updateFlight" parameterType="com.atanycosts.entity.UserFlight"> UPDATE atanycosts.flight SET `id`=#{id}, `flight_id`=#{flight_id}, `company`=#{company}, `departure_airport`=#{departure_airport}, `arrive_airport`=#{arrive_airport}, `departure_time`=#{departure_time}, `arrive_time`=#{arrive_time}, `model`=#{model}, `is_delete`=#{is_delete} WHERE (`id` = #{id}); </update> <delete id="deleteByIdFlight" parameterType="int"> delete from atanycosts.flight where id=#{id}; </delete>四 . 映射文件标签五 . 多条件查询六 . 注解开发
JDBC学习Java Database Connectivity，简称JDBC是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口，提供了诸如查询和更新数据库中数据的方法一个学生管理系统demo : JDBC.zip一 . 基础1 . 初识jdbc的本质就是 ：java官方提供的一套规范接口，用于帮助程序员开发者操作不同的关系型数据库(mysql/Oracle/SQLServer)数据库中 varchar 类型对应 Java 中 string数据库中 int 类型对应 Java 中 Integer2 . 实体类package com.entity; public class StudentEntity { private Long id; private String name; private Long phone; private String address; private String pwd; /** * 获取所有 * @param id * @param name * @param phone * @param address */ public StudentEntity(Long id, String name, Long phone, String address) { this.id = id; this.name = name; this.phone = phone; this.address = address; } /** * 获取指定信息 * @param name * @param phone * @param address */ public StudentEntity(String name, Long phone, String address) { this.name = name; this.phone = phone; this.address = address; } /** * 修改信息 * @param name * @param phone * @param address * @param pwd */ public StudentEntity(String name, Long phone, String address, String pwd) { this.name = name; this.phone = phone; this.address = address; this.pwd = pwd; } /** * 返回修改信息 * @param id * @param name * @param phone * @param address * @param pwd */ public StudentEntity(Long id, String name, Long phone, String address, String pwd) { this.id = id; this.name = name; this.phone = phone; this.address = address; this.pwd = pwd; } public StudentEntity(Long id, Long phone, String address, String pwd) { this.id = id; this.phone = phone; this.address = address; this.pwd = pwd; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Long getPhone() { return phone; } public void setPhone(Long phone) { this.phone = phone; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "{" + " id = " + id + " name = " + name + " phone = " + phone + " address = " + address + " }"; } }3 . 数据库访问层allStudent() 没有进行封装其他都进行了连接和释放的封装package com.dao; import com.entity.StudentEntity; import java.sql.*; import java.util.ArrayList; public class StudentDao { public ArrayList<StudentEntity> allStudent() throws SQLException { Connection connection =null; Statement statement = null; ResultSet resultSet = null; //导入jar包 //注册驱动 try { Class.forName("com.mysql.cj.jdbc.Driver"); //数据库连接 connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/atanycosts?serverTimezone=UTC&useSSL=false", "root", "root"); //获取执行者对象 statement = connection.createStatement(); //执行SQL语句 resultSet = statement.executeQuery("select * from user"); //处理结果 ArrayList<StudentEntity> studentEntities = new ArrayList<>(); while (resultSet.next()) { Long id = resultSet.getLong("id"); String name = resultSet.getString("name"); Integer age = resultSet.getInt("age"); String address = resultSet.getString("address"); StudentEntity studentEntity = new StudentEntity(id, name, age, address); studentEntities.add(studentEntity); } return studentEntities; } catch (ClassNotFoundException e) { e.printStackTrace(); return null; }finally { //释放 try { if (resultSet != null) { resultSet.close(); } if (statement != null) { statement.close(); } if (connection != null) { connection.close(); } } catch (Exception e) { e.printStackTrace(); } } } /** * 根据学生id 查询学生信息 学生的id */ public StudentEntity getByIdStudent(Long stuId) { /** * 判断用户是否传递学生id的值 */ if (stuId == null) { return null; } Connection connection = null; Statement statement = null; ResultSet resultSet = null; try { //调用封装的连接类 connection = JdbcUtils.getConnection(); // 获取执行者对象 statement = connection.createStatement(); // 执行sql语句并获取返回结果 自己拼接 查询sql语句 resultSet = statement.executeQuery("select * from user where id=" + stuId); boolean result = resultSet.next(); // 查询不到数据 false // 判断如果查询不到数据 则不会取值 if (!result) { return null; } // 对结果进行处理 // 获取该行数据的第一列 id Long id = resultSet.getLong("id"); // 获取该行数据的第二列 name String name = resultSet.getString("name"); // 获取该行数据的第三列 age Long phone = resultSet.getLong("phone"); // 获取该行数据的第四列 address String address = resultSet.getString("address"); // 将db中查询到数据封装成java学生对象 StudentEntity studentEntity = new StudentEntity(id, name, phone, address); return studentEntity; } catch (Exception e) { e.printStackTrace(); return null; } finally { // 释放jdbc资源 JdbcUtils.closeConnection(resultSet, statement, connection); } } /** * 插入学生 */ public int insertStudent(StudentEntity stu) { Connection connection = null; Statement statement = null; try { //调用封装的连接类 connection = JdbcUtils.getConnection(); // 获取执行者对象 statement = connection.createStatement(); // 执行sql语句并获取返回结果 executeUpdate执行 insert sql语句 String insertStudentSql = "INSERT INTO user values(null,'" + stu.getName() + "'," + stu.getPhone() + ",'" + stu.getAddress() + "','" + stu.getPwd() + "')"; System.out.println("insertStudentSql:" + insertStudentSql); int result = statement.executeUpdate(insertStudentSql); // 执行该sql语句 影响行数 return result; } catch (Exception e) { e.printStackTrace(); return 0; } finally { // 释放jdbc资源 JdbcUtils.closeConnection(statement, connection); } } /** * 修改学生的信息 */ public int updateStudent(StudentEntity stu) { Connection connection = null; Statement statement = null; try { //调用封装的连接类 connection = JdbcUtils.getConnection(); // 获取执行者对象 statement = connection.createStatement(); //5. 执行sql语句并获取返回结果 executeUpdate执行 update sql语句 String updateStudentSql = "update user set name='" + stu.getName() + "' ,phone=" + stu.getPhone() + "," + "address='" + stu.getAddress() + "' where id=" + stu.getId() + "";System.out.println("updateStudentSql:" + updateStudentSql); int result = statement.executeUpdate(updateStudentSql); // 执行该sql语句 影响行数 return result; } catch (Exception e) { e.printStackTrace(); return 0; } finally { // 释放jdbc资源 JdbcUtils.closeConnection(statement, connection); } } /** * 根据主键id删除学生信息 */ public int delStudent(Long id) { // 判断id是否为null if (id == null) { return 0; } Connection connection = null; Statement statement = null; try { //调用封装的连接类 connection = JdbcUtils.getConnection(); // 获取执行者对象 statement = connection.createStatement(); // 执行sql语句并获取返回结果 executeUpdate执行 delete sql语句 String delSQL = "delete from user where id=" + id; String resetID="ALTER TABLE user AUTO_INCREMENT = 1"; System.out.println("delSql:" + delSQL); int result = statement.executeUpdate(delSQL); statement.executeUpdate(resetID); // 执行该sql语句 影响行数 return result; } catch (Exception e) { e.printStackTrace(); return 0; } finally { // 释放jdbc资源 JdbcUtils.closeConnection(statement, connection); } } }4 . 业务逻辑层package com.serivce; import com.dao.StudentDao; import com.entity.StudentEntity; import java.sql.SQLException; import java.util.ArrayList; //业务逻辑层 public class StudentService { /** * new 学生对象dao层 */ private StudentDao studentDao = new StudentDao(); /** * 查询所有成员 */ public ArrayList<StudentEntity> allStudent() throws SQLException { // ArrayList<StudentEntity> studentEntities = studentDao.allStudent(); return studentDao.allStudent(); } /** * 根据ID查询 */ public StudentEntity getByIdStudent(Long stuId) { return studentDao.getByIdStudent(stuId); } /** * 插入 */ public int insertStudent(StudentEntity stu) { return studentDao.insertStudent(stu); } /** * 修改学生的信息 */ public int updateStudent(StudentEntity stu) { return studentDao.updateStudent(stu); } /** * 根据ID删除 */ public int delStudent(Long id) { return studentDao.delStudent(id); } } 5 . 控制层后面实现功能package com.controller; import com.entity.StudentEntity; import com.serivce.StudentService; import java.sql.SQLException; import java.util.ArrayList; public class StudentController { public static void main(String[] args) throws SQLException { StudentService studentService = new StudentService(); ArrayList<StudentEntity> studentEntities = studentService.allStudent(); for (StudentEntity studentEntity : studentEntities) { System.out.println(studentEntity); } } } 二 . 封装创建 JdbcUtils 封装类,将 StudentDao 层的 连接数据 和 释放资源 封装查询调用方法public static void closeConnection(ResultSet resultSet, Statement statement, Connection connection) { // 1. 查询时释放 resultSet statement connection try { if (resultSet != null) resultSet.close(); if (statement != null) statement.close(); if (connection != null) connection.close(); } catch (Exception e) { e.printStackTrace(); } }增删改调用方法public static void closeConnection(Statement statement, Connection connection) { // 2. 增删改时释放 statement connection closeConnection(null, statement, connection); }整体代码package com.utils; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; import java.util.Properties; //封装 public class JdbcUtils { public static void main(String[] args) { System.out.println(JdbcUtils.driverClass); } /** * 1.需要将我们的构造方法私有化 ---工具类 不需要 new出来 是通过类名称.方法名称访问 */ private JdbcUtils() { } /** * 2.定义工具类 需要 声明 变量 */ private static String driverClass; private static String url; private static String user; private static String password; /** *3.使用静态代码快 来给我们声明好 jdbc变量赋值（读取config.properties） */ static { try { // 1. 读取config.properties 相对路径 InputStream resource = JdbcUtils.class.getClassLoader().getResourceAsStream("config.properties"); // 2.赋值给我们声明好的变量 Properties properties = new Properties(); properties.load(resource); driverClass = properties.getProperty("driverClass"); url = properties.getProperty("url"); user = properties.getProperty("user"); password = properties.getProperty("password"); // 3.注册驱动类 Class.forName(driverClass); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } } /** * 4.封装连接方法 */ public static Connection getConnection() { try { Connection connection = DriverManager.getConnection(url, user, password); return connection; } catch (Exception e) { e.printStackTrace(); return null; } } /** * 5.封装释放连接方法 (重载) */ public static void closeConnection(ResultSet resultSet, Statement statement, Connection connection) { // 1. 查询时释放 resultSet statement connection try { if (resultSet != null) resultSet.close(); if (statement != null) statement.close(); if (connection != null) connection.close(); } catch (Exception e) { e.printStackTrace(); } } public static void closeConnection(Statement statement, Connection connection) { // 2. 增删改时释放 statement connection closeConnection(null, statement, connection); } } 三 . 功能实现登录与注册直接在 StudentController 控制层写package com.controller; import com.entity.StudentEntity; import com.serivce.StudentService; import java.sql.SQLException; import java.util.ArrayList; import java.util.Scanner; public class StudentController { private static StudentService studentService = new StudentService(); public static void main(String[] args) throws SQLException { mainMenu(); } /** * 1.需要定义主菜单程序的入口 */ public static void mainMenu() throws SQLException { while (true) { // 1.提示语 System.out.println("欢迎来到学生管理系统"); System.out.println("1. 查询所有学生"); System.out.println("2. 根据id查询学生"); System.out.println("3. 注册新的学生"); System.out.println("4. 根据id修改学生"); System.out.println("5. 根据id删除学生"); System.out.println("6. 退出系统"); System.out.println("请选择 : "); Scanner scanner = new Scanner(System.in); int result = scanner.nextInt(); // 3.在根据用户选择的序号 判断 switch (result) { case 1: showAllStudent(); break; case 2: findByIdStudent(); break; case 3: insertStudent(); break; case 4: updateIdStudent(); break; case 5: delIdStudent(); break; case 6: System.out.println("已退出"); return; } } } /** * 查询所有学生信息 */ public static void showAllStudent() throws SQLException { System.out.println("所有学生信息 : "); ArrayList<StudentEntity> studentEntities = studentService.allStudent(); for (StudentEntity stu : studentEntities) { System.out.println(stu ); } } /** * 根据学生id查询学生信息 */ public static void findByIdStudent() { System.out.println("请输入学生的id："); Scanner scanner = new Scanner(System.in); Long stuId = scanner.nextLong(); // 根据用户输入的学生id查询学生信息 // 根据用户输入的学生id查询学生信息 查询不到 查询得到 StudentEntity student = studentService.getByIdStudent(stuId); if (student == null) { System.out.println("该学生id" + student + ",不存在的"); return; } // 查询到了学生信息 System.out.println("学生信息：" + student + "\n"); } /** * 根据学生id删除学生信息 */ public static void delIdStudent() { System.out.println("请输入学生的id："); Scanner scanner = new Scanner(System.in); Long stuId = scanner.nextLong(); int result = studentService.delStudent(stuId); if (result > 0) { System.out.println("删除成功" + "\n"); } else { System.out.println("删除失败" + "\n"); } } /** * 插入我们的学生信息 */ public static void insertStudent() { Scanner scanner = new Scanner(System.in); System.out.println("请输入名字："); String name = scanner.nextLine(); System.out.println("请输入手机号："); Long phone = scanner.nextLong(); scanner.nextLine(); // 跳过 System.out.println("请输入地址："); String address = scanner.nextLine(); System.out.println("请输入密码："); String pwd = scanner.nextLine(); StudentEntity studentEntity = new StudentEntity(name, phone, address, pwd); int result = studentService.insertStudent(studentEntity); if (result > 0) { System.out.println("插入学生信息成功" + "\n"); } else { System.out.println("插入学生信息失败" + "\n"); } } /** * 根据学生的id修改该学生信息 */ public static void updateIdStudent() { // 需要先根据学生的id查询该学生信息 如果查询得到的情况下 才会修改学生信息 Scanner scanner = new Scanner(System.in); System.out.println("请输入学生的id："); Long stuId = scanner.nextLong(); // 根据学生id查询学生信息 StudentEntity student = studentService.getByIdStudent(stuId); // 查询不到的情况下 就不会修改学生信息 if (student == null) { System.out.println("没有查询该学生的id对应学生信息" + "\n"); return; } scanner.nextLine(); System.out.println("请输入名字："); String name = scanner.nextLine(); System.out.println("请输入手机号："); Long phone = scanner.nextLong(); scanner.nextLine(); // 跳过 System.out.println("请输入地址："); String address = scanner.nextLine(); System.out.println("请输入密码："); String pwd = scanner.nextLine(); // 封装接受的参数 变成学生对象 StudentEntity studentEntity = new StudentEntity(stuId, name, phone, address, pwd); int result = studentService.updateStudent(studentEntity); if (result > 0) { System.out.println("修改成功" + "\n"); } else { System.out.println("修改失败" + "\n"); } } } 四 . SQL注入就是SQL语句拼接问题select * from user where phone='' and pwd='' or 1='1'; ' or 1='1如何解决?使用JDBC的 预编译执行者对象 PreparedStatemnet 通过站位符 ? 写死SQL语句//需要将 statement 改为 PreparedStatemnet 类型 String Sql = "select * from user where phone=? and pwd=?;"; statement = connection.prepareStatement(Sql); statement.setString(1, xxx.getPhone()); statement.setString(2, xxx.getPwd());五 . 事务用到深入学习六 . 数据库连接池