框架阶段学习

框架阶段

学习内容总结: mysql加强mavenMybatis加强springioc,di,aopSpringMvcjQuery

day01 MYSQL加强!

1.1 数据库的三范式

1.1.1 什么是数据库的三范式呢?

第一范式(1NF):一个列中只能存储一个值,也就是不能有多个值

第二范式(2NF):数据库中的每个实例或者行必须被唯一的区分(也就是要有主键)

第三范式(3NF):数据库中的表的外键不能包含其他表的非主键信息

  • 由于当我们需要进行修改dept类的某个部门名称时,user类相关联的要全部跟着一起改,很麻烦
  • 根据自己的需求,有时候我们可以不遵循第三范式,比如说,当我们经常查询而很少修改的时候

在这里插入图片描述
在这里插入图片描述

1.2 单表查询

1.2.1 消除重复
 --------------------------消除重复----------------------------------------
查询所有有员工的部门编号
SELECT DISTINCT DEPTNO FROM emp 
查询有员工的部门和职位
SELECT DISTINCT job ,deptno from emp
1.2.2 算术运算符
--------------------------算术运算符---------------------------------------
查询所有员工的年薪,IFNULL(a,b)如果a不为空则等于a,否则等于b,类似三元运算符
select sal  * 12 FROM emp
查询所有员工的年薪(使用别名)
select sal  * 12 "年薪" FROM emp
1.2.3 过滤查询
-------------------------常用算术比较运算符---------------------------------
查询所有员工的年薪((月薪 + 奖金) * 12)
select (sal + IFNULL(comm,0)) * 12 FROM emp
查询有奖金的员工信息
SELECT * FROM emp WHERE comm > 0
查询公司的老板
SELECT * FROM emp WHERE job = "PRESIDENT"
查询出基本工资高于 1500 的所有员工信息
SELECT * FROM emp WHERE sal > 1500
查询名字叫 SCOTT 的员工所从事的工作
SELECT job FROM emp WHERE ename = "SCOTT"
查询 1981 年入职的员工信息
SELECT * FROM emp WHERE hiredate >= "1981-01-01" AND hiredate < "1982-01-01"
查询年薪小于 3W 的员工
SELECT * FROM emp WHERE (sal + IFNULL(comm,0)) * 12 < 30000
查询所有不是销售人员的员工信息
SELECT * FROM emp WHERE job != "SALESMAN"
查询工资在 2000-3000 之间的员工信息
SELECT * FROM emp WHERE sal BETWEEN 2000 AND 3000
查询 1981 年入职的员工
SELECT * FROM emp WHERE hiredate >= "1981-01-01" AND hiredate < "1982-01-01"
查询工资为 800 或 1600 或 3000 的员工
SELECT * FROM emp WHERE sal IN (800,1600,3000)
查询出所有雇员姓名是以 A 开头的全部雇员信息。
SELECT * FROM emp WHERE ename like "A%"
查询出雇员姓名第二个字母是 M 的全部雇员信息。
SELECT * FROM emp WHERE ename like "_M%"
查询出雇员姓名任意位置上包含字母 A 的全部雇员信息。
SELECT * FROM emp WHERE ename like "%A%"
-------------------------------------逻辑运算符------------------------------
查询姓名中有 e 或者 a 的员工姓名
SELECT  * FROM emp WHERE ename like "%e%" OR ename like "%a%"
查询工资在 1500~3000 之间的全部员工信息
SELECT  * FROM emp WHERE sal >= 1500 AND sal <= 3000
查询工资不在 2000-3000 之间的员工信息
SELECT  * FROM emp WHERE   sal < 2000 OR sal >3000
查询工资不为 800 或 1600 或 3000 的员工
SELECT  * FROM emp WHERE sal not IN(800,1600,3000)
查询出职位是办事员 (CLERK) 或者是销售人员 (SALESMAN) 的全部信息,且工资在 1000 以上
SELECT  * FROM emp WHERE job IN("CLERK","SALESMAN") AND sal > 1000
------------------------------结果排序-----------------------------------
查询所有员工信息,按照工资排序
SELECT  * FROM emp ORDER BY sal 
查询所有员工信息,按照年薪降序排序
SELECT  * FROM emp ORDER BY sal * 12 DESC 
查询所有员工信息,按照部门和年薪降序排序
SELECT  * FROM emp ORDER BY  DEPTNO, sal * 12 DESC 

注意:NULL与任何运算都是NULL!! AND的运算级别比OR优先!!

1.3 多表查询

1.3.1为什么要进行多表查询呢?

当我们想要获取的数据散乱在各个表中时,我们需要进行多表查询,多表查询可以提高维护的性能,减少成本

1.3.2 笛卡尔积

​ 当我们进行多表查询的时候,联合两张表的时候,会产生将全部数据进行组队,而这些数据是没有意义的,还有些数据是有矛盾的。

因此为了消除笛卡尔,我们要要在where语句中加入有效的连接条件来消除笛卡尔积来拿到自己想要的数据

1.4 内连接

1.4.1 隐式内连接 ( = )

也就是等值查询!!

--------------------多表查询--------------------------------------------
1,隐式内连接查询(等值查询)
查询员工编号,员工名称,员工所属部门的编号和名称
SELECT  empno, ename, dname FROM emp  , dept   WHERE emp.deptno = dept. deptno
查询员工的姓名,工资,所在部门的名称,以及工资的等级
SELECT   ename, sal,dname ,grade FROM emp  , dept,salgrade  
     WHERE emp.deptno = dept. deptno 
		       and  emp.sal >= salgrade.losal
					 	and sal <= salgrade.hisal
1.4.2 显式内连接 JOIN
查询员工编号,员工名称,员工所属部门的编号和名称
SELECT  empno, ename, dname FROM emp  JOIN  dept   ON emp.deptno = dept. deptno
查询员工的姓名,工资,所在部门的名称,以及工资的等级
SELECT   ename, sal,dname ,grade FROM emp  
      (inner)JOIN dept ON  emp.deptno = dept. deptno
      JOIN  salgrade   ON   emp.sal >= salgrade.losal and sal <= salgrade.hisal

1.5 外连接

1.5.1 左外连接 (LEFT JOIN)
3,外连接查询
查询出员工的编号,名字,薪水和所在部门的名称(使用内连接查询),
SELECT empno,ename,sal,dname FROM emp JOIN dept ON emp.deptno = dept. deptno
实验:可以发现员工没有部门的,没有被查询出来
1.左外连接查询
SELECT empno,ename,sal,dname FROM emp  LEFT JOIN dept ON emp.deptno = dept. deptno
实验:我们可以发现左边的表全部查询出来,而右边不符合条件的用NULL来代替

实验:我们可以发现左边的表全部查询出来,而右边不符合条件的用NULL来代替

1.5.2 右外连接 (RIGHT JOIN)
2.右外连接查询
SELECT empno,ename,sal,dname FROM emp  RIGHT JOIN dept ON emp.deptno = dept. deptno
实验:我们可以发现右边的数据全部查询出来,左边的数据不符合条件的用NULL来代替

在这里插入图片描述

实验:我们可以发现右边的数据全部查询出来,左边的数据不符合条件的用NULL来代替

1.6 分组函数

------------------分组函数--------------------------------------------------
多行函数
NUll值与任何运算都是null
统计函数忽略空值,可以使用 IFNULL, 因为是 NULL 不会影响汇总值,但会影响汇总数量;
不能在 where 语句中使用分组函数。

SELECT count(deptno) FROM emp   
实验:实验结果为13条,而实际有14条,有一个deptno为null没被算进去
SELECT sum(deptno) FROM emp
实验:显示null值没有被算进去
SELECT avg(comm) FROM emp
实验:结果发现null值是没有被算进去的,2200/4=550,应该是2200/14=157,
所以说当我们统计数量的时候,会发生错误!!!


练习:
查询所有员工每个月的平均工资及总工资
SELECT sum(sal) / count(*) FROM emp
SELECT avg(IFNULL(comm,0) ) FROM emp

查询月薪在 2000 以上的员工总人数
SELECT sum(sal > 2000) FROM emp 
查询员工最高工资和最低工资差距
SELECT max(sal) - min(sal) from emp

1.7 分组查询 (GROUP BY)

应用场景:用于做数据分析,柱状图,折线图…

-------------------分组查询-------------------------------------------------------
使用 GROUP BY 子句将表分成小组;
结果集隐式按升序排列,如果需要改变排序方式可以使用 ORDER BY 子句。
练习:
按照职位分组,求出每个职位的最高和最低工资
select job,min(sal),max(sal) FROM emp GROUP  BY job
查询出每一个部门员工的平均奖金
SELECT deptno, avg(IFNULL(comm,0)) FROM emp GROUP BY  DEPTNO
分组注意:
SELECT 子句出现的字段,要不在统计函数中,要不出现在 GROUP BY 子句中,否则不合理(整体与个体);
查询出每一个部门员工的平均工资
select deptno, avg(sal) from emp  GROUP BY DEPTNO
查询各个部门和岗位的平均工资
SELECT avg(sal),deptno,job FROM emp GROUP BY deptno, job

分组限定
where语句,在group by前执行
HAVING语句,在group by后执行
查询部门平均工资高于 2000 的部门及其平均工资
SELECT deptno,avg(sal) from emp GROUP BY deptno HAVING avg(sal) > 2000
查询在 80, 81, 82 年各进公司多少人
SELECT count(*) from emp  WHERE Year(hiredate) IN ("1980","1981","1982")  GROUP BY YEAR(hiredate) 
查询各个管理人员下员工的平均工资,其中最低工资不能低于 1300,不计算老板
-- SELECT avg(sal) FROM emp   GROUP BY MGR HAVING  job  <> "president" AND min(sal) >= 1300 
SELECT avg(sal),mgr FROM emp   GROUP BY MGR HAVING  mgr is NOT NULL AND min(sal) >= 1300

1.8 单行函数 (日期函数)

-------------------------------单行函数-------------------------
SELECT DATE_FORMAT(now(),'%Y-%m-%d')

1.9 子查询 (select …)

------------------------------子查询------------------------
单行单列:
查询出工资比 MARTIN 还要高的全部雇员信息
select * from emp  WHERE sal > (SELECT sal from emp WHERE ename = "MARTIN")
查询平均工资高于公司平均工资的部门信息
SELECT   dname,avg(sal) FROM emp JOIN dept ON emp.deptno = dept.deptno
GROUP BY emp.DEPTNO
 HAVING avg(sal) >= (SELECT avg(sal) FROM emp)

 多行单列:
 查询工资等于部门经理(职位是 MANAGER)的员工信息。
 SELECT * FROM emp WHERE sal  > ALL (SELECT sal FROM emp WHERE job = "MANAGER")

多行多列:
查询出每个部门的编号、名称、部门人数、平均工资
SELECT t.deptno, t.count,t.avgSal ,d.dname  FROM dept d 
 JOIN  (SELECT deptno,count(deptno) count,avg(sal) avgSal FROM emp GROUP BY deptno) t 
     ON t.deptno = d.deptno


day02 maven

2.1 关于maven的理解

2.1.1 什么是maven?

当我们进行代码的测试,测试,打包,部署到服务器时,还有项目需要依赖一些第三方的jar包(而这些依赖包还需要版本进行对应,且很麻烦),这些操作非常的繁琐。而maven就是用来将我们这些简化这些操作。

maven是一种项目管理工具提供项目的一种依赖配置

2.1.2 maven的原理和作用?

当我们项目配置一个依赖,表示需要用到某个jar包时,

  • 会先去本地仓库(也就是一个文件夹)找有没有对应的jar包,
  • 如果没有,则去中央仓库下载,下载后放到本地仓库,然后项目在去本地仓库直接拿取!

在这里插入图片描述

在这个过程中,我们发现中央仓库加载时,会因为国内防火墙(中央仓库是国外的)或者网速原因而导致下载失败,因此我们可以使用阿里的镜像服务器进行下载,阿里翻墙同步了外网的中央仓库的jar包更新。

在这里插入图片描述

2.1.3 maven的标准项目目录结构

在这里插入图片描述

2.2 maven的安装与环境配置

安装

到官网下载maven,查看对应的maven有对应的jdk版本,解压安装包即可

环境配置

在这里插入图片描述

2.3 maven的本地仓库和镜像设置

在这里插入图片描述

2.4 在idea中的配置maven

全局配置,可以用于配置项目使用编译的jdk,和编译版本的jdk

在这里插入图片描述

在这里插入图片描述

配置maven

在这里插入图片描述

2.5 搭建基于maven的javaweb项目

1.创建 src/main/webapp/WEB-INF/web.xml 结构

在这里插入图片描述

2.编写pom.xml文件的基本配置
  • 配置项目的打包方式

  • 配置java的编译和运行版本

  • 配置servlet-api 和 tomcat 插件

    注意:

servlet-api只是在编译的时候使用,而运行时使用tomcat里面的servlet-api

因此要给servlet-api加个范围:

<scope>provided</scope>

这样只会在编译和测试的时候运行

<?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>cn.ybzy</groupId>
    <artifactId>maven_se</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!--打包方式为war包,否则webapp只是普通文件夹,不能创建jsp文件-->
    <packaging>war</packaging>


     <!-- 如果不配置默认给你1.5,1.6的低版本进行编译和运行-->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>

        <!-- 编译运行时用这个servlet包,而运行时用tomcat里面的-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.0.1</version> <!-- 记得一定配置 -->
            <scope>provided</scope>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.1</version>
                <configuration>
                    <port>80</port> <!-- 端口 -->
                    <path>/</path> <!-- 上下路径 -->
                    <uriEncoding>UTF-8</uriEncoding> <!-- 针对 GET 方式乱码处理 -->
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>
3.运行

