1、新建springboot项目
2、选择版本依赖
3、maven配置
4、yml文件配置
# YML文件的语法
# 1.数据结构 key-value结构
# 2.写法: key:(空格)value
# 3.层级代码结构,注意缩进 !!!!!
# 4.字符集 文件读取时,默认采用UTF-8编码 可以写中文
# 规则: 命名时最好指定前缀.
server:
port: 8080
mysql:
username: root
password: root
5、动态为属性赋值,编辑JDBCController
/**
* 说明:
* 1.将该类交给Spring容器管理
* 2.SpringMVC负责调用该对象接收用户的请求.
* 3.将业务处理之后的结果,为页面返回JSON数据.
* @ResponseBody作用: 将数据转化为JSON串
*/
@RestController
public class JDBCController {
//${key} Spring提供的springel表达式 简称为:spel表达式
//语法: 从spring容器内部获取key,动态为属性赋值.
@Value("${mysql.username}")
String username; // = "root|";
@Value("${mysql.password}")
String password; // = "root";
@RequestMapping("/getMsg")
public String getMsg(){
return "你好数据库:"+ username +password;
}
}
6、访问:http://localhost:8080/getMsg,浏览器页面显示
7、利用properties文件为属性赋值,编辑mysql.properties
#默认ISO-8859-1 中文必定乱码
mysql.username2=mysql数据库
mysql.password2=你猜猜
8、通过注解为属性赋值,可以指定字符集
/**
* 说明:
* 1.将该类交给Spring容器管理
* 2.SpringMVC负责调用该对象接收用户的请求.
* 3.将业务处理之后的结果,为页面返回JSON数据.
* @ResponseBody作用: 将数据转化为JSON串
*
* propertySource: value属性指定路径
* encoding属性指定配置文件编码格式
*/
@RestController
@PropertySource(value="classpath:/mysql.properties", encoding = "UTF-8")
public class JDBCController {
//${key} Spring提供的springel表达式 简称为:spel表达式
//语法: 从spring容器内部获取key,动态为属性赋值.
@Value("${mysql.username}")
String username; // = "root|";
@Value("${mysql.password}")
String password; // = "root";
@RequestMapping("/getMsg")
public String getMsg(){
return "你好数据库:"+ username +password;
}
/**
* 难点: 如何将pro文件交给Spring容器管理????
* 解决方案: @PropertySource("xxxxxx/xxx.properties") 指定配置文件交给Spring容器管理
*/
@Value("${mysql.username2}")
private String username2;
@Value("${mysql.password2}")
private String password2;
@RequestMapping("/getMsg2")
public String getMsg2(){
return "你好数据库:"+ username2 +password2;
}
}
9、访问:http://localhost:8080/getMsg2,浏览器页面显示
10、springboot高级用法,创建springboot项目
模板,maven
11、编辑pom.xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.jt</groupId>
<artifactId>springboot_demo2</artifactId>
<version>1.0-SNAPSHOT</version>
<!--只需要复制 除了坐标之外的文件即可-->
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.4.1</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.4.1</version>
<configuration>
<mainClass>com.jt.SpringbootDemo1Application</mainClass>
</configuration>
<!--排除一些指定的配置-->
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
12、yml配置文件
开发 dev: 8080端口 生产 prod: 9000端口
#指定环境的默认配置
spring:
profiles:
active: dev
---
#为环境定义名称
server:
port: 8080
spring:
config:
activate:
on-profile: dev
# 采用---的方式实现环境分割
---
server:
port: 9000
spring:
config:
activate:
on-profile: prod
13、热部署
在开发阶段,需要频繁的修改配置文件/代码
需求:要求将代码保存之后,程序自动的编译,并且完成tomcat服务的重启
个别IDEA版本可能不生效
<!--支持热部署 开发阶段有效-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
14、创建pojo,DemoUser
/**
* 实体对象要求:
* 1.类名一般与表名关联
* 2.属性名称一般与字段关联
* 3.pojo中的属性类型必须为引用类型(包装类型)
* 4.实体对象必须有get/set方法
* 5.一般实体对象需要实现序列化接口(规则)
* 原因: 数据可能跨平台(跨服务器)传输,必须序列化
*/
public class DemoUser implements Serializable {
private Integer id;
private String name;
private Integer age;
private String sex;
}
15、lombok依赖
lombok可以为POJO实体对象,动态的生成get/set/toString/hashcode/equals等方法,无需程序员手动编辑
<!--引入插件lombok 自动的set/get/构造方法插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
16、采用注解
@Data //动态生成get/set/toString/equals等方法
@Accessors(chain = true) //开启链式加载 重写set方法
@NoArgsConstructor //无参构造
@AllArgsConstructor //有参构造
public class DemoUser implements Serializable {
private Integer id;
private String name;
private Integer age;
private String sex;
//方法测试
public void add(){
DemoUser user = new DemoUser();
user.setId(100).setName("aaaa").setAge(18).setSex("女");
}
}
17、Mybatis依赖
<!--mybatis依赖包-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<!--jdbc依赖包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
18、编辑DemoUser
@Data //动态生成get/set/toString/equals等方法
@Accessors(chain = true) //开启链式加载 重写set方法
@NoArgsConstructor //无参构造
@AllArgsConstructor //有参构造
public class DemoUser implements Serializable {
private Integer id;
private String name;
private Integer age;
private String sex;
}
19、编辑mybatis-config.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--核心配置文件-->
<configuration>
<!--环境配置标签 default 默认加载的环境 只能写一个 -->
<environments default="development">
<!--编辑开发环境 id是环境唯一标识符 -->
<environment id="development">
<!--事物管理器 利用jdbc控制事务 -->
<transactionManager type="JDBC"/>
<!--mybatis采用数据库链接池的方式整合数据源 -->
<dataSource type="POOLED">
<!--高版本数据库驱动 需要添加cj-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/jt?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--Mybatis加载Mapper映射文件-->
<mappers>
<mapper resource="mybatis/mappers/UserMapper.xml"/>
</mappers>
</configuration>
20、构建DemoUserMapper接口
/**
* 说明:
* 1.根据面向接口开发的思想需要定义一个Mapper接口
* 2.在接口中可以写接口方法, 谁用谁去实现!!!
*/
public interface DemoUserMapper {
//1.查询所有的表数据
public List<DemoUser> findAll();
}
21、编辑DemoUserMapper.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">
<!--xml映射文件 必须与接口一对一绑定
namespace: 指定需要绑定的接口名称. 不能重复.
-->
<mapper namespace="com.jt.mapper.DemoUserMapper">
<!--实现接口中的方法
id: 需要与接口中的方法绑定. 一般复制粘贴
resultType: 对象的包路径.
规则: sql语句不要添加多余的;号 Oracle数据库不能添加;号
-->
<select id="findAll" resultType="com.jt.pojo.DemoUser">
select id,name,age,sex from demo_user
</select>
<!-- <insert id=""></insert>
<update id=""></update>
<delete id=""></delete>-->
</mapper>
22、编辑测试类TestMybatis,测试mybatis实现数据库查询
/**
* 业务说明: 实现mybatis入门案例
* 步骤:
* 1.动态生成SqlSessionFactory
*/
@Test
public void demo1() throws IOException {
//指定配置文件地址
String resource = "mybatis-config.xml";
//通过IO流 加载指定的配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
//动态生成SqlSessionFactory
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession 类比 数据库链接
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取Mapper接口
DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class);
//获取数据
List<DemoUser> userList = demoUserMapper.findAll();
System.out.println(userList);
//关闭链接
sqlSession.close();
}
23、查询结果
24、编辑DemoUserMapper,根据ID查询数据
DemoUser findOne(int id);
25、编辑DemoUserMapper.xml映射文件
<!--
parameterType: 参数类型
mybatis中通过 #{} 获取参数
resultType: 返回值结果对象
-->
<select id="findOne" parameterType="int" resultType="com.jt.pojo.DemoUser">
select * from demo_user where id = #{id}
</select>
26、编辑测试类TestMybatis测试
/***
* 需求: 根据ID查询数据库记录 id=1的数据
*/
@Test
public void testFindOne() throws IOException {
//指定配置文件地址
String resource = "mybatis-config.xml";
//通过IO流 加载指定的配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
//动态生成SqlSessionFactory
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取接口
DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class);
int id = 1;
DemoUser demoUser = demoUserMapper.findOne(id);
System.out.println(demoUser);
//关闭链接
sqlSession.close();
}
27、测试结果
28、@BeforeEach
该注解的作用是在执行@Test方法前调用,是测试方法提供的测试API
编辑TestMybatis2
public class TestMybatis2 {
//定义公共的属性
private SqlSessionFactory sqlSessionFactory;
/**
* mybatis的核心 SqlSessionFacotry对象
* @BeforeEach: 测试API中的注解 在执行@Test注解方法时,会提前执行!!!
*/
@BeforeEach
public void init() throws IOException {
//1.指定资源文件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testMybatis01(){
SqlSession sqlSession = sqlSessionFactory.openSession();
DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class);
List<DemoUser> list = demoUserMapper.findAll();
System.out.println(list);
sqlSession.close();
}
}
29、运行结果
30、根据名称查询,编辑TestMybatis2
/**
* 作业:
* 1. 查询name="王昭君"的用户
*/
@Test
public void testFindByName(){
//保证每个线程都能获取一个链接
SqlSession sqlSession = sqlSessionFactory.openSession();
DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class);
String name = "王昭君";
//如果不能保证结果唯一,则使用List集合接收数据.
List<DemoUser> list = demoUserMapper.findByName(name);
System.out.println(list);
sqlSession.close();
}
31、编辑DemoUserMapper.xml
<!--说明: parameterType其中的类型程序可以根据参数自动判断,所以可以省略不写
根据名称,动态取值 使用#{}关键字
-->
<select id="findByName" resultType="com.jt.pojo.DemoUser">
select * from demo_user where name = #{name}
</select>
32、编辑DemoUserMapper
List<DemoUser> findByName(String name);
33、运行结果
34、编辑TestMybatis2,mybatis中参数封装
/**
* 需求 :2. 查询sex=女 and age > 18岁
* 条件 2个
*/
@Test
public void testFindBySA(){
//保证每个线程都能获取一个链接
SqlSession sqlSession = sqlSessionFactory.openSession();
DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class);
//编程习惯: 面向对象
DemoUser user = new DemoUser();
user.setAge(18).setSex("女");
List<DemoUser> list = demoUserMapper.findBySA(user);
System.out.println(list);
sqlSession.close();
}
35、编辑DemoUserMapper
List<DemoUser> findBySA(DemoUser user);
36、编辑DemoUserMapper.xml
<!--
查询sex=女 and age > 18岁
参数: DemoUser user 意图:传递属性的
规则: 如果传递的参数是对象,则通过#{属性} 可以直接获取数据.
-->
<select id="findBySA" resultType="com.jt.pojo.DemoUser">
select * from demo_user where sex= #{sex} and age > #{age}
</select>
37、运行结果
38、编辑TestMybatis2,参数封装的3种常见情景
/**
* 需求 :2. 查询sex=女 and age > 18岁
* 方式1: User对象封装
*/
@Test
public void testFindBySA(){
//保证每个线程都能获取一个链接
SqlSession sqlSession = sqlSessionFactory.openSession();
DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class);
//编程习惯: 面向对象
DemoUser user = new DemoUser();
user.setAge(18).setSex("女");
List<DemoUser> list = demoUserMapper.findBySA(user);
System.out.println(list);
sqlSession.close();
}
/**
* sex=女 and age > 18
* 方式2: @Param方式封装.
*/
@Test
public void testFindBySA2(){
//保证每个线程都能获取一个链接
SqlSession sqlSession = sqlSessionFactory.openSession();
DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class);
String sex = "女";
int age = 18;
List<DemoUser> list = demoUserMapper.findBySA2(sex,age);
System.out.println(list);
sqlSession.close();
}
/**
* sex=女 and age > 18
* 方式3: map集合封装
*/
@Test
public void testFindBySA3(){
//保证每个线程都能获取一个链接
SqlSession sqlSession = sqlSessionFactory.openSession();
DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class);
Map<String,Object> map = new HashMap<>();
map.put("sex","女");
map.put("age",18);
List<DemoUser> list = demoUserMapper.findBySA3(map);
System.out.println(list);
sqlSession.close();
}
39、编辑DemoUserMapper.xml
<!--
查询sex=女 and age > 18岁
参数: DemoUser user 意图:传递属性的
规则: 如果传递的参数是对象,则通过#{属性} 可以直接获取数据.
-->
<select id="findBySA" resultType="com.jt.pojo.DemoUser">
select * from demo_user where sex= #{sex} and age > #{age}
</select>
<!--
如果参数被@Param("sex") String sex修饰
则#{参数key}即可获取数据
-->
<select id="findBySA2" resultType="com.jt.pojo.DemoUser">
select * from demo_user where sex= #{sex} and age > #{age}
</select>
<!--
Map<String, Object> map
sex=女 age=18
规则: 如果参数是一个map集合,则通过#{key}获取数据.
-->
<select id="findBySA3" resultType="com.jt.pojo.DemoUser">
select * from demo_user where sex= #{sex} and age > #{age}
</select>
40、编辑DemoUserMapper
List<DemoUser> findBySA(DemoUser user);
List<DemoUser> findBySA2(@Param("sex") String sex, @Param("age") Integer age);
List<DemoUser> findBySA3(Map map);
41、编辑TestMybatis2,Mybatis常规CURD操作,新增操作
/**
* 需求: 实现用户入库操作
* 关于事务说明:
* mybatis中的"更新"操作,默认事务都是开启的,如果进行更新操作,
* 则必须提交事务.
*/
@Test
public void testSaveUser(){
SqlSession sqlSession = sqlSessionFactory.openSession();
DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class);
//数据库主键自增,所以对象的ID可以为null.
DemoUser user = new DemoUser(null,"宁荣荣",26,"女");
int rows = demoUserMapper.saveUser(user);
if(rows > 0){
System.out.println("影响的行数:"+rows);
//事务提交
sqlSession.commit();
}
sqlSession.close();
}
42、编辑DemoUserMapper
int saveUser(DemoUser user);
43、编辑DemoUserMapper.xml
<!--
需求: 需要返回影响的行数.
mybatis执行"更新"操作时,自动的返回影响的行数
-->
<insert id="saveUser">
insert into demo_user value (null,#{name},#{age},#{sex})
</insert>
44、运行结果
45、查看数据库
46、Mybatis中的转义字符
xml文件中的转义字符.
> > 大于
< < 小于
& & 号
说明:如果sql中有大量的转义字符 建议使用转义标签体
语法: <![CDATA[ xxx内容 报文 ]]>
47、编辑TestMybatis2,查询age> 18 and age< 100 的用户信息
/**
* 需求: 查询age> 18 and age< 100 的用户信息.
* 规则: 如果不能使用对象封装,则一般使用Map集合
*/
@Test
public void testSelect01(){
SqlSession sqlSession = sqlSessionFactory.openSession();
DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class);
Map<String,Object> map = new HashMap<>();
map.put("minAge",18);
map.put("maxAge",100);
List<DemoUser> userList = demoUserMapper.findByAge(map);
System.out.println(userList);
sqlSession.close();
}
48、编辑DemoUserMapper
List<DemoUser> findByAge(Map<String,Object> map);
49、编辑DemoUserMapper.xml
<select id="findByAge" resultType="com.jt.pojo.DemoUser">
<!--select * from demo_user where age > #{minAge} and age < #{maxAge}-->
<![CDATA[ select * from demo_user where age > #{minAge} and age < #{maxAge}]]>
</select>
50、运行结果
51、编辑TestMybatis2,Mybatis集合用法,批量删除
/**
* 例如: 删除id=232/233/234的数据?
* Sql: delete from demo_user where id in (232,233,234)
* 规则: 如果遇到相同的多个数据,则一般采用集合的方式封装数据.
* 封装方式:
* 1. array
* 2. list
* 3. map<List>
*/
@Test
public void testDeleteIds(){
SqlSession sqlSession = sqlSessionFactory.openSession(true);
DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class);
//将数据封装为数组
int[] ids = {232,233,234};
demoUserMapper.deleteIds(ids);
System.out.println("删除操作成功!!!");
}
52、编辑DemoUserMapper
void deleteIds(int[] ids);
53、编辑DemoUserMapper.xml
<!--
需求: 批量删除多个数据
难点: 如果使用#{集合}获取的是集合对象的整体.删除无效.
思路: 将数组拆分为单个数据. 可以通过遍历的方式操作
语法: mybatis为了参数取值方便,特意封装了遍历的标签 foreach
关于标签参数说明:
<foreach collection=""></foreach>
1.如果传递的参数是数组, 则collection="array"
2.如果传递的参数是list集合, 则collection="list"
3.如果传递的参数是Map集合, 则collection="map中的key"
标签属性说明:
1.collection 集合的名称
2.item 每次遍历的数据的形参变量
3.open 循环的开始标签
4.close 循环的结束标签
5.index 循环遍历下标 一般不用
6.separator 循环遍历的分割符
-->
<delete id="deleteIds">
delete from demo_user where id in (
<foreach collection="array" item="id" separator=",">
#{id}
</foreach>
)
</delete>
54、编辑TestMybatis2,Mybatis集合用法,练习list/map的用法
@Test
public void testDeleteList(){
SqlSession sqlSession = sqlSessionFactory.openSession(true);
DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class);
List list = new ArrayList();
list.add(232);
list.add(233);
list.add(234);
demoUserMapper.deleteList(list);
System.out.println("删除操作成功!!!");
}
/*
* 说明: 有时业务需求导致需要使用map封装list集合
*/
@Test
public void testDeleteMap(){
SqlSession sqlSession = sqlSessionFactory.openSession(true);
DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class);
List list = new ArrayList();
list.add(232);
list.add(233);
list.add(234);
HashMap map = new HashMap();
map.put("ids",list);
demoUserMapper.deleteMap(map);
System.out.println("删除操作成功!!!");
}
55、编辑DemoUserMapper
void deleteList(List list);
void deleteMap(HashMap map);
56、编辑DemoUserMapper.xml
<!--删除List集合中的数据-->
<delete id="deleteList">
delete from demo_user where id in (
<foreach collection="list" item="id" separator=",">
#{id}
</foreach>
)
</delete>
<!--删除List集合中的数据 如何是map,则写map中的key-->
<delete id="deleteMap">
delete from demo_user where id in (
<foreach collection="ids" item="id" separator=",">
#{id}
</foreach>
)
</delete>
57、编辑TestMybatis2,模糊查询
/*
Mybatis作业:
需求: 查询name中包含"王"的数据.并且按照年龄降序排列
*/
@Test
public void findLike(){
SqlSession sqlSession = sqlSessionFactory.openSession(true);
DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class);
String name = "%王%";
List<DemoUser> list = demoUserMapper.findLike(name);
System.out.println(list);
sqlSession.close();
}
58、编辑DemoUserMapper.xml
<!--关于模糊查询的说明: 使用%号需要使用""号包裹.
注意事项: mybatis中的sql 最好小写. 因为不同的系统对于大小写 不敏感.
键位: eclipse ctrl + shift + y 小写
ctrl + shift + u 大写/小写
-->
<select id="findLike" resultType="com.jt.pojo.DemoUser">
<!--select * from demo_user where name like "%"#{name}"%" order by age desc -->
select * from demo_user where name like #{name} order by age desc
</select>
59、编辑DemoUserMapper
List<DemoUser> findLike(String name);
60、运行结果
61、编辑TestMybatis2,批量更新操作
/**
* 作业2: 将name为貂蝉/黄月英/宁荣荣的年龄改为28岁,性别女
*/
@Test
public void updateUser(){
SqlSession sqlSession = sqlSessionFactory.openSession(true);
DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class);
Map<String,Object> map = new HashMap<>();
String[] array = {"貂蝉","黄月英","宁荣荣"};
map.put("names",array);
map.put("age",28);
map.put("sex","女");
demoUserMapper.updateUser(map);
sqlSession.close();
}
62、编辑DemoUserMapper.xml
<!--批量更新操作-->
<update id="updateUser">
update demo_user set age = #{age}, sex = #{sex}
where name in (
<foreach collection="names" item="name" separator=",">
#{name}
</foreach>
)
</update>
63、编辑DemoUserMapper
void updateUser(Map<String,Object> map);
64、编辑mybatis-config.xml,Mybatis简化
<!--核心配置文件-->
<configuration>
<!--配置别名-->
<typeAliases>
<typeAlias type="com.jt.pojo.DemoUser" alias="DemoUser"></typeAlias>
</typeAliases>
65、编辑DemoUserMapper.xml,可以使用简化写法,代替类型的全路径
<mapper namespace="com.jt.mapper.DemoUserMapper">
<!--实现接口中的方法
id: 需要与接口中的方法绑定. 一般复制粘贴
resultType: 对象的包路径.
规则: sql语句不要添加多余的;号 Oracle数据库不能添加;号
-->
<!--
<select id="findAll" resultType="com.jt.pojo.DemoUser">-->
<select id="findAll" resultType="DemoUser">
select id,name,age,sex from demo_user
</select>
66、编辑mybatis-config.xml,如果有多个pojo,可以这样配置
<!--核心配置文件-->
<configuration>
<!-- 配置别名 -->
<typeAliases>
<!--别名标签只对某个类有效.-->
<!--<typeAlias type="com.jt.pojo.DemoUser" alias="DemoUser"></typeAlias>-->
<!--package 指定的是包路径的信息.-->
<package name="com.jt.pojo"/>
</typeAliases>
67、编辑DemoUserMapper.xml
<mapper namespace="com.jt.mapper.DemoUserMapper">
<!--映射原理:
如果配置文件中定义了包路径,则映射对象时会自动的完成路径的拼接
resultType="com.jt.pojo.DemoUser"
-->
<select id="findAll" resultType="DemoUser">
select id,name,age,sex from demo_user
</select>
68、编辑DemoUser,注意新增注解
@Data //动态生成get/set/toString/equals等方法
@Accessors(chain = true) //开启链式加载 重写set方法
@NoArgsConstructor //无参构造
@AllArgsConstructor //有参构造
@Alias("DemoUser")
public class DemoUser implements Serializable {
private Integer id;
private String name;
private Integer age;
private String sex;
}
69、Mybatis简化,sql重复,可以使用sql标签
1. select id,name,age,sex from demo_user where id = 1
2. select id,name,age,sex from demo_user where name = xxx
70、用法
<!--2.简化Sql标签 -->
<sql id="demo_user_sql">
select id,name,age,sex from demo_user
</sql>
<!--include 代表包含Sql标签 -->
<select id="findAll" resultType="DemoUser">
<include refid="demo_user_sql"/>
</select>
71、sql标签的说明
优势:
1.使用Sql标签可以节省xml的文件大小.
2.代码的结构相对简单.
弊端:
1.Sql只能抽取公共的Sql语句,局限性稍大.
2.如果大量的使用Sql标签,则代码的可读性差
72、编辑TestMybatis2,Mybatis,动态sql
IF-WHERE用法
/**
* 封装DemoUser的对象,根据对象中不为null的属性查询
*/
@Test
public void testFindWhere(){
SqlSession sqlSession = sqlSessionFactory.openSession();
DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class);
DemoUser demoUser = new DemoUser();
demoUser.setAge(3000);
List<DemoUser> list = demoUserMapper.findWhere(demoUser);
System.out.println(list);
sqlSession.close();
}
73、编辑DemoUserMapper.xml
<!--动态Sql案例
思路: 如果数据不为null,mybatis才会当做条件
if标签说明:
test: 判断的条件 直接写属性即可
where标签: 去除条件中多余的 and 或者 or的
说明: if和 where 几乎一起出现.
-->
<select id="findWhere" resultType="DemoUser">
select id,name,age,sex from demo_user
<where>
<if test="name != null">name = #{name}</if>
<if test="age !=null"> and age=#{age}</if>
<if test="sex !=null"> and sex=#{sex}</if>
</where>
</select>
74、编辑DemoUserMapper
List<DemoUser> findWhere(DemoUser demoUser);
75、运行结果
76、编辑TestMybatis2,动态Sql-SET标签
/**
* 需求: 根据Id,动态的实现数据的更新.
*/
@Test
public void testUpdateSet(){
SqlSession sqlSession = sqlSessionFactory.openSession(true);
DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class);
DemoUser user = new DemoUser();
user.setId(1).setName("守山大使");
demoUserMapper.updateUser2(user);
sqlSession.close();
}
77、编辑DemoUserMapper
void updateUser2(DemoUser user);
78、编辑DemoUserMapper.xml
<!--
规则: 根据对象中不为null的属性当做set条件
set标签说明: 去除set条件中多余的 ,号
-->
<update id="updateUser2">
update demo_user
<set>
<if test="name !=null">name = #{name},</if>
<if test="age !=null">age = #{age},</if>
<if test="sex !=null">sex = #{sex}</if>
</set>
where
id = #{id}
</update>
79、编辑TestMybatis2,动态Sql-choose when otherwise
/**
* 需求: 如果存在name则按照name查询,否则按照sex查询.
*/
@Test
public void testSelectChoose(){
SqlSession sqlSession = sqlSessionFactory.openSession(true);
DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class);
DemoUser user = new DemoUser();
user.setSex("男");
List<DemoUser> list = demoUserMapper.selectChoose(user);
System.out.println(list);
sqlSession.close();
}
80、编辑DemoUserMapper.xml
<!--
需求: 如果不想将全部的条件当做if的判断.则mybatis提供了分支结构 switch
语法说明:
choose:代表分支结构,只有一个条件有效.
when: 指定判断的条件 和if类似.
otherwise: 如果上述的条件都不满足时,该行代码有效.
-->
<select id="selectChoose" resultType="DemoUser">
select * from demo_user
where
<choose>
<when test="name !=null">name = #{name}</when>
<otherwise>sex = #{sex}</otherwise>
</choose>
</select>
81、编辑DemoUserMapper
List<DemoUser> selectChoose(DemoUser user);
82、运行结果
83、ResultMap用法
创建表dept
84、编辑Dept
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class Dept implements Serializable {
//驼峰命名规则
private Integer deptId;
private String deptName;
}
85、标签说明
resultType说明:
当结果集中的字段名称,如果与属性的名称一致时,才会实现自动的数据封装
resultMap说明:
当结果集中的字段名称,与对象中的属性不一致时,可以使用resultMap实现自定义的封装.
86、编辑TestMybatis2,测试
@Test
public void testFindDept(){
SqlSession sqlSession = sqlSessionFactory.openSession();
DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class);
List<Dept> list = deptMapper.findAll();
System.out.println(list);
sqlSession.close();
}
87、编辑DeptMapper
public interface DeptMapper {
List<Dept> findAll();
}
88、编辑DeptMapper.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.jt.mapper.DeptMapper">
<!--
ORM思想: 对象关系映射.
属性: deptId,deptName
字段: dept_id,dept_name
resultType说明:
当结果集中的字段名称,如果与属性的名称一致时,
才会实现自动的数据封装.
resultMap="" 自定义的封装数据的结构
-->
<select id="findAll" resultMap="deptRM">
select * from dept
</select>
<!--自定义映射关系的
语法:
1.id标签代表主键 (每张表中都会有一个主键)
1.1.column: 代表结果集中的字段.
1.2.property: 对象中的属性
2.result 除了主键之外的配置信息
-->
<resultMap id="deptRM" type="Dept">
<id column="dept_id" property="deptId"/>
<result column="dept_name" property="deptName"/>
</resultMap>
</mapper>
89、加一些数据
90、编辑mybatis-config.xml
<!--Mybatis加载Mapper映射文件-->
<mappers>
<mapper resource="mybatis/mappers/DemoUserMapper.xml"/>
<mapper resource="mybatis/mappers/DeptMapper.xml"/>
</mappers>
</configuration>
91、运行结果
92、编辑mybatis-config.xml,驼峰映射规则
<!--核心配置文件-->
<configuration>
<!--Mybatis的核心配置-->
<settings>
<!--开启了驼峰映射规则 dept_id 自动映射到 deptId -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
93、Mybatis中的缓存机制
引入缓存,可以有效降低用户访问物理设备的频次,提高用户响应速度
mybatis自身缓存 一级缓存/二级缓存
一级缓存:Mybatis默认开启一级缓存,一级缓存可以在同一个SqlSession对象中查询相同的数据,可以实现数据的共享
编辑TestMybatis2,测试
/**
* Mybatis一级缓存: 默认开启
* 规则: 同一个SqlSession内部有效.
*/
@Test
public void cache1(){
SqlSession sqlSession = sqlSessionFactory.openSession();
DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class);
List<DemoUser> list1 = demoUserMapper.findAll();
List<DemoUser> list2 = demoUserMapper.findAll();
List<DemoUser> list3 = demoUserMapper.findAll();
System.out.println(list1 == list2);
System.out.println(list1 == list3);
System.out.println(list2 == list3);
sqlSession.close();
}
94、运行结果
95、二级缓存
二级缓存mybatis中默认也是开启的,但是需要手动标识
二级缓存可以在同一个SqlSessionFactory内部有效
编辑DemoUserMapper.xml,二级缓存的使用
<mapper namespace="com.jt.mapper.DemoUserMapper">
<!--应用二级缓存-->
<cache/>
96、编辑TestMybatis2,测试
/**
* 二级缓存说明:
* sqlSession查询数据之后,会将缓存信息保存到一级缓存中.但是不会立即将
* 缓存交给二级缓存保管.如果需要使用二级缓存,则必须将sqlSession业务逻辑执行成功之后关闭.
*/
@Test
public void cache2(){
SqlSession sqlSession = sqlSessionFactory.openSession();
DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class);
demoUserMapper.findAll();
//关闭一级缓存
sqlSession.close();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
DemoUserMapper demoUserMapper2 = sqlSession2.getMapper(DemoUserMapper.class);
demoUserMapper2.findAll();
sqlSession2.close();
}
97、代理对象说明
JDK动态代理
特点:
1.要求被代理者必须实现(有)接口.
2.JDK代理是jdk默认提供的.
CGLIB动态代理
特点:
1.不管被代理者是否有接口,都可以为其创建代理对象. 代理对象是目标对象的子类.
2.cglib需要手动导入jar包
3.spring为了创建代理对象方便,自身自动添加cglib依赖项.
98、三大框架整合
框架概述
Spring框架:Spring为了团队开发将复杂的框架进行整合,使得程序从控制到调用浑然一体,以一种统一的方式进行调用
核心:整合第三方框架
Spring框架核心机制
IOC:
控制反转: 将对象创建的权利交给Spring容器管理,由Spring容器管理对象的生命周期
DI: 依赖注入
创建对象时,如果该对象中有需要依赖的属性,Spring负责为属性赋值
@RestController
public class UserController {
@Autowired
private UserService userService;
}
AOP 面向切面编程
99、SpringMVC
该框架的主要的作用,接收用户的请求,之后完成业务处理,最终返回响应给用户
创建项目:模型,springboot项目
100、选择版本信息
101、pom.xml文件
<?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.5.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.jt</groupId>
<artifactId>springboot_ssm</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot_ssm</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.5.6</spring-boot.version>
</properties>
<dependencies>
<!--springBoot整合mvc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--springBoot整合测试方法-->
<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-devtools</artifactId>
</dependency>
<!--引入插件lombok 自动的set/get/构造方法插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--mybatis依赖包-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<!--jdbc依赖包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.4.1</version>
<configuration>
<mainClass>com.jt.SpringbootDemo1Application</mainClass>
</configuration>
<!--排除一些指定的配置-->
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
102、编辑User
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
private Integer id;
private String name;
private Integer age;
private String sex;
}
103、编辑UserMapper
public interface UserMapper {
//查询demo_user表中的所有数据
List<User> findAll();
}
104、编辑UserMapper.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.jt.mapper.UserMapper">
<select id="findAll" resultType="com.jt.pojo.User">
select * from demo_user
</select>
</mapper>
105、编辑UserService接口及其实现类
public interface UserService {
//查询user表中的所有的数据
List<User> findAll();
}
@Service //将该类交给Spring容器管理
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper; //代理对象 JDK动态代理
@Override
public List<User> findAll() {
//List<User> userList = userMapper.findAll();
//return userList;
return userMapper.findAll();
}
}
106、编辑UserController
@RestController //@Controller 将该类交给Spring容器管理 +//@ResponseBody 业务返回值时,将数据转化为JSON.
public class UserController {
@Autowired
private UserService userService;
/**
* 需求: 查询全部user表数据
* 请求类型: get/post/put/delete
* 路径: /findUser
* 参数: 无
* 返回值: List<User>
*/
@RequestMapping("/getUser")
public List<User> findUser(){
return userService.findAll();
}
}
107、编辑核心配置application.yml,数据源配置
1. serverTimezone=GMT%2B8 指定时区 东八区
2. useUnicode=true&characterEncoding=utf8
开启使用Unicode编码,并且指定字符集utf-8
3. autoReconnect=true 断线是否重新链接.
4. &allowMultiQueries=true 是否允许批量操作
#语法: 1.key:(空格)value结构
server:
port: 8090
#整合数据源
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/jt?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
username: root
password: root
#SpringBoot整合mybatis
mybatis:
#指定别名包
type-aliases-package: com.jt.pojo
#加载指定的xml映射文件
mapper-locations: classpath:/mybatis/mappers/*.xml
#开启驼峰映射
configuration:
map-underscore-to-camel-case: true
108、启动一下项目,报错
109、解决问题的两种方式,UserMapper接口和启动类
@Mapper //将该接口交给Spring容器管理
public interface UserMapper {
//查询demo_user表中的所有数据
List<User> findAll();
}
@SpringBootApplication
@MapperScan("com.jt.mapper") //根据包扫描路径,扫描全部的mapper接口文件
public class SpringbootSsmApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootSsmApplication.class, args);
}
}
110、访问:localhost:8090/getUser,浏览器页面显示
111、RestFul实现参数传递
根据ID查询数据
Get请求类型
url:http://localhost:8090/findUserById?id=1
编辑UserController
/**
* 业务: 根据ID查询用户数据.
* 请求类型: get
* URL:http://localhost:8090/findUserById?id=1
* 参数: id=1
* 返回值: User对象
* SpringMVC业务规范:
* 1.接收参数时,必须与用户参数保持一致.
*/
//@RequestMapping(value = "findUserById",method = RequestMethod.GET)
@GetMapping("findUserById") //只能接收Get请求类型
public User findUserById(Integer id){
return userService.findUserById(id);
}
112、编辑UserService接口及其实现类
User findUserById(Integer id);
@Override
public User findUserById(Integer id) {
return userMapper.findUserById(id);
}
113、编辑UserMapper接口和UserMapper.xml映射文件
User findUserById(Integer id);
<select id="findUserById" resultType="User">
select * from demo_user where id = #{id}
</select>
114、访问:localhost:8090/findUserById?id=6,浏览器页面显示
115、什么是Servlet
Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。
狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
概括: Servlet是java后台程序与用户交互的机制(媒介)
编辑UserController
//http://localhost:8090/findUserByIds?id=1,3,5,6,7
@GetMapping("/findUserByIds")
public String findUserByIds(HttpServletRequest request){
String id = request.getParameter("id");//id串
String[] idStr = id.split(",");//数字数组
Integer[] intArray = new Integer[idStr.length];//多少个数字长度的Integer类型的新空数组
for (int i=0; i<idStr.length; i++){
intArray[i] = Integer.parseInt(idStr[i]);//对应位置赋值
System.out.println(intArray[i]);
}
return "参数接收成功!!!";
}
116、访问:localhost:8090/findUserByIds?id=1,3,5,7,9,11,页面和控制台分别显示
117、编辑application.yml,Sql日志输出
#Sql日志文件打印
logging:
level:
com.jt.mapper: debug
118、编辑UserController,SpringMVC参数传值说明,简单参数传值
/**
* URL:http://localhost:8090/mvc?name="李四"
* @return
*/
@GetMapping("/mvc")
public Object testDemo(String name){
return "参数名称:"+name;
}
119、访问:localhost:8090/mvc?name="李四",浏览器页面显示
120、编辑UserController,使用对象接收数据
如果用户传递的数据有多个,则可以使用对象接收
/**
* URL:http://localhost:8090/mvc2?name="李四"&age=18&sex="女"
* 说明: 如果使用对象的方式进行接收,则必须有Setxx方法.
* @return
*/
@GetMapping("/mvc2")
public Object testDemo(User user){
return "参数名称:"+user;
}
121、访问:localhost:8090/mvc2?name="牛魔王"&age=44&sex="男",浏览器页面显示
122、编辑UserController,restful业务说明
/**
* 更新操作 利用restFul的结构, 根据Id修改数据,修改name/age
* URL:http://localhost:8090/user/貂蝉/18/227 PUT
* 查询: http://localhost:8090/user/18 GET
* 解析: URL:http://localhost:8090/user/{name}/{age}/{id}
*
* RestFul语法:
* 1.用户url编辑
* 1.1 参数与参数之间使用/分割.
* 1.2 restFul结构顺序一旦确定,不能随意更改
* 1.3 如果使用restFul结构,请求路径中不能出现动词.隐藏目的.
*
* 2.用户规范:
* 由于restFul结构请求路径都是名词,所以不能区分业务逻辑.
* 所以采用请求类型,严格区分业务需求.
* 2.1 GET 查询操作
* 2.2 POST 新增操作/form表单提交
* 2.3 PUT 修改操作
* 2.4 DELETE 删除操作
*
* 3.参数接收
* 1.参数与参数之间使用/分割.
* 2.参数必须使用{xxx}包裹
* 3.使用特定的注解@PathVariable("name") String name 接收参数
* 4.如果接收参数的名称与对象中的属性名称一致,则可以使用对象接收
*/
@GetMapping("/user/{name}/{age}/{id}")
public Object restFul(User user){
userService.update(user);
return "数据更新成功!!!";
}
123、编辑UserService及其实现类
void update(User user);
@Override
public void update(User user) {
userMapper.update(user);
}
124、编辑UserMapper
//@Delete("xxxxx")
//@Insert("sql语句")
//@Select("查询操作的sql")
@Update("update demo_user set name=#{name}, age=#{age} where id=#{id}")
void update(User user);
125、访问:localhost:8090/user/戴沐白/37/23,页面和数据库分别显示
126、前后端调用,vue入门案例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>hello 入门案例</title>
</head>
<body>
<div id="app">
<h1>双向数据绑定测试</h1>
<!-- {{插值表达式}} -->
<h3>获取数据:{{ msg }}</h3>
</div>
<!-- 使用步骤:
1.导入js.文件
2.准备根标签
3.创建vue对象,并且实现挂载.
4.定义属性.实现数据动态取值
知识补充: new Vue({}) 函数式编程
关于变量说明:
1. js中变量定义 早期 var 全局变量 没有作用域.
2. let有作用域的概念
3. const 常量定义 不允许修改
-->
<!-- 引入js -->
<script src="../js/vue.js"></script>
<script type="text/javascript">
const app = new Vue({
//element 元素
el: "#app",
data: {
msg: "您好 Vue JS"
}
})
</script>
</body>
</html>
127、HBuilderX上显示
128、点击运行,运行到浏览器,Chrome,浏览器上显示
129、VUE生命周期函数
概念: 生命周期函数,是VUE针对与用户提供的扩展的功能.如果编辑了生命周期函数,则vue对象自动执行,无需手动调用.
生命周期函数种类:
1. 初始化阶段 beforeCreate created beforeMount mounted VUE对象正在的实例化
2. Vue对象的修改 beforeUpdate, updated
3. 对象销毁 beforeDestroy destroyed
130、案例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>测试vue生命周期函数</title>
</head>
<body>
<div id="app">
<h3 v-text="msg"></h3>
<button @click="update">更新</button>
<button @click="destroy">销毁</button>
</div>
<!--引入js函数类库 -->
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el : "#app",
data : {
msg: "vue生命周期"
},
//在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
beforeCreate(){
console.log("beforeCreate")
},
//在实例创建完成后被立即调用
created(){
console.log("created")
},
//在挂载开始之前被调用:相关的 render 函数首次被调用。
beforeMount(){
console.log("beforeMount")
},
//实例被挂载后调用,这时 el 被新创建的 vm.$el 替换了。
mounted(){
console.log("mounted")
},
//数据更新时调用,发生在虚拟 DOM 打补丁之前
beforeUpdate(){
console.log("beforeUpdate")
},
//由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
updated(){
console.log("updated")
},
//实例销毁之前调用。在这一步,实例仍然完全可用
beforeDestroy(){
console.log("beforeDestroy")
},
//实例销毁后调用。
destroyed(){
console.log("destroyed")
},
methods:{
destroy(){
this.$destroy()
},
update(){
this.msg="vue"
}
}
})
</script>
</body>
</html>
131、运行至浏览器,打开开发者工具,清除刷新,点击页面按钮,页面和开发者工具上分别显示
132、前后端调用Axios,Ajax
同步:一个线程依次加载执行,如果数据没有加载完成,则其他数据处于等待的状态
异步:Ajax特点:局部刷新,异步访问 Ajax为什么可以异步:Ajax的设计原理-Ajax引擎
Ajax引擎
用户发起请求,交给Ajax引擎进行处理, 用户可以执行其它的操作.
Ajax引擎接收到用户请求之后, 发起Http请求访问目标服务器.
后台服务器将数据返回给Ajax引擎.
Ajax引擎 将最终获取的数据 通过回调函数的方式 交给用户处理.
133、Axios案例练习
134、启动后台,运行页面,打开开发者工具,刷新,出现错误
135、解决跨域问题
136、开发者工具页面显示
137、根据id查询数据
/* 带参数的数据传递 通过?号传递数据*/
let url1 = "http://localhost:8090/axios/findUserById?id=8"
axios.get(url1)
.then(function(result){
console.log(result.data)
})
138、编辑AxiosController
@RestController
@CrossOrigin
@RequestMapping("/axios")
public class AxiosController {
@Autowired
private UserService userService;
/**
* 根据ID查询用户信息
* URL: http://localhost:8090/axios/findUserById?id=1
*/
@GetMapping("/findUserById")
public User findUserById(Integer id){
return userService.findUserById(id);
}
139、重启后台,刷新页面,开发者工具页面显示
140、根据name和age查询数据
/**
* 作业2: 根据age/sex查询用户信息
* url地址: http://localhost:8090/axios/findUserByAS
* 参数: id=1
* 返回值: console.log输出
* 知识点: 根据对象传递参数时,使用params关键字
*/
let url2 = "http://localhost:8090/axios/findUserByAS"
//封装对象
let user = {
age: 18,
sex: "女"
}
axios.get(url2,{params : user})
.then(function(promise){
console.log(promise.data)
})
141、编辑AxiosController
/**
* 根据age和sex查询数据
* URL地址:http://localhost:8090/axios/findUserByAS?age=18&sex=%E5%A5%B3
* 请求类型: get
* 参数: age/sex
* 返回值: list<User>
*/
@GetMapping("/findUserByAS")
public List<User> findUserByAS(User user){
return userService.findUserByAS(user);
}
142、编辑UserService及其实现类
List<User> findUserByAS(User user);
@Override
public List<User> findUserByAS(User user) {
return userMapper.findUserByAS(user);
}
143、编辑UserMapper
@Select("select * from demo_user where age=#{age} and sex=#{sex}")
List<User> findUserByAS(User user);
144、开发者工具页面显示
145、Axios-GET-RestFul
/*
案例三: 利用restFul实现数据获取
需求: 根据name/sex查询数据
模板字符串: 语法ES7的写法 一对反引号进行数据拼接,通过${} 获取变量
*/
let name = "宁荣荣"
let sex = "女"
//let url3 = "http://localhost:8090/axios/user/"+name+"/"+sex
let url3 = `http://localhost:8090/axios/user/${name}/${sex}`
axios.get(url3)
.then(function(promise){
console.log(promise.data)
})
146、编辑AxiosController
/**
* 业务说明: 接收restFul请求
* URL:http://localhost:8090/axios/user/${name}/${sex}
* 参数: name/sex
* 结果: List<User>
*/
@GetMapping("/user/{name}/{sex}")
public List<User> findUserByNS(User user){
return userService.findUserByNS(user);
}
147、编辑UserService及其实现类
List<User> findUserByNS(User user);
@Override
public List<User> findUserByNS(User user) {
return userMapper.findUserByNS(user);
}
148、编辑UserMapper
@Select("select * from demo_user where name=#{name} and sex=#{sex}")
List<User> findUserByNS(User user);
149、开发者工具页面显示
150、Axios-POST/PUT,编辑JS
/**
* 业务: 实现post请求入库操作 实现入库用户
* 知识点:
* 1.post请求中的参数 axios.post(url,user)
*/
let url = "http://localhost:8090/axios/saveUser"
let user2 = {
name: "石昊",
age: 25,
sex: "男"
}
axios.post(url,user2)
.then(function(promise){
console.log(promise.data)
})
151、编辑AxiosController
/**
* 业务:实现用户的新增
* URL: http://localhost:8090/axios/saveUser
* 参数: {"name":"石昊","age":25,"sex":"男"} JSON串
* 返回值: String 新增成功
* 知识点: post请求传递对象时,传递的数据是一个json串.不能直接为User对象赋值.
* 解决方案:
* 1.对象转化为JSON @ResponseBody
* 2.JSON转化为对象 @RequestBody
*/
@PostMapping("/saveUser")
public String saveUser(@RequestBody User user){
System.out.println(user);
userService.saveUser(user);
return "用户入库成功!!!";
}
152、编辑UserService及其实现类,编辑UserMapper
void saveUser(User user);
@Override
public void saveUser(User user) {
userMapper.saveUser(user);
}
@Insert("insert into demo_user value(null,#{name},#{age},#{sex})")
void saveUser(User user);
153、重启后台,打开开发者工具页面,清除,刷新,显示
154、查询一下数据库
155、Axios简化操作
let user2 = {
name: "火灵儿",
age: 24,
sex: "女"
}
//指定公共的请求前缀
axios.defaults.baseURL = "http://localhost:8090"
axios.post("/axios/saveUser",user2)
.then(function(promise){
console.log(promise.data)
})
156、可以新增成功
157、async-await关键字
//1.指定请求前缀
axios.defaults.baseURL = "http://localhost:8090"
/**
* 需求: 要求ajax请求在一行完成!
* 解决方案: async-await
* 语法说明:
* 1.async 必须标识函数方法
* 2.await 必须标识ajax请求
* 实际意义:
* 解决了ajax中的头号难题, "回调地狱"问题
*/
//1.定义函数
async function getUser(){
let promise = await axios.get("/getUser")
console.log(promise.data)
//解构赋值 固定写法 死记硬背
let {data: result} = await axios.get("/getUser")
console.log(result)
}
//2.调用函数
getUser()
158、开发者工具页面显示
159、前后端项目调用案例
用户列表展现
用户只要一访问页面,就要发起Ajax请求,访问后端服务器获取用户列表数据
编辑页面
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>用户列表展现案例</title>
</head>
<body>
<div id="app">
<div align="center">
<h3 align="center">用户新增</h3><br>
<p>
用户名称: <input type="text" name="name"/>
用户年龄: <input type="text" name="age"/>
用户性别: <input type="text" name="sex"/>
<button>新增</button>
</p>
</div>
<hr />
<div align="center">
<h3 align="center">用户修改操作</h3><br>
<p>
用户ID号: <input type="text" name="id" disabled/>
用户名称: <input type="text" name="name"/>
用户年龄: <input type="text" name="age"/>
用户性别: <input type="text" name="sex"/>
<button>修改</button>
</p>
</div>
<h1 align="center">用户列表展现案例</h1>
<table align="center" border="1px" width="80%">
<tr align="center">
<td>ID编号</td>
<td>姓名</td>
<td>年龄</td>
<td>性别</td>
<td>操作</td>
</tr>
<tr align="center" v-for="user in userList">
<td v-text="user.id"></td>
<td v-text="user.name"></td>
<td v-text="user.age"></td>
<td v-text="user.sex"></td>
<td>
<button>修改</button>
<button>删除</button>
</td>
</tr>
</table>
</div>
<script src="../js/axios.js"></script>
<script src="../js/vue.js"></script>
<script>
//为axios执行前缀
axios.defaults.baseURL="http://localhost:8090"
const app = new Vue({
el: "#app",
data: {
//3.定义数据列表
userList: []
},
methods: {
//2.定义获取列表数据的函数
async getUserList(){
let {data: result} = await axios.get("/axios/findAll")
//console.log(result)
//将ajax返回值传递给Vue
this.userList = result
}
},
mounted(){
//1利用生命周期函数调用getUser函数
this.getUserList()
}
})
</script>
</body>
</html>
160、运行至浏览器,显示
161、编辑AxiosController
/**
* 需求:获取user全部列表
* URL: /axios/findAll
* 参数: 没有
* 返回值: List<User>
*/
@GetMapping("/findAll")
public List<User> findAll(){
return userService.findAll();
}
162、重启,刷新一下页面,显示
163、用户新增,编辑页面
<div align="center">
<h3 align="center">用户新增</h3><br>
<p>
<!--
用户名称: <input type="text" name="name"/>
用户年龄: <input type="text" name="age"/>
用户性别: <input type="text" name="sex"/>
<button>新增</button> -->
<!-- 看到input框架 第一时间想到双向数据绑定 -->
用户名称: <input type="text" name="name" v-model="addUser.name"/>
用户年龄: <input type="text" name="age" v-model="addUser.age" />
用户性别: <input type="text" name="sex" v-model="addUser.sex" />
<button @click="addUserBtn">新增</button>
</p>
</div>
164、JS封装属性
const app = new Vue({
el: "#app",
data: {
//3.定义数据列表
userList: [],
//指定新增对象
addUser: {
name: '',
age: '',
sex: ''
}
},
165、JS方法实现
},
async addUserBtn(){
//将数据实现入库操作
let {data: result} = await axios.post("axios/saveUser", this.addUser)
//用户提示
alert(result)
//清空原有数据
this.addUser = {}
//如果新增成功,应该重新获取列表信息
this.getUserList()
}
},
166、在页面操作,可以新增成功
167、用户修改,数据回显
168、定义更新对象
169、实现数据回显
170、添加修改点击事件
<div align="center">
<h3 align="center">用户修改操作</h3><br>
<p>
<!--
用户ID号: <input type="text" name="id" disabled/>
用户名称: <input type="text" name="name"/>
用户年龄: <input type="text" name="age"/>
用户性别: <input type="text" name="sex"/>
<button>修改</button> -->
用户ID号: <input type="text" name="id" v-model="updateUser.id" disabled/>
用户名称: <input type="text" name="name" v-model="updateUser.name"/>
用户年龄: <input type="text" name="age" v-model="updateUser.age" />
用户性别: <input type="text" name="sex" v-model="updateUser.sex" />
<button @click="updateUserCommit">修改</button>
</p>
</div>
171、修改函数
172、编辑AxiosController
/**
* 业务: 实现用户更新
* URL: /axios/updateUser
* 参数: json串
* 返回值: 提示信息
*/
@PutMapping("updateUser")
public String updateUser(@RequestBody User user){
userService.updateUser(user);
return "更新操作成功!";
}
173、编辑UserService及其实现类,编辑UserMapper
void updateUser(User user);
@Override
public void updateUser(User user) {
userMapper.updateUser(user);
}
@Update("update demo_user set name=#{name},age=#{age},sex=#{sex} where id=#{id}")
void updateUser(User user);
174、用户删除
<tr align="center" v-for="user in userList">
<td v-text="user.id"></td>
<td v-text="user.name"></td>
<td v-text="user.age"></td>
<td v-text="user.sex"></td>
<td>
<button @click="updateBtn(user)">修改</button>
<button @click="deleteBtn(user)">删除</button>
</td>
</tr>
175、删除函数
176、编辑AxiosController
/**
* 业务:实现用户删除
* URL:axios/deleteUser'+id
* 参数:id
* 返回值:提示信息
*/
@DeleteMapping("/deleteUser/{id}")
public String deleteUser(@PathVariable Integer id){
userService.deleteUserById(id);
return "删除操作成功!";
}
177、编辑UserService及其实现类,编辑UserMapper
void deleteUserById(Integer id);
@Override
public void deleteUserById(Integer id) {
userMapper.deleteUserById(id);
}
@Delete("delete from demo_user where id=#{id}")
void deleteUserById(Integer id);
178、京淘项目前端项目搭建,node.js安装
179、导入前端项目后的效果
180、后端项目搭建,模板,springboot项目
181、编辑pom.xml文件
<?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.5.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.jt</groupId>
<artifactId>jt</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>jt</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.5.6</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.4.1</version>
<configuration>
<mainClass>com.jt.SpringbootDemo1Application</mainClass>
</configuration>
<!--排除一些指定的配置-->
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
182、将ssm的src文件导入jt
183、编辑application.yml,修改后端端口号
184、项目环境结构
185、项目测试,访问:localhost:8091/findAll,浏览器显示
186、首页跳转说明
当用户访问localhost:8080时,开始访问系统首页
根据路由规则实现跳转,当用户访问/时,重定向发起新请求/login
根据“/login”的请求,跳转到登录组件
187、关于axios引入说明
在核心main.js中引入axios
定义axios请求访问前缀
实现父子组件参数传递
子组件引用axios,通过this.$http引用axios
188、完成用户登录,编辑SysResult对象
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class SysResult {
private Integer status; //200正常 201失败
private String msg; //服务器返回提示信息
private Object data; //服务器返回业务数据
/**
* 说明: SysResult对象是系统的返回值对象,调用的次数较多.
* 如果每次都需要手动添加200/201 则较为繁琐,能否封装一些方法
* 简化代码调用????
* 解决方案: 添加静态方法简化调用!!!
*/
public static SysResult fail(){
return new SysResult(201,"服务器调用失败!",null);
}
public static SysResult success(){
return new SysResult(200,"服务器调用成功!",null);
}
//重载规则:参数不要耦合,否则会产生歧义.
public static SysResult success(Object data){
return new SysResult(200,"服务器调用成功!",data);
}
public static SysResult success(String msg,Object data){
return new SysResult(200,msg,data);
}
}
189、实现用户登录,编辑UserController
/**
* 需求: 根据u/p查询数据库,返回秘钥token
* URL: /user/login
* 类型: post
* 参数: username/password json
* 返回值: SysResult对象(token)
*/
@PostMapping("/login")
public SysResult login(@RequestBody User user){
String token = userService.findUserByUP(user);
if(token == null || "".equals(token)){
//表示用户名和密码错误
return SysResult.fail();
}
//表示用户名和密码正确,返回秘钥信息
return SysResult.success(token);
}
190、编辑UserService及其实现类
String findUserByUP(User user);
//根据username/password查询数据库
@Override
public String findUserByUP(User user) {
//1.将密码加密
byte[] bytes = user.getPassword().getBytes();
String md5Pass = DigestUtils.md5DigestAsHex(bytes);
user.setPassword(md5Pass);
//2.根据用户名和密文查询数据库
User userDB = userMapper.findUserByUP(user);
//3.判断userDB是否有值
if(userDB == null){
//查询没有数据.返回null
return null;
}
String token = "秘钥";
return token;
}
191、编辑UserMapper
@Select("select * from user where username=#{username} and password=#{password}")
User findUserByUP(User user);
192、编辑UserServiceImpl
//根据username/password查询数据库
@Override
public String findUserByUP(User user) {
//1.将密码加密
byte[] bytes = user.getPassword().getBytes();
String md5Pass = DigestUtils.md5DigestAsHex(bytes);
user.setPassword(md5Pass);
//2.根据用户名和密文查询数据库
User userDB = userMapper.findUserByUP(user);
//3.判断userDB是否有值
if(userDB == null){
//查询没有数据.返回null
return null;
}
//秘钥特点: 唯一性,迷惑性 UUID:几乎可以保证唯一性
return UUID.randomUUID().toString().replace("-","");
}
193、登录的前端JS的说明
login () {
// 获取表单对象之后进行数据校验
// valid 表示校验的结果 true表示通过 false表示失败
this.$refs.loginFormRef.validate(async valid => {
// 如果没有完成校验则直接返回
if (!valid) return
// 如果校验成功,则发起ajax请求
const { data: result } = await this.$http.post('/user/login', this.loginForm)
if (result.status !== 200) return this.$message.error('用户登录失败')
this.$message.success('用户登陆成功')
// 获取用户token信息
const token = result.data
window.sessionStorage.setItem('token', token)
// 用户登录成功之后,跳转到home页面
this.$router.push('/home')
})
}
194、Session和Cookie选择
特点:
1.如果数据需要临时保存,则选用Session, 如果数据需要长时间存储选用Cookie.
2.如果对于安全性要求较高的数据,选用Session,如果安全性要求不高选用Cookie.
问题: 财务系统用户信息选用什么技术保存信息? 选用session.
195、实现系统首页跳转
用户完成登录之后,系统应该跳转到系统首页,用户请求如下
localhost:8080/#/home
编辑路由实现系统首页跳转,index.js
196、登录之后,页面效果
197、VUE的路由导航守卫
用户在未经登录的条件下,可以直接访问其他页面,导致系统不安全。如果需要控制该漏洞,则需要在前端进行权限的校验。
校验的规则:
如果用户访问没有token信息,则表示用户没有登录,则需要跳转到登录页面
难点:如何实现每一次的请求都要校验
解决方案:拦截器
198、路由导航守卫(拦截器),编辑index.js
// 定义路由导航守卫 考点: 拦截器
/**
* 1.遍历的每个路由都会执行回调函数.
* 2.参数信息: 3个
* 2.1 to: 请求访问的地址 到哪去
* 2.2 from: 请求从哪里跳转而来 从哪来
* 2.3 next: 是一个函数 next() 请求放行
* next("/login") 发起login请求
*/
router.beforeEach((to, from, next) => {
if(to.path === '/login'){
//1.直接跳转并且结束代码
return next()
}
// 2.不是访问的登录页面,所以判断用户是否登录. 判断依据token
let token = window.sessionStorage.getItem('token')
if(token !== null && token !==''){ //更加安全
//有值,跳转指定页面
return next()
}
//3.如果数据为空则跳转到登录页面
next('/login')
})
export default router
199、左侧菜单列表实现,权限层级代码结构
200、前端页面JS分析,生命周期函数,触发ajax请求
201、获取远程服务器数据
202、菜单列表实现,编辑RightsController
@RestController
@CrossOrigin
@RequestMapping("/rights")
public class RightsController {
@Autowired
private RightsService rightsService;
/**
* 业务说明: 完成菜单列表查询 1-2级
* URL: /rights/getRightsList
* 请求方式: get请求
* 参数: 没有参数
* 返回值: SysResult(List)
*/
@GetMapping("getRightsList")
public SysResult getRightsList(){
List<Rights> list = rightsService.getRightsList();
return SysResult.success(list);
}
}
203、编辑Rights
@Data
@Accessors(chain = true)
public class Rights implements Serializable {
private Integer id;
private String name;
private Integer parentId;
private String path;
private Integer level;
private Date created;
private Date updated;
private List<Rights> children;
}
204、编辑RightsService及其实现类
public interface RightsService {
List<Rights> getRightsList();
}
@Service
public class RightsServiceImpl implements RightsService {
@Autowired
private RightsMapper rightsMapper;
@Override
public List<Rights> getRightsList() {
return rightsMapper.getRightsList();
}
}
205、编辑RightsMapper
public interface RightsMapper {
List<Rights> getRightsList();
}
206、编辑RightsMapper.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.jt.mapper.RightsMapper">
<select id="getRightsList" resultMap="rightsRM">
SELECT p.id,p.name,p.parent_id,p.path,p.level,p.created,p.updated,
c.id c_id,c.name c_name,c.parent_id c_parent_id,
c.path c_path,c.level c_level,c.created c_created,
c.updated c_updated FROM
(SELECT * FROM rights WHERE parent_id = 0) p
LEFT JOIN
rights c
ON
p.id = c.parent_id
</select>
<!--
完成左侧菜单列表的数据封装 1-2级
-->
<resultMap id="rightsRM" type="Rights" autoMapping="true">
<id property="id" column="id"></id>
<!--封装一对多数据-->
<collection property="children" ofType="Rights" javaType="java.util.List">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<result property="parentId" column="c_parent_id"/>
<result property="path" column="c_path"/>
<result property="level" column="c_level"/>
<result property="created" column="c_created"/>
<result property="updated" column="c_updated"/>
</collection>
</resultMap>
</mapper>
207、启动后台服务器,登录系统,浏览器页面显示
208、利用mybatis子查询的方式获取数据(扩展),编辑RightsMapper
public interface RightsMapper {
List<Rights> getRightsList();
List<Rights> getRightsList2();
}
209、编辑RightsMapper.xml
<!--
利用子查询的方式获取数据
弊端: 查询的效率低
-->
<select id="getRightsList2" resultMap="childrenRM">
select * from rights where parent_id=0
</select>
<resultMap id="childrenRM" type="Rights" autoMapping="true">
<id property="id" column="id"></id>
<!--一对多封装 column="子查询的条件中传递的参数"-->
<collection property="children" ofType="Rights"
select="selectRightsByParentId" column="id"/>
</resultMap>
<!--单表查询子结果-->
<select id="selectRightsByParentId" resultType="Rights">
SELECT * FROM rights WHERE parent_id=#{id}
</select>
210、用户模块实现,实现子级路由跳转
路由填充位设定,在Home.vue中定义路由占位符
211、实现父子关系的路由映射,通过children属性实现父子组件跳转功能
212、页面效果
213、用户列表展现,页面JS分析,生命周期函数,user.vue
214、获取列表页面的JS
215、编辑分页对象PageResult
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class PageResult {
private String query; //查询数据
private Integer pageNum; //页数
private Integer pageSize; //条数
private Long total; //总数
private Object rows; //查询的结果
}
216、编辑UserController
/**
* 需求: 利用分页查询用户信息
* URL: /user/list
* 参数: http://localhost:8091/user/list?query=查询关键字&pageNum=1&pageSize=10
* 返回值: SysResult(pageResult)
*/
@GetMapping("/list")
public SysResult getUserListByPage(PageResult pageResult){
pageResult = userService.getUserListByPage(pageResult);
return SysResult.success(pageResult);
}
217、编辑UserService及其实现类
public interface UserService {
List<User> findAll();
String findUserByUP(User user);
PageResult getUserListByPage(PageResult pageResult);
}
/**
* 分页Sql: 每页10条
* select * from user limit 起始位置,查询条数
* 第一页:
* select * from user limit 0,10 0-9 含头不含尾
* 第二页:
* select * from user limit 10,10
* 第三页:
* select * from user limit 20,10
* 第N页:
* select * from user limit (页数-1)条数,条数
* @param pageResult
* @return
*/
@Override
public PageResult getUserListByPage(PageResult pageResult) {
//1.总数
long total = userMapper.findTotal();
//2.分页结果
int size = pageResult.getPageSize(); //条数
int start = (pageResult.getPageNum()-1) * size; //起始位置
String query = pageResult.getQuery(); //查询条件
//查询分页数据
List<User> userList = userMapper.findUserListByPage(start,size,query);
//将返回值结果进行封装
return pageResult.setTotal(total).setRows(userList);
}
218、编辑UserMapper
public interface UserMapper {
//查询demo_user表中的所有数据
List<User> findAll();
@Select("select * from user where username=#{username} and password=#{password}")
User findUserByUP(User user);
@Select("select count(1) from user")
long findTotal();
List<User> findUserListByPage(@Param("start") int start,
@Param("size") int size,
@Param("query") String query);
}
219、编辑UserMapper.xml
<!--需求:query有参数 则添加like关键字
如果数据不等于null并且同时不等于''串时,条件生效 &&
-->
<select id="findUserListByPage" resultType="User">
SELECT * FROM USER
<where>
<!--<if test="query !=null && query !=''" >username LIKE "%"#{query}"%"</if>-->
<if test="query !=null and query !=''" >username LIKE "%"#{query}"%"</if>
</where>
LIMIT #{start},#{size}
</select>
220、页面效果
221、用户状态修改,当点击开关,则实现状态信息的修改,status=true/false
222、请求路径,开发者工具
223、页面JS分析
224、添加修改事件
225、编辑UserController
/**
* 需求: 根据Id修改状态
* URL: /user/status/{id}/{status} restFul
* 参数: id/status
* 请求方式: put
* 返回值: SysResult对象
*/
@PutMapping("/status/{id}/{status}")
public SysResult updateStatusById(User user){
userService.updateStatusById(user);
return SysResult.success();
}
226、编辑UserService及其实现类
public interface UserService {
List<User> findAll();
String findUserByUP(User user);
PageResult getUserListByPage(PageResult pageResult);
void updateStatusById(User user);
}
@Override
public void updateStatusById(User user) {
userMapper.updateStatusById(user);
}
227、编辑UserMapper
@Update("update user set status = #{status} where id = #{id}")
void updateStatusById(User user);
228、用户新增,页面弹出框效果
<!-- 编辑用户新增对话框 visible.sync 控制对话框的显示-->
<el-dialog title="添加用户" :visible.sync="dialogVisible" width="50%" @close="closeDialog">
<!-- 定义用户提交表单数据-->
<el-form :model="addUserModel" :rules="rules" ref="addUserRef" label-width="100px" class="demo-ruleForm">
<el-form-item label="用户名" prop="username">
<el-input v-model="addUserModel.username"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="addUserModel.password" type="password"></el-input>
</el-form-item>
<el-form-item label="密码确认" prop="password2">
<el-input v-model="addUserModel.password2" type="password"></el-input>
</el-form-item>
<el-form-item label="电话" prop="phone">
<el-input v-model="addUserModel.phone"></el-input>
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="addUserModel.email"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="addUserBtn">确 定</el-button>
</span>
</el-dialog>
229、页面JS分析,点击确定事件
230、函数说明
231、编辑UserController
/**
* 用户新增
* URL: /user/addUser
* 请求方式: post
* 参数: JS对象经过浏览器解析变为JSON串
* {"username":"111","password":"222","password2":"222","email":"22@qq.com","phone":"13111111111"}
* 返回值: SysResult对象
* 对象转化为JSON @ResponseBody
* JSON转化为对象 @RequestBody 规则
*/
@PostMapping("/addUser")
public SysResult saveUser(@RequestBody User user){
userService.saveUser(user);
return SysResult.success();
}
232、编辑UserService及其实现类
public interface UserService {
List<User> findAll();
String findUserByUP(User user);
PageResult getUserListByPage(PageResult pageResult);
void updateStatusById(User user);
void saveUser(User user);
}
/**
* 业务:实现业务数据封装
* 1.密码加密
* 2.设定默认状态
* 3.设定默认时间
*/
@Override
public void saveUser(User user) {
String password = user.getPassword();
String MD5Pass = DigestUtils.md5DigestAsHex(password.getBytes());
//获取当前时间
Date date = new Date();
user.setPassword(MD5Pass).setStatus(true).setCreated(date).setUpdated(date);
userMapper.saveUser(user);
}
233、编辑UserMapper
public interface UserMapper {
//查询demo_user表中的所有数据
List<User> findAll();
@Select("select * from user where username=#{username} and password=#{password}")
User findUserByUP(User user);
@Select("select count(1) from user")
long findTotal();
List<User> findUserListByPage(@Param("start") int start,
@Param("size") int size,
@Param("query") String query);
@Update("update user set status = #{status} where id = #{id}")
void updateStatusById(User user);
void saveUser(User user);
}
234、编辑UserMapper.xml
<!--实现user入库操作-->
<insert id="saveUser">
insert into user value (null,#{username},#{password},#{phone},#{email},#{status},#{created},#{updated})
</insert>
235、用户修改数据回显,页面JS
236、根据id获取数据
237、页面JS函数
238、编辑UserController
/**
* 业务分析:根据用户ID查询数据库
* URL地址:"/user/"+user.id
* 请求方式:get
* 返回值:SysResult对象
*/
@GetMapping("/{id}")
public SysResult findUserById(@PathVariable Integer id){
User user = userService.findUserById(id);
return SysResult.success(user);
}
239、编辑UserService及其实现类
User findUserById(Integer id);
@Override
public User findUserById(Integer id) {
return userMapper.findUserById(id);
}
240、编辑UserMapper
@Select("select * from user where id=#{id}")
User findUserById(Integer id);
241、页面效果
242、用户修改操作,页面JS分析,修改按钮说明
243、 编辑页面JS
244、编辑UserController
/**
* 用户修改操作
* URL: /user/updateUser
* 参数: User(id/phone/email) JSON串
* 请求方式: put
* 返回值: SysResult对象
*/
@PutMapping("/updateUser")
public SysResult updateUser(@RequestBody User user){
userService.updateUser(user);
return SysResult.success();
}
245、编辑UserService及其实现类
void updateUser(User user);
@Override
public void updateUser(User user) {
//设定当前时间
user.setUpdated(new Date());
userMapper.updateUser(user);
}
246、编辑UserMapper
@Update("update user set phone=#{phone},email=#{email},updated=#{updated} where id=#{id}")
void updateUser(User user);
247、用户删除操作,编辑页面JS,删除按钮说明
248、删除JS函数
249、 编辑UserController
/**
* 删除数据
* URL: /user/{id}
* 参数: id
* 请求方式: delete
* 返回值: SysResult对象
*/
@DeleteMapping("/{id}")
public SysResult deleteById(@PathVariable Integer id){
//防止与MP方法冲突 业务方法最好添加业务名称
userService.deleteUserById(id);
return SysResult.success();
}
250、编辑UserService及其实现类
void deleteUserById(Integer id);
@Override
public void deleteUserById(Integer id) {
userMapper.deleteUserById(id);
}
251、编辑UserMapper
@Delete("delete from user where id=#{id}")
void deleteUserById(Integer id);
252、全局异常处理
当后台服务器发生异常时,前端用户无法获取异常信息,用户体验差
如果业务代码中添加了大量的try-catch语句,则会导致业务的复杂度变高,不便于维护
Spring的全局异常处理机制
//1.标识该类是全局异常处理机制.返回值都是JSON串
// 通知: AOP中的技术,解决特定问题的
// 特点: 该异常处理机制,只拦截Controller层抛出的异常
@RestControllerAdvice
public class SystemExe {
/**
* 说明: 需要为全局异常定义一个方法.
* 要求: 返回的统一的业务数据 SysResult
* 拦截: 指定遇到某种异常实现AOP处理.
* 注意事项: 在业务方法中不要随意添加try-catch
*/
@ExceptionHandler({RuntimeException.class})
public SysResult fail(Exception e){
e.printStackTrace();
return SysResult.fail();
}
}
253、Spring中事务控制
事务特性
原子性: 事务要么同时成功,要么同时失败(不可分割)
隔离性: 多个事务之间相互独立,互补干扰
一致性: 保证数据的一致 (脏读/幻读/不可重复读)
持久性: 一旦事务提交,就应该持久化保存.
254、Spring添加事务@Transactional,UserServiceImpl
/**
* 问题: 数据是否真的被删除?????
* 解决方案: @Transactional
* 作用:
* 1.默认条件下,只拦截运行时异常
* 2.rollbackFor: 指定异常的类型回滚 rollbackFor = RuntimeException.class
* 3.noRollbackFor: 指定异常不回滚 noRollbackFor = RuntimeException.class
*/
@Transactional
@Override
public void deleteUserById(Integer id) {
userMapper.deleteUserById(id);
}
255、MybatisPlus,准备新测试环境,新的项目,模板,springboot
256、项目结构
257、ORM知识回顾
核心概念: 对象关系映射,以对象的方式操作数据库.
mybatis回顾: Mybatis是半自动化的ORM映射框架.
半自动: Sql是自己手写的,但是结果集映射是自动的.
MybatisPlus: 全自动的ORM映射框架,对Mybatis的扩展.
258、MP入门案例,导入jar包
<!--mybatis依赖包-->
<!-- <dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
-->
<!--MP内部已经加载了mybatis,所以无需再次添加jar包-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
259、编辑POJO对象
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
@TableName("demo_user") //对象与表名映射
public class User implements Serializable {
/**
* 规则:
* 1.如果数据库中的字段与表中的属性名称一致,则可以省略不写
* 2.如果对象名称与表名一致,则名称可以省略
*/
@TableId(type = IdType.AUTO)//主键自增
private Integer id;
private String name;
//@TableField("age") //实现属性与字段映射
private Integer age;
private String sex;
}
260、编辑UserMapper
public interface UserMapper extends BaseMapper<User> {
List<User> findAll();
}
261、SpringBoot整合MybatisPlus
MP:是Mybatis的增强版本,只做增强不做改变
编辑application.yml
#SpringBoot整合mybatisPlus
mybatis-plus:
#指定别名包
type-aliases-package: com.jt.pojo
#加载指定的xml映射文件
mapper-locations: classpath:/mybatis/mappers/*.xml
#开启驼峰映射
configuration:
map-underscore-to-camel-case: true
262、MP入门案例,编辑测试类SpringbootSsmApplicationTests
//效果:只要@Test测试方法执行,则整个Spring容器启动,可以根据自身的需要实现依赖注入
//注意事项: 该注解只能在测试类中使用.
// 测试类的包路径必须在主启动类的同包及子包中编辑.
@SpringBootTest
class SpringbootSsmApplicationTests {
@Autowired
private UserMapper userMapper;
/**
* MP入门案例
*/
@Test
public void insertUser() {
User user = new User();
user.setId(null).setName("MybatisPlus").setAge(10).setSex("男");
userMapper.insert(user);
}
}
263、查看数据库
264、 MybatisPlus工作原理
1. 用户调用接口方法 userMapper.insert(User)方法
2. 根据UserMapper的接口找到父级接口BaseMapper<T>
3. 根据父级接口动态获取当前接口的泛型对象T
4. 根据泛型T 获取指定的注解@TableName("demo_user"),之后获取表名demo_user
5. 根据泛型对象T,获取其中的属性,之后再找到属性的注解@TableField("id"),之后再次获取注解的值, 即字段名称.
6. 根据字段名称,获取对应属性的值.
7. 根据Sql拼接 形成最终的可以执行的Sql.
8. MP将生成的Sql交给Mybatis执行入库操作.
265、MybatisPlus API介绍,编辑测试类,根据id查询数据
/**
* 学习技巧: MP设计思想!!!! 对象
* 查询Id=1的用户
*/
@Test
public void selectById() {
int id = 271;
User user = userMapper.selectById(id);
System.out.println(user);
}
266、编辑测试类,根据name和sex查询数据
/**
* 查询name="火灵儿",sex="女"的用户
* 规则: 根据对象中不为null的属性进行业务操作
* 语法:
* 1.QueryWrapper条件构造器 动态拼接where条件
* 2.默认的关系连接符 and
* 例子:
* select * from demo_user where xx=xx and xx=xx
*/
@Test
public void selectByNS() {
User user = new User();
user.setName("火灵儿").setSex("女");
QueryWrapper<User> queryWrapper = new QueryWrapper<>(user);
List<User> list = userMapper.selectList(queryWrapper);
System.out.println(list);
}
267、编辑测试类,利用queryWrapper查询数据
/**
* 查询name="比比东",sex="女"的用户
* 方式2: 利用条件构造器,构建条件
* 说明:
* 1. eq =
* 2. gt >
* 3. lt >
* 4. ge >=
* 5. le <=
* 6. ne <>
*/
@Test
public void selectByNS2() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name","比比东").eq("sex","女");
List<User> list = userMapper.selectList(queryWrapper);
System.out.println(list);
}
/**
* 需求: 查询age>18岁的用户,并且sex=男.
*/
@Test
public void selectByAS() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.gt("age",18).eq("sex","男");
List<User> list = userMapper.selectList(queryWrapper);
System.out.println(list);
}
268、编辑测试类,like关键字
/**
* 需求: 查询name中包含"君",性别="女"
* Sql: where name like "%君%"
* 需求2: 查询name中以"君"结尾的,性别="女" like "%君"
*/
@Test
public void selectLike() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//queryWrapper.like("name","君") //"%君%"
queryWrapper.likeLeft("name","君") //"%君"
.eq("sex","女");
List<User> list = userMapper.selectList(queryWrapper);
System.out.println(list);
}
269、编辑测试类,in关键字
/**
* 需求: 查询id=1,3,4,5的数据 并且按照年龄降序排列
* 规则: 基本类型有没有方法? 所以使用包装类型
* 面向对象开发
*/
@Test
public void selectIds() {
Integer[] ids = {1,3,4,5};
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.in("id",ids)
.orderByDesc("age");
List<User> list = userMapper.selectList(queryWrapper);
System.out.println(list);
}
270、编辑测试类,查询第一列字段
/**
* 需求:
* 只想获取第一列数据(主键),sex="女"
* 用法: .selectObjs(queryWrapper);
* 实际用途: 做关联查询时可以使用
*/
@Test
public void selectObjs() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("sex","女");
List list = userMapper.selectObjs(queryWrapper);
System.out.println(list);
}
271、编辑测试类,动态sql实现
/**
* 说明: 根据不为null的属性当做where 动态sql实现
* 需求: 查询age>18岁,sex=女的用户
* 参数说明:
* 1. boolean condition, true时,当前的条件才会成立
* false 该条件不拼接.
* 2.R column 字段信息
* 3.Object val 值
* 判断字符串API:
* Spring提供的API StringUtils.hasLength(sex);
*/
@Test
public void selectList2() {
Integer age = 20;
String sex = "女";
//boolean flag = sex !=null && "".equals(sex);
boolean flag = StringUtils.hasLength(sex);
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.gt(age!=null, "age",age )
.eq(flag,"sex",sex);
List<User> list = userMapper.selectList(queryWrapper);
System.out.println(list);
}
272、商品分类实现,item_cat表
273、项目改造,导入jar包
<!--MP内部已经加载了mybatis,所以无需再次添加jar包-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
274、编辑ItemCat
@Data
@Accessors(chain = true)
@TableName("item_cat")
public class ItemCat extends BasePojo {
@TableId(type = IdType.AUTO) //主键自增
private Integer id; //定义主键
private Integer parentId; //定义父级菜单
private String name; //分类名称
private Boolean status; //分类状态 0 停用 1 正常
private Integer level; //商品分类等级 1 2 3
@TableField(exist = false) //属性sql不负责操作
private List<ItemCat> children; //业务属性
}
275、编辑ItemCatMapper
public interface ItemCatMapper extends BaseMapper<ItemCat> {
//CURD操作如果没有特殊需求可以省略
//如果没有sql的需求,则xml映射文件可以简化
}
276、编辑application.yml
#SpringBoot整合MP
mybatis-plus:
#指定别名包
type-aliases-package: com.jt.pojo
#加载指定的xml映射文件
mapper-locations: classpath:/mybatis/mappers/*.xml
#开启驼峰映射
configuration:
map-underscore-to-camel-case: true
277、项目结构
278、商品分类页面跳转
279、商品分类列表展现,页面分析,生命周期函数,ItemCat.vue
280、调用业务函数
281、编辑ItemCatController
@RestController
@CrossOrigin
@RequestMapping("/itemCat")
public class ItemCatController {
@Autowired
private ItemCatService itemCatService;
/**
* 需求: 查询3级分类数据信息
* 类型: get
* URL: /itemCat/findItemCatList/{level}
* 参数: level
* 返回值: SysResult(list)
*/
@GetMapping("/findItemCatList/{level}")
public SysResult findItemCatList(@PathVariable Integer level){
List<ItemCat> list = itemCatService.findItemCatList(level);
return SysResult.success(list);
}
}
282、编辑ItemCatService及其实现类(常规写法-效率低)
public interface ItemCatService {
List<ItemCat> findItemCatList(Integer level);
}
@Service
public class ItemCatServiceImpl implements ItemCatService {
@Autowired
private ItemCatMapper itemCatMapper;
/**
* 弊端: 由于多次循环遍历 查询数据库,导致数据库查询次数太多效率极低.
* 思路:
* 1.刚才的业务逻辑梳理
* 2.如何优化????? 提高效率
* @param level
* @return
*/
@Override
public List<ItemCat> findItemCatList(Integer level) {
//查询一级商品分类信息
QueryWrapper<ItemCat> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("parent_id",0);
List<ItemCat> oneList = itemCatMapper.selectList(queryWrapper);
//查询二级商品分类信息
for(ItemCat oneItemCat: oneList){
//1.为了复用条件构造器 将之前的数据清空
queryWrapper.clear();
//查询二级数据 parent_id = 一级ID
queryWrapper.eq("parent_id",oneItemCat.getId());
List<ItemCat> twoList = itemCatMapper.selectList(queryWrapper);
//遍历二级列表 查询三级数据,封装数据返回
oneItemCat.setChildren(twoList);
}
return oneList;
}
}
283、商品分类页面显示
284、商品分类列表优化,Map封装策略
遍历数据,如果该数据的父级不存在,准备list集合,将自己作为第一个元素
遍历数据,如果该数据的父级存在,获取list集合,将自己追加到其中
封装Map集合,编辑ItemCatServiceImpl
/**
* 优化手段:
* 思路:获取所有的数据库记录,之后按照父子级关系进行封装
* 数据结构: Map<k,v>
* Map<parentId,List当前父级的子级信息(不嵌套)>
* 例子: Map<0,List[{id=1,name="xx",children=null}.....]>
*
* 封装数据规则:
* 1.遍历所有的数据.
* 2.获取parentId
* 3.判断parentId是否存在,之后实现数据封装
*/
public Map<Integer,List<ItemCat>> getMap(){
Map<Integer,List<ItemCat>> map = new HashMap<>();
//查询所有的数据库记录
List<ItemCat> list = itemCatMapper.selectList(null);
//1.遍历数据
for(ItemCat itemCat:list){
int parentId = itemCat.getParentId();
if(map.containsKey(parentId)){
//表示数据存在,将自己追加
map.get(parentId).add(itemCat);
}else{
//key不存在, 定义list集合,将自己作为第一个元素追加
List<ItemCat> childrenList = new ArrayList<>();
childrenList.add(itemCat);
//将数据保存到map集合中
map.put(parentId,childrenList);
}
}
return map;
}
285、获取二级数据信息
//该方法获取1-2级数据信息
public List<ItemCat> getTwoList(Map<Integer,List<ItemCat>> map){
//1.先查询一级菜单数据
List<ItemCat> oneList = map.get(0);
//2.遍历每个一级菜单去封装二级数据
for(ItemCat oneItemCat : oneList){
//parent_id = 一级ID
int parentId = oneItemCat.getId();
//查询二级数据
List<ItemCat> twoList = map.get(parentId);
//将数据进行封装
oneItemCat.setChildren(twoList);
}
//返回一级数据
return oneList;
}
286、商品分类全部数据获取
@Service
public class ItemCatServiceImpl implements ItemCatService {
@Autowired
private ItemCatMapper itemCatMapper;
/**
* 弊端: 由于多次循环遍历 查询数据库,导致数据库查询次数太多效率极低.
* 思路:
* 1.刚才的业务逻辑梳理
* 2.如何优化????? 提高效率
* @param level
* @return
*/
@Override
public List<ItemCat> findItemCatList(Integer level) {
//查询一级商品分类信息
QueryWrapper<ItemCat> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("parent_id",0);
List<ItemCat> oneList = itemCatMapper.selectList(queryWrapper);
//查询二级商品分类信息
for(ItemCat oneItemCat: oneList){
//1.为了复用条件构造器 将之前的数据清空
queryWrapper.clear();
//查询二级数据 parent_id = 一级ID
queryWrapper.eq("parent_id",oneItemCat.getId());
List<ItemCat> twoList = itemCatMapper.selectList(queryWrapper);
//遍历二级列表 查询三级数据,封装数据返回
oneItemCat.setChildren(twoList);
}
return oneList;
}
/**
* 优化手段:
* 思路:获取所有的数据库记录,之后按照父子级关系进行封装
* 数据结构: Map<k,v>
* Map<parentId,List当前父级的子级信息(不嵌套)>
* 例子: Map<0,List[{id=1,name="xx",children=null}.....]>
*
* 封装数据规则:
* 1.遍历所有的数据.
* 2.获取parentId
* 3.判断parentId是否存在,之后实现数据封装
*/
public Map<Integer,List<ItemCat>> getMap(){
Map<Integer,List<ItemCat>> map = new HashMap<>();
//查询所有的数据库记录
List<ItemCat> list = itemCatMapper.selectList(null);
//1.遍历数据
for(ItemCat itemCat:list){
int parentId = itemCat.getParentId();
if(map.containsKey(parentId)){
//表示数据存在,将自己追加
map.get(parentId).add(itemCat);
}else{
//key不存在, 定义list集合,将自己作为第一个元素追加
List<ItemCat> childrenList = new ArrayList<>();
childrenList.add(itemCat);
//将数据保存到map集合中
map.put(parentId,childrenList);
}
}
return map;
}
//该方法获取1-2级数据信息
public List<ItemCat> getTwoList(Map<Integer,List<ItemCat>> map){
//1.先查询一级菜单数据
List<ItemCat> oneList = map.get(0);
//2.遍历每个一级菜单去封装二级数据
for(ItemCat oneItemCat : oneList){
//parent_id = 一级ID
int parentId = oneItemCat.getId();
//查询二级数据
List<ItemCat> twoList = map.get(parentId);
//将数据进行封装
oneItemCat.setChildren(twoList);
}
//返回一级数据
return oneList;
}
/**
* 实现思路:
* 1. 获取二级分类列表信息
* 2. 遍历一级菜单,获取二级数据
* 3. 根据二级菜单查询三级数据 防止二级数据为null的现象
* 4. 将三级数据封装到二级中
* @param map
* @return
*/
public List<ItemCat> getThreeList(Map<Integer,List<ItemCat>> map){
//1.获取1-2数据信息 包含了2级的children
List<ItemCat> oneList = getTwoList(map);
//2.编辑一级数据,获取二级数据
for(ItemCat oneItemCat : oneList){
List<ItemCat> twoList = oneItemCat.getChildren();
if(twoList == null || twoList.size()==0){
//跳过本地循环,进入下一次循环
continue;
}
//3.遍历二级数据,查询三级信息
for (ItemCat twoItemCat : twoList){
//查询三级 parentId = 二级ID
int parentId = twoItemCat.getId();
List<ItemCat> threeList = map.get(parentId);
//将三级封装到二级中
twoItemCat.setChildren(threeList);
}
}
return oneList;
}
/**
* 如果使用常规的写法 耗时: 900毫秒
* 经过优化: 20毫秒
* CURD: 好代码!!!
* @param level
* @return
*/
@Override
public List<ItemCat> findItemCatList2(Integer level) {
long startTime = System.currentTimeMillis();
//获取所有集合数据
Map<Integer,List<ItemCat>> map = getMap();
if(level == 1){
//1.一级商品分类信息
return map.get(0);
}
//获取一级菜单和二级菜单
if(level == 2){
return getTwoList(map);
}
//获取三级菜单数据 1-2-3
List<ItemCat> allList = getThreeList(map);
long endTime = System.currentTimeMillis();
System.out.println("耗时:"+(endTime-startTime)+"毫秒");
return allList;
}
}
287、展现效果
288、商品分类新增,页面分析
如果只写商品分类名称,则表示一级菜单
如果父级分类为一级时,则表示二级菜单
如果父级分类为二级时,则表示三级菜单
289、页面的确定按钮
290、页面JS函数
291、编辑ItemCatController
/**
* 实现商品分类新增操作
* URL: /itemCat/saveItemCat
* 参数: this.itemCatForm JSON
* 请求类型: post
* 返回值: SysResult对象
*/
@PostMapping("/saveItemCat")
public SysResult saveItemCat(@RequestBody ItemCat itemCat){
itemCatService.saveItemCat(itemCat);
return SysResult.success();
}
292、编辑BasePojo
@Data
@Accessors(chain = true)
public class BasePojo implements Serializable {
@TableField(fill = FieldFill.INSERT)//表示入库时需要赋值
private Date created;
@TableField(fill = FieldFill.INSERT_UPDATE)//表示入库/更新时赋值
private Date updated;
}
293、编辑ItemCatService及其实现类
void saveItemCat(ItemCat itemCat);
@Override
@Transactional
public void saveItemCat(ItemCat itemCat) {
Date date = new Date();
itemCat.setStatus(true).setCreated(date).setUpdated(date);
itemCatMapper.insert(itemCat);
}
294、新增显示
295、商品分类删除
如果不是父级则直接删除,如果是父级,则先删除子级
页面按钮
296、删除JS函数
297、编辑ItemCatController
/**
* 需求: 实现商品分类删除操作
* 类型: delete
* URL: /itemCat/deleteItemCat?id=xx&level=xx
* 参数: id/level
* 返回值: SysResult
*/
@DeleteMapping("/deleteItemCat")
public SysResult deleteItemCat(ItemCat itemCat){
itemCatService.deleteItemCat(itemCat);
return SysResult.success();
}
298、编辑ItemCatService及其实现类
void deleteItemCat(ItemCat itemCat);
/**
* 业务: 如果是父级 则应该删除子级和自己.
* 思路:
* 1. 判断是否为3级标签 直接删除
* 2. 判断是否为2级标签 先删除三级再删除二级
* 3. 判断是否为1级标签 先查询二级,再删除三级和二级再删除一级
* @param itemCat
*/
@Override
@Transactional
public void deleteItemCat(ItemCat itemCat) {
if(itemCat.getLevel() == 3){
int id = itemCat.getId();
itemCatMapper.deleteById(id);
return;
}
if(itemCat.getLevel() == 2){ //id=二级id
//Sql: xxx where parent_id = 二级ID
int id = itemCat.getId();
QueryWrapper<ItemCat> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("parent_id",id);
//删除的是三级数据
itemCatMapper.delete(queryWrapper);
//再删除自己
itemCatMapper.deleteById(id);
return;
}
/**
* 删除一级菜单
*/
//1.查询二级数据 Sql: where parent_id=id
QueryWrapper queryWrapper = new QueryWrapper();
int id = itemCat.getId();
queryWrapper.eq("parent_id",id);
//由于是删除的业务,只需要获取id即可 所以使用objs
List idList = itemCatMapper.selectObjs(queryWrapper);
//判断是否有二级数据.如果有直接删除.如果没有直接删除一级
if(idList.size()>0){
//根据二级Id删除三级数据 Sql where parent_id in (1,2,3)
queryWrapper.clear();
queryWrapper.in("parent_id",idList);
itemCatMapper.delete(queryWrapper);
//将所有的1-2级的ID,封装到一个集合中
idList.add(id);
//将所有1级2级全部删除 batch 实质就是in关键字
itemCatMapper.deleteBatchIds(idList);
}else{
itemCatMapper.deleteById(id);
}
}
299、编辑BasePojo,自动填充时间
@Data
@Accessors(chain=true)
public class BasePojo implements Serializable{
//新增操作时 自动填充
@TableField(fill = FieldFill.INSERT)
private Date created;
//新增和修改操作时自动填充
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updated;
}
300、编辑config配置
@Component //将对象交给Spring容器管理
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
Date date = new Date();
this.setFieldValByName("created",date,metaObject);
this.setFieldValByName("updated",date,metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updated",new Date(),metaObject);
}
}
301、自动填充代码测试,编辑ItemCatServiceImpl里面的saveItemCat方法
@Override
@Transactional
public void saveItemCat(ItemCat itemCat) {
//Date date = new Date();
//itemCat.setStatus(true).setCreated(date).setUpdated(date);
itemCat.setStatus(true);
itemCatMapper.insert(itemCat);
}
302、状态修改,页面JS
303、编辑ItemCatController
/**
* 需求:修改状态
* URL: /itemCat/status/{id}/{status}
* 参数: id/status
* 请求类型: put
* 返回值: SysResult
*/
@PutMapping("/status/{id}/{status}")
public SysResult updateStatus(ItemCat itemCat){
itemCatService.updateStatus(itemCat);
return SysResult.success();
}
304、编辑ItemCatService及其实现类
void updateStatus(ItemCat itemCat);
/**
* id/status
* Sql: update item_cat set status=xx,updated=xx
* where id=xx
* 解释: updateById ID当做唯一where条件,其它不为null的属性
* 当做set条件
*/
@Override
public void updateStatus(ItemCat itemCat) {
itemCatMapper.updateById(itemCat);
}
305、修改一下ItemCatServiceImpl里面的findItemCatList,也可以查询到所有数据
/**
* 弊端: 由于多次循环遍历 查询数据库,导致数据库查询次数太多效率极低.
* 思路:
* 1.刚才的业务逻辑梳理
* 2.如何优化????? 提高效率
* @param level
* @return
*/
@Override
public List<ItemCat> findItemCatList(Integer level) {
long startTime = System.currentTimeMillis();
//查询一级商品分类信息
QueryWrapper<ItemCat> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("parent_id",0);
List<ItemCat> oneList = itemCatMapper.selectList(queryWrapper);
//查询二级商品分类信息
for(ItemCat oneItemCat: oneList){
//1.为了复用条件构造器 将之前的数据清空
queryWrapper.clear();
//查询二级数据 parent_id = 一级ID
queryWrapper.eq("parent_id",oneItemCat.getId());
List<ItemCat> twoList = itemCatMapper.selectList(queryWrapper);
//遍历二级列表 查询三级数据,封装数据返回
oneItemCat.setChildren(twoList);
//查询三级商品分类信息
for(ItemCat twoItemCat: twoList){
//1.为了复用条件构造器 将之前的数据清空
queryWrapper.clear();
//查询三级数据 parent_id = 二级ID
queryWrapper.eq("parent_id",twoItemCat.getId());
List<ItemCat> threeList = itemCatMapper.selectList(queryWrapper);
//遍历二级列表 查询三级数据,封装数据返回
twoItemCat.setChildren(threeList);
}
}
long endTime = System.currentTimeMillis();
System.out.println("耗时:"+(endTime-startTime)+"毫秒");
return oneList;
}
306、商品分类修改,页面JS分析
307、修改的JS
308、修改的JS函数
309、编辑ItemCatController
/**
* 需求: 修改商品分类信息
* URL: /itemCat/updateItemCat
* 参数: 表单数据 ItemCat对象 {id,name,parentId....}
* 类型: put
* 返回值: SysResult对象
*/
@PutMapping("/updateItemCat")
public SysResult updateItemCat(@RequestBody ItemCat itemCat){
itemCatService.updateItemCat(itemCat);
return SysResult.success();
}
310、编辑ItemCatService及其实现类
void updateItemCat(ItemCat itemCat);
/**
* itemCat对象,不为null的数据执行业务
* 只改了name属性 set name=xxx where id=xx
* @param itemCat
*/
@Override
public void updateItemCat(ItemCat itemCat) {
ItemCat temp = new ItemCat();
temp.setId(itemCat.getId()).setName(itemCat.getName());
itemCatMapper.updateById(temp);
}
311、商品业务模块实现,实现路由跳转,router/index.js
312、 页面效果展现
313、商品模块搭建,Item表,价格扩大100倍之后的数据
314、编辑Item
@Data
@Accessors(chain = true)
@TableName("item") //映射表
public class Item extends BasePojo {
@TableId(type = IdType.AUTO)
private Integer id; //商品Id号
private String title; //商品标题信息
private String sellPoint; //卖点信息
private Integer price; //商品价格
private Integer num; //商品数量
private String images; //商品图片
private Integer itemCatId; //商品分类ID号
private Boolean status; //状态信息 0 下架 1 上架
}
315、编辑ItemMapper
public interface ItemMapper extends BaseMapper<Item> {
}
316、构建层级代码
317、商品列表展现,页面分析,调用生命周期函数
318、检查JS函数
319、编辑ItemController
@RestController
@CrossOrigin
@RequestMapping("/item")
public class ItemController {
@Autowired
private ItemService itemService;
/**
* 业务: 实现商品列表分页展现
* 类型: get
* url: /item/getItemList?query=&pageNum=1&pageSize=10
* 参数: pageResult
* 返回值: SysResult(pageResult)
*/
@GetMapping("/getItemList")
public SysResult getItemList(PageResult pageResult){//3
pageResult = itemService.getItemList(pageResult);
return SysResult.success(pageResult);
}
}
320、编辑ItemService及其实现类
public interface ItemService {
PageResult getItemList(PageResult pageResult);
}
@Service
public class ItemServiceImpl implements ItemService {
@Autowired
private ItemMapper itemMapper;
/**
* 语法:selectPage语法说明
* 1.page: MP内部指定的分页对象
* 2.queryWrapper 条件构造器
* Sql: where title like "%xxx%"
* @param pageResult
* @return
*/
@Override
public PageResult getItemList(PageResult pageResult) {
//判断用户的数据是否有值
boolean flag = StringUtils.hasLength(pageResult.getQuery());
QueryWrapper<Item> queryWrapper = new QueryWrapper<>();
queryWrapper.like(flag,"title",pageResult.getQuery());
//编辑MP的分页对象 四个属性有用(页数/条数/总数/记录) 传递=页数/条数
IPage<Item> page = new Page<>(pageResult.getPageNum(),pageResult.getPageSize());
page = itemMapper.selectPage(page,queryWrapper);
//获取总数
long total = page.getTotal();
//获取记录数
List<Item> rows = page.getRecords();
//将数据封装
return pageResult.setTotal(total).setRows(rows);
}
}
321、MP实现分页配置类
需要指定的数据库类型,按照MP的基本结构进行配置
//什么是bean spring容器管理的对象叫做bean
@Configuration //标识这是一个配置类 相当于早期的xml文件
public class MybatisPlusConfig {
/**
* @Bean作用: 将方法的返回值交给Spring容器管理
* @return
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MARIADB));
return interceptor;
}
}
322、页面效果展现
323、商品新增,编辑路由
324、商品新增,页面JS函数分析
325、封装ItemVO对象
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class ItemVO { //该对象封装商品所有的参数信息
private Item item;
private ItemDesc itemDesc;
}
326、编辑ItemController
/**
* 需求: 实现商品新增
* URL: http://localhost:8091/item/saveItem
* 参数: {item:this.addItemForm,itemDesc:this.itemDesc }
* 类型: post
* 接收数据: ItemVO
* 返回值: SysResult对象
*/
@PostMapping("/saveItem")
public SysResult saveItem(@RequestBody ItemVO itemVO){
itemService.saveItem(itemVO);
return SysResult.success();
}
327、编辑ItemService及其实现类
void saveItem(ItemVO itemVO);
@Override
public void saveItem(ItemVO itemVO) {
Item item = itemVO.getItem();
item.setStatus(true);
itemMapper.insert(item);
}
328、页面效果展现
329、富文本编辑器,页面JS说明
330、组件引用,main.js
331、已安装
332、实现商品详情入库,ItemDesc说明
说明: item表是商品的基本信息,itemDesc表是商品的详情信息. 用户一般查询时都是查询的基本信息.只有点击某个商品时才会查询详情信息.为了提高查询效率 将商品分为 item/itemDesc
逻辑关系:
1. 一个商品对应一个详情
2. 一个详情对应一个商品
数据库表示: item.id = itemDesc.id
333、商品详情参数传递
当用户点击添加商品时,会将item/itemDesc的对象进行传递,则在后端动态接收数据则可以获取2个对象数据
addItem.vue页面的addItemBtn函数
334、编辑ItemDesc
@Data
@Accessors(chain = true)
@TableName("item_desc")
public class ItemDesc extends BasePojo {
//由于item.id=itemDesc.id 所以ID不能主键自增
@TableId
private Integer id;
private String itemDesc;
}
335、编辑ItemDescMapper
public interface ItemDescMapper extends BaseMapper<ItemDesc> {
}
336、编辑ItemServiceImpl里面的saveItem方法
当用户点击入库时,应该将item/itemDesc一起入库操作
@Override
@Transactional
public void saveItem(ItemVO itemVO) {
Item item = itemVO.getItem(); //id=null
item.setStatus(true);
//要求item入库之后,动态返回Id!!!!
//MP原则: 入库之后动态回显数据!!
itemMapper.insert(item);
//实现itemDesc对象入库
ItemDesc itemDesc = itemVO.getItemDesc();
itemDesc.setId(item.getId());
itemDescMapper.insert(itemDesc);
}
337、文件上传,图片上传UI
338、 请求路径说明
339、编辑ImageVO
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class ImageVO {
private String virtualPath; //图片实际目录
private String urlPath; //请求路径
private String fileName; //图片名称
}
340、编辑FileController
@RestController
@CrossOrigin
@RequestMapping("/file")
public class FileController {
/**
* 需求: 实现文件上传
* URL地址: "http://localhost:8091/file/upload",
* 参数: file=xxxx
* 类型: POST请求
* 返回值: SysResult(ImageVO)
* 基础知识:
* inputStream,outputStream
* 高级API: SpringMVC MultipartFile
* 需求: 接收用户信息,保存到本地磁盘中
* 控制图片大小: 默认大小1M
*/
@PostMapping("/upload")
public SysResult upload(MultipartFile file) throws IOException {
//1.获取图片的名称
String fileName = file.getOriginalFilename();
//2.封装文件上传目录
String fileDir = "D:/Java/File/images";
//3.检查目录是否存在
File dir = new File(fileDir);
if(!dir.exists()){//如果目录不存在
//如果目录不存在,则创建目录
dir.mkdirs();
}
//4.封装文件的全路径
String path = fileDir + "/" +fileName;
//5.上传文件
file.transferTo(new File(path));
return SysResult.success();
}
}
341、编辑FileController
/**
* 需求: 实现文件上传
* URL地址: "http://localhost:8091/file/upload",
* 参数: file=xxxx
* 类型: POST请求
* 返回值: SysResult(ImageVO)
* 基础知识:
* inputStream,outputStream
* 高级API: SpringMVC MultipartFile
* 需求: 接收用户信息,保存到本地磁盘中
* 控制图片大小: 默认大小1M
*/
@PostMapping("/upload")
public SysResult upload2(MultipartFile file){
ImageVO imageVO = fileService.upload(file);
if(imageVO == null){
return SysResult.fail();
}
return SysResult.success(imageVO);
}
342、编辑FileService及其实现类
public interface FileService {
ImageVO upload(MultipartFile file);
}
@Service
public class FileServiceImpl implements FileService {
private String localDir = "D:/Java/File/images";
private String urlPath = "http://image.jt.com";
/**
* 考虑的问题:
* 1. 校验图片类型 xx.jpg
* 2. 校验是否为恶意程序 xx.exe.jpg
* 3. 将文件分目录存储.
* 4. 为了保证图片唯一性 ,UUID
* @param file
* @return
*/
@Override
public ImageVO upload(MultipartFile file) {
//xxxxxx.jpg|png|gif 防止大小写问题,将所有字母转化为小写
String fileName = file.getOriginalFilename().toLowerCase();
//利用正则判断是否为图片
if(!fileName.matches("^.+\\.(jpg|png|gif)$")){
//如果校验不通过,则终止程序
return null;
}
System.out.println("图片类型正确的!!!!!!");
//第二步 防止恶意程序 判断图片是否有宽度和高度
try {
BufferedImage bufferedImage = ImageIO.read(file.getInputStream());
int width = bufferedImage.getWidth();
int height = bufferedImage.getHeight();
if(width == 0 || height == 0){
return null;
}
System.out.println("用户上传的是图片");
//第三步: 目录如何划分 yyyy/MM/dd
String dateDir = new SimpleDateFormat("/yyyy/MM/dd/").format(new Date());
// E:/images + /2022/11/11/ 拼接目录
String dirPath = localDir + dateDir;
File dirFile = new File(dirPath);
if(!dirFile.exists()){
//如果目录不存在时, 创建目录
dirFile.mkdirs();
}
//第四步: 使用uuid实现文件名称 uuid.jpg
//4.1 生成UUID
String uuid = UUID.randomUUID().toString().replace("-","");
//截取文件类型
int index = fileName.lastIndexOf(".");
String fileType = fileName.substring(index);
//生成新文件名称
String newFile = uuid + fileType;
//第五步:实现文件上传 全路径 再上传
// E:/images/2021/10/15/uuid.jpg
String path = dirPath + newFile;
file.transferTo(new File(path));
System.out.println("文件上传成功!!!!");
//第六步: 返回ImageVO数据
//6.1 虚拟路径只写动态变化的数据 /2021/11/11/uuid.jpg
String virtualPath = dateDir + newFile;
//6.2 真实图片名称
String fileNameVO = newFile;
//6.3 网络地址 http://image.jt.com/xx/uuid.jpg
String url = urlPath + virtualPath;
return new ImageVO(virtualPath,url,fileNameVO);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
343、关于磁盘地址与网络地址的说明
磁盘地址:
用户将图片上传到本地磁盘中 有磁盘地址.
D:/Java/File/images/2021/10/15/a.jpg
网络地址:
准备一个可以通过网络访问服务器的一个固定的地址.该地址指向真实的磁盘地址,即可以看到该图片
http://image.jt.com/2021/10/15/a.jpg
344、文件删除操作,页面JS分析
345、 编辑FileController
/**
* 需求: 删除图片信息
* url: http://localhost:8091/file/deleteFile
* 类型: delete
* 参数: virtualPath
* 返回值: SysResult对象
*/
@DeleteMapping("/deleteFile")
public SysResult deleteFile(String virtualPath){
fileService.deleteFile(virtualPath);
return SysResult.success();
}
346、编辑FileService及其实现类
public interface FileService {
ImageVO upload(MultipartFile file);
void deleteFile(String virtualPath);
}
/**
* 1.准备全文件路径
* 2.实现文件删除
* @param virtualPath
*/
@Override
public void deleteFile(String virtualPath) {
String path = localDir + virtualPath;
File file = new File(path);
if(file.exists()){
//如果文件存在,则删除文件
file.delete();
}
}
347、代理机制
反向代理
反向代理服务器位于用户与目标服务器之间,但是对于用户而言,反向代理服务器就相当于目标服务器,即用户直接访问反向代理服务器就可以获得目标服务器的资源。同时,用户不需要知道目标服务器的地址,也无须在用户端作任何设定。反向代理服务器通常可用来作为Web加速,即使用反向代理作为Web服务器的前置机来降低网络和服务器的负载,提高访问效率
反向代理特点
代理服务器介于用户和服务器之间.
用户以为反向代理服务器就是目标服务器.
用户不清楚真实的服务器到底是谁!
反向代理服务器保护了目标服务器的信息 所以也称之为"服务器端代理".
正向代理
正向代理,意思是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端才能使用正向代理。
正向代理特点
正向代理服务器介于用户和服务器之间
用户将请求发送给代理服务器,并且指定目标服务器.
目标服务器以为是代理服务器访问的,保护了用户的信息,所以也称之为 “客户端代理”
正向和反向代理特点
用户的每一次请求都包含了正向代理和反向代理机制.
348、Nginx服务器介绍
Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler.ru站点(俄文:Рамблер)开发的,第一个公开版本0.1.0发布于2004年10月4日。
其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、简单的配置文件和低系统资源的消耗而闻名。2011年6月1日,nginx 1.0.4发布。
Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,在BSD-like 协议下发行。其特点是占有内存少,并发能力强,事实上nginx的并发能力在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。
Nginx特点
1. 占有内存少 运行时内存占用量不超过2M
2. 并发能力强 官网测试数据 5万次/秒 实测 3万次/秒
tomcat服务器 并发能力 150-220左右 1000/秒
扩展: F5 负载均衡服务器 运营商一般使用 20万/秒
Nginx下载
选右边的Windows版本,下载下来之后解压
http://nginx.org/en/download.html
Nginx安装说明
nginx路径说明
路径问题:
nginx是c语言开发的所以对中文不友好. 额外需要注意空格
端口问题:
1.Nginx运行的端口是80. 80端口不能被其它的服务占用.
2.如果80端口被其它的服务占用,则通过dos命令 kill 杀死进程.
占用端口:
http协议默认端口号80端口.
https协议默认端口号443端口
nginx进程项说明
nginx 每次启动都会有2个进程. 一个主进程, 一个是守护进程
主进程: 主要提供反向代理服务. 占用内存空间大
守护进程: 防止主进程意外关闭的.
如果需要关闭nginx 则先关闭守护 再关闭主进程.
nginx启动占用端口80,所以需要释放80资源
查询80端口被哪个进程占用
netstat -ano | findstr "80"
检查nginx启动是否正常,访问:localhost
nginx命令(必须掌握)
说明: 要求执行nginx 命令 应该在nginx.exe所在目录执行.
命令:
1. start nginx 启动nginx
2. nginx -s reload 重启nginx 只有nginx启动时才可以重启
3. nginx -s stop 停止nginx
注意事项: nginx的运行只能启动一次,如果启动多次则会产生多余项,影响程序的正常运行
349、Nginx反向代理说明
#Nginx只能支持http/https
http {
#反向代理服务 一个服务一个server
server {
#监听端口 一般都是80
listen 80;
#拦截的域名
server_name localhost;
# 进行反向代理服务配置
# / 根目录 拦截用户所有的请求
location / {
#root关键字 代理的是一个目录
root html;
# 默认访问的页面
index index.html index.htm;
}
}
}
350、图片回显
磁盘地址: D:\Java\File\images\2021\10\15\a.jpg
网络地址: http://image.jt.com\2021\10\15\a.jpg
代理核心: http://image.jt.com 映射到 D:\Java\File\images
实现域名代理,编辑conf目录下的nginx.conf:
注意事项:
1. start nginx 命令 如果配置文件有错, 不会提示.
2. nginx -s reload 重启命令 会有错误提示
351、图片回显原理
根据hosts文件可以实现域名与IP的映射关系,在本机实现域名与IP的映射,只对本机有效
修改hosts文件
位置:C:\Windows\System32\drivers\etc
如果勾选只读,则去掉
352、编辑hosts文件
353、hosts测试,启动后台服务器, 访问:web.jt.com:8091/rights/getRightsList
354、后台运行,前台运行,Nginx启动,页面效果展现
355、查看本地目录中的文件
356、京淘项目前端发布
前端组成部分
原始文件-开发阶段:
1. node.js
2. 脚手架项目
3. Vue组件
4. Vue路由
5. Axios ajax请求.
项目发布阶段:
1.html
2.css样式
3.js
说明: 如果前端项目需要发布项目,则将项目打包部署即可.
357、前端部署准备工作
路径说明
前端向后端发送请求时路径都是 http://localhost:8091/xxx/xxx,该请求需要转化为网络地址. http://manage.jt.com/xxx/xxx
图片上传的地址 http://localhost:8091 换为http://manage.jt.com
358、修改ajax请求地址,修改main.js
359、修改addItem.vue
360、项目前端发布
任务,build,运行,对号
361、发布之后的文件位置,dist
362、复制到nginx的根目录
363、前端项目发布
用户通过域名: http://www.jt.com:80 访问前端的项目路径 dist/index.html
编辑nginx.conf文件
364、http协议自动转化为https问题
开发: 使用谷歌浏览器 最为标准的浏览器
解决方案:
1.浏览器中输入: “chrome://net-internals/#hsts:“
2.删除指定的域名
365、清空浏览器缓存,然后重启
366、访问:http://www.jt.com/#/login(有问题)
367、实现域名的代理
后端请求的网址: http://manage.jt.com 转向到 http://localhost:8091
代理规则: 反向代理.
368、修改之后,重启nginx(出现问题)
369、Nginx实现负载均衡
动态获取端口号,编辑PortController
@RestController
@CrossOrigin
public class PortController {
@Value("${server.port}")
private Integer port;
//动态获取端口号
@GetMapping("/getPort")
public String getPort(){
return "当前端口号:" + port;
}
}
370、关闭热部署
说明: 由于需要同时开启2台tomcat服务器,所以需要关闭热部署操作
将POM.xml文件中的热部署jar包去除即可.
<!--热部署,开发阶段有效-->
<!--
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>-->
371、修改端口号
先8091启动,再改为8092,更改一下下面的选项,再启动
分别启动2台tomcat服务器. 端口号分别为8091/8092. 通过下图实现进程项执行2次的操作.
访问:http://localhost:8091/getPort
访问:http://localhost:8092/getPort
372、负载均衡策略
373、找到nginx.conf文件
374、编辑nginx.conf,配置后端集群,并更改上面的代理
#manage.jt.com:80 映射localhost:8091
server {
listen 80;
server_name manage.jt.com;
location / {
#代理请求
#proxy_pass http://127.0.0.1:8091;
proxy_pass http://tomcats;
}
}
#配置后端集群
upstream tomcats {
server 127.0.0.1:8091;
server 127.0.0.1:8092;
}
375、保存,启动一下nginx,在有nginx.exe文件夹上cmd,start nginx
376、访问:http://manage.jt.com/getPort
刷新
377、负载均衡策略
轮询策略
根据配置文件内容,依次访问服务器
#manage.jt.com:80 映射localhost:8091
server {
listen 80;
server_name manage.jt.com;
location / {
#代理请求
#proxy_pass http://127.0.0.1:8091;
proxy_pass http://tomcats;
}
}
#配置后端集群
upstream tomcats {
server 127.0.0.1:8091;
server 127.0.0.1:8092;
}
权重策略(十年以前,现在是微服务的轮询)
根据服务器的性能,手动分配服务器的负载
#配置后端集群 1.默认轮询 2.权重 weight
upstream tomcats {
server 127.0.0.1:8091 weight=4;
server 127.0.0.1:8092 weight=1;
}
保存,重启nginx
访问:manage.jt.com/getPort,8091出现的概率比较多,80%
IPHASH
让用户的请求与服务器绑定,用户访问某台服务器,以后永远访问该服务器
#配置后端集群 1.默认轮询 2.权重 weight 3.iphash策略
upstream tomcats {
ip_hash;
server 127.0.0.1:8091;
server 127.0.0.1:8092;
}
保存,重启nginx,nginx -s reload,再访问:http://manage.jt.com/getPort,是8091,后面刷新,都是8091