2.5 scope__依赖概念

2.5.1 scope
  • compile默认,适用于所有阶段,会随着项目一起发布,在编译,测试,运行时都有效;
  • provided编译和测试阶段使用;典型的如 servlet-api.jar,打包时不需要,容器来提供;
  • runtime测试和运行阶段使用,用于接口和实现分离,典型的如 jdbc 具体驱动实现;
  • test测试阶段使用,不会随项目发布,如 junit
2.5.2 依赖

当我A包依赖B包时,而B依赖C,因此我们要使用A时要把B,C都加载进来。配置了maven的时候,会自动帮我们把依赖包全部导入。

day03 MyBatis加强day01

3.1 使用maven构建的项目编写最基本的crud

注意XXXMapper.xml文件放在Resrource目录下

所有的配置文件都放在Resource下,所有的java文件放在java文件夹下

2.2 使用XXXMapper接口绑定XXXMapper.xml、和传参

2.2.1 使用接口
public interface UserMapper {
    public User get(Integer id);
    public void  delete(Integer id);
    public void update(User user);
    public  void  save(User user);
}
public class mybatisTest {
   private SqlSession sqlSession = MyBatisUtil.getSession();
        /**根据接口来返回接口的实现类
     * 实现类哪里来的呢?Mybatis底层使用jdk的动态代理帮我们实现,存到Map的键值对中
     * UserMapper.class    UserMapperProxy@123
     * 当我们调用getMapper方法时,会从Map中通过传入的XXX.class的字节码对象拿到对应的接口实现类
     */
   private UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    @Test
    public void  getTest(){
        System.out.println(userMapper.get(2));
    }

注意当我们编译运行时,我们找到输出文件夹发现接口和配置文件输出在同一个目录

所以当我们创建包时,接口和配置文件的所在的包必须相同,否则完成不了绑定

2.2.2 源码:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gsgUmRqp-1619680344280)(img/a75785.png)]

当我们使用Mapper接口执行sql时,底层mybatis会根据sql的标签判断是什么操作,然后进行对应的方法,后获取我们命名空间+方法名 = this.command.getName()和待人的参数

相当于帮我们执行了

User u = new User(3, "sdff75758757", "df", "dfd");
 sqlSession.update("cn.wolfcode.mapper.UserMapper.update", u);
2.2.3 传参
  • 当我们使用#时,会从一个map中去找对应的键,找到则返回,找不到则显示 no getter for …
  • 当我们使用mybatis时,我们发现只允许传入一个参数,当我们传入多个参数时会报错!!
  • 当我们使用#{}时就会遍历map,找到对应的键值对

解决办法:

1.使用mybatis的内置参数(param,arg),当我们传入值时,map里面会形成键值对

如下代码,key:param1 value: 传入的值 key:param2 value: 传入的值

2.使用注解@Param

当我们使用注解时,可以自定封装map的键值对

@Param("password")  ---> 封装键    String password----对应传入的值

当我们使用#{}时就会遍历map,找到对应的键值对

public User checkLogin(@Param("username") String username, @Param("password") String password);
<select id="checkLogin" resultType="cn.wolfcode.domain.User">
   select  * from user where username = #{param1} and password  = #{param2}
    select  * from user where username = #{username} and password  = #{password}
    select  * from user where username = #{arg0} and password  = #{arg1}
</select>

2.3 动态sql,if,where,foreach

2.3.1 if
        /**
         * 当我们在if里面时使用的是${}取值(根据键值对取),因此要用#{}的话必须贴个@param标签,存进map中
         *       类比之前我们使用SelectById时,是没有使用if标签的,所以
         *
         * 当我们使用多个if时,会导致逗号问题
         * 所以可以使用where
         */
public  List<User> selectByUsername(@Param("username") String username);
<select id="selectByUsername" resultType="cn.wolfcode.domain.User">
    select * from user
    <if test="username != null and username != ''">
        where  username like concat("%",#{username},"%")
    </if>
</select>
2.3.2 where
public  List<User> selectByUsername2(@Param("username") String username,@Param("username2") String username2);
<select id="selectByUsername2" resultType="cn.wolfcode.domain.User">
    select * from user
    <where>
        <if test="username != null and username != ''">
             and username like concat("%",#{username},"%")
        </if>
        <if test="username2 != null and username2 != ''">
             or  username like concat("%",#{username},"%")
        </if>
    </where>
</select>
2.3.3 set
//动态标签set的练习
//当我们只需要更新类对象的一个值的时候
public void updateAll(User user);
public void updateHeadImg(User user);
/**
 * 关于动态sql的实验和总结
 *   案列:修改客户的头像
 *   当我们需要修改用户的某个东西时,不可以将用户的所有其他东西给改变,因此我能需要if的动态标签
 *               update   user set
 *                         <if test="username != null and username !=''">
 *                             username = #{username},
 *                         </if>
 *                         <if test="password != null and password !=''">
 *                             password = #{password},
 *                         </if>
 *                         <if test="headImg != null and headImg !=''">
 *                             headImg = #{headImg}
 *                         </if>
 *                         where id =#{id}
 *        可是我们发现这样还是有问题的,当我们只是修改用户图片headImg时没有问题
 *        当我们只是修改的用户的账号或者密码时,则会多一个“ ,”逗号吗,导致语法错误
 *    解决办法:使用set标签
 *      <set>
 *         <if test="username != null and username !=''">
 *             username = #{username},
 *         </if>
 *         <if test="password != null and password !=''">
 *             password = #{password},
 *         </if>
 *         <if test="headImg != null and headImg !=''">
 *             headImg = #{headImg}
 *         </if>
 *     </set>
 *     使用set标签可以帮我们去除多余的逗号!!!!!!!!!!!
 *
 */
2.3.4 forEach
public List<User> selectByListId(@Param("ids") Integer[] ids);

可以使用内置参数array,或者使用@Param自定义键值对

collection:要遍历的集合或者数组
open:开始拼接的字符
close:结束拼接的字符
separator:分隔器
item:遍历的每一项

<select id="selectByListId" resultType="cn.wolfcode.domain.User">
    select * from user where  id  in
     <foreach collection="ids" open="(" close=")" separator=","  item="ele">
         #{ele}
     </foreach>
</select>

2.4 $与#的区别!!

案例1:

<select id="checkUsername1" resultType="cn.wolfcode.domain.User">
    select  * from user where username = #{username}
</select>
<select id="checkUsername2" resultType="cn.wolfcode.domain.User">
    select  * from user where username = ${username}
</select>

案例2:

<select id="OrderDesc" resultType="cn.wolfcode.domain.User">
    select * from user order by #{columnName} desc;
</select>
<select id="OrderDesc2" resultType="cn.wolfcode.domain.User">
    select * from user order by ${columnName} desc;
</select>
/**
 * 用于测试$,#的区别
 *
 *     案列1:根据用户名来查询用户
 *     //#,运行成功
 *     public User checkUsername1(String username);
 *     //$: 运行不成功,“1”是字符串,String类型,它回去String类型里面找属性为username
 *     public User checkUsername2(String username);
 *     System.out.println(userMapper.checkUsername2("1"));
 *
 *     案列2:根据用户传递列名来进行倒叙查询
 *      public List<User> OrderDesc(String columnName);
 *     public List<User> OrderDesc2(@Param("columnName") String columnName);
 *     我们发现,使用#时,采用倒序无效,使用$时,倒序才有效
 *     因此当我们要使用order by,group by时使用$
 *
 *
 */

KaTeX parse error: Expected 'EOF', got '#' at position 2: 与#̲的区别就相当于Statemen…有sql注入风险,#没有

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3jdGpK3o-1619680344281)(img/45455a.png)]

$:

在xml中的sql语句使用 , 会 去 找 对 应 的 键 值 对 , ,会去找对应的键值对, ,{username},根据传入的参数值去找到username这个键然后返回

#:

在xml中的sql语句使用时

当传入一个参数时,传入的是什么就是什么

​ 如果是基本类型和包装类型,则根据你的使用,如果使用#则不需要贴标签,如果使用$,还是需要进行判断的

​ 如果是array,或者list等,可以使用内置的array参数,或者使用@Param标签

​ 如果是map或者自定义的类型,直接传入补贴注解

当传入多个参数时,需要封装map键值对,使用@Param标签

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QXmohEn7-1619680344282)(img/a4545.png)]

总结:

当我要进行列的查询时使用order by 或者 group by时,只能使用$,

当我们使用if标签时,其使用的是$的方式去找键值对的,所以我们要添加一个自定义的键值对使用@Param,#就可以

其他情况使用#

2.5 free mybatis 插件的使用

day04 MyBatis加强day02

4.0 关系与如何处理关系

4.0.0 为什么要讲关系呢?

因为我们是数据的搬运工,搬运的数据之间存在关系,所以处理这样的数据的时候也要处理关系

4.0.1 那么如何处理关系呢?

1.首先明确需求‘

2.设计表,决定存到关系数据列定义在哪里

3.实体类的设计,根据查询决定关联的属性定义在哪里,是什么类型的

4.编写Mapper,接口以及mapper.xml

4.1 单向多对一

4.1.1 需求:

员工对部门多对一,现在需要查询员工,并且把所属的部门查询出来

4.1.2 设计表:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XqpnlhXt-1619680344283)(img/erea.png)]

4.1.3 实体类:

由于要查询员工数据后,要显示部门数据’

所以我们在员工类中加一个成员变量: private Department deparment

4.1.4(添加)保存:
INSERT INTO employee(name, deptId) VALUES(#{name}, #{dept.id})   ---------注意这里是dept.id

INSERT INTO department(name) VALUES(#{name})

实体类创建时,先创建department实体类,后创建employee,employee.setDepartment(deparment)

4.1.5 查询:
select * from employee where id = #{id};
/**
 * 1.
 * 由于我们放回的结果集包装是Employee,id,name,department
 * 而数据库查询的是id,name,deptId,所以导致deptId为空,没有对应的值
 * 2.
 * 通过retrunMap来封装结果集,
 * 所以可以通过拿到用户的department对象,在获取到部门id,找到属于员工的部门对象,再设置
 *
 */

方式一:使用resultMap进行简单的封装结果集

  <resultMap id="baseResultMap" type="Employee">-->
        <id column="id" property="id"></id>-->
        <result column="name" property="name"></result>-->
        <result column="deptId" property="department.id"></result>-->
    </resultMap>   

方式一:操作繁琐
     EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
     DepartmentMapper departmentMapper = sqlSession.getMapper(DepartmentMapper.class);
     Employee employee = employeeMapper.selectById((long) 1);
     Department department = departmentMapper.selectById(employee.getDepartment().getId());
     employee.setDepartment(department);
     System.out.println(employee);

方式二:使用联合查询(额外sql)

额外Sql先发起一条sql语句,后通过下面的配置发送一条额外的sql

先通过id找到拿到返回值id,name,deptId,在将deptId拿去给DepartmentMapper.xml进行查询deptId = id,最后将数据返回

 <resultMap id="baseResultMap" type="Employee">
    <id column="id" property="id"></id>
    <result column="name" property="name"></result>
    <!--
          联合查询中,
                   select:进行的查询操作
                   coulum:表示根据那个列来进行查询
                   property:表示retrunType封装的结果集属性名
                   javaType:表示数据的对象类型

    -->
    <association  select="cn.wolfcode.mapper.DepartmentMapper.selectById"
     column="deptId"  property="department" javaType="Department" >
    </association>
</resultMap>

但是当我们查询所有用户的数据时,会执行 N + 1条sql语句,导致sql执行很多,效率低,数据库压力大

于是我们可以采用多表查询,这样就只是发起一条sql语句

由于字段名称一样,所以必须要使用别名,不使用别名可能会导致错误!!!

<select id="listAll" resultMap="baseMap">
    select e.id,e.name, d.id d_id,d.name d_name  from employee e,department d where e.deptId = d.id
</select>
<resultMap id="baseMap" type="Employee">
    <id column="id" property="id"></id>
    <result column="name" property="name"></result>
    <association  columnPrefix="d_" property="department" javaType="Department">
        <result column="id" property="id"></result>
        <result column="name" property="name"></result>
    </association>
</resultMap>

4.2 单向一对多

4.2.1 需求:

根据id来查询部门,把该部门下的所有员工全部查询出来,部门对员工 = 一对多

4.2.2 设计表:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wONpP1jo-1619680344284)(img/erea.png)]

4.2.3 实体类:
@Getter
@Setter
@ToString
public class Department {
    private Long id;
    private String name;
    //必须new,防止知道不到然后空指针异常
    private List<Employee> employees = new ArrayList<>();

}

@Getter
@Setter
@ToString
public class Employee {
    private Long id;
    private String name;
    private Long deptId;

}
4.2.4 保存:简单
4.2.5 查询与多对一类型
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
    insert  into department  set name = #{name}

</insert>

<select id="selectById" resultType="cn.wolfcode.domain.Department">
    select * from department where id = #{id}
</select>

<select id="selectByDeptId" resultMap="baseResultMap">
    select  * from department where id = #{id}
</select>
<resultMap id="baseResultMap" type="Department">
    <id column="id" property="id"></id>
    <result column="name" property="name"></result>
    <collection  column="id" select="cn.wolfcode.mapper.EmployeeMapper.SelectByDeptId" property="employees"></collection>
</resultMap>
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
    insert  into employee set name = #{name},deptId = #{deptId}
</insert>
<select id="SelectByDeptId" resultType="Employee">
    select * from employee where  deptId = #{deptId};
</select>

4.3 单向多对多

4.3.1 需求:

根据id查找学生,并把学生的老师全部找出来,学生对老师 = 多对多

4.3.2 设计表:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cckjYhW6-1619680344285)(img/4545a.png)]

4.3.3 实体类:
@Getter
@Setter
@ToString
public class Student {
    private Long id;
    private String name;
    private List<Teacher> teachers = new ArrayList<>();

}
@Getter
@Setter
@ToString
public class Teacher {
    private Long id;
    private String name;
}
4.3.4 查询:
<select id="selectById" resultMap="baseMap">
    select id,name from student where  id = #{id};
</select>
<resultMap id="baseMap" type="Student">
    <id column="id" property="id"></id>
    <result column="name" property="name"></result>
    <collection column="id" select="cn.wolfcode.mapper.TeacherMapper.selectById" property="teachers"></collection>
</resultMap>

连表查询时,有两种方式,内连接(等值查询),子查询

使用内连续查询时,内部使用索引机制

使用子查询时,没有使用索引,所以性能更低

<select id="selectById" resultType="Teacher">
    <!--没有使用索引,使用子查询的时候查询效率低 -->
    <!--select  id,name from  teacher where  id in (
      select  teacher_id from teacher_student where student_id = #{student_id}
    )-->
    <!-- 使用索引,查询效率高-->
    select  id,name from teacher t,teacher_student ts where t.id = ts.teacher_id and  student_id = #{student_id}
</select>
4.3.5 删除:
<delete id="delete">
    delete from  teacher where  id =#{id}
</delete>
<delete id="delete">
    delete from  student where  id = #{id}
</delete>
<delete id="deleteRelation">
    delete  from  teacher_student where  student_id = #{studentId}
</delete>

当我们进行删除时,要把有关联关系的数据给删除掉,删除中间表的部分关联信息!!

注意:

当我们在中间表设置主键的时候,当我们想删除student表时,由于中间表有对应的关联数据,会导致删除不了。

因此我们要先删除关联表,后删除关联数据student数据(这样不管中间表有无设置主键都万无一失!!!)

4.4 总结

1.不管是多对一,还是多对多,都是根据查询出来的列,然后拿到这个列根据需求去执行对应的sql语句,然后使用returnMap来封装结果集,执行对应的sql语句时要使用到额外的sql语句

2.当我们使用association时,对应javaType,而使用collection时,对应oFType

3.多表查询时时是不需要写javaType或者oFType的,额外sql需要写,为了保险起见,我们统一使用写类型

id和result的区别:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R8E9yHVb-1619680344286)(img/7878.png)]

当我们进行查询的时候,如果没有id的话,会拿所有的字段进行比较,如果全部相同则表示为同一个对象就不封装返回。而当我们有id时,id作为唯一的识别比较符号,这样不需要比较全部字段,提高了整体的性能!!!!!!!!!!!!

day05 Springday01_SpringIoc_SPringDI

5.1 关于Spring

什么是Spring?

Spring是一个轻量级的Ioc / DI 和Aop 容器的开源框架!!

那么什么是容器?

容器就是用来装对象的对象,因为存在,放入等操作,所以容器还要管理容器对象的生命后期。容器可以比作生活中的水杯,或者看作一个Map。

为什么要用Spring?

  • 解决了代码耦合度高的问题.当我们修改某个类,可能需要修改很多地方。
  • 控制事务的繁琐。在做事务处理时,总要写一套的事务套路代码,当事务多时,后期的维护成本巨高
  • 第三方框架运用太麻烦,第三方框架只关心对象的使用,却不关心对象的创建,所以当使用第三方框架时,对象的创建需要一些繁琐的套路代码

5.2 SpringIoc

通过SpringIoc容器来帮我们创建对象!!

5.2.1 SpringIoc的入门:

1.添加对应的Spring依赖

2.编写类

3.编写对应的配置xml文件

配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <bean id="person" class="cn.wolfcode.spring._01_SpringIoc.Person">
        <property name="name" value="库里"></property>
    </bean>
</beans>

对应的类


public class Person {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    public  void  doWork(){
        System.out.println(this.name + "工作啦");
    }

    public Person() {
        System.out.println("实例被创建");
    }
}

测试:

@Test
public void doWork2() {

    //解析xml文件启动Spring的ioc容器
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:SpringIoc.xml");
    //通过Spring的Api 拿到ioc容器创建的对象
    Person person = (Person) applicationContext.getBean("person");
    person.doWork();
}
5.2.2 那么容器的创建和对象的顺序呢?

思考:我们应该在容器创建的时候,就把所有Bean对象给创建了,这样用户想用什么就用什么。而不是等用户用什么,我的容器再去加载容器对应的Bean对象,这样的话第一个用户体验极差。我们愿意容器一加载所有对象,可能内存占用多,启动慢一点,但是用户体验却是很好。

public class Person {
    private String name;
    public String getName() {
       return name;
     }

    public void setName(String name) {
      this.name = name;
     }
   public  void  doWork(){
     System.out.println(this.name + "工作啦");
    }

  public Person() {
    System.out.println("实例被创建");
   }
}    
@Test
public void doWork2() {
    /**
     * 通过在Person里的构造器,我们可以知道:
     *    当我们解析xml文件启动Spring的Ioc容器时,xml中配置的Bean对象全部被创建!!!
     */


    //解析xml文件启动Spring的ioc容器
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("SpringIoc.xml");
    System.out.println("ddddd");
    //通过Spring的Api 拿到ioc容器创建的对象
    Person person = (Person) applicationContext.getBean("person");
    System.out.println("fffffff");
    person.doWork();
}
测试结果:
实例被创建
ddddd
fffffff
库里工作啦

总结:也就是说,当我们解析xml配置文件的时候并且启动springioc容器时,所有的Bean对象都给创建了,由SpringIoc容器进行管理!!

5.2.3 @RunWith@ContextConfiguration@Autowired

我们发现这样测试每次都要解析xml’文件启动容器和获取对应Bean,这样非常繁琐

于是我们导入对应的Spring的test包,使用对应的注解

//使用Spring得到特定的测试方法,首先导入对应的依赖jar包
@RunWith(SpringJUnit4ClassRunner.class) //在测试方法前启动容器
@ContextConfiguration("classpath:SpringIoc.xml") //加载对应的配置文件,相当于往容器里面放东西
public class PersonTest {

由于这样获取Bean对象也是非常麻烦,于是我们使用了注解@Autowired

    //@Autowired:自动注入,根据Person.class内省从容器中找到对应的对象
    /**
     * 注意我们通过XXX.class反射获取到对象创建对象,必须要有构造方法,一般我们使用空的构造方法,如果使用有参数的话,必须要配置对应的参数
     *
     */
@Autowired
private Person person;     person--->对应的是Bean对象的id值!!!!!!!!!!!!


@Test
public void doWork3() {
    person.doWork();
}
5.2.4 关于工厂设计模式的理解:

工厂类的创建:

public class PersonFactoryBean implements FactoryBean<Person> {
    @Override
    public Person getObject() throws Exception {
        Person person = new Person();
        return person;
    }

    @Override
    public Class<?> getObjectType() {
        return Person.class;
    }
}

xml的配置:

<bean id="personFactoryBean" class="cn.wolfcode.spring._01_SpringIoc.PersonFactoryBean">
</bean>

测试类:

@Test
public void doWork4() {
    /**
     * 关于工厂设计模式FactoryBean:
     * 当我们启动容器时,会先创建这个工厂对象,后发现这个bean对象实现了FactoryBean接口
     * 就会调用GetObject方法去实列化对象,而这个工厂对象真正想要实例化的Bean对象是getObject里面的
     * 所以当我们看到XXXFactoryBean时,我们需要点进去看,它到底实例化的bean对象是谁!!!!!!!
     *
     */

    //当我们把xml里面的Person的Bean给注释了,发现还是使用自动主键来调用其方法
    person.doWork();
}
5.2.5 关于Bean的init-method ,destroy-method属性的理解:

分别用于对象被创建后的初始化,以及spring容器关闭后的摧毁方法!

容器对象的close本质是底层变量map

把map中的所有对象,若这个对象配置的摧毁化方法,都会被其调用!!

现在模拟一下数据源对象,

在我们数据源对象创建之后,我们需要进行init初始化操作,创建一个梯子,也就是连接池,并进行校验用户传来的连接四要素!!

public class MyDateSource {
    public MyDateSource() {
        System.out.println("对象创建");
    }
    public void init(){
        System.out.println("初始化");
    }
    public  void getSession(){
        System.out.println("获取连接");
    }
    public void  close(){
        System.out.println("关闭");
    }
}
<bean id="myDateSource" class="cn.wolfcode.spring._01_SpringIoc.MyDateSource" init-method="init" destroy-method="close" scope="prototype">
</bean>
@RunWith(SpringJUnit4ClassRunner.class) //在测试方法前启动容器
@ContextConfiguration("classpath:SpringIoc.xml") //加载对应的配置文件,相当于往容器里面放东西
public class MyDateSourceTest {
    @Autowired
    private MyDateSource myDateSource;

    /**
     * 使用initmethod(用于对象的初始化,指定一个初始化方法),destoryMethod(容器正常关闭调用)方法
     * 一版解析xml容器启动,MyDataSource对象创建调用构造器方法,然后执行指定的初始化方法,
     * 由于scope作用域的范围是多例模式,也就是说,它并不会被Spring容器保存起来,而是自己管理,这样的话配置了指定的摧毁方法也不会
     * 被执行,因此摧毁方法是容器被关闭的时候执行
     */
    @Test
    public void testLifer() {
        myDateSource.getSession();
    }
}
5.2.6 scope作用域的理解
  • singleton:单例 ,在 Spring IoC 容器中仅存在一个 bean 实例 (缺省默认的 scope)。
  • prototype:多例 ,每次从容器中调用 Bean 时,都返回一个新的实例,即每次调用 getBean() 时,相当于执行 new XxxBean():不会在容器启动时创建对象。

总结:在开发中主要使用 scope=“singleton”。对于 Struts1 的 Action 使用 request,Struts2 中的 Action 使用 prototype 类型,其他使用 singleton,即不配置。

多例模式的bean创建和保存都在spring容器外!!!因为用了一次就不用了,没必要存在容器中浪费空间

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J9vLkkRS-1619680344287)(img/77888a.png)]

5.3 SpringDI

5.3.1 属性注入

SpringDI表示为依赖的注入,也就是对象中的Set方法

属性注入: name:注入的属性名(Setter方法中对应的属性) value设置的值为字符串String类型

<bean id="person" class="cn.wolfcode.spring._01_SpringIoc.Person">
    <property name="name" value="库里"></property>
</bean>
public class Person {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    public  void  doWork(){
        System.out.println(this.name + "工作啦");
    }

    public Person() {
        System.out.println("实例被创建");
    }
}
5.3.2 Bean注入

由于value是设置字符串类型的,如果我们有成员变量,需要设置呢?

name:表示注入的属性名 ref:表示给注入的属性对应的另外一个Bean

<bean id="employeeService" class="cn.wolfcode.spring._02_SpringDi.EmployeeService">
    <property name="emplopyeeDao" ref="emplopyeeDao"></property>
</bean>
public class EmployeeService {
    private EmplopyeeDao emplopyeeDao;

    public void setEmplopyeeDao(EmplopyeeDao emplopyeeDao) {
        this.emplopyeeDao = emplopyeeDao;
    }

    @Override
    public String toString() {
        return "EmployeeService{" +
                "emplopyeeDao=" + emplopyeeDao +
                '}';
    }
}
5.3.3 综合案例解析_插入用户(数据库连接的配置,多态的特性!!!)
public class StudentDaoImpl implements IStudentDao {
    private DruidDataSource druidDataSource;   druidDataSource : 对应Bean的id!!!!!!!!

    public DruidDataSource getDruidDataSource() {
        return druidDataSource;
    }

    public void setDruidDataSource(DruidDataSource druidDataSource) {
        this.druidDataSource = druidDataSource;
    }


    @Override
    public void insert(String username, String password) throws SQLException {
        //加 连 预编译  执行 释放
        DruidPooledConnection connection = druidDataSource.getConnection();
        String sql = "insert into student(username,password) value (?,?)";
        PreparedStatement statement = connection.prepareStatement(sql);
        statement.setString(1,username);
        statement.setString(2,password);
        statement.executeUpdate();
    }
}
public class StudentServiceImpl implements  StudentService {
    private IStudentDao iStudentDao;     iStudentDao : 对应Bean的id!!!!!!!!!!

    public void setiStudentDao(IStudentDao iStudentDao) {
        this.iStudentDao = iStudentDao;
    }


    @Override
    public void register(String username, String password) throws SQLException {
        iStudentDao.insert(username,password);
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--由于会优先从用户的电脑里面找对应的键值对变量,所以username会找到用户的名字,而不是db.properties
        解决1: system-properties-mode="NEVER"
        解决2:将db.properties的键进行更改,加个前缀
-->
    <context:property-placeholder location="db.properties" system-properties-mode="NEVER"></context:property-placeholder>

<!--    配置数据库连接池对象
         需要配置初始化方法,因为DateSource初始化会创建一个桥,创建池子,然后在检查用户传入的连接四要素

-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
       <property name="driverClassName" value="${driver}"></property>
       <property name="url" value="${url}"></property>
       <property name="username" value="${username}"></property>
       <property name="password" value="${password}"></property>
    </bean>
    <bean id="studentDao" class="cn.wolfcode.spring.Dao.StudentDaoImpl">
        <property name="druidDataSource" ref="dataSource"></property>
    </bean>
    <bean id="studentService" class="cn.wolfcode.spring.Service.StudentServiceImpl">
        <property name="iStudentDao" ref="studentDao"></property>
    </bean>
</beans>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml") //加载对应的配置文件,相当于往容器里面放东西
public class StudentDaoImplTest {

    @Autowired
    private StudentService studentService ;

    @Test
    public void insert() throws SQLException {
        studentService.register("uu","erer");
    }
}

这样的话,当我们还有一个PerfectDaoImpl 实实现了接口时,我们只是需要更改xml配置文件,而不需要更改java代码,配置文件进行Bean的注入,而不使用对象的set方法,解耦!

day06 Springday02_springioc_DI_静态,动态代理

6.1 Di注解之Autowired和Value

@Value:用于贴在字段,属性上,给默认值,相当于xml配置了初始值

public class Cat {
    @Value("ddddd")
    private String name;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public  void  doWork(){
        System.out.println(this.name + "工作啦");
    }

}

6.2 Di注解之Resource注解

import javax.annotation.Resource;

public class EmployeeService {

    /**
     * 依赖的注入可以通过在字段或者setter方法上贴 @Autowired注解,这样也可以不需要xml'配置
     *
     * Resource的作用和Autowired相同,都可以从容器中找对象实例,还可以注入依赖
     * 区别:
     * 只不过Resource是通过名称(Beand_id)来找,名称找不到就找类型
     * 而Autowired是先通过类型进行比对,找到对应的,然后根据名称(Bean_id)进行比对
     *
     * 当ioc容器中存在类型相同,但是Bean_id不同时,我们需要指定使用的是哪一个!!!
     * Resource(name = ”“)
     * Autowired(”“)
     */
//    @Autowired
     @Resource
    private EmplopyeeDao emplopyeeDao;

    public void setEmplopyeeDao(EmplopyeeDao emplopyeeDao) {
        this.emplopyeeDao = emplopyeeDao;
    }

    @Override
    public String toString() {
        return "EmployeeService{" +
                "emplopyeeDao=" + emplopyeeDao +
                '}';
    }
}

6.3 Ioc注解

四个注解的功能是相同的,只是用于标注不同类型的类上:

  • @Repository:用于标注数据访问组件,即 DAO 实现类上。
  • @Service:用于标注业务层实现类上。
  • @Controller:用于标注控制层类上(如 SpringMVC 的 Controller)。
  • @Component:当不是以上的话,可以使用这个注解进行标注。

6.4 Scope和PostConstruct以及PreDestory注解

这三个注解之前都是用xml配置,现在只是用注解配置而已,只是很多人向作者反应xml配置麻烦,就推出了注解,作用是一样的!

@Component
@Scope("prototype")
public class MyDateSource {
    public MyDateSource() {
        System.out.println("对象创建");
    }
    //对象在创建后调用,也就是初始化
    @PostConstruct
    public void init(){
        System.out.println("初始化");
    }
    public  void getSession(){
        System.out.println("获取连接");
    }
    //容器销毁时调用
    @PreDestroy
    public void  close(){
        System.out.println("关闭");
    }
}

6.5 静态代理

为什么要使用代理模式?

客户端直接使用的是代理对象,而不知道真正的对象是谁,这时代理对象可以在客户端和真实对象之间起到中介的作用

代理对象包含了真实对象,代理对象实现了真实对象不该做的事情。实现了责任分离。

举例子:

客户 - 房东

当一天有很多客户找房东看房租房,或者说看了又不想要,房东一个人根本忙不过来

这时候我们需要一个中介,这个中介帮房东执行了收房租,带客户看房等操作,而中介只需要跟房东进行交流即可。

这个中介就是代理对象,房东则是真实类型

public class MyTransiatorManager {
    public void beginTx(){
        System.out.println("开启事务");
    }
    public void commit(){
        System.out.println("提交事务");
    }
    public  void  rollback(){
        System.out.println("事务回滚");

    }


}
/**
 * 中介
 * 用于代理完成房东的事务操作,实现责任分离
 *
 * 因此它需要和房东进行交易(StudentDaoImpl)
 * 和要处理的事情的一些特殊的场景的人进行交易(事务工具类)
 *
 *
 * 中介要实现房东的接口,因为中介也要跟房东进行交流
 */
//中介
//@Service
public class StudentServiceproxy implements StudentService {
    //房东引用
    private StudentService studentService;
//    @Autowired
    public void setStudentService(StudentService studentService) {
        this.studentService = studentService;
    }


    //要处理的事情的一些特殊的场景的人进行交易(事务工具类)
    private MyTransiatorManager tx;
//    @Autowired
    public void setTx(MyTransiatorManager tx) {
        this.tx = tx;
    }

    @Override
    public void register(String username, String password) throws SQLException {
        try {
           tx.beginTx();
           studentService.register("hhh","fffff");
           tx.commit();
        }catch (Exception e){
           tx.rollback();
        }
    }
}

通过静态代理,我们发现一个项目中有很多类需要进行事务的处理,然而又要写相对应的事务处理,本就是相同的代码,确实要写多个,繁琐,且不利于后期的维护。

6.5 java动态代理

6.5.1 关于动态代理的理解和基本操作

动态代理理解:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y04VBXba-1619680344288)(img/a33333333.png)]

动态代理有很多种,这里我们使用的是jdk的动态代理

由于静态代理,每次生成的给真实对象代理的代理对象是死的,所以为了实现动态代理,我们可以给处理器程序写一个Object(所以所有类的父类),这样通过xml配置我们想要给Object的真实类型,只需要xml配置而不要写任何代码,invkoe方法也是调用了真实类型的方法,通过反射执行。

处理器程序(必须实现 InvocationHandler,因此底层是通过代理类的父类的InvocationHandler接口的多态执行invoke方法的)

public class TransactionInvocationHandler implements InvocationHandler {
    private Object target;
    public void setTarget(Object target) {
        this.target = target;
    }
    public Object getTarget() {
        return target;
    }
    private MyTransactionManager tx;
    public void setTx(MyTransactionManager tx) {
        this.tx = tx;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws
            Throwable {
        System.out.println("到此一游");
        Object retVal = null;
        try {
            tx.begin(); // 调用事务管理器对象的方法
            retVal= method.invoke(target, args); // 调用真实对象的方法
            tx.commit(); // 调用事务管理器对象的方法
        }catch (Exception e) {
            tx.rollback(); // 调用事务管理器对象的方法
        }
        return retVal; // 返回方法调用的结果
    }
}

xml配置:

<!-- 配置事务管理器对象 -->
<bean id="tx" class="cn.wolfcode.tx.MyTransactionManager"/>
<!-- 配置处理器执行器对象 -->
<bean id="transactionInvocationHandler"class="cn.wolfcode.handler.TransactionInvocationHandler">
  <property name="target">
    <bean class="cn.wolfcode.service.impl.EmployeeServiceImpl"/>
  </property>
  <property name="tx" ref="tx"/>
</bean>

测试类:

通过Proxy类的newProxyInstance方法进行创建代理类,需要告诉真实类型实现的接口,类加载器,处理程序

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TransactionInvocationHandlerTest {

    @Autowired
    private TransactionInvocationHandler invocationHandler;

    @Test
    public void  text() throws SQLException {
        //动态生成代理类,并且创建对象  参数(真实类型的类加载器,真实类实现的接口,告诉Api具体要做啥)
        StudentService o =  (StudentService)Proxy.newProxyInstance(
                //获取类加载器,初始化object
                invocationHandler.getObject().getClass().getClassLoader(),
                 //获取需要代理的对象的实现接口,当前生成的代理类也实现这些接口,这样才能有对应的全部方法和属性
                invocationHandler.getObject().getClass().getInterfaces(),
                invocationHandler
        );
        o.register("111","2222");

    }
}

代理流程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-evqC0AIx-1619680344289)(img/a88888888888.png)]

6.5.2 关于动态代理的各个解释

1.InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,每一个proxy代理实例都有一个关联的调用处理程序;在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法。

2.public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler hanlder)

  • loader:一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载
  • interfaces:一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。
  • h:一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。

3.主要方法:public Object invoke(Object proxy, Method method, Object[] args)。

方法职责:负责集中处理动态代理类上的所有方法调用,让使用者自定义做什么事情, 对原来方 法增强(加什么功能)。

参数:

proxy :生成的代理对象;

method:当前调用的真实方法对象;

args :当前调用方法的实参。 返回:真实方法的返回结果。

6.5.3 关于InvocationHandler h的原理图解析:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3qCGGg6j-1619680344290)(img/787878787878787878a.png)]

进入newProxyInstance后

当我们传入三个参数时,先通过传入的接口加载器用,通过反射来创建构造器

之后通过newProxyInstance的重载方法,传入处理程序h,通过构造器来创建实例对象

通过反编译时,我发现代理类继承了Proxy类,实现了我们传入的接口

代理类的有参构造器里处理程序h,则是调用父类的h,

我们观看源码后,发现Proxy类种也有个有参构造器包含处理程序h,Ctril+h后发现,h为成员变量

处理程序h为接口,接口里定义了invoke方法

当我们传入了实现InvocationHandler接口的处理程序,相当于传给了Proxy的成员h,由他进行调用了invoke方法

6.6 CGLIB动态代理

与java动态代理雷同!!!!!!!!

处理器程序:

//注意这里实现cglib的处理接口
public class TranInvocationHandler implements InvocationHandler {
    private  Object target;

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    private MyTranstior tx;

    public MyTranstior getTx() {
        return tx;
    }

    public void setTx(MyTranstior tx) {
        this.tx = tx;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object retVal = null;
        try {
            tx.begin();
            retVal = method.invoke(target, args);
            tx.commit();
        } catch (Exception e) {
            e.printStackTrace();
            tx.rollback();
        }
        return retVal;
    }
}

测试类:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class StudentServicePorxyTest {
    @Autowired
    private TranInvocationHandler tranInvocationHandler;
    @Test
    public void Test() throws SQLException {
        //创建一个代理
        Enhancer enhancer = new Enhancer();
        //设置代理的要继承的类
        enhancer.setSuperclass(tranInvocationHandler.getTarget().getClass());
        //设置代理的处理程序,
        //注意:处理程序是实现了CGLIB的InvocationHandler接口,不是proxy!!!!
        enhancer.setCallback(tranInvocationHandler);
        //根据代理来创建代理类
        StudentService proxy = (StudentService) enhancer.create();
        proxy.register("wewe", "qqq");

    }


}

day07 Springday03_SpringAop

7.1 aop是什么?

引出aop:在开发中我们需要给业务方法增加日志记录,权限检查,事务处理等功能,这时我们需要去修改业务方法。为了考虑代码的重用性,和减少后期的维护的成本。我们需要遵循开闭原则。所以我们需要使用aop思想。

Aop是一个面向切面编程技术,把一个个的横切关注点放到某个模块中去,称之为切面。而每一个切面都能够影响到业务的某一个功能。

所以切面的目的也就是功能增强!!!!所以当我们需要给业务方法增加日志记录时,我们只需要向某个模块插入日志切面即可!!

Spring通过动态代理实现aop

7.1.1 使用xml配置aop

自定义要加强的方法或者功能,这里为事务处理

public class MyTranstior {
    public void begin(){
        System.out.println("开始事务");
    }
    public  void commit(){
        System.out.println("提交事务");
    }
    public void  rollback(){
        System.out.println("回滚事务");
    }
}

xml配置aop

<!--    配置事务管理器-->
   <bean id="tx" class="cn.wolfcode.spring.util.MyTranstior"></bean>
<!--    配置aop-->
    <aop:config>
<!--        配置切面-->
        <aop:aspect ref="tx">
<!--        配置切入点-->
             <aop:pointcut id="txPointcut" expression="execution(* cn.wolfcode.spring.Service.*ServiceImpl.*(..))"/>
<!--             从切入点到方法,在方法执行之前执行-->
             <aop:before method="begin" pointcut-ref="txPointcut"></aop:before>
<!--                 从切入点到方法,在方法正常执行后,执行-->
             <aop:after-returning method="commit" pointcut-ref="txPointcut"></aop:after-returning>
<!--               在方法抛出异常之后执行-->
             <aop:after-throwing method="rollback" pointcut-ref="txPointcut"></aop:after-throwing>
        </aop:aspect>
    </aop:config>
7.1.2 注解配置aop

将xml里的配置转到类里来定义

@Component
@Aspect//定义切面
public class MyTranstior {

    //定于切入点,toPointout()方法,对应映射的是符合切入点表达式的方法
    @Pointcut("execution(* cn.wolfcode.spring.Service.*ServiceImpl.*(..))")
    public void toPointcut(){

    }
    @Before("toPointcut()")
    public void begin(){
        System.out.println("开始事务");
    }
    @AfterReturning("toPointcut()")
    public  void commit(){
        System.out.println("提交事务");
    }
    @AfterThrowing("toPointcut()")
    public void  rollback(){
        System.out.println("回滚事务");
    }
}

xml配置:

<!--    配置aop注解,使aop注解起作用-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

7.2 spring集成mybatis

7.2.1 关于sqlSessionFactory

因为在myabis里,我们需要拿到sqlSessionFactory,然后调用openSession()方法,获取SqlSession对象,在通过SqlSession.getMapper();每次都需要创建对象,所以我们可以将对象的创建交给Spring管理。我们发现sqlSessionFactory是个接口,想到工厂设计模式,发现有个SqlSessionFactoryBean,它真实的获取对象是sqlSessionFactory。

在这个bean里面,我们需要配置mybatis-config.xml里面的配置,通过property或者直接引入一个配置好的mybatis-config.xm!!

<!-- 创建SqlSessionFactory对象 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!--配置核心配置文件,现在可以不配置, 后期用Mybatis插件的时候需要配置-->
    <!--<property name="configLocation" value="classpath:mybatis-config.xml"/>-->
    <property name="dataSource" ref="dataSource"/>
    <property name="typeAliasesPackage" value="cn.wolfcode.domain"/>
<!--  配置关联关联路径,StudentMapper.xml,-->
<!-- 由于我们最后输出时Usermapper接口和UserMapper.xml是一起的,所以我们可以不用写-->
<!-- <property name="mapperLocations" value="classpath:cn/wolfocde/spring/mapper/*Mapper.xml"></property>-->
</bean>
7.2.2 关于mapper的代理对象

配置Mapper接口的代理对象(和SqlSessionFactoryBean一样,利用工厂设计模式)

在我们获取代理对象时,我们需要SqlSessionFactory.openSession().getMapper(XXXMapper.class)

所以在Bean里面我们需要配置对应的SqlSessionFactory和具体实现什么接口的代理对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vofJQzmM-1619680344291)(img/7899898989.png)]

<!--创建AccountMapper对象-->
<bean id="accountMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <!-- 通过Session工厂类对象,获取session对象 -->
    <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    <!-- 具体创建什么接口类型的代理对象 -->
    <property name="mapperInterface" value="cn.wolfcode.mapper.AccountMapper"/>
</bean>

从上面的mapper代理对象的获取,我们知道每配置一个mapper,都要再写一次,这样十分麻烦,我们可以配置一个Mapper的扫描器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NnOCk5sK-1619680344292)(img/454857885757a.png)]

<!--配置mapper扫描器,批量生成mapper代理对象  -->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <!--        指定mapper文件的所在位置-->
    <property name="basePackage" value="cn.wolfcode.mapper"></property>
</bean>

7.3 Spring集成maybatis使用事务

7.3.1 使用注解配置
<!--    配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>
<!--    配置事务增强-->

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

<aop:config>
    <aop:pointcut id="pointcut" expression="execution(* cn.wolfcode.service.impl.*ServiceImpl.*(..) )"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"></aop:advisor>
</aop:config>
7.3.2 使用注解

使用注解我们需要配置事务解析器,这样才可以在类中贴aop注解,才可以生效

<!--    配置事务注解解析器-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

在某个方法上贴@Transactional,表示该方法使用事务处理

在类上贴,表示这个类的所有方法都使用事务处理

public class AccountServiceImpl implements IAccountService {

    private AccountMapper accountMapper;

    public void setAccountMapper(AccountMapper accountMapper) {
        this.accountMapper = accountMapper;
    }

    @Transactional
    @Override
    public void transfer(Long outId, Long inId, BigDecimal amount) {
        accountMapper.addBalance(inId, amount);
        int i = 1 /0;
        accountMapper.subtractBalance(outId,amount);
    }

    //实现业务
}

day08 SpringDay04_SpringMvc

8.1 Mvc思想

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gyiUO7qn-1619680344293)(img/a1212.png)]

8.2 前端控制器

SpringMvc为我们提供了 DispatcherServlet类作为前端控制器。使用时需要在web.xml进行配置,配置拦截的路径。

当我们发送请求时,会先到前端控制器,进入前端控制器后,我们根据映射路径来分发给对应的控制器,控制器对象则调用对应的方法

8.3 springMvc入门

8.3.1 配置xml文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
    <!--    配置springmvc的前端控制器-->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--        配置spring容器启动后加载的文件所在地址-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:mvc.xml</param-value>
        </init-param>
        <!--        配置启动加载的级别,优先启动,tomcat启动初始化-->
        <load-on-startup>1</load-on-startup>
<!--        跟原来的servlet配置一样,只不过是xml配置,只有配置了才可以使用req.getpart获取文件对象-->
    </servlet>
    <servlet-mapping>
8.3.2 编写控制器
@Controller
public class HellowController {
    /**
     *
     * SpringMvc的DispatcherServlet其实就是一个servlet
     * 是一个前端控制器,只不过他是中心控制器,负责将前端的传来的请求进行转发
     *
     *我们需要配置注解@Controller,这样spring就可以帮我们创建hellowController对象
     *这样才可以通过地址栏访问,解析地址时,通过对应的类对象才可以调用对应的方法
     *
     * ModelAndView 用来设置键值对给作用域和设置返回试图
     */

    @RequestMapping("/hellow")
    public ModelAndView hellow(){
        ModelAndView view = new ModelAndView();
        view.addObject("msg","hellow Controller");
        view.setViewName("/WEB-INF/hellow.jsp");
        return view;
    }
8.3.3 编写springmvc文件
   <context:component-scan base-package="cn.wolfcode.springmvc.Controller"></context:component-scan>
    <!--    配置mvc注解解析器,如果不配置则使用不了mvc的注解-->
    <mvc:annotation-driven></mvc:annotation-driven>

8.4 关于DispatcherServlet的映射路径的配置

@Controller
public class PathController {

    /**
     * 关于访问  http://localhost/static/demo.html
     * tomcat里有两个内置的serlvet
     * DefaultServlet:当访问静态资源时(html,视频,图片....),通过它   “/”
     * JspServlet,当访问Jsp资源时,通过它
     *
     * Springmvc框架里配置了DispatcherServlet
     * 它的通过路径是 “/” ,和DefaultServlet通过路径相同,所以DefaultServlet被覆盖!!!
     * 因此当我们访问静态资源时,会通过DispatcherServlet,
     * DispatcherServlet不能解析动态资源文件,导致访问不到404
     * 解决方法1:
     *    改变DispatcherServlet的拦截路径为*.do,
     *    这样就不会覆盖DefaultServlet,不过COntroller的请求路径必须带.do
     *解决方法2:
     *    在mvc.xml配置<mvc:default-servlet-handler/>
     *    这样配置就会在springmvc的上下文创建一个 DefaultServletHttpRequestHandler的Bean对象,它会对进入
     *    DispatcherServlet进行筛选,如果不是映射请求,则交给容器默认的servlet(DefaultServlet)处理
     *
     *注意:DispatcherServlet的拦截路径不可以配/*,这样的话把JspServlet给i覆盖掉了,
     *     当我们访问jsp资源时,就会通过前端控制器,其解析不了
     *
     */


    @RequestMapping("/test")
    public ModelAndView test1(){
        ModelAndView modelAndView = new ModelAndView();
//        modelAndView.setViewName("/WEB-INF/a.jsp");
        modelAndView.setViewName("a");
        return modelAndView;
    }
}

8.5 处理响应之 ModelAndView – String

    @RequestMapping("/hellow")
    public ModelAndView hellow(){
        ModelAndView view = new ModelAndView();
        view.addObject("msg","hellow Controller");
        view.setViewName("/WEB-INF/hellow.jsp");
        return view;
    }

    @RequestMapping("/str")
    public String rView(){
        return "a";
    }

    @RequestMapping("/model")
    public String getByModel(Model model){
        //往作用域模型中存入数据
        model.addAttribute("msg","hhhh");
        return "a";
    }

8.6 消除前后缀

    <!--    配置试图解析器-->
<!--    这样在解析视图路径时候,就可以在路径前加上配置的前缀和后缀-->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/view/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

8.7 请求转发和重定向

@Controller
public class forward_RedirctController {

    /**
     * 关于转发和重定向的路径问题
     * 当使用/,为绝对路径(推荐使用)
     * 不使用/,则表示使用相对路径,在转发或者重定向前的上一个上下文路径下拼接转发/重定向路径!!
     * 为了不出错,我们统一使用绝对路径,全部使用"/"
     *
     */
    @RequestMapping("/f")
    public String forward(){
        return "forward:/WEB-INF/view/a.jsp";
    }

    @RequestMapping("/d")
    public String redirct(){
        return "redirect:/static/demo.html";
    }
}

8.7 参数的获取问题和@ModelAttribute的使用

8.7.1 参数的使用
@Controller
public class parameterController {
    /**
     * 当我们在练习参数的获取时,我们只关注数据的获取,而不关注页面
     * 所以我们最好的返回结果为ModelAndView,然后retrun null这样就会返回一个空页面
     * 而当我们使用String,return null 时,就会去找对应的视图页面,会报错
     *
     */

    /**
     *
     * 参数封装
     * 简单参数:当我们地址栏传来的参数的时候,会封装成map,然后通过映射的方法,形参作为键,可以直接拿取
     *         不过形参的key和地址栏的key必须一样
     *数组参数:地址栏输入多个参数,可以通过形参数组获取,不过形参的key和地址栏的key必须一样
     *自定义类型参数: 当我们发送请求时,会通过User.class拿到字节码对象,
     *              然后通过反射获取属性和浏览器参数进行对比,如果相同,则调用set方法,设置为浏览器属性值!!!
     *日期类型: 默认的识别格式是2020/5/20,
     *         我们想要用户传入的参数按照自己想要的格式时,需要形参注解@DateTimeFormat(pattern ="yyyy-MM-dd")Date date
     *         当然我们也可以在自定义的类上面的字段贴上同样的注解,这样当获取用户的时间格式是可以按照自己想要格式
     
      * 当实参和新参不同时,我们需要使用一个注解
     * //localhost/req0?username=11&age=1
     * @RequestParam("username") String name, int age
     
     */

    /**
     *关于@ModelAttribute的使用
     * 当我们想页面回显的时候,我们需要按照给定的默认首字母小写的引用,比如user.id,employee.id之类
     * 这样我们发现当我们回显数据时,要写很长的引用,不方便
     * 我们可以通过这个注解改变对应的key值
     * @ModelAttribute("u") User user
     */

    //localhost/req?username=11&age=1
    @RequestMapping("/req")
    public ModelAndView req(String username,int age){
        System.out.println(username);
        System.out.println(age);
        return null;
    }


    //localhost/req2?ids=11&ids=8
    @RequestMapping("/req2")
    public ModelAndView req2(Long[] ids){
        System.out.println(Arrays.toString(ids));
        return null;
    }

    //localhost/req3?id=3&username=l&password=u
    @RequestMapping("/req3")
    public ModelAndView req3(User user){
        System.out.println(user);
        return null;
    }

    //localhost/req4?date=2020/5/20
    //localhost/req4?date=2020-5-20
    @RequestMapping("/req4")
    public ModelAndView req4(@DateTimeFormat(pattern ="yyyy-MM-dd") Date date){
        System.out.println(date);
        return null;
    }
    //localhost/req5?date=2020-5-20
    @RequestMapping("/req5")
    public ModelAndView req5( User user){
        System.out.println(user);
        return null;
    }

    //localhost/req6?date=2020-5-20
    @RequestMapping("/req6")
    public String req6( @ModelAttribute("u") User user){
        System.out.println(user);
        return "a";
    }
}

自定义类型

public class User {
    private Long id;
    private String username;
    private String password;
    @DateTimeFormat(pattern ="yyyy-MM-dd")
    private Date date;
8.7.2 关于请求参数包含中文,会导致乱码问题的处理

GET方式

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.1</version>
                <configuration>
                    <port>80</port>
                    <path>/</path>
<!--                    解决GET请求参数获取的中文编码问题
                       tomcat已经处理这个问题-->
                    <uriEncoding>UTF-8</uriEncoding>
                </configuration>
            </plugin>
        </plugins>
    </build>

POST方式

<!-- 编码过滤器,仅针对 POST 方式 -->
<filter>
    <filter-name>characterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

8.8 文件上传

十分注意由于我们在webapp下定义了一个文件夹upload,而控制器的映射也是/upload,表单提交请求/upload时,会重定向到我们创建的upload文件夹!!!!!!!

8.8.1 准备上传表单
<form action="/upload" method="POST" enctype="multipart/form-data">
     文件:<input type="file" name="part"><br> <input
        type="submit" value="提交"/></form>
</body>

修改web,跟以前用servlet文件上传一样,相当于配置@MultipartConfig

    <!--    配置springmvc的前端控制器-->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--        配置spring容器启动后加载的文件所在地址-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:mvc.xml</param-value>
        </init-param>
        <!--        配置启动加载的级别,优先启动,tomcat启动初始化-->
        <load-on-startup>1</load-on-startup>
<!--        跟原来的servlet配置一样,只不过是xml配置,只有配置了才可以使用req.getpart获取文件对象-->
        <multipart-config>
            <max-file-size>52428800</max-file-size>
            <max-request-size>52428800</max-request-size>
        </multipart-config>
    </servlet>
8.8.2 配置上传解析器
<!--    配置上传视图解析器,注意这里的id名称为固定的为multipartResolver-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver"/>
8.8.3 编写上传控制器
@Controller
public class UploadController {
    
    @Autowired
    private ServletContext servletContext;
    
    @RequestMapping("/upload")
    public ModelAndView upload(Part part) throws IOException {
       //文件类型
        System.out.println(part.getContentType());
        //文件名称
        System.out.println(part.getName());
        //文件大小
        System.out.println(part.getSize());
        //文件输入流
        System.out.println(part.getInputStream());
        //获取真实路径
        System.out.println(servletContext.getRealPath("/upload"));
        return null;
    }

8.9 拦截器

自定义拦截器

8.9.1 编写拦截器
//自定义拦截类
public class MyInterceptor implements HandlerInterceptor {

    //处理方法前执行
    //return true 放行  return false  拦截不放行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHeandle");
        return true;
    }

    //在处理方法后,渲染视图前执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptor.postHandle");
    }

    //渲染视图后执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MyInterceptor.afterCompletion");
    }
}
8.9.2 在xml文件配置拦截器
    <mvc:interceptors>
        <mvc:interceptor>
<!--            设置拦截的路径-->
            <mvc:mapping path="/**"/>
<!--            设置排除路径(不拦截路径)-->
            <mvc:exclude-mapping path="/req"/>
            <bean class="cn.wolfcode.springmvc.util.MyInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>

8.10 关于请求的理解(细节!!重要!!!!!!!!!!!!!!!!!)

/:不会拦截页面,只会拦截路径。

/* :会路径和页面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9hBgs6nB-1619680344294)(img/888888a.png)]

/**
 * 关于访问  http://localhost/static/demo.html
 * tomcat里有两个内置的serlvet
 * DefaultServlet:当访问静态资源时(html,视频,图片....),通过它   “/”
 * JspServlet,当访问Jsp资源时,通过它
 *
 * Springmvc框架里配置了DispatcherServlet
 * 它的通过路径是 “/” ,和DefaultServlet通过路径相同,所以DefaultServlet被覆盖!!!
 * 因此当我们访问静态资源时,会通过DispatcherServlet,而/只能解析请求,不能解析页面
 * DispatcherServlet不能解析动态资源文件,导致访问不到404
 *
 *当我们把DispatcherServlet改为/*时,它会覆盖JspServlet和DefaultServlet,这样jsp资源也访问不了
 *因为其不会解析jsp页面
 *
 *
 
 */


    <!--    配置springmvc的前端控制器-->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--        配置spring容器启动后加载的文件所在地址-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:mvc.xml</param-value>
        </init-param>
        <!--        配置启动加载的级别,优先启动,tomcat启动初始化-->
        <load-on-startup>1</load-on-startup>
    </servlet>

当我们启动Tomcat容器时,会先加载web.xml文件,在根据DispatcherServlet指定的配置文件位置去加载mvc.xml文件

day09 Spring_SpringMvc_Mybatis综合案例

使用ssm框架写增删改查,高级查询,多表查询

9.1 项目准备

创建webapp文件夹,构建web项目

导入项目需要的依赖,需要的插件

9.2 MyBatis 逆向工程

什么是MyBatis逆向工程?

mybatis官方为了提高开发效率,提高自动对单表生成sql,所以给我们提供了逆向工程。针对表生成mybatis执行所需要的代码(Mapper接口,Mapper的xml文件,pojo的java实体类)

如何使用呢?

第一步:因为mybatis逆向工程是根据表来进行创建对应的代码,所以第一步我们要先创建表

第二步:我们要在pom配置一个maven插件

    <plugin>
        <groupId>org.mybatis.generator</groupId>
        <artifactId>mybatis-generator-maven-plugin</artifactId>
        <version>1.3.2</version>
        <configuration>
            <verbose>true</verbose>
            <overwrite>false</overwrite>
        </configuration>
        <dependencies>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.45</version>
            </dependency>
        </dependencies>
    </plugin>
</plugins>

第三步:然后在resource下面新建配置件 generatorConfig.xml,该文件中配置我们生成的代码的信息。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
      PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
      "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<!-- 配置生成器 -->
<generatorConfiguration>

   <context id="mysql" defaultModelType="hierarchical"
          targetRuntime="MyBatis3Simple">

      <!-- 自动识别数据库关键字,默认false,如果设置为true,根据SqlReservedWords中定义的关键字列表; 一般保留默认值,遇到数据库关键字(Java关键字),使用columnOverride覆盖 -->
      <property name="autoDelimitKeywords" value="false" />
      <!-- 生成的Java文件的编码 -->
      <property name="javaFileEncoding" value="UTF-8" />
      <!-- 格式化java代码 -->
      <property name="javaFormatter"
              value="org.mybatis.generator.api.dom.DefaultJavaFormatter" />
      <!-- 格式化XML代码 -->
      <property name="xmlFormatter"
              value="org.mybatis.generator.api.dom.DefaultXmlFormatter" />

      <!-- beginningDelimiter和endingDelimiter:指明数据库的用于标记数据库对象名的符号,比如ORACLE就是双引号,MYSQL默认是`反引号; -->
      <property name="beginningDelimiter" value="`" />
      <property name="endingDelimiter" value="`" />

      <commentGenerator>
         <property name="suppressDate" value="true" />
         <property name="suppressAllComments" value="true" />
      </commentGenerator>

      <!-- 必须要有的,使用这个配置链接数据库 @TODO:是否可以扩展 -->
      <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                  connectionURL="jdbc:mysql:///springdemo" userId="root" password="admin">
         <!-- 这里面可以设置property属性,每一个property属性都设置到配置的Driver上 -->
      </jdbcConnection>

      <!-- java类型处理器 用于处理DB中的类型到Java中的类型,默认使用JavaTypeResolverDefaultImpl; 注意一点,默认会先尝试使用Integer,Long,Short等来对应DECIMAL和 
         NUMERIC数据类型; -->
      <javaTypeResolver
            type="org.mybatis.generator.internal.types.JavaTypeResolverDefaultImpl">
         <!-- true:使用BigDecimal对应DECIMAL和 NUMERIC数据类型 false:默认, scale>0;length>18:使用BigDecimal; 
            scale=0;length[10,18]:使用Long; scale=0;length[5,9]:使用Integer; scale=0;length<5:使用Short; -->
         <property name="forceBigDecimals" value="false" />
      </javaTypeResolver>


      <!-- java模型创建器,是必须要的元素 负责:1,key类(见context的defaultModelType);2,java类;3,查询类 
         targetPackage:生成的类要放的包,真实的包受enableSubPackages属性控制; targetProject:目标项目,指定一个存在的目录下,生成的内容会放到指定目录中,如果目录不存在,MBG不会自动建目录 -->
      <javaModelGenerator targetPackage="cn.wolfcode.domain"
                     targetProject="src/main/java">
         <!-- for MyBatis3/MyBatis3Simple 自动为每一个生成的类创建一个构造方法,构造方法包含了所有的field;而不是使用setter; -->
         <property name="constructorBased" value="false" />

         <!-- for MyBatis3 / MyBatis3Simple 是否创建一个不可变的类,如果为true, 那么MBG会创建一个没有setter方法的类,取而代之的是类似constructorBased的类 -->
         <property name="immutable" value="false" />

         <!-- 设置是否在getter方法中,对String类型字段调用trim()方法
         <property name="trimStrings" value="true" /> -->
      </javaModelGenerator>

      <!-- 生成SQL map的XML文件生成器, 注意,在Mybatis3之后,我们可以使用mapper.xml文件+Mapper接口(或者不用mapper接口),
         或者只使用Mapper接口+Annotation,所以,如果 javaClientGenerator配置中配置了需要生成XML的话,这个元素就必须配置
         targetPackage/targetProject:同javaModelGenerator -->
      <sqlMapGenerator targetPackage="cn.wolfcode.mapper"
                   targetProject="src/main/resources">
         <!-- 在targetPackage的基础上,根据数据库的schema再生成一层package,最终生成的类放在这个package下,默认为false -->
         <property name="enableSubPackages" value="true" />
      </sqlMapGenerator>


      <!-- 对于mybatis来说,即生成Mapper接口,注意,如果没有配置该元素,那么默认不会生成Mapper接口 targetPackage/targetProject:同javaModelGenerator
         type:选择怎么生成mapper接口(在MyBatis3/MyBatis3Simple下): 1,ANNOTATEDMAPPER:会生成使用Mapper接口+Annotation的方式创建(SQL生成在annotation中),不会生成对应的XML;
         2,MIXEDMAPPER:使用混合配置,会生成Mapper接口,并适当添加合适的Annotation,但是XML会生成在XML中; 3,XMLMAPPER:会生成Mapper接口,接口完全依赖XML;
         注意,如果context是MyBatis3Simple:只支持ANNOTATEDMAPPER和XMLMAPPER -->
      <javaClientGenerator targetPackage="cn.wolfcode.mapper"
                      type="XMLMAPPER" targetProject="src/main/java">
         <!-- 在targetPackage的基础上,根据数据库的schema再生成一层package,最终生成的类放在这个package下,默认为false -->
         <property name="enableSubPackages" value="true" />

         <!-- 可以为所有生成的接口添加一个父接口,但是MBG只负责生成,不负责检查 <property name="rootInterface"
            value=""/> -->
      </javaClientGenerator>

      <table tableName="employee">
         <property name="useActualColumnNames" value="true"/>
         <property name="constructorBased" value="false" />
         <generatedKey column="id" sqlStatement="JDBC" />
      </table>
   </context>
</generatorConfiguration>

9.3 ssm框架的整合

注意:当我们启动项目时会加载web.xml文件,我们配置前端控制器时,可以配置web.xml里加载的xml,而我们SpringMVC,Spring分别有mvc.xml,applicationContext.xml两个配置文件,因此我们可以把两个文件的配置写在一起,或者说将其中一个配置文件引入到另外一个配置文件。为了后期更好的维护,我们选择web.xml文件加载时加载mvc.xml文件,而在mvc.xml文件中引入applicationContext.xml配置文件。

9.3.0 web.xml文件的配置
第一步:配置前端控制器
<!--    配置前端控制器-->
<servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
第二步:配置参数获取时的乱码问题:

关于GET请求,在pom文件的tomcat的maven插件进行配置,或者tomcat7以后就默认配置

<plugin>
    <groupId>org.apache.tomcat.maven</groupId>
    <artifactId>tomcat7-maven-plugin</artifactId>
    <version>2.1</version>
    <configuration>
        <port>80</port> <!-- 端口 -->
        <path>/</path> <!-- 上下路径 -->
        <uriEncoding>UTF-8</uriEncoding> <!-- 针对 GET 方式乱码处理 -->
    </configuration>
</plugin>

关于POST请求,配置编码过滤器

   <!-- 配置编码过滤器, 针对 POST -->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
9.3.1 Spring集成Mybatis
第一步:配置数据连接
<!--    配置数据库连接-->
    <context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
第二步:配置SqlSessionFactoryBean,用于获取数据连接
<!--    配置数据库连接的获取-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <property name="typeAliasesPackage" value="cn.wolfcode.domain"></property>
    </bean>
第三步:配置Mapper bean,用于获取Mapper接口的代理对象
<!--     配置Mapper代理对象的获取-->
    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--        配置Mapper接口所在的位置-->
        <property name="basePackage" value="cn.wolfcode.mapper"></property>
    </bean>
第四步:配置ioc,di的解析器
<!--    配置ioc注解的解析器-->
     <context:component-scan base-package="cn.wolfcode"></context:component-scan>
9.3.2 集成SpringMvc
第一步:配置ioc,di解析器
<!--    配置ioc注解解析器-->
      <context:component-scan base-package="cn.wolfcode.service"/>
第二步:配置mvc注解解析器
<!--        配置mvc注解解析器-->
      <mvc:annotation-driven></mvc:annotation-driven>
第三步:配置静态资源的处理
<!--       处理静态资源-->
      <mvc:default-servlet-handler></mvc:default-servlet-handler>
第四步:配置事务解析器
<!--      配置事务解析器-->
      <bean id="resourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="prefix" value="/WEB-INF/views/"></property>
            <property name="suffix" value=".jsp"></property>
      </bean>
第五步:引入Spring的配置文件
<!--      引入applicationContext.xml-->
      <import resource="applicationContext.xml"></import>

9.4 部门的增删改查和分页

逻辑和之前学的web.crud一样,只是采用的技术不同!

注意:结果集的封装,当如果count数据为0时,则直接返回空集合,不需要进行下面的操作,提高了性能

@Override
public PageResult<Department> listPage(QueryObject queryObject) {
    int count = departmentMapper.count();
    List<Department> departments;
    if (count == 0){
        departments = Collections.emptyList();
    }else {
         departments = departmentMapper.listPage(queryObject);
    }
    PageResult<Department> pageResult = new PageResult<>(count,departments,queryObject.getCurrentPage(),queryObject.getPageSize());
    return pageResult;
}
@Controller
@RequestMapping("/department")
public class DepartmentController {

    private DepartmentService departmentService;
    @Autowired
    public void setDepartmentService(DepartmentService departmentService) {
        this.departmentService = departmentService;
    }

//    @RequestMapping("/list")
//    public String listAll(Model model){
//        List<Department> departments = departmentMapper.selectAll();
//        model.addAttribute("departments",departments);
//        return "department/list";
//    }

    @RequestMapping("/list")
    public String listAll(Model model,QueryObject queryObject){
        System.out.println(queryObject);
        PageResult<Department> pageResult = departmentService.listPage(queryObject);
        model.addAttribute("pageResult",pageResult);
        return "department/list";
    }

    @RequestMapping("/delete")
    public String delete(Long id){
        if (id != null){
            departmentService.deleteByPrimaryKey(id);
        }
        return "redirect:/department/list";
    }

    @RequestMapping("/input")
    public String input(Long id,Model model){
        if (id != null){
            Department department = departmentService.selectByPrimaryKey(id);
            model.addAttribute("department", department);
        }
        return "department/input";
    }

    @RequestMapping("/saveOrUpdate")
    public String saveOrUpdate(Department department,Model model){
        if (department.getId() != null){
            departmentService.updateByPrimaryKey(department);
        }else {
            departmentService.insert(department);
        }
        return "redirect:/department/list";
    }


}

9.5 员工的增删改查,分页,高级查询,多表关联

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0274JDOl-1619680344294)(img/848748.png)]

增删改查:

员工的增删改查和部门一样,所以我们 可以进行拷贝修改即可

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-68m5TjtV-1619680344295)(img/a554544.png)]

高级查询:

由于显示员工信息页面要显示关联的部门名称,因此我们需要将employee和department关联起来

实体类的设计

@ToString
@Getter
@Setter
public class Employee {
    private Long id;
    private String name;
    private String password;
    private String email;
    private Integer age;
    private boolean admin;
    private Long deptId;   //字段关联
    private Department dept;  //关联部门

关于页面高级查询的参数封装

@Getter
@Setter
@ToString
public class EmployeeObject extends QueryObject {
    private String keyword;
    private Long deptId;
}

关于Mapper.xml配置文件的sql语句编写

  <select id="listPage" resultMap="BaseResultMap">

        select  e.id, e.name, e.password, e.email, e.age, e.admin, e.deptId,d.id as d_id,d.name as d_name,d.sn as  d_sn
    from employee e left  join  department d
    on   e.deptId = d.id
    <include refid="advancedQuery"></include>
    limit #{index},#{pageSize}

  </select>
<select id="count" resultType="java.lang.Integer">
  select count(*) from  employee e
   <include refid="advancedQuery"></include>
</select>
<sql id="advancedQuery">
  <where>
    <if test="keyword != null and keyword != ''">
      and (e.name like concat('%',#{keyword},'%') or e.email like  concat('%',#{keyword},'%'))
    </if>
    <if test="deptId != null and deptId != ''">
      and e.deptId = #{deptId}
    </if>
  </where>
</sql>

day10 jQueryDay01

10.1 关于jQuery的认识

什么jQuery?

jQuery是一个javaScript框架,对js的操作属性…等等进行了封装,原生js操作代码量多,且繁琐

当我们使用这个框架时,需要引入对应的js

<script type="text/javascript" src="/static/jQuery-1.11/jquery-1.11.3.min.js">

10.2 JQuery对象和OM对象之间的转换

<script>
    console.log($ = jQuery);  //为同一个对象,开始是JQuery,后来为了简便就出现了$
    window.onload = function ()
 {

       // btn 变量存的就是 DOM 对象
       var btn = document.getElementById("btn");
        console.log(btn);
       // 找页面元素jQuery,获取JQuery对象
        var $btn  = $('#btn')
        console.log($btn);
        // DOM 对象转成 jQuery 对象
         console.log($(btn));
         // jQuery 对象转成 DOM 对象,这个很少用
         //由于转为id为btn的dom对象可能有很多个,所以要加个索引
          console.log($btn.get(0));
         // 以后尽量使用 jQuery 方法操作页面元素,绑定事件等等.
 }
 </script>

10.3 JQ的常用方法

<script>
    //jq语法的页面加载完成
    $(function () {
        // 问题 1:获取 jQuery 中包含 DOM 的个数,比如获取页面上 p 元素的个数
        console.log($('p').size());
        // 问题 2:获取 id 为 username 元素的 value 属性值
        console.log($('#username').val());
        // 问题 3:设置 id 为 username 元素的 value 属性值为"叩丁狼教育"
        $('#username').val("叩丁狼教育")
        // 问题 4:对比 h1 元素的内容和纯文本的区别
        console.log($('#h1').html());;  //获取标签内的所有内容,包括标签里的其他标签
        $('#h1').html('<i>hhh</i>')
        console.log($('#h1').text());;   //获取标签内的所有文本内容。不包括标签里的其他标签
        // 问题 5:把 h1 元素内容的颜色改为黄色
        $('#h1').css('color','yellow');
    })
</script>

10.4 基本,层次,过滤选择器

10.4.1 基本选择器
<script>
    $(function () {
        // 问题 1:获取 id 为 msg 的元素的内容
        console.log($('#msg').html());
        // 问题 2:获取所有的 li 元素并打印数量
        console.log($('li').size());
        // 问题 3:获取所有 class 为 selected 的元素,字体颜色改为 red
        $('.selected').css('color','red');
        //获取整个html页面的dom对象
        console.log($('*').size());
        //使用hq可以获取多个jq对象,使用,逗号进行连接
        console.log($('#msg,.selected').size());

    })
</script>
10.4.2 层次选择器
<script>
    $(function () {
        // 问题 1:获取所有 ul 下的所有 li 元素,并打印分析结果
        console.log($('ul li'));
        // 问题 2:获取 id 为 myul 下的所有子 li 元素,并打印分析结果
        console.log($('#myul > li'));
        // 问题 3:获取所有 label 元素后的 input 元素,并打印分析结果
        console.log($('label ~ input'));
        // 问题 4:获取紧跟着 label 元素后的 input 元素,并打印分析结果
        console.log($('label + input'));
    })
</script>
10.4.3 过滤选择器
$(function () {
    // 问题 1:获取隐藏 input 的 value 属性值, 不能使用根据元素名, 也不能通过给元素加id 属性,再通过 id 选择器找
    console.log($('input[name=id]').val());
    // 问题 2:获取选中的 option
    console.log($('option:selected').val());

})

10.5 jq的事件绑定

<script>
    $(function () {
         $('#btn1').click(function () {
             console.log(this); //获取触发事件的事件源,这里指的是dom对象
             console.log($(this)) //用$将dom对象转为jq对象
         })
        // 找到多少个绑定多少个
        //在js中的话要写两个一个个绑定,而jq可以通过class属性全部一起绑定
        $('.myBtn').click(function () {
            console.log('hello');
        });
    });
    //在js中使用window.onload(页面加载完函数),最后一次会覆盖前面的
    window.onload = function () {
        console.log(1);
    };
    window.onload = function () {
        console.log(2);
    };
    //而在jq中,底层相当于
    // window.onload = function () {
    //    f1();
    //    f2();
    //   }
    //相当于不会相互影响,两个互不影响,当我们有需求时,可以在这两个里面定义相同的局部变量名称

    //f1()
    $(function () {
        console.log(3);
    });
    //f2()
    $(function () {
        console.log(4);
    });
</script>

10.6 jq常用Dom操作的方法

<script>
    $(function () {
        $('#btn').click(function () {
            console.log(1)
        })
        //向内部插入子节点
        $('#append').click(function () {
          $('#div1').append($('#span'));
        })
        //外部插入兄弟节点
        $('#after').click(function () {
            $('#div2').after($('#div1'));
        })
        //删除所有子节点:断子绝孙
        $('#empty').click(function () {
            $('#ul').empty();
        })
        //删除节点
        //拿到删除之前的指
        var $btn;
        //恢复节点
        //方式一:使用after恢复后,该dom对象的点击绑定消失,埋死尸
        $('#remove').click(function () {
            $btn= $('#btn').remove();
            console.log($btn.val());
        })
        //方式二:使用after恢复后,该dom对象的点击绑定不会消失,埋活尸
        $('#remove').click(function () {
            $btn= $('#btn').detach();
            console.log($btn.val());
        })
        $('#resume').click(function () {
             $('#ul').after($btn)
        })

    })
</script>

10.7 jq元素属性操作的方法

<script>
    $(function () {
       //点击获取属性值
        $('#getAttr').click(function () {
            console.log($('#btn').attr('type'))
            console.log($('#btn').prop('type'))
        })
        //设置属性值
        $('#setAttr').click(function () {
            console.log($('#btn').attr('class','other'));
            console.log($('#btn').attr('my', 'hhhhh').get(0));
        })
        //添加css
        $('#addClass').click(function () {
            $('#btn').attr('class','other myBtn');
            $('#btn').addClass('other myBtn');
        })
        //删除css
        $('#removeClass').click(function () {
            $('#btn').removeClass('myBtn');
        })
        //轮换css
        $('#toggleClass').click(function () {
            $('#btn').toggleClass('other');
        })
        //判断css
        $('#hasClass').click(function () {
            console.log($('#btn').hasClass('other'));
        })

        // css 方法针对 style 属性
        console.log($('#gender').css('color'));
        // val 方法针对 value 属性
        console.log($('#gender').val());
        // addClass 方法、removeClass 方法,针对 class 属性
        $('#gender').removeClass('other');
        // prop 方法针对 checked 属性 selected 属性
        console.log($('#gender').prop('checked'));
        $('#gender').prop('checked', false);
        // data 方法,针对 data- 开头的属性的获取,若值是 JSON 格式, 自动转成 JS 对象
        console.log($('#gender').data('option'));
        // 其他情况使用 attr 方法

    })
</script>

10.8 关于jq的四个练习

10.8.1 下拉框的回显:
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>下拉框回显</title>
    <script src="/static/jQuery-1.11/jquery-1.11.3.min.js"></script>
    <script>
        function echo() {
          //使用‘ : ’过滤选择器,过滤条件
          $('#s1 > option:eq(2)').prop('selected',true);

        }
    </script>
</head>
<body>
<select id="s1">
    <option value="1">选项1</option>
    <option value="2">选项2</option>
    <option value="3">选项3</option>
    <option value="4">选项4</option>
    <option value="5">选项5</option>
</select>
<br/>
<input type="button" value="回显第3个选项" onclick="echo();"/><br/>
</body>
</html>
10.8.2 列表的移动
<script>
     //全部移动
     function moveAll(srcId,desId) {
        $('#'+desId).append($('#'+srcId+' > option'));
     }
     //选中部分进行移动
     function moveSelected(srcId,desId) {
         $('#'+desId).append($('#'+srcId+' > option:selected'));
     }
</script>
10.8.3 下拉框去重:
<script>
    //去除重复,右边为标准,根据右边的去除左边的
    function distinct() {
        //首先将右边的存入一个数组里面
        var arr = [];
        $('#s2 > option').each(function (i,domEle) {
            // console.log(arguments);
            //根据打印arguments,来缺点该函数的参数类型和个数
            //Arguments(2) [1, option, callee: ƒ, Symbol(Symbol.iterator): ƒ]
            // arr[i] = domEle.value;
            arr.push(domEle.value);
        })
        //遍历左边的跟右边进行对比
        $('#s1 > option').each(function (i,domEle) {
            var  $option = $(domEle);
            var val = $option.val();
            //调用jq的函数,判断这个元素是否在数组之中.如果不在则返回-1,在的话返回索引
            if ($.inArray(val,arr) >= 0){
                $option.remove();
            }
        })
    }
</script>
10.8.4 全选:
<script>
    $(function () {
        //当点击所有复选框的时候,进行检查,如果全部点完,则全选
        $('input[name=hobby]').click(function () {
              check();
        })
    })
    function checkAll(flag) {
       $('input[name=hobby]').prop('checked',flag);
        check();
    }
    function checkChange(event) {
        var $event = $(event).prop("checked");
        checkAll($event);
    }
    function checkUnAll() {
        $('input[name=hobby]').each(function (i,domEle) {
               var $input = $(domEle);
               var  oldValue = $input.prop("checked");
               var newValue = !oldValue;
            $input.prop("checked",newValue);
        })

        //当我们点击全部复选框时,id=checkAll应该被选上
        //定义一个标记,使用&&的性质特点,
        //将标记 && 每一个input的checked的值,如果有一个没选上的话,为false
        //最后flag为复选框有没有全选的状态,赋值给,id=checkAll就行

       check();
    }
    function check() {
        var flag = true;
        $('input[name=hobby]').each(function (i,domEle) {
            var $input = $(domEle);
            var Val = $input.prop("checked");
            flag = flag && Val;
             $('#checkAll').prop('checked', flag);
        })
    }
</script>

day11 jQueryDay02

11.1 关于Json的理解

什么是json?

json是一种轻量级的数据交换格式,其实就是一种有格式的字符串,可以表示任意类型

11.2 在JavaScript中的Json

11.2.1 表示json
<script>
    //什么是json?,json是一个响应的数据交换格式
    //1.关于json字符串的基本表示和使用
    //编写json字符串时,外部使用单引号‘’,内部使用双引号,按照这个格式才是json字符串

    // 写 JSON,这个格式的字符串里面存一个员工数据 id 1 name zs age 18
    var  json1 = '{"id":1,"name":"zs","age":18}';
    '{"id":1,"name":"zs","sn":"15151"}'
    // 写 JSON,这个格式的字符串里面存两个个员工数据 id 1 name zs age 18 id 2 name ls age 19
    //当我们需要存储多个json对象时,使用[]
    var  json2 = '[{"id":1,"name":"zs","age":18},{"id":2,"name":"ls","age":19}]';
    // 写 JSON,这个格式的字符串里面存一个员工数据 id 1 name zs age 18 部门 id 5 name 开发部
    //一个json为另外一个json对象的儿子,内嵌
    var  json3 = '{"id":1,"name":"zs","age":18,"dept":{"id":5,"name":"开发部"}}';
    //{"id":5,"name":"开发部"}
    //关于json对象和js对象
    var  jsonObject = {"id":1,"name":"zs","age":18};  //json对象
    var  jsObject = {id:1,name:"zs",age:18};    //js对象,js对象的key不一定要字符串类型,不需要加引号!!!

    //2.关于值的获取  json字符串,json对象,js对象的比对
    //json字符串获取不到值,因为json字符串只是一个字符串,并不能根据keyValue键值对来获取
    //json,js对象,都是对象,可以对应的key来获取到值
    console.log(json1.name);  //unfinished
    console.log(jsonObject.name);  //zs
    console.log(jsObject.name);   //zs
11.2.2 json与js对象的转换
//3.关于json对象和js对象的转换 和错误格式的json字符串
//假设获取到服务器响应的数据是 JSON 格式,想获取到具体数据怎么?
// 有一种方式切割字符串, 不可取的
// 另一种方式,JSON 是浏览器环境提供一个工具, 里面提供方法实现两者的转换

//json字符串转为js对象
console.log(JSON.parse(json1).age);
console.log(JSON.parse(json2));
console.log(JSON.parse(json3).dept.name);

//js对象转为json对象
console.log(JSON.stringify(jsObject));

var json4 = "{'id':1,'name':'zs','age':18}";
// 错误格式的JSON
console.log(JSON.parse(json4)); // 报错

11.3 在java中的json

11.3.1 表示json
//关于在java中的json字符串的表示
@Test
public void jsonTest(){
    //当我们从js中写的json字符串复制过来时候,会进行转义,因为双引号”“在java中有特殊的含义
    //加\进行转义是为了,不让它有特殊的含义,让作为""双引号
    String jsonStr = "'{\"id\":1,\"name\":\"zs\",\"age\":18}'";
}
11.3.2 json和java对象的转换

使用jackson

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.6</version>
</dependency>
//关于json和java对象的转换
//第一种方式:使用jsckson
//第一步添加springmvc中内置的处理json的jar包  jackson
@Test
public void jsonTest2() throws IOException {
    Department department = new Department();
    department.setSn("15152");
    department.setName("开发部");
    department.setId(1L);

    //创建一个jackson包的数据绑定对象
    ObjectMapper objectMapper = new ObjectMapper();
    //通过一个参数传入,写入一个str字符串给绑定对象
    String jsonStr = objectMapper.writeValueAsString(department);
    //java转为json对象
    System.out.println(jsonStr);
    //json转为java对象
    System.out.println(objectMapper.readValue("{\"id\":1,\"name\":\"开发部 \",\"sn\":\"45474\"}", Department.class));

}

使用阿里巴巴的fastJson

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.47</version>
</dependency>
//关于json和java对象的转换
//第二种方式:使用Fastjson
//添加阿里巴巴的jar包
@Test
public void jsonTest3() throws IOException {
    Department department = new Department();
    department.setSn("15152");
    department.setName("开发部");
    department.setId(1L);
    //java转为json对象
    System.out.println(JSON.toJSONString(department));
    //json转为java对象
    System.out.println(JSON.parseObject("{\"id\":1,\"name\":\"开发部 \",\"sn\":\"45474\"}", Department.class));
}

11.4 SpringMvc响应json

//用于Controller响应前端的请求,返回对应的json字符串信息封装
@Getter
@Setter
public class jsonResult {
    private boolean success;
    private String msg;
}
@Controller
public class jsonController {

    //关于springmvc放回json字符串的数据交换格式的两种方式


    //springmvc响应浏览器的原始方式(很挫)  需求:返回当前时间给浏览器
    @RequestMapping("/getTime")
    public void getTime(HttpServletResponse response) throws IOException {
        //设置返回的数据交换格式  与原来的返回html的数据交换格式进行杜比
        response.setContentType("application/json;charset=utf-8");
        //从响应对象获取打印输出流
        PrintWriter writer = response.getWriter();
        //封装用于返回浏览器的json对象信息
        jsonResult jsonResult =new jsonResult();
        jsonResult.setSuccess(true);
        jsonResult.setMsg(new Date().toLocaleString());
        //向响应对象的输出流写入json字符串
        writer.print(JSON.toJSONString(jsonResult));
        writer.flush(); //刷新响应端的打印流的缓冲
    }


    //springmvc响应浏览器的注解方式(推荐)  需求:返回当前时间给浏览器
    @RequestMapping("/getTime2")
    @ResponseBody  //使用这个注解,当我们返回对象给html时,会解析为json字符串并且放回
    public jsonResult getTime2(HttpServletResponse response) throws IOException {
        jsonResult jsonResult =new jsonResult();
        jsonResult.setSuccess(true);
        jsonResult.setMsg(new Date().toLocaleString());
         return jsonResult;
    }

}

11.4 关于AJAX

什么是AJAX?

AJAX不是一门具体的技术,而是几门技术的综合运用。可以用来发布异步请求增加用户体验

同步请求:发送方发送请求后,等到接收方响应后才能处理

异步请求:发送方发送请求后,不需要等接收方收到响应,可以之间进行下一次的请求

可以在用户浏览页面的同时和服务器进行异步交互,从而实现网页内容的局部刷新!!!!

如下面的网易邮箱注册就是异步请求就是经典的例子:

1.当我们填完用户名时,他会通过ajax发送请求去验证该用户名是否存在

2.这时因为是异步请求页面并不会刷新。而不是使用同步请求等用户填完信息后,才告诉他用户名被注册,由于发送了一次请求导致页面刷新,页面数据丢失,用户需要重新填写信息,这样用户体验不好

11.5 关于ajax的练习

11.5.1 POST 请求用户登录操作
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
    <script src="/static/jQuery-1.11/jquery-1.11.3.min.js"></script>
    <script>

        $(function () {
            //使用ajax发送异步请求
            //获取页面用户输入的用户名和密码,封装成参数
/*            var username =  ($('#username')).val();
            var password =  ($('#password')).val();*/

            //注意如果参数获取写在外面是获取不到的

            $('#login').click(function () {
                //1.构造返回的参数
                //这样的方式,当有两个或者多个参数进行拼接的时候非常麻烦,而且容易导致写错
                // var Parament = "username="+username+"&"+"password"+password;
                //因此我们可以构造js对象来作为请求参数
                var username =  ($('#username')).val();
                var password =  ($('#password')).val();
                var  param = {username : username, password : password};
                console.log(param);
                //2.通过ajax发送请求
                $.post("/checkLogin",param,function (data) {
                    if (data.success){
                        //登录成功,跳转到其他页面
                        location.href = "https://www.baidu.com/s?ie=UTF-8&wd=%E7%99%BE%E5%BA%A6";
                    }else {
                        $('#result').html(data.msg).css('color','red');
                    }
                })
            })
        })

    </script>
</head>
<body>
    <span id="result"></span><br/>
    用户名:<input type="text" name="username" id="username"><br><br>
    密码:<input type="text"  name="password"  id="password"><br><br>
    <button id="login">登录</button>
</body>
</html>
11.5.2 GET 请求检查用户名是否存在
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="/static/jQuery-1.11/jquery-1.11.3.min.js"></script>
    <script>
       //使用ajax发送请求给浏览器,异步请求,局部请求
        //当输入框的焦点消失后,触发ajax的提交请求
        $(function () {
            $('#username').blur(function () {
                //打印事件源,看是什么对象,
                //根据打印,我们知道调用该方法的是dom对象
                //而$('#username')为jq对象
                console.log(this);
                //获取用户名
                var username = $(this).val();
                //拼接字符串来构造参数
                //相当与跟之前的比,根据name来获取参数一样,只不过时自己构造返回的参数字符串
                //而之前的是通过name获取,来绑定构造字符串
                var ParamentInfo = "username="+ username;
                //使用ajax来发送请求
                //参数1:发送的请求
                //参数2:发送的参数
                //参数3:回调函数,data为请求后拿到的数据
                $.get('/checkName',ParamentInfo,function (data) {
                    console.log(data);
                    //由于当我们设置html内容时,放回的是当前对象,所以可以使用方法链进行操作减少代码量
                    $('#result').html(data.msg).css('color',data.success== true ? 'green':'red');
                })
            })

        })
     </script>
</head>
<body>
   <span id="result"></span><br/>
   用户名:<input type="text" name="username" id="username"></body>
</html>
//注意ajax是异步请求,所以不是通过表单form,通过表单form提交的是同步请求
@Controller
public class CheckController {

    //根据用户传入的用户名信息来返回封装好的json结果集对象
    @RequestMapping("/checkName")
    @ResponseBody
    public jsonResult checkName(String username){
        jsonResult jsonResult = new jsonResult();
        System.out.println(username);
        if ("admin".equals(username)){
            jsonResult.setMsg("用户名已经被注册");
            jsonResult.setSuccess(false);
        }else {
            jsonResult.setMsg("恭喜注册成功");
            jsonResult.setSuccess(true);
        }
        return jsonResult;

    }
}
11.5.3 练习 - 二级联动
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>二级联动</title>
    <script src="/static/jQuery-1.11/jquery-1.11.3.min.js"></script>
    <script>
        $(function () {
            //1.当页面加载完的时候,拿到所有省份信息   给对应的省份下拉框设置儿子option

            //2.当下拉框改变的时候,获取对应的省份  通过省份获取对应的城市
            var $c = $('#c');
            var $p = $('#p');

            //当页面加载完的时候,拿到所有省份信息
            //给对应的省份下拉框设置儿子option
            $.get("/province",function (data) {
               data.forEach(function (value) {
                    $p.append('<option value="'+value.id+'">'+value.name+'</option>');
               })
            })
            
            //当省份下拉框改变时,加载对应的城市
            $p.change(function () {
                var id =$p.val();
                var  param = {id : id};
                //在增加前把之前的清除掉,否则城市一直叠加
                $c.empty(); //断子绝孙
                $c.append('<option value="-1">请选择</option>');
                if (id >= 1){
                    $.get('/city',param,function (data) {
                        data.forEach(function (value) {
                            $c.append('<option value="'+value.id+'">'+value.name+'</option>');
                        })
                    })
                }
            })
        })
    </script>
</head>
<body>

省份:<select id="p">
    <option value="-1">请选择</option>
</select>

城市:<select id="c">
    <option value="-1">请选择</option>
</select></body>
</html>
@Controller
public class ProvinceController {

    //获取所有省份的json数据
    @RequestMapping("/province")
    @ResponseBody
    public List<Province> getProvince(){
        List<Province> allProvince = Province.getAllProvince();
        return allProvince;
    }

    //获取对应省份的所有城市的json数据
    @RequestMapping("/city")
    @ResponseBody
    public List<City> getCity(Long id){
        List<City> cities = City.getCityByProvinceId(id);
        return cities;
    }

}
>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="/static/jQuery-1.11/jquery-1.11.3.min.js"></script>
    <script>
       //使用ajax发送请求给浏览器,异步请求,局部请求
        //当输入框的焦点消失后,触发ajax的提交请求
        $(function () {
            $('#username').blur(function () {
                //打印事件源,看是什么对象,
                //根据打印,我们知道调用该方法的是dom对象
                //而$('#username')为jq对象
                console.log(this);
                //获取用户名
                var username = $(this).val();
                //拼接字符串来构造参数
                //相当与跟之前的比,根据name来获取参数一样,只不过时自己构造返回的参数字符串
                //而之前的是通过name获取,来绑定构造字符串
                var ParamentInfo = "username="+ username;
                //使用ajax来发送请求
                //参数1:发送的请求
                //参数2:发送的参数
                //参数3:回调函数,data为请求后拿到的数据
                $.get('/checkName',ParamentInfo,function (data) {
                    console.log(data);
                    //由于当我们设置html内容时,放回的是当前对象,所以可以使用方法链进行操作减少代码量
                    $('#result').html(data.msg).css('color',data.success== true ? 'green':'red');
                })
            })

        })
     </script>
</head>
<body>
   <span id="result"></span><br/>
   用户名:<input type="text" name="username" id="username"></body>
</html>
//注意ajax是异步请求,所以不是通过表单form,通过表单form提交的是同步请求
@Controller
public class CheckController {

    //根据用户传入的用户名信息来返回封装好的json结果集对象
    @RequestMapping("/checkName")
    @ResponseBody
    public jsonResult checkName(String username){
        jsonResult jsonResult = new jsonResult();
        System.out.println(username);
        if ("admin".equals(username)){
            jsonResult.setMsg("用户名已经被注册");
            jsonResult.setSuccess(false);
        }else {
            jsonResult.setMsg("恭喜注册成功");
            jsonResult.setSuccess(true);
        }
        return jsonResult;

    }
}
11.5.3 练习 - 二级联动
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>二级联动</title>
    <script src="/static/jQuery-1.11/jquery-1.11.3.min.js"></script>
    <script>
        $(function () {
            //1.当页面加载完的时候,拿到所有省份信息   给对应的省份下拉框设置儿子option

            //2.当下拉框改变的时候,获取对应的省份  通过省份获取对应的城市
            var $c = $('#c');
            var $p = $('#p');

            //当页面加载完的时候,拿到所有省份信息
            //给对应的省份下拉框设置儿子option
            $.get("/province",function (data) {
               data.forEach(function (value) {
                    $p.append('<option value="'+value.id+'">'+value.name+'</option>');
               })
            })
            
            //当省份下拉框改变时,加载对应的城市
            $p.change(function () {
                var id =$p.val();
                var  param = {id : id};
                //在增加前把之前的清除掉,否则城市一直叠加
                $c.empty(); //断子绝孙
                $c.append('<option value="-1">请选择</option>');
                if (id >= 1){
                    $.get('/city',param,function (data) {
                        data.forEach(function (value) {
                            $c.append('<option value="'+value.id+'">'+value.name+'</option>');
                        })
                    })
                }
            })
        })
    </script>
</head>
<body>

省份:<select id="p">
    <option value="-1">请选择</option>
</select>

城市:<select id="c">
    <option value="-1">请选择</option>
</select></body>
</html>
@Controller
public class ProvinceController {

    //获取所有省份的json数据
    @RequestMapping("/province")
    @ResponseBody
    public List<Province> getProvince(){
        List<Province> allProvince = Province.getAllProvince();
        return allProvince;
    }

    //获取对应省份的所有城市的json数据
    @RequestMapping("/city")
    @ResponseBody
    public List<City> getCity(Long id){
        List<City> cities = City.getCityByProvinceId(id);
        return cities;
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值