框架阶段总结(华清远见)

1.框架(framework)

根据一些编程思想, 把一些功能封装起来,发布为jar包,供其他程序员使用,这样的一些软件,被称为框架, 比如spring , spring mvc , mybatis , hibernate , spring boot 等。

2.spring框架

2.1 地址

官网地址:https://spring.io/

spring学习地址: https://docs.spring.io/spring-framework/docs/current/reference/html/

2.2 spring主要特点

  • Inversion of Control (IoC) : 控制反转 , spring框架创建对象,管理对象,以及管理对象之间的依赖关系。

  • dependency injection (DI) :依赖注入, 通过构造函数,或set方法, 给对象的属性赋值。IoC是通过DI来实现的。

  • bean: java类型 , 由spring框架创建的对象,就被称为bean (pojo: 纯java类(私有的属性,公开的get/set方法这种类。))

  • Aspect-Oriented Programming (AOP): 面向切面编程 , 对比OOP(面向对象编程)

2.3 spring框架使用

1.导入框架需要的jar包

<!-- spring mvc的jar包-->
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-webmvc</artifactId>
	<version>5.3.21</version>
</dependency>

2.创建spring框架的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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans.xsd">
	<!-- id: 给bean设置的唯一标志, 可以通过id ,找到这个bean对象
	class: 类的全名,用于表示bean的类型。
	*** 如果没有指定使用某个构造函数,那么默认用无参构造函数
	-->
	<bean id="p1" class="com.hqyj.entity.Phone"></bean>
</beans>

3.定义一个java类, 然后在spring的xml文件中配置bean对象

java类需要提供无参构造函数

public class Phone {
	private String pinPai; // 品牌
	private Double price ; // 价格
	private Integer mem ; // 内存
	// get/set方法
}

spring.xml中bean的配置

<bean id="p1" class="com.hqyj.entity.Phone"></bean>

4.创建spring的容器,然后在容器中获取对象,使用对象
在这里插入图片描述
测试类

@Test
public void getPhone(){
	// 1. 创建出spring的容器对象
	ApplicationContext ac = new ClassPathXmlApplicationContext("springday1.xml" );
	System.out.println("ac:" + ac);
	// 2. 通过容器对象,获取bean。
	// getBean(bean的id, bean的类型)
	// Phone p1 = ac.getBean("p1", Phone.class);
	// Object p1 = ac.getBean("p1");// 根据id查找,但是没有设置查找的结果的类型,所以返回Object.
	Phone p1= ac.getBean(Phone.class); //根据bean的类型查找, 如果有多个符合条件的类型,就出错。
	// 3. 使用对象
	p1.setPinPai("vivo");
	System.out.println(p1.getPinPai());
	// *** 类型
	System.out.println(Phone.class); //class com.hqyj.entity.Phone
	System.out.println(ApplicationContext.class);// interfaceorg.springframework.context.ApplicationContext
	System.out.println(p1.getClass());//class com.hqyj.entity.Phone
	// --- 编译的时候 ac看引用的类型,是ApplicationContext接口。
	// --- 程序运行的时候, ac看实际指向的对象的类型, 是
	ClassPathXmlApplicationContextSystem.out.println(ac.getClass());//class org.springframework.context.support.ClassPathXmlApplicationContext
	// if(p1.getClass()== Phone.class) : 判断是否为同一种类型
	// 4. 容器关闭
}

2.4 spring中三种创建对象的配置

java中创建对象的方式,对应为在spring中bean的配置。

<!-- 创建对象方式:1. 调用构造函数-->
<bean id="date" class="java.util.Date"></bean>
<!-- 创建对象方式:2. 调用自己的静态方法:
factory-method="getInstance" ,factory-method属性,用于指定需要调用的静态方法的名字
-->
<bean id="cal" class="java.util.Calendar" factory-method="getInstance">
</bean>
<!-- 创建对象方式:3. 调用容器中的其他bean的方法,创建对象:
factory-bean="cal" , factory-bean指定调用的容器中的其他bean的名字。
factory-method="getTime" ,factory-method属性,用于指定需要调用的bean的方法的名字-->
<bean id="date1" class="java.util.Date" factory-bean="cal" factory-method="getTime"></bean>

2.5 对象初始化

  • 自己初始化
// 对象的初始化方式:
// 1. 构造函数初始化
// Phone phone = new Phone();
Phone phone = new Phone("iphone" , 100.0 , 128);
System.out.println(phone.getPinPai() +"-" + phone.getPrice() + "-"
+ phone.getMem());
// 2. 调用set方法初始化
Phone phone1 = new Phone();
phone1.setPinPai("小米");
phone1.setPrice(200.0);
phone1.setMem(128);
System.out.println(phone1.getPinPai() +"-" + phone1.getPrice() + "-" + phone1.getMem());
  • spring容器初始化

2.6 bean标签的属性设置

  • id : bean的唯一标志
  • class :bean的类型

  • factory-method :调用工厂方法,创建对象

  • factory-bean :调用创建对象的bean , 然后再factory-method 设置需要调用的具体的方法。

  • scope:

<!-- 通过scope设置bean对象时单例还是非单例,
prototype : 非单例, 每次获取到的对象都是一个新的对象。
singleton :单例, 容器创建好这个对象之后,每次你使用都是同一个对象。
-->
<bean id="test" class="com.hqyj.entity.Phone" scope="prototype"></bean>
<bean id="test1" class="com.hqyj.entity.Phone" scope="singleton"></bean>
  • 初始化方法和销毁方法的设置:
<!-- init-method="init" : 创建对象的时候,执行init方法,destroy-method="destroy" : 容器关闭,bean对象就调用其destroy方法,默认:scope="singleton"
-->
<bean id="example" class="com.hqyj.entity.ExampleBean" init-method="init" scope="prototype" destroy-method="destroy"></bean>
  • 测试类
package com.hqyj;
import com.hqyj.entity.ExampleBean;
import com.hqyj.entity.Phone;
import com.hqyj.entity.SingleA;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestBean {
	ClassPathXmlApplicationContext ac;
	@Before // @Test方法执行之前,先执行@Before修饰的方法
	public void begin(){
		ac=new ClassPathXmlApplicationContext("springday1-pm.xml");
	}
	@After// @Test方法执行之后,再执行@Before修饰的方法
	public void end(){
		ac.close();
	}
	@Test
	public void testExample(){
		ExampleBean example = ac.getBean("example",ExampleBean.class);
		for(int i = 0 ; i <10 ; i ++){
			example.service();
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

2.7 单例程序

程序1

// 单例的程序 : 构造函数私有化,提供一个静态方法,返回对象。
public class SingleA {
	private static SingleA sa;
	private SingleA(){}
	public static synchronized SingleA getInstance(){
		if(sa == null){
			sa = new SingleA();
		}
		return sa;
	}
}

程序2

// 单例的程序 : 构造函数私有化,提供一个静态方法,返回对象。
class SingleB {
	private static SingleB sb = new SingleB();
	private SingleB(){}
	public static synchronized SingleB getInstance(){
		return sb;
	}
}

测试

@Test
public void testA(){
	SingleA a1 = SingleA.getInstance();
	SingleA a2 = SingleA.getInstance();
	System.out.println(a1);
	System.out.println(a2);
	System.out.println(a1==a1);
}

2.8 bean的lazy-init属性

<!-- lazy-init: 设置bean的创建时机,默认是false.
* true: 表示当需要第一次使用这个对象的时候,才创建这个对象
* false: 表示创建spring的容器的时候,就创建对象。
-->
<bean id="e1" class="com.hqyj.entity.ExampleBean" init-method="init" destroy-method="destroy" scope="singleton" lazy-init="true"></bean>

2.9 bean的autowire属性

  • autowire:自动装配, 为了减少构造器函数初始化对象和set注入初始化对象, 可以使用autowire , 让spring框架的容器对象,自动匹配容器中的其他bean对象,然后给bean进行初始化

  • autowire: 自动装配, 根据spring容器中的对象的id 或对象的类型 ,按照自动装配的规则,给对象的属性初始化。
    ** constructor :按构造器函数进行匹配
    ** byType : set注入 ,调用无参构造函数,然后根据容器中的对象的类型和类的属性的类型,调用set方法 ,如果有多个符号条件的类型,则抛出异常
    ** byName : set注入 ,调用无参构造函数,然后根据容器中的对象的id和类的属性的属性名 ,调用set方法, 如果id匹配成功,但是类型不正确,则抛出异常。
    ** byType , byName使用的时候,对于容器中的jdk提供的类型不能自动装配
    
  • spring.xml

    <!-- <bean id="p1" class="com.hqyj.entity.Phone" autowire="constructor"></bean>-->
    <!-- <bean id="p1" class="com.hqyj.entity.Phone"-->
    <!-- autowire="byType"></bean>-->
    <bean id="p1" class="com.hqyj.entity.Phone" autowire="byName"></bean>
    <bean id="pinpai" class="java.lang.String">
    	<constructor-arg index="0" value="华为888"></constructor-arg>
    </bean>
    <bean id="price" class="java.lang.Double">
    	<constructor-arg index="0" value="333.33"></constructor-arg>
    </bean>
    <bean id="mem" class="java.lang.Integer">
    	<constructor-arg index="0" value="256"></constructor-arg>
    </bean>
    <bean id="sh" class="com.hqyj.entity.PhoneShell">
    	<property name="color" value="red"></property>
    	<property name="size" value="14"></property>
    </bean>
    <bean id="phoneShell" class="com.hqyj.entity.PhoneShell">
    	<property name="color" value="绿色"></property>
    	<property name="size" value="10"></property>
    </bean>
    
  • byName
    在这里插入图片描述

  • byType
    在这里插入图片描述

2.10 java中各类型在bean对象中的使用

  • null

    使用null标签

    <bean id="sh" class="com.hqyj.entity.PhoneShell">
    	<property name="color">
    		<null></null>
    	</property>
    </bean>
    
  • 基本数据类型和包装类

    使用value属性,自动类型转换;也可以使用ref引用一个存在的bean对象

    <bean id="sh" class="com.hqyj.entity.PhoneShell">
    	<property name="size" value="14"></property>
    </bean>
    
  • String

    使用value属性,也可以使用ref引用一个存在的bean对象。

    <bean id="sh" class="com.hqyj.entity.PhoneShell">
    	<property name="name" value="tom"></property>
    </bean>
    
  • 普通引用类型

    使用ref引用一个存在的bean对象

    <!-- set注入: 调用set方法,给属性赋值
    property : 属性, 表示给对象的属性赋值。 name是属性名, value/ref是对应的属性值-->
    <bean id="person1" class="com.hqyj.entity.Person">
    	<property name="name" value="tom"></property>
    	<property name="phone" ref="phone"></property>
    	<property name="age" value="19"></property>
    </bean>
    
  • 复杂引用类型

    • List

      <property name="firends">
      	<list>
      		<value>tom</value>
      		<ref bean="name"></ref>
      		<value>jack</value>
      		<value>rose</value>
      	</list>
      </property>
      
    • Map

      <property name="banks">
      	<map>
      		<entry key="工商银行" value="123456"></entry>
      		<entry key-ref="zhaoshang" value-ref="zscard"
       </entry>
      	</map>
      </property>
      
    • Set

      <property name="hobby">
      	<set>
      		<value>钓鱼</value>
      		<value>唱歌</value>
      		<value>看电影</value>
      	</set>
      </property>
      
    • Properties

      <property name="contact">
      	<props>
      		<prop key="email">fjm@qq.com</prop>
      		<prop key="phone">151522222</prop>
      		<prop key="qq">39778</prop>
      	</props>
      </property>
      

2.11 spring注解的使用

  • 开启注解

    <?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
    		https://www.springframework.org/schema/context/spring-context.xsd">
    	<!-- 开启组件扫描功能, 指定组件扫描的包 :
    	 	base-package="com.hqyj.dao" 
    	 	表示能扫描到的包是com.hqyj.dao及其子包组件: 可以理解为一个bean, 容器中创建的对象。
    	-->
    	<context:component-scan base-package="com.hqyj.dao"></context:component-scan>
    	<!-- 表示支持注解的使用,如果设置了包扫描(context:component-scan),就默认开启了支持注
    	解的功能 -->
    	<context:annotation-config></context:annotation-config>
    </beans>
    
  • spring容器创建对象的注解

    @Component : 在类前面添加这个注解,spring框架,看到这个注解,就会在spring容器中创建对象

    // Component-- 组件
    //@Component // 注解的功能: 通知spring框架,创建对象 ,默认的id,就是类名首字母小写。
    // 简化xml中的bean的配置
    @Component(value = "yy") // 设置了bean的id为yy.
    public class EmpDao {
    	public void add(){
    		System.out.println("add...");
    	}
    	public void delete(){
    		System.out.println("delete...");
    	}
    }
    

    @Configuration:在类前这个注解,spring框架,看到这个注解,就会在spring容器中创建对象

    @Configuration // config -配置 , 这个注解一般用于设置一些配置内容。
    // 本质就是让spring框架,创建了对象。
    public class BeanTest {
    	@Bean // 表示这里会创建一个bean对象 ,容器会调用方法,把返回值
    	// 保存在容器中 ,这个bean对应的id,就是方法的名字
    	public String getStr(){
    		return new String("hello");
    	@Bean // 表示这里会创建一个bean对象 ,容器会调用方法,把返回值
    	// 保存在容器中
    	public String getStr1(){
    		return new String("你好");
    	}
    	@Bean // 表示这里会创建一个bean对象
    	public Date getDate(){
    		return new Date();
    	}
    }
    

    @Bean:一般在方法前面添加这个注解,spring框架会在容器中创建这个对象

    @Bean // 表示这里会创建一个bean对象
    public Date getDate(){
    	return new Date();
    }
    

    一些其他添加在类前面的注解,可以让框架创建对象

    @Service ,@Controller ,@Repository

  • 对象属性初始化的注解

    @Value: 对于8种特殊数据类型和String,可以使用@Value(“xx”)的方式赋值 也可以读取属性文件 的值 @Value(“${key}”)

    @Component // spring容器创建对象的注解
    @Data
    @ToString
    public class JDBCConn {
    	@Value("root") // 直接给user属性赋值为root. 每个注解只对应一个属性,或者一个方法,或者一个类。
    	private String user;
    	@Value("123456")
    	private String pwd;
    	@Value("${mydb.url}") // 获取到属性文件的value值 ,赋值给属性url.
    	private String url;
    	@Value("${mydb.driver}")
    	private String drvier;
    }
    
    <!--加载属性文件, 属性文件中的内容可以通过${key}的方式获取。-->
    <context:property-placeholder location="mydb.properties">
    </context:property-placeholder>
    

    @Resource (javax.annotation.Resource;) : 对于应用类型,可以采用自动装配的功能,让spring 容器根据容器中的对象,根据情况赋值。

    @Component
    @Data
    @ToString
    public class EmpDao {
    	@Value("true")
    	Boolean flag ;
    	@Resource // 自动装配, 在spring的容器中,找JDBCConn对象,找到了就给属性赋值。
    	JDBCConn jdbcConn ;
    	@Resource // 自动装配, 在spring的容器中,找Date对象,找到了就给属性赋值。
    	Date date ;
    	/*
    	@Resource// 自动装配, 优先看bean名字(名字相同并且类型同,就赋值),
    	// 没有找到bean名字同, 就找类型,如果有多个类型都符合,就抛异常
    	// 如果只有一个类型相同,就给属性赋值。
    	String say;
    	*/
    	@Resource
    	@Qualifier("getStr1") // 指定找bean的名字是getStr1的bean。
    	String say;
    	public void add(){
    		System.out.println("add...");
    	}
    	public void delete(){
    		System.out.println("delete...");
    	}
    }
    

    @Qualifier(bean的id ) : 为了避免spring容器根据类型查找的时候,出现多个匹配的类型,造成异 常,可以使用该注解,主动设置好需要自动装配的bean的id

    @Resource
    @Qualifier("getStr1") // 指定找bean的名字是getStr1的bean。
    String say;
    

    @Autowired: 自动装配

2.12 spring整合junit

  • 导入包

    spring-test包依赖junit的4.12版本的包,所以需要先导入junit4.12的包

    <dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-test</artifactId>
    	<version>5.3.21</version>
    </dependency>
    
  • 测试类的注解

    • 创建出容器对象

      @RunWith(SpringJUnit4ClassRunner.class) , 该注解会在调用的类 SpringJUnit4ClassRunner 中创建出spring的容器对象 。 @ContextConfiguration(“classpath:springday3.xml”) ,容器对象在创建的时候,需要加载的spring配置文件的路径,便于创建spring容器对象的时候,使用到该配置文件。

      @RunWith(SpringJUnit4ClassRunner.class) // 测试相关类的引入
      @ContextConfiguration("classpath:springday3.xml") // 告知spring的容器对象,需要加载的spring配置文件的路径
      public class TestStu {
      }
      
    • 自动装配bean对象

      @RunWith(SpringJUnit4ClassRunner.class) // 测试相关类的引入
      @ContextConfiguration("classpath:springday3.xml") // 告知spring的容器对象,需要加载的spring配置文件的路径
      public class TestStu {
      	@Autowired // 自动装配
      	Student stu ;
      	@Autowired
      	Student1 stu1;
      	@Autowired
      	List<String> list;
      }
      
  • 测试方法

    @RunWith(SpringJUnit4ClassRunner.class) // 测试相关类的引入
    @ContextConfiguration("classpath:springday3.xml") // 告知spring的容器对象,需要加载的spring配置文件的路径
    public class TestStu {
    	@Autowired // 自动装配
    	Student stu ;
    	@Autowired
    	Student1 stu1;
    	@Autowired
    	List<String> list;
    	@Test
    	public void test1(){
    		System.out.println(list);
    		Assert.assertNotNull(list);
    	}
    	@Test
    	public void testC(){
    		// System.out.println(stu);
    		System.out.println("stu1:" + stu1);
    	}
    }
    

2.13 @Autowired

@Autowired注解,是spring框架提供的自动装配的注解。 可以byName, byType查找对象,然后自动set注入。

  • 通过required=true/false属性,指定是否必须要执行自动装配功能

    @Autowired(required = true) // 容器中有,则装配,没有则抛异常。
    @Autowired(required = false) // 容器中有,则装配,没有则不装配。
    
  • 结合Qualifier使用,指定必须装配的bean的id, 避免因为多个类型相同,框架不知道要装配那个 bean对象的问题。

    @Autowired 
    @Qualifier("cat") // 指定使用bean的id是cat的对象,进行装配
    
  • @Autowired可以放在属性前, 方法前,构造函数前

    • 属性前

      @Autowired
      private Dog dog ;
      
    • 方法前

      @Autowired
      public void setGetCat(Cat getCat) {
      	this.getCat = getCat;
      }
      
    • 构造函数前

      @Autowired
      public Student1( @Value("alice")String name,
                      @Value("202210001")Integer code,
                      @Value("女") Character sex, 
                      Cat getCat, Dog dog) {
      	this.name = name;
      	this.code = code;
      	this.sex = sex;
      	this.getCat = getCat;
      	this.dog = dog;
      }
      
  • 复杂类型的自动装配,泛型优先。

    @Autowired // 自动装配的时候,优先以泛型为准。 如果没有一个符合泛型的bean, 则找List类型(byName, byType)
    List<String> aa;
    

    如果spring容器中有复杂类型对应的对象,最好使用@Qualifier(“xx”) 的方式,指定好具体要装配的bean对象。

    @Autowired // 自动装配的时候,优先以泛型为准。 如果没有一个符合泛型的bean, 则找List类型
    @Qualifier("list")
    List<String> aa;
    

2.14 bean相关的其他的注解

id , class , init-method , destroy-method , scope , lazy-init…

  • @Component(“exam”) // id ,class

  • @Scope , 设置单例,非单例

  • @Lazy ,设置懒加载

@Component("exam") // id ,class
@Scope(value = "singleton") // 单例,默认
//@Scope(value = "prototype") // 非单例 ,也叫多例
@Lazy(value = true) // 懒加载
//@Lazy(value = false) // 非懒加载 ,默认
public class ExampleBean {
	public ExampleBean(){
		System.out.println("构造函数");
	}
	@PostConstruct // 初始化执行
	public void begin(){
		System.out.println("init .....");
	}
	public void service(){ // 普通方法,可以自己反复调用
		System.out.println("service.....");
	}
	@PreDestroy// 销毁执行
	public void destroy(){
		System.out.println("destroy......");
	}
}

2.15 aop编程

aop: 面向切面编程

oop:面向对象编程

  • 导入aop相关的jar包

    spring-webmvc中包含了spring-aop的包

    <!-- 导入aop相关的jar包-->
    <dependency>
    	<groupId>org.aspectj</groupId>
    	<artifactId>aspectjtools</artifactId>
    	<version>1.9.4</version>
    </dependency>
    
    
  • 抽取公共功能,封装为类

    logging.java

    package com.hqyj.dao;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;
    import org.springframework.stereotype.Component;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    @Component
    public class Logging {
    	// *** 公共功能: 希望在方法执行之前,显示方法执行的时间。
    	// -- Joinpoint 连接点 (可以理解为正在调用showTime的方法相关的对象)
    	public void showTime(JoinPoint joinpoint){
    		String methodName = joinpoint.getSignature().getName();// 正在被执行的方法的名字
    		Date time = new Date();
    		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    		String info = sdf.format(time);
    		System.out.println( methodName + "方法开始执行:" + info);
    	}
    	// ** 公共功能: 统计出每个方法执行消耗的时长。
    	public Object exeTime(ProceedingJoinPoint joinPoint) throws Throwable {
    		long begin = System.currentTimeMillis();
    		// 调用正在执行的方法
    		Object proceed = joinPoint.proceed();
    		long end = System.currentTimeMillis();
    		long cha = end - begin;
    		System.out.println(joinPoint.getSignature().getName() + ":执行消耗时间" + cha + "毫秒");
    		//return proceed;
    		return proceed;
    	}
    	// 练习: 方法执行结束之后,输出方法的返回值
    	public void returnVal(JoinPoint joinPoint , Object obj){
    		// obj表示方法的返回值
    		System.out.println(joinPoint.getSignature().getName() + "返回值:" + obj);
    	}
    }
    

    目标类

    package com.hqyj.dao;
    import org.springframework.stereotype.Component;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.List;
    // 要求: 1.dao中的每个方法执行之前,记录执行的信息: updateEmp方法开始执行:2022-9-16 14:08:39
    // 2.dao中的每个方法,执行时间的统计。
    // 解决方式1 : 在每个方法内部,添加这一段功能相关的代码
    // 解决方式2 : 把公共的功能,封装成方法, 然后在需要这个公共功能的地方,就调用这个方法。
    // aop的思想: 面向切面编程 , 抽取项目中的公共的功能,单独封装。然后使用aop的配置 ,
    // 通过动态代理的方式,实现封装的功能的调用。
    @Component
    public class EmpDao {
    	public Integer addEmp(){
    		System.out.println("addEmp ...... ");
    		return 1;
    	}
    	public Integer updateEmp(){
    		System.out.println("updateEmp ...... ");
    		return 1;
    	}
    	public Integer deleteEmp(){
    		System.out.println("deleteEmp ...... ");
    	return 1;
    	}
    	public List<String> queryEmp(){
    		System.out.println("queryEmp ...... ");
    		return null;
    	}
    }
    
  • aop的配置

    <?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"
    	xmlns:aop="http://www.springframework.org/schema/aop"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans
    		http://www.springframework.org/schema/beans/spring-beans.xsd
    		http://www.springframework.org/schema/context
    		https://www.springframework.org/schema/context/spring-context.xsd
    		http://www.springframework.org/schema/aop
    		https://www.springframework.org/schema/aop/spring-aop.xsd">
    	<!-- 开启组件扫描功能: 指定扫描的包-->
    	<context:component-scan base-package="com.hqyj"></context:component-scan>
    	<context:annotation-config/>
    	<aop:config>
    		<!--aspect: 切面 , 配置的是封装公共功能的对象。-->
    		<aop:aspect ref="logging">
    			<!-- pointcut: 切入点 , 通过一个表达式配置需要调用公共方法的类的方法。
    			execution(public * com.hqyj.dao.*.*(..)) : 通过这个表达式,找到要调用的方法
    			第一个* :表示方法的返回值
    			com.hqyj.dao.* : 这个星号表示所有类。
    			com.hqyj.dao.*.*: 第二个星号表示所有方法。
    			(..) :表示任意参数-->
    			<aop:pointcut id="all" expression="execution(public * com.hqyj.dao.*.*(..))"/>
    			<!-- pointcut-ref="all" : 表示切入点引用all.
    			before: 表示前置通知,当方法执行之前,先执行这个前置通知对应的method.-->
    			<aop:before method="showTime" pointcut-ref="all"></aop:before>
    			<aop:around method="exeTime" pointcut-ref="all"></aop:around>
    			<aop:after-returning method="returnVal" pointcut-ref="all" returning="obj"/>
    		</aop:aspect>
    	</aop:config>
    </beans>
    
  • aop的注解

package com.hqyj.dao;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
@Component
@Aspect // 切面的设置
@EnableAspectJAutoProxy // 开启aop的自动代理功能
public class Logging1 {
	// *** 公共功能: 希望在方法执行之前,显示方法执行的时间。
	// public * com.hqyj..*.*(..) --- com.hqyj包
	// public * com.hqyj.dao.*.*(..) --- com.hqyj.dao包
	@Before("execution(public * com.hqyj.dao.*.*(..))")
	public void showTime(JoinPoint joinpoint){
		String methodName = joinpoint.getSignature().getName();// 正在被执行的方法的名字
		Date time = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		String info = sdf.format(time);
		System.out.println( methodName + "方法开始执行:" + info);
	}
	@Around("execution(public * com.hqyj.dao.*.*(..))") // ** 公共功能: 统计出每个方法执行消耗的时长。
	public Object exeTime(ProceedingJoinPoint joinPoint) throws Throwable {
		long begin = System.currentTimeMillis();
		Object proceed = joinPoint.proceed(); // 调用正在执行的方法
		long end = System.currentTimeMillis();
		long cha = end - begin;
		System.out.println(joinPoint.getSignature().getName() + ":执行消耗时间" + cha + "毫秒");
		return proceed;
	}
	@AfterReturning(value = "execution(public * com.hqyj.dao.*.*(..))",returning = "obj")
	public void returnVal(JoinPoint joinPoint , Object obj){
		// obj表示方法的返回值
		System.out.println(joinPoint.getSignature().getName() + ":" + obj);
	}
}

3.spring mvc框架

3.1 拦截器的使用

  • 定义拦截的类型

    implements HandlerInterceptor , 重写方法

    package com.hqyj.interceptor;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    public class LoginInterceptor implements HandlerInterceptor {
    	@Override
    	// preHandle方法。当发起请求的时候,就会进入到拦截器中的preHanle方法的运行。
    	// 如果preHandle方法返回值是true,表示不被拦截,继续访问请求。 返回false,表示被拦截了, 不能访问请求了。
    	public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws Exception {
    		System.out.println("进入拦截器了:preHandle。。。。。。。。。");
    		HttpSession session = request.getSession();
    		Object loginuser = session.getAttribute("loginuser");
    		if(loginuser == null){// 没有登陆过
    			response.sendRedirect(request.getContextPath() +"/toLogin"); // 重定向到登陆界面
    			return false;
    		}else{//登陆过
    			return true;
    		}
    	}
    	@Override
    	public void postHandle(HttpServletRequest request, HttpServletResponse response,Object handler, ModelAndView modelAndView) throws Exception {
    		System.out.println("postHandle............");
    	}
    }
    
  • 拦截器的配置

    xml配置拦截器

    <mvc:interceptors>
    	<!-- 拦截器的配置-->
    	<mvc:interceptor>
    		<!-- 表示,所有的路径都要被拦截-->
    		<mvc:mapping path="/**"/>
    		<!-- exclude: 例外 , 表示那些请求不被拦截-->
    		<mvc:exclude-mapping path="/toLogin"/>
    		<mvc:exclude-mapping path="/login"/>
    		<!-- 静态资源不被拦截-->
    		<mvc:exclude-mapping path="/js/**"/>
    		<mvc:exclude-mapping path="/html/**"/>
    		<!-- 拦截器对应的类型-->
    		<bean class="com.hqyj.interceptor.LoginInterceptor"></bean>
    	</mvc:interceptor>
    	<mvc:interceptor>
    		<!-- 表示只拦截/emp/** -->
    		<mvc:mapping path="/emp/**"/>
    		<bean class="com.hqyj.interceptor.RoleInterceptor"></bean>
    	</mvc:interceptor>
    </mvc:interceptors>
    

    java类中配置拦截器

    要求:在spring mvc的配置文件中不能出现mvc:annotation-driven标签。

    @Configuration // 配置文件的注解
    @EnableWebMvc // 替代配置文件中的:<mvc:annotation-driven></mvc:annotation-driven>
    public class MVCConfig implements WebMvcConfigurer {
    	@Override // 拦截器的设置
    	public void addInterceptors(InterceptorRegistry registry) {
    		registry.addInterceptor(new LoginInterceptor())
    				.addPathPatterns("/**")
    				.excludePathPatterns("/toLogin")
    				.excludePathPatterns("/login")
    				.excludePathPatterns("/js/**")
    				.excludePathPatterns("/html/**");
    		registry.addInterceptor(new RoleInterceptor())
    				.addPathPatterns("/emp/**");
    	}
    }
    

3.2 文件上传

  • 导入jar包

    <dependency>
    	<groupId>commons-fileupload</groupId>
    	<artifactId>commons-fileupload</artifactId>
    	<version>1.4</version>
    </dependency>
    
  • spring.xml中的配置

    <!-- 文件上传相关bean对象
    *** bean的id 是 multipartResolver
    -->
    <bean id="multipartResolver"class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    	<!-- set注入,设置了上传的文件的最大字节数-->
    	<property name="maxUploadSize" value="100000000"></property>
    </bean>
    
  • 界面要求

    表单提交方式为post , 并且设置enctype=“multipart/form-data”

    <!DOCTYPE html>
    <html lang="en">
    <head>
    	<meta charset="UTF-8">
    	<title>Title</title>
    </head>
    <body>
    	<h1>注册</h1>
    	<form action="../reg" method="post" enctype="multipart/form-data">
    		<label>用户名:</label>
    		<input type="text" name="nickname"><br>
    		<label>邮箱:</label>
    		<input type="text" name="email"><br>
    		<label>头像:</label>
    		<input type="file" name="photo"><br>
    		<input type="submit" value="注册">
    		<input type="reset" value="重置">
    	</form>
    </body>
    </html>
    
  • controller

    请求参数中的文件类型,需要使用 MultipartFile 类型接收, 然后上传文件。

    @Controller
    public class FileUploadController {
    	@Value("${myfilepath}")
    	String filePath;
    	@Value("${myimgurl}")
    	String imgUrl;
    	@RequestMapping(value = "/reg" , method = RequestMethod.POST)
    	public String reg(String nickname , String email ,MultipartFile photo , Model model) {
    		// 1. 文件后缀名
    		String filename = photo.getOriginalFilename();
    		String suffix = filename.substring(filename.indexOf("."));
    		// 2. 文件新名字 : 保证名字不重复
    		UUID uuid = UUID.randomUUID(); // 生成一个包含不重复字符串的对象
    		String newname = uuid.toString() + suffix ;
    		// 3. 把文件上传到指定的硬盘目录
    		try {
    			photo.transferTo(new File(filePath + newname));
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    		// 4. 把上传到硬盘的地址,映射为一个可以访问的网络地址,
    		// -- 然后这里把文件的网络地址,转发到jsp页面上。
    		String imgsrc = imgUrl + newname;// 文件访问的网络地址
    		model.addAttribute("imgsrc" , imgsrc);
    		model.addAttribute("regname" , nickname);
    		return "showinfo";
    	}
    }
    
  • 硬盘上的静态资源,映射为一个请求地址。

    选择tomcat的外部资源
    在这里插入图片描述
    选择需要映射路径的文件夹
    在这里插入图片描述
    修改映射的路径
    在这里插入图片描述

3.3 spring中的中文乱码的过滤器配置

post请求的时候,可能出现中文乱码,可以在web.xml中配置spring 框架提供的Filter。

<!-- spring框架提供的,用于解决中文乱码的过滤器-->
<filter>
	<filter-name>character</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>
	<init-param>
		<param-name>forceRequestEncoding</param-name>
		<param-value>true</param-value>
	</init-param>
</filter>
<filter-mapping>
	<filter-name>character</filter-name>
	<!-- / , 除了jsp
		/* , 所有的
		*.do , 按后缀匹配-->
	<url-pattern>/*</url-pattern>
</filter-mapping>

3.4 软件的国际化(i18n)

  • 在resource目录中创建属性文件。

    默认的源文件名message.properties

    其他资源文件的命名:message开始,用下划线连接地区和语言对应的编码。

    message.properties

    user.name=user name
    user.pwd=user password
    user.reg.success=reg success
    user.title=welcome reg!
    user.commit=ok!
    user.reset=cancel!
    user.login= login view
    

    message_en_US.properties

    user.name=user name
    user.pwd=user password
    user.reg.success=reg success
    user.title=welcome reg!
    user.commit=ok!
    user.reset=cancel!
    user.login= login view
    

    message_zh_CN.properties

    user.name=用户名
    user.pwd=密码
    user.reg.success=欢迎使用
    user.title=登陆成功
    user.commit=提交
    user.reset=重置
    user.login=登陆界面
    
  • 避免中文乱码
    在这里插入图片描述

  • 国际化相关的bean配置

    property name=“basename” value=“message”,这个value值可以设置为需要的指定的属性文件

    <!--
    	支持国际化的bean对象的配置:
    	id必须是"messageSource"
    -->
    <bean id="messageSource"class="org.springframework.context.support.ResourceBundleMessageSource">
    	<!-- 资源文件的名字(不包含后缀名)-->
    	<property name="basename" value="message"></property>
    </bean>
    
  • 在jsp页面的使用

    导入spring的标签库,使用spring:message标签获取到属性文件的数据值

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%--导入spring的标签库--%>
    <%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
    <html>
    <head>
    	<%-- spring标签的使用:读取到属性文件中的字符串,显示在指定位置--%>
    	<title><spring:message code="user.login"/></title>
    </head>
    <body>
    	<form action="i18nlogin" method="get">
    		<label><spring:message code="user.name"/></label>
    		<input type="text" name="uname"><br>
    		<label><spring:message code="user.pwd"/></label>
    		<input type="text" name="pwd"><br>
    		<input type="submit" value="<spring:message code="user.commit"/>">
    		<input type="reset" value="<spring:message code="user.reset"/>">
    	</form>
    </body>
    </html>
    
  • 在Controller中的使用

    @Controller
    public class I18nController {
    	@Autowired
    	ResourceBundleMessageSource messageSource; // 可以读取属性文件中的内容
    	@RequestMapping("/toi18n")
    	public String toLogin(){
    		return "i18n_login";
    	}
    	@RequestMapping("/i18nlogin")
    	public String login(Model model , Locale locale){
    		// 把属性文件中的内容读取出来,赋值给字符串变量。
    		String message = messageSource.getMessage("user.reg.success", null,locale);
    		model.addAttribute("success" , message);
    		return "i18n_show";
    	}
    }
    

3.5 异常处理相关的设置

  • 局部异常处理

    // 局部异常处理方式
    /*
    	@ExceptionHandler// 表示如果EmpController内部的请求发生异常,就执行这里toException方法。
    	public String toException(Exception e){
    		e.printStackTrace(); // 打印异常的堆栈信息
    		System.out.println(e.getCause());
    		return "part"; // 如果请求中发生异常,就转发到part.jsp
    	}
    */
    @ExceptionHandler(NullPointerException.class) // 表示如果EmpController内部的请求发生异常,就执行这里toException方法。
    public String toException(Exception e){
    	e.printStackTrace(); // 打印异常的堆栈信息
    	System.out.println(e.getCause());
    	return "part"; // 如果请求中发生异常,就转发到part.jsp
    }
    
  • 全局异常处理

    // 通过注解设置为全局异常处理的类
    @ControllerAdvice // Controller 控制器 , Advice 通知
    public class GlobalExceptionHandler {
    	@ExceptionHandler
    	public String toException(Exception e){
    		e.printStackTrace();
    		return "global";// 找global.jsp
    	}
    }
    

4.mybatis

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几 乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置 和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的 记录。

mybatis是一个数据库相关的框架,主要为了简化jdbc的操作

mybatis使用了orm, ORM是对象关系映射的英文缩写,ORM是一种程序技术,用于实现面向对象编程语 言里不同类型系统的数据之间的转换 。

4.1 mybatis框架的使用

  • 导入jar包

    数据库jar包,mybatis的jar包

    <dependency>
    	<groupId>mysql</groupId>
    	<artifactId>mysql-connector-java</artifactId>
    	<version>8.0.30</version>
    </dependency>
    <!-- mybatis的包-->
    <dependency>
    	<groupId>org.mybatis</groupId>
    	<artifactId>mybatis</artifactId>
    	<version>3.5.10</version>
    </dependency>
    
  • mybatis的核心配置文件

    数据库连接的环境配置

    日志输出的配置

    需要加载的mapper.xml文件的配置

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
    	PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    	"http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    	<!-- 设置控制台日志输出-->
    	<!-- 配置数据库连接的环境 : 默认使用id为"development"的环境-->
    	<environments default="development">
    		<environment id="development">
    			<transactionManager type="JDBC"/>
    			<dataSource type="POOLED">
    				<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
    				<property name="url" value="jdbc:mysql://localhost:3306/gamedb?serverTimezone=Asia/Shanghai"/>
    				<property name="username" value="root"/>
    				<property name="password" value="123456"/>
    			</dataSource>
    		</environment>
    	</environments>
    	<!-- 引用存在的mapper.xml文件-->
    	<mappers>
    		<mapper resource="mapper/HeroMapper.xml"/>
    	</mappers>
    </configuration>
    
  • 实体类

    package com.hqyj.entity;
    import lombok.Data;
    import lombok.ToString;
    import java.util.Date;
    // 属性名和table-hero的字段名完全一样。
    @Data
    @ToString
    public class Hero {
    	private Integer id;
    	private String name;
    	private String sex;
    	private String position;
    	private Integer price;
    	private Date shelf_date;
    }
    
  • 持久层接口

    package com.hqyj.dao;
    import com.hqyj.entity.Hero;
    import java.util.List;
    // 定义数据库操作的方法
    public interface HeroDao {
    	public abstract int addHero(Hero hero);
    	public abstract int deleteHero(Integer id);
    	public abstract int updateHero(Hero hero);
    	public abstract List<Hero> queryAll();
    }
    
    
  • mapper.xml的配置

    在resource目录中创建一个文件夹mapper。 专门用于存放mapper.xml .

    在mybatis-config.xml中引用mapper.xml

    <mappers>
    	<mapper resource="mapper/HeroMapper.xml"/>
    </mappers>
    

    EntityDaoMapper.xml

    <?xml version="1.0" encoding="utf-8" ?>
    <!DOCTYPE mapper
    	PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    	"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!-- namespace: 命名空间 , 设置mapper.xml文件中的sql 对应的interface。-->
    <mapper namespace="com.hqyj.dao.HeroDao">
    	<!-- insert:表示这是一个插入的sql语句
    	id: 对应dao(interface)中方法的名字
    	parameterType: 设置的dao(interface)中方法的参数
    	insert标签的内部: sql语句
    	sql语句中的#{属性名} , 就是在获取参数对象的属性的值 -->
    	<insert id="addHero" parameterType="com.hqyj.entity.Hero">
    		insert into hero 
            values(null, #{name} , #{sex}, #{position} , #{price} , #{shelf_date})
    	</insert>
    	<!-- 方法的参数是一个,并且属于8种类型 ,就可以省略。
    	也可以设置parameterType="int" ,这里的int就是简写
    	#{id} ,这里的id和方法参数名完全一致, deleteHero(Integer id)-->
    	<delete id="deleteHero">
    		delete from hero where id = #{id}
    	</delete>
    	<!-- price=#{price} , price是列名(table的列名),
    	#{price}(实体类的属性) ,这里是属性值的获取。-->
    	<update id="updateHero" parameterType="com.hqyj.entity.Hero">
    		update hero set `name` = #{name} , sex=#{sex},
    		`position` =#{position } , price=#{price} ,
    		shelf_date=#{shelf_date} where id=#{id}
    	</update>
    <!-- resultType="com.hqyj.entity.Hero":返回值是一个实体,或者泛型为实体的集合,设置的返回值类型都是这个实体类。-->
    	<select id="queryAll" resultType="com.hqyj.entity.Hero">
    		select * from hero
    	</select>
    </mapper>
    
  • mybatis功能的测试

    更新操作一定要commit

    package com.hqyj;
    import com.hqyj.dao.HeroDao;
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    import org.junit.After;
    import org.junit.Before;
    import org.junit.Test;
    import java.io.InputStream;
    public class AppTest{
    	SqlSession sqlSession;
    	// HeroDao --> SqlSession --> SqlSessionFactory ---> io(mybatisconfig.xml)
    	@Before
    	public void createSession() throws Exception{
    		String location = "mybatis-config.xml"; // mybatis配置文件的路径
    		InputStream resourceAsStream = Resources.getResourceAsStream(location);
    		// 创建session工厂类
    		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    		// 获取session对象
    		sqlSession = sqlSessionFactory.openSession();
    	}
    	@After
    	public void closeAndCommit(){
    		sqlSession.commit(); // 提交 (更新必须提交 ,不然数据库并不会写入)
    		sqlSession.close();
    	}
    	@Test
    	public void testQuery(){
    		HeroDao dao = sqlSession.getMapper(HeroDao.class);
    		// ---- 编译的时候是引用类型 , 运行的时候是实际类型
    		System.out.println("dao:" + dao.getClass());
    		dao.queryAll();
    	}
    }
    

4.2 resultMap的使用

如果实体类的属性和表的字段名不一致,可以使用resultMap 设置实体类的属性和表的字段的对应关系。

GoodsInfo.java

@Data
@ToString
public class GoodsInfo {
	// 采用驼峰命名法
	private Integer giId;
	private Integer gtId;
	private String giName;
	private Double giPrice;
	private Integer giNum;
	private String giNote;
	private String giImg;
}

GoodsInfoDao.java

public List<GoodsInfo> queryAll();

GoodsInfoMapper.xml

<!-- 自定义resultMap类型, 通过id,引用这个类型。-->
<resultMap id="GIMap" type="com.hqyj.entity.GoodsInfo">
	<id column="gi_id" property="giId"></id>
	<result column="gt_id" property="gtId" javaType="java.lang.Integer"></result>
	<result column="gi_name" property="giName"></result>
	<result column="gi_price" property="giPrice"></result>
	<result column="gi_num" property="giNum"></result>
	<result column="gi_note" property="giNote"></result>
	<result column="gi_img" property="giImg"></result>
</resultMap>
<!-- 返回值类型设置为resultMap ,引用定义好的resultMap。-->
<select id="queryAll" resultMap="GIMap">
	select * from goods_info
</select>

4.3 方法接口的定义

  • 接口方法的返回值

    // dao中的方法的返回值的情况: void ,int(Integer) --- insert, delete , update
    // 实体类,实体类的集合 --- select
    // Map , List<Map> --- select
    // int(统计行数) ---- select
    

    java

    public List<GoodsInfo> queryAll();
    public GoodsInfo queryByID(Integer giId);
    public Map<String , Object> queryByIDMap(Integer giId);
    public int queryTotal(); // 统计总函数 count(*)
    public List<Map<String ,Object>> queryByGroup();
    public List<GroupGoods> queryByGroupGoods();
    public void addGoodsInfo(GoodsInfo info);
    

    xml

    <!-- 自定义resultMap类型, 通过id,引用这个类型。-->
    <resultMap id="GIMap" type="com.hqyj.entity.GoodsInfo">
    	<id column="gi_id" property="giId"></id>
    	<result column="gt_id" property="gtId" javaType="java.lang.Integer"></result>
    	<result column="gi_name" property="giName"></result>
    	<result column="gi_price" property="giPrice"></result>
    	<result column="gi_num" property="giNum"></result>
    	<result column="gi_note" property="giNote"></result>
    	<result column="gi_img" property="giImg"></result>
    </resultMap>
    <!-- public List<GoodsInfo> queryAll();
    public GoodsInfo queryByID(Integer giId);-->
    <select id="queryAll" resultMap="GIMap">
    	select * from goods_info
    </select>
    <select id="queryByID" resultMap="GIMap" parameterType="int">
    	select gi_name , gi_price , gi_num
    	from goods_info where gi_id= #{giId}
    </select>
    <!-- public int queryTotal(); // 统计总函数 count(*)-->
    <select id="queryTotal" resultType="java.lang.Integer">
    	select count(*) from goods_info
    </select>
    <!-- public Map<String , Object> queryByIDMap(Integer giId);-->
    <select id="queryByIDMap" resultType="map">
    	select gi_name , gi_price , gi_num
    	from goods_info where gi_id= #{giId}
    </select>
    <!-- public List<Map<String ,Object>> queryByGroup();-->
    <select id="queryByGroup" resultType="java.util.Map">
    	SELECT count(goods_info.gt_id) as count , SUM(gi_num) as total
    	, goods_info.gt_id as type,gt_name as name from goods_info , goods_type
    	where goods_info.gt_id = goods_type.gt_id
    	GROUP BY goods_info.gt_id
    </select>
    <!-- public List<GroupGoods> queryByGroupGoods();-->
    <select id="queryByGroupGoods" resultType="com.hqyj.entity.GroupGoods">
    	SELECT count(goods_info.gt_id) as count , SUM(gi_num) as total
    	, goods_info.gt_id as type,gt_name as name from goods_info , goods_type
    	where goods_info.gt_id = goods_type.gt_id
    	GROUP BY goods_info.gt_id
    </select>
    <!-- public void addGoodsInfo(GoodsInfo info);
    *** 希望把插入成功之后的主键值,保存在GoodsInfo对象中,这个案列,就会保存在参数info中。
    suseGeneratedKey="true" keyColumn="gi_id" keyProperty="giId"
    -->
    <insert id="addGoodsInfo" parameterType="com.hqyj.entity.GoodsInfo"
    useGeneratedKeys="true" keyColumn="gi_id" keyProperty="giId">
    	insert into goods_info(gi_id , gt_id , gi_name , gi_price , gi_num ,
    	gi_note , gi_img)
    	values(null , #{gtId} , #{giName} , #{giPrice} ,#{giNum} , #{giNote} ,#{giImg})
    </insert>
    
  • 接口方法的参数

    // dao中方法的参数的情况:
    // 插入(insert) , 更新(update) --- 实体类 , map ,一个参数(8中类型,String)
    // ----多个参数 addGoodsInfo(Integer gi_id , String gi_name , Double
    gi_price ......);
    // 删除(delete) --- integer(主键) , map(多个条件) ,一个参数(8中类型,
    String)
    // 查询(select) --- 实体类, map , 多个参数,一个参数(8中类型,String)
    

    java

    public void updateByWhere(Map map) ; 
    // map添加几个key ,在sql语句中使用 #{key}获取value值
    // 如果方法参数超过一个 ,就需要在参数前添加@Param注解,给参数重命名,在sql
    // 语句中规定,通过#{param注解重命名的key}获取参数值
    public List<GoodsInfo> selectWhere(@Param("gid") Integer id ,
    @Param("type") Integer type ,@Param("jiage") Double price );
    // select * from goods_info where gi_id>#{gid} and gt_id=#{type}
    // and gi_price<#{jiage}
    public void delete(Integer giId);
    

    xml

    <!-- public void updateByWhere(Map map) -->
    <update id="updateByWhere" parameterType="map">
    	update goods_info set
    	gi_num=#{num} where gt_id=#{type}
    </update>
    <!-- public List<GoodsInfo> selectWhere(@Param("gid") Integer id ,
    @Param("type") Integer type ,@Param("jiage") Double price );
    -->
    <select id="selectWhere" resultMap="GIMap" >
    	select * from goods_info where gi_id>#{gid} and gt_id=#{type}
    	and gi_price &lt; #{jiage}
    </select>
    <delete id="delete" >
    	delete from goods_info where gi_id=#{giId}
    </delete>
    

4.4 动态sql语句

使用一些标签,实现sql语句可以根据条件,拼出不同的sql语句,实现不同的功能。

if: 判断是否需要添加sql语句段

where:拼where关键字

set:拼set关键字

trim: 对where 和set可以进行简化

java

public interface DynamicGoodsInfoDao {
	// 根据参数是否为空,动态完成sql语句的拼接
	public List<GoodsInfo> findWhere(GoodsInfo info);
	// 根据参数是否为空,动态完成sql语句的拼接
	public List<GoodsInfo> findWhereTwo(GoodsInfo info);
	public void updateSelectiveById(GoodsInfo info);
	public void updateSelectiveByIdTrim(GoodsInfo info);
}

xml

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper
	PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
	"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace: 命名空间 , 设置mapper.xml文件中的sql 对应的 interface。 -->
<mapper namespace="com.hqyj.dao.DynamicGoodsInfoDao">
	<!-- 自定义resultMap类型, 通过id,引用这个类型。-->
	<resultMap id="GIMap" type="com.hqyj.entity.GoodsInfo">
		<id column="gi_id" property="giId"></id>
		<result column="gt_id" property="gtId" javaType="java.lang.Integer"></result>
		<result column="gi_name" property="giName"></result>
		<result column="gi_price" property="giPrice"></result>
		<result column="gi_num" property="giNum"></result>
		<result column="gi_note" property="giNote"></result>
		<result column="gi_img" property="giImg"></result>
	</resultMap>
	<!-- 使用if标签, 判断传入的参数值,如果参数值不是null,则拼对应的sql语句-->
	<select id="findWhere" resultMap="GIMap" parameterType="com.hqyj.entity.GoodsInfo">
		select * from goods_info where 1=1
		<if test="giId!=null">
			and gi_id = #{giId}
		</if>
		<if test="gtId!=null">
			and gt_id = #{gtId}
		</if>
		<if test="giPrice!=null">
			and gi_price>#{giPrice}
		</if>
		<if test="giNum!=null">
			and gi_num > #{giNum}
		</if>
	</select>
	<!-- 使用where标签, 然后where标签会自动添加where关键字,并且可以去掉多于的and 。-->
	<select id="findWhereTwo" resultMap="GIMap" parameterType="com.hqyj.entity.GoodsInfo">
		select * from goods_info
		<where>
			<if test="giId!=null">
				and gi_id = #{giId}
			</if>
			<if test="gtId!=null">
				and gt_id = #{gtId}
			</if>
			<if test="giPrice!=null">
				and gi_price>#{giPrice}
			</if>
			<if test="giNum!=null">
				and gi_num > #{giNum}
			</if>
		</where>
	</select>
	<!-- public void updateSelectiveById(GoodsInfo info);
	set标签,会添加set关键字,并且去掉多于的逗号 ,如果传入的参数的属性都是null,
	那么抛异常-->
	<update id="updateSelectiveById" parameterType="com.hqyj.entity.GoodsInfo">
		update goods_info
		<set>
			<if test="gtId!=null">
				gt_id=#{gtId} ,
			</if>
			<if test="giName!=null">
				gi_name=#{giName} ,
			</if>
			<if test="giPrice!=null">
				gi_price=#{giPrice} ,
			</if>
			<if test="giNum!=null">
				gi_num=#{giNum} ,
			</if>
			<if test="giNote!=null">
				gi_note=#{giNote} ,
			</if>
			<if test="giImg!=null">
				gi_img=#{giImg} ,
			</if>
		</set>
		<where>
			<if test="giId!=null">
				gi_id=#{giId}
			</if>
		</where>
	</update>
	<!-- public void updateSelectiveByIdTrim(GoodsInfo info);
	trim标签:可以通过前缀,后缀等设置,替代where,set等标签。
	prefix="" ,前面添加什么内容
	prefixOverrides="" ,把前面多于的什么内容给去掉,
	suffix="" 后面添加什么内容
	suffixOverrides="" , 把后面多于的什么内容给去掉
	-->
	<update id="updateSelectiveByIdTrim" parameterType="com.hqyj.entity.GoodsInfo">
		update goods_info
		<trim prefix="set" prefixOverrides="" suffix=" where gi_id=#{giId}" suffixOverrides=",">
			<if test="gtId!=null">
				gt_id=#{gtId} ,
			</if>
			<if test="giName!=null">
				gi_name=#{giName} ,
			</if>
			<if test="giPrice!=null">
				gi_price=#{giPrice} ,
			</if>
			<if test="giNum!=null">
				gi_num=#{giNum} ,
			</if>
			<if test="giNote!=null">
				gi_note=#{giNote} ,
			</if>
			<if test="giImg!=null">
				gi_img=#{giImg} ,
			</if>
		</trim>
	</update>
</mapper>

4.5 两个表的关联查询

  • 实体类

    BookInfo.java

    // 两个表的关联查询: 查询书的时候,查出书的类型相关信息
    // 一本书对应一个类型
    @Data
    @ToString
    public class BookInfo {
    	// id -- 主键
    	private Integer bookId;
    	// result --- 普通的列
    	private Integer typeId;
    	private String bookName;
    	private String bookAuthor;
    	private Double bookPrice;
    	private Date bookPublishDate;
    	private Integer bookNum;
    	// 书的类型信息
    	// -- 关联的对象
    	private BookType bookType ;
    }
    
    

    BookType.java

    // 两个表的关联查询 : 查询书的类型的时候,就查询出该类型下的所有书。
    // 一对多:一个类型下有多个书
    @Data
    @ToString
    public class BookType {
    	private Integer typeId;
    	private String typeName;
    	// 属于某个类型中的所有书
    	private List<BookInfo> bookInfoList;
    }
    
  • mapper.java

    public interface BookInfoDao {
    	public List<BookInfo> queryAll();
    }
    
  • mapper.xml

    <?xml version="1.0" encoding="utf-8" ?>
    <!DOCTYPE mapper
    	PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    	"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!-- namespace: 命名空间 , 设置mapper.xml文件中的sql 对应的 interface。 -->
    <mapper namespace="com.hqyj.dao.BookInfoDao">
    	<resultMap id="BookInfoMap" type="com.hqyj.entity.BookInfo">
    		<id property="bookId" column="book_id"></id>
    		<result property="typeId" column="type_id"></result>
    		<result property="bookName" column="book_name"></result>
    		<result property="bookAuthor" column="book_author"></result>
    		<result property="bookPrice" column="book_price"></result>
    		<result property="bookPublishDate" column="book_publish_date"></result>
    		<result property="bookNum" column="book_num"></result>
    		<!-- association : 设置关联关系对应的属性
    		property="bookType" : 有关联关系的属性名
    		javaType="com.hqyj.entity.BookType" :有关联关系的属性对应的类型
    		-->
    		<association property="bookType" javaType="com.hqyj.entity.BookType">
    			<id property="typeId" column="type_id"></id>
    			<result property="typeName" column="typeName"></result>
    		</association>
    	</resultMap>
    	<!-- public List<BookInfo> queryAll();-->
    	<select id="queryAll" resultMap="BookInfoMap" >
    		select * from bookinfo as info , book_type as type 
            where info.type_id = type.type_id
    	</select>
    </mapper>
    

4.6 分页插件的使用

导入分页插件相关的jar包

<dependency>
	<groupId>com.github.pagehelper</groupId>
	<artifactId>pagehelper</artifactId>
	<version>5.1.10</version>
</dependency>

在mybatis-config.xml中配置分页的插件

<!-- 分页插件的配置-->
<plugins>
	<plugin interceptor="com.github.pagehelper.PageInterceptor">
	<!-- 分页参数不对的时候, page <1 , 设置显示第一页的内容,
	page > maxPage , 显示最后一页的内容-->
	<property name="reasonable" value="true"/>
	</plugin>
</plugins>

测试分页的功能

@Test
public void testPageHelper(){
	BookInfoDao dao = sqlSession.getMapper(BookInfoDao.class);
	// 查询之前,设置要查询的页面和要查询的条数
	// PageHelper.startPage(页码, 几行)
	PageHelper.startPage(2, 1) ;//
	List<BookInfo> bookInfos = dao.queryAll();
	// 把跟分页相关的所有信息,保存在pageInfo对象中。
	PageInfo<BookInfo> pageInfo = new PageInfo<>(bookInfos); // 必须把dao.queryAll();的结果作为参数传给PageInfo对象
	System.out.println("============pageInfo=============");
	//System.out.println(pageInfo);
	// System.out.println(pageInfo.getPages());
	// System.out.println(pageInfo.getList());
	System.out.println(pageInfo.getList().get(0));
}

4.7 mybatis的自动生成功能

  • 安装mybatis自动生成的插件

    在plugin中搜索添加
    在这里插入图片描述
    或者在idea插件官网下载,然后添加
    在这里插入图片描述

  • 导入jar包

    依赖包

    <dependency>
    	<groupId>org.mybatis.generator</groupId>
    	<artifactId>mybatis-generator-core</artifactId>
    	<version>1.4.0</version>
    </dependency>
    

    插件包

    <plugin>
    	<groupId>org.mybatis.generator</groupId>
    		<artifactId>mybatis-generator-maven-plugin</artifactId>
    			<version>1.4.0</version>
    			<executions>
    				<execution>
    					<id>Generate MyBatis Artifacts</id>
    					<goals>
    						<goal>generate</goal>
    					</goals>
    				</execution>
    			</executions>
    	<configuration>
    		<verbose>true</verbose>
    		<overwrite>true</overwrite>
    	</configuration>
    	<dependencies>
    		<dependency>
    			<groupId>mysql</groupId>
    			<artifactId>mysql-connector-java</artifactId>
    			<version>8.0.30</version>
    		</dependency>
    	</dependencies>
    </plugin>
    
  • 自动生成文件的配置

    <?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="cont" targetRuntime="MyBatis3">
    		<!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
    		<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
    			connectionURL="jdbc:mysql://localhost:3306/empdb? serverTimezone=Asia/Shanghai"
    			userId="root" password="123456">
    		<!-- 避免数据库中有重复的表名的时候,找错数据库。
    		** 阻止生成empdb之前的其他数据库的同名的表
    		-->
    			<property name="nullCatalogMeansCurrent" value="true"/>
    		</jdbcConnection>
    		<!-- 所生成实体类的位置默认的资源包 -->
    		<javaModelGenerator targetPackage="com.hqyj.entity" targetProject="src/main/java"></javaModelGenerator>
    		<!-- mapper.xml的位置 -->
    		<sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources"></sqlMapGenerator>
    		<!--mapper.java的位置-->
    		<javaClientGenerator type="XMLMAPPER" targetPackage="com.hqyj.dao" targetProject="src/main/java"></javaClientGenerator>
    		<!-- 为哪些表生成映射文件 tableName:表名 schema:写空即可 -->
    		<!-- 1.一次可以设置多个table
    		2. tableName - 数据库的表名
    		3. domainObjectName - 生成实体类的名字, 可以省略, 省略了就采用表名的首字
    		母大写。
    		4. columnOverride - 指定列名的对应的实体类的属性名 ,一般简写
    		-->
    		<table tableName="emp" domainObjectName="Emp">
    			<columnOverride column="eno" property="eid"></columnOverride>
    		</table>
    		<table tableName="user" domainObjectName="MyUser"></table>
    	</context>
    </generatorConfiguration>
    
  • 配置文件的运行
    在这里插入图片描述

  • 测试生成的类

    // crud方法的测试
    @Test
    public void testQuery(){
    	EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    	// List<Emp> emps = mapper.selectByExample(null); // 全表查询
    	// id > 3, 并且工资3000
    	EmpExample example = new EmpExample();
    	example.or().andEidGreaterThan(3).andEsalaryGreaterThan(new BigDecimal(3000.0));
    	example.or().andDeptnoEqualTo(1);
    	List<Emp> emps = mapper.selectByExample(example);
    	System.out.println(emps);
    	// 删除工资小于3000.
    	example.clear(); // 清除前面的条件 -- example= new EmpExample();
    	example.or().andEsalaryLessThan(new BigDecimal(3000));
    	mapper.deleteByExample(example);
    }
    

5.SSM整合

  • 1.创建web项目

    创建java , resources文件夹,设置为对应的类型

    修改web.xml版本

    配置tomcat

  • 2.导入依赖包

    mybatis.jar

    spring mvc.jar

    mysql.jar

    mybatis-spring.jar

    junit-4.12.jar c3p0.jar (数据源)

    servlet-api.jar , jstl.jar, standard.jar

    spring-tx.jar , spring-jdbc.jar

    其他… (需要的时候在导入)

  • 3.spring mvc的配置

    web.xml

    spring-mvc.xml

  • 4.mybatis的配置

    mybatis-config.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
    	PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    	"http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    	<!-- 设置控制台日志输出-->
    	<settings>
    		<setting name="logImpl" value="STDOUT_LOGGING"/>
    	</settings>
    	<!-- 分页插件的配置-->
    	<plugins>
    		<plugin interceptor="com.github.pagehelper.PageInterceptor">
    	<!-- 分页参数不对的时候, page <1 , 设置显示第一页的内容,page > maxPage , 显示最后一页的内容-->
    			<property name="reasonable" value="true"/>
    		</plugin>
    	</plugins>
    </configuration>
    

    mydb.properties

  • 5.ssm整合的配置

    <?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"
    	xmlns:mvc="http://www.springframework.org/schema/mvc"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans
    		http://www.springframework.org/schema/beans/spring-beans.xsd
    		http://www.springframework.org/schema/context
    		https://www.springframework.org/schema/context/spring-context.xsd
    		http://www.springframework.org/schema/mvc
    		https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    	<context:property-placeholder location="classpath:mydb.properties"></context:property-placeholder>
    	<!-- bean的配置-->
    	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    		<property name="dataSource" ref="ds"></property>
    		<property name="configLocation" value="classpath:mybatisconfig.xml"></property>
    		<!-- 指定mapper.xml的路径-->
    		<property name="mapperLocations" value="classpath:mapper/*.xml"></property>
    	</bean>
    	<!-- 数据源对应的bean的配置-->
    	<bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    		<property name="password" value="${mydb.pwd}"></property>
    		<property name="user" value="${mydb.user}"></property>
    		<property name="jdbcUrl" value="${mydb.url}"></property>
    		<property name="driverClass" value="${mydb.driver}"></property>
    	</bean>
    	<!-- mapper.java的扫描配置
    	*** spring框架的容器中可以找到mapper.java的实现类对象。程序员通过自动装配,就
    	比较方便的使用这些mapper.java
    	-->
    	<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    		<!-- set注入:设置mapper.java所在的package-->
    		<property name="basePackage" value="com.hqyj.ssm.dao"></property>
    		<!-- set注入: 指定sqlSessionFactoryBean-->
    		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    	</bean>
    </beans>
    
  • 6.mybatis的自动生成

6.ajax在ssm中的使用

  • 1.导入jar包: object 和 json的转换相关的jar包

    <dependency>
    	<groupId>com.alibaba</groupId>
    	<artifactId>fastjson</artifactId>
    	<version>2.0.11</version>
    </dependency>
    
  • 2.spring.xml中配置转换器

    <mvc:annotation-driven>
    	<mvc:message-converters>
    		<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
    			<property name="supportedMediaTypes">
    				<list>
    					<value>text/html;charset=utf-8</value>
    					<value>application/json;charset=utf-8</value>
    				</list>
    			</property>
    		</bean>
    		</mvc:message-converters>
    </mvc:annotation-driven>
    
  • 3.controller

    @RestController // 类中所有的请求的返回值都会被转换为json格式。(Map , List<T> , 实体类, Object)
    @RequestMapping("/emp")
    @CrossOrigin(value = "http://127.0.0.1:8848/")// 允许被跨域访问
    public class EmpController {
    	@Autowired
    	EmpService service ;
    	@RequestMapping("/list")
    	public Map<String , Object> queryOnePage(@RequestParam(defaultValue = "1") Integer page ,@RequestParam(defaultValue = "3") Integerlimit){
    		PageInfo<Emp> pageInfo = service.queryByPage(page, limit);
    		Map<String , Object> map = new HashMap<>();
    		map.put("count" , pageInfo.getTotal()); //pageInfo.getTotal()-- 总行数
    		map.put("code" , 0) ;
    		map.put("msg" ,"");
    		map.put("data" ,pageInfo.getList() ); //pageInfo.getList() --当前页的数据
    		return map;
    	}
    	@RequestMapping("/add")
    	public Map<String ,Object> addEmp(Emp emp){
    		Boolean aBoolean = service.addEmp(emp);
    		Map<String , Object> map = new HashMap<>();
    		map.put("success" , aBoolean);
    	return map;
    	}
    	@RequestMapping("/one")
    		public Emp queryByEno(Integer eno){
    		return service.queryByEno(eno);
    	}
    }
    
  • 4.页面上的使用

    $.ajax({
    	url:"http://localhost:8080/ssm_ajax/emp/add", // 请求地址
    	data: data.field, // 提交请求的表单的数据
    	type:"post", // 请求方式
    	dataType:"json", // 期待的返回值类型
    	success:function(res){ // res就是响应结果
    		if(res.success){
    			layer.msg("添加成功"); // layer.msg-- 弹出一个信息
    		}else{
    			layer.msg("添加失败")
    		}
    	}
    })
    
  • 5.安装postman ,可以测试请求
    在这里插入图片描述

  • 6.layui的使用

    参考网页: https://layui.mnorg.cn/

7.springboot框架

7.1 浏览器的本地存储

localStorage: 本地存储中的数据,一直存在。

sessionStorage: 保存的数据,浏览器关闭,数据被清除。

  • 存储数据

    window.localStorage.key=value

    // 把loginname保存在浏览器的本地存储中。
    //在本域名下的所有的html页面中,可以通过js获取出来,然后使用。
    window.localStorage.name = res.loginname; // json
    window.localStorage.token = res.token;
    

    window.localStorage.setItem(key,value)

    window.localStorage.setItem("name" , res.lognname) 
    //-- 调用setItem方法存储数据。
    
  • 获取数据

    let m = window.localStorage.key;
    // 或者
    let m = window.localStorage.getItem(key)
    
  • 删除数据

    window.localStorage.key = null ;
    window.localStorage.removeItem(key)
    

7.2 jwt的使用

  • 导入jar包

    <dependency>
    	<groupId>io.jsonwebtoken</groupId>
    	<artifactId>jjwt</artifactId>
    	<version>0.9.1</version>
    </dependency>
    
  • 创建类

    package com.hqyj.ssm.Util;
    import com.hqyj.ssm.entity.MyUser;
    import io.jsonwebtoken.*;
    import org.apache.ibatis.logging.stdout.StdOutImpl;
    import org.springframework.stereotype.Component;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    // 封装一个类: 对对象进行加密, 对对象进行解密, 判断token是否有效
    @Component // 注解:容器会创建对象
    public class JWTUtil {
    	// 定义过期时间
    	private long expire = 30*60*1000 ;// 毫秒
    	// 定义加密的密钥
    	private String secret = "nice.to.meet.you";
    	// 1. 生成token
    	public String createToken(MyUser user){
    		JwtBuilder builder = Jwts.builder();
    		Map<String ,Object> map = new HashMap<>();
    		map.put("user" , user);
    		builder.setClaims(map);
    		builder.setSubject(user.getUname()) ;// 设置主题
    		builder.setIssuedAt(new Date()) ;// 设置发布日期
    		builder.setExpiration(new Date(new Date().getTime() + expire));// 设置过期时间
    		// SignatureAlgorithm.HS512 :选择的加密算法, 也可以选择其他的加密算法
    		// secret :加密的密钥
    		builder.signWith(SignatureAlgorithm.HS512 , secret) ;
    		String token = builder.compact();
    		return token;
    	}
    	// 2. 解析token
    	public Claims parseToken(String token){
    		JwtParser parser = Jwts.parser();
    		parser.setSigningKey(secret);// 设置密钥(加密的密钥和解密的密钥相同)
    		Jws<Claims> claimsJws = parser.parseClaimsJws(token); // 解析token
            Claims body = claimsJws.getBody();
    		return body;
    	}
    	// 3. 判断token是否有效
    	public Boolean validToken(String token){
    		try {
    			parseToken(token);
    			return true;
    		}catch (Exception e){
    			e.printStackTrace();
    			return false;
    		}
    	}
    	// 4. 获取MyUser对象
    	public MyUser getUser(String token){
    		try {
    			Claims claims = parseToken(token);
    			Object obj = claims.get("user");
    			Map<String ,Object> map = (Map<String, Object>)obj;
    			MyUser myUser = new MyUser();
    			if(map.get("uid") != null){
    				myUser.setUid((Integer) map.get("uid"));
    			}
    			if(map.get("uname") != null){
    				myUser.setUname((String) map.get("uname"));
    			}
    			if(map.get("upwd") != null){
    				myUser.setUpwd((String) map.get("upwd"));
    			}
    			if(map.get("rid") != null){
    				myUser.setRid((Integer) map.get("rid"));
    			}
    			return myUser;
    		}catch (Exception e){
    			e.printStackTrace();
    			return null;
    		}
    	}
    	public static void main(String[] args) {
    		JWTUtil jwtUtil = new JWTUtil();
    		//MyUser myUser = new MyUser() ;
    		//myUser.setUname("tom"); myUser.setRid(1);
    		//String token = jwtUtil.createToken(myUser);
    		//System.out.println(token);
    		String token = "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0b20iLCJleHAiOjE2NjQ0MjQyODYsInVzZXIiOnsidWlkIjpudWxsLCJ1bmFtZSI6InRvbSIsInVwd2QiOm51bGwsInJpZCI6MX0sImlhdCI6MTY2NDQyMjQ4Nn0.csOTYTX8XjfNfmUPAMfknRVxbgYwCh9RUeUF7xFV9SBS2yHf8pI3Z_8aCKqiqXfbjPH1vKBhe1cBVizgpwQ86g";
    		Claims claims = jwtUtil.parseToken(token);
    		System.out.println(claims);
    	}
    }
    
  • 调用方法

    • 拦截中使用

      @CrossOrigin
      public class LoginInterceptor implements HandlerInterceptor {
      	@Autowired
      	JWTUtil jwtUtil ;
      	@Override
      	public boolean preHandle(HttpServletRequest request,
      	HttpServletResponse response, Object handler) throws Exception {
      
      		//String token1 = request.getHeader("token"); 表示token 是放在请求头(headers)中传输的
      		String token = request.getParameter("token");// 表示token和请求参数一起发送的
      		Boolean aBoolean = jwtUtil.validToken(token); // 判断token是否有效
      		if(aBoolean) {
      			MyUser user = jwtUtil.getUser(token); // 从token中获取对象
                  request.setAttribute("user" , user); // 把登陆用户的user对象,存储在request中,方便后续的Controller中使用。
      			return true;
      		}
      		response.setContentType("application/json;charset=utf-8");
      		response.getWriter().println("{\"msg\" : \"nologin\"}");
      		return false;
      	}
      }
      
    • 登录的请求中使用

      @Autowired
      MyUserService service;
      @Autowired
      JWTUtil jwtUtil ;
      @RequestMapping("login")
      public Map<String , Object> login(String uname , String upwd){
      	MyUser myUser = service.selectByName(uname , upwd);
      	Map<String , Object> map = new HashMap<>();
      	if(myUser == null){
      	map.put("success" ,false);
      	}else{
      		map.put("success" , true);
      		map.put("loginname" , myUser.getUname());
      		String token = jwtUtil.createToken(myUser);
      		map.put("token" , token); // myUser对象进行加密 , 转换为一个string ,然后传到前端
      	}
      	return map;
      }
      

7.3 springboot项目创建

  • springboot项目是spring 官方推出的替代springmvc的一种项目结构。简化web编程。

    • 创建springboot的子项目
    • 项目集成tomcat
    • 目录结构不一样
    • 运行方式不一样
    • 导入的依赖包不一样
    • 配置不一样(application.properties)
    • 推荐使用ajax的方式访问请求
  • 创建springboot项目
    在这里插入图片描述

7.4 spring boot + mybatis

  • 导入包

    <dependency>
    	<groupId>mysql</groupId>
    	<artifactId>mysql-connector-java</artifactId>
    	<scope>runtime</scope>
    </dependency>
    <!-- mybatis-spring-boot-starter -->
    <dependency>
    	<groupId>org.mybatis.spring.boot</groupId>
    	<artifactId>mybatis-spring-boot-starter</artifactId>
    	<version>2.2.1</version>
    </dependency>
    
  • 配置

    #数据源的设置
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost:3306/empdb?
    serverTimezone=Asia/Shanghai
    spring.datasource.username=root
    spring.datasource.password=123456
    #数据源的配置 (druid的数据源)
    spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
    #mapper.xml的路径
    mybatis.mapper-locations=classpath:mapper/*.xml
    #日志输出
    mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
    mapper.java包扫描的设置
    @MapperScan("com.hqyj.sbm.dao")
    @Mapper(在每个mapper.java类之前添加)
    测试类的使用: 需要导入spring-boot-starter-test包
    service中的使用
    #server的配置
    server.port=8080
    
  • mapper.java包扫描的设置

    @MapperScan(“com.hqyj.sbm.dao”)

    或者@Mapper(在每个mapper.java类之前添加)

    @SpringBootApplication
    @MapperScan("com.hqyj.sbm.dao") 
    // 指定mapper.java所在的包, spring 容器中可以找到这些接口的实现类对象,
    // 在service中就可以自动装配,然后使用这些对象
    // *** 就不用单独在每个mapper.java前添加@Mapper的注解
    public class SpringbootmybatisApplication {
    	public static void main(String[] args) {
    		SpringApplication.run(SpringbootmybatisApplication.class, args);
    	}
    }
    
  • 测试类的使用: 需要导入spring-boot-starter-test包

    @SpringBootTest // springboot测试相关注解
    class SpringbootmybatisApplicationTests {
    	@Autowired // 自动装配
    	EmpService empService ;
    	@Test
    	void contextLoads() {
    		//EmpService empService = new EmpService() ; // 自己创建的对象,对象内部的注解,无效。
    		empService.queryByPage(1, 5);
    	}
    }
    
  • service中的使用

    @Service
    public class EmpService {
    	@Autowired
    	EmpMapper empMapper ;
    	// code.....
    }
    
  • 拦截器的配置

    springboot项目中有bean对象的配置,可以使用java的方式配置

    @Configuration
    public class InterceptorConfig implements WebMvcConfigurer {
    	@Autowired
    	LoginInterceptor loginInterceptor;
    	@Override
    	public void addInterceptors(InterceptorRegistry registry) {
    		registry.addInterceptor(loginInterceptor).addPathPatterns("/emp/**");
    	}
    }
    
    @Configuration
    public class BeanConfig {
    	// bean的创建
    	@Bean
    	public String createStr(){
    		return new String("xxx");
    	}
    }
    

8.springboot + mybatis plus

8.1 创建spring boot项目

创建细节见第7节

8.2 导入mybatis plus的包

<dependency>
	<groupId>com.baomidou</groupId>
	<artifactId>mybatis-plus-boot-starter</artifactId>
	<version>3.5.2</version>
</dependency>

8.3 mybatis plus的自动生成

  • 导入包

    <!-- mybatis plus 自动生成-->
    <dependency>
    	<groupId>com.baomidou</groupId>
    	<artifactId>mybatis-plus-generator</artifactId>
    	<version>3.5.3</version>
    </dependency>
    <!-- freemarker的包-->
    <dependency>
    	<groupId>org.freemarker</groupId>
    	<artifactId>freemarker</artifactId>
    	<version>2.3.31</version>
    </dependency>
    <!-- swagger的包-->
    <dependency>
    	<groupId>io.springfox</groupId>
    	<artifactId>springfox-boot-starter</artifactId>
    	<version>3.0.0</version>
    </dependency>
    
  • 自动生成工具类

    public class GenMyBatisPlus {
    public static void main(String[] args) {
    	FastAutoGenerator.create("jdbc:mysql://localhost:3306/empdb?serverTimezone=Asia/Shanghai","root", "123456")
    		.globalConfig(builder -> {
    			builder.author("fengjm") // 设置作者
    			.enableSwagger() // 开启 swagger 模式
    			.fileOverride() // 覆盖已生成文件
    			.outputDir("D:/javaweb/sbmp/src/main/java"); // 指定输出目录
    		})
    		.packageConfig(builder -> {
    			builder.parent("com.hqyj.sbmp") // 设置父包名
    				// .moduleName("emp") // 设置父包模块名
    				.pathInfo(Collections.singletonMap(OutputFile.xml,"D://")); // 设置mapperXml生成路径
    		})
    		.strategyConfig(builder -> {
    			builder.addInclude("emp" ,"user" , "dept") // 设置需要生成的表名
    				.addTablePrefix("t_", "c_"); // 设置过滤表前缀
    			})
    		.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
    		.execute();
    	}
    }
    
  • 测试功能

    @SpringBootTest
    class SbmpApplicationTests {
    	@Autowired
    	IEmpService service ;
    	@Test
    	public void testSelect(){
    		Emp byId = service.getById(3);
    		// System.out.println(byId.getEname());
    		System.out.println(byId.getEname());
    	}
    }
    

8.4 mybatis plus的分页查询

  • 分页的拦截器的配置

    @Configuration
    public class PageConfig {
    	@Bean // set注入 mybatis plus 分页插件
    	public MybatisPlusInterceptor pageAdd(){
    		MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    		interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
    		return interceptor ;
    	}
    }
    
  • 测试分页的功能

    @Test
    public void testSelect(){
    	Page<Emp> page =
    	service.page(new Page<>(1, 5));
    	List<Emp> records = page.getRecords();
    	System.out.println(records);
    }
    

8.5 swagger3的使用

  • 导入相关包

    <!-- swagger的包-->
    <dependency>
    	<groupId>io.springfox</groupId>
    	<artifactId>springfox-boot-starter</artifactId>
    	<version>3.0.0</version>
    </dependency>
    
  • swagger3相关注解

    contorller的注解

    @RestController
    @RequestMapping("/emp")
    @Api(tags = "员工管理模块")
    public class EmpController {
    	@Autowired
    	IEmpService iEmpService ;
    	// 查询一页的请求
    	@RequestMapping(value = "/page" , method = RequestMethod.POST)
    	@ApiOperation(value = "分页查询" , notes = "当前页码的员工们")
    	public List<Emp> selectPage(@ApiParam(value = "页码" , required = true)
    	@RequestParam(defaultValue = "1") int page, @ApiParam(value = "每页显示的行数") @RequestParam(defaultValue = "3") int limit){
    		Page<Emp> page1 = iEmpService.page(new Page<>(page, limit));
    		return page1.getRecords(); // 获取到当前页的数据
    	}
    }
    

    实体类的注解

    @ApiModel(value = "Emp对象", description = "")
    public class Emp implements Serializable {
    	private static final long serialVersionUID = 1L;
    	@ApiModelProperty("员工编号")
    	@TableId(value = "eno", type = IdType.AUTO)
    	private Integer eno;
    	@ApiModelProperty("员工名字")
    	private String ename;
    	@ApiModelProperty("工作岗位")
    	private String ejob;
    	@ApiModelProperty("部门经理")
    	private Integer emanager;
    	@ApiModelProperty("入职日期")
    	@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss" , timezone = "GMT+8")
    	private LocalDateTime ehiredate;
    	@ApiModelProperty("工资")
    	private BigDecimal esalary;
    	@ApiModelProperty("部门编号")
    	private Integer deptno;
    	@ApiModelProperty("员工照片的地址")
    	private String empImg;
    	// code .......
    }
    
  • 启动springboot项目, 访问地址

    http://localhost:8080/swagger-ui/

  • 可以添加一个单独的配置文件,对swagger3进行一些设置,也可以不添加。

    @Configuration
    @EnableOpenApi
    public class Swagger3Config {
    	/**
          * ture 启用Swagger3.0, false 禁用(生产环境要禁用)
    	*/
    	Boolean swaggerEnabled=true;
    	@Bean
    	public Docket createRestApi(){
    		return new Docket(DocumentationType.OAS_30)
    			.apiInfo(apiInfo())
    			// 是否开启
    			.enable(swaggerEnabled)
    			.select()
    			// 扫描的路径使用@Api的controller
    			//.apis(RequestHandlerSelectors.withMethodAnnotation(Api.class))
    			.apis(RequestHandlerSelectors.basePackage("com.hqyj.sbmp.controller"))
    			// 指定路径处理PathSelectors.any()代表所有的路径
    			.paths(PathSelectors.any())
    			.build();
    		}
    	private ApiInfo apiInfo(){
    		return new ApiInfoBuilder()
    			.title("员工管理系统")
    			.description("员工管理系统接口说明文档")
    			//作者信息
    			.contact(new Contact("alice","https://xxxx.icu/","alice@qq.com"))
    			.version("1.0")
    			.build();
    	}
    }
    

8.6 mybatis plus的条件查询

// 等于和and的查询 : 把传给QueryWrapper的实体类对象的非空属性值,用“等于”进行条件拼接 ,多个条件用and 连接。
// -- SELECT eno,ename,ejob,emanager,ehiredate,esalary,deptno,emp_img FROM emp
// WHERE ejob=? AND emanager=?
@Test
public void query(){
	Emp emp = new Emp();
	emp.setEjob("yyyy");
	emp.setEmanager(1);
	QueryWrapper<Emp> empQueryWrapper = new QueryWrapper<>(emp);
	List<Emp> list = service.list(empQueryWrapper);
	System.out.println(list);
}
	// 用QueryWrapper , 拼接各种复杂的条件
	// 比如: eq , like gt , lt , in....
	// 比如: apply拼sql语句段
	// 比如:or , 拼"或"的条件
	// 比如: orderByAsc , orderByDesc
	//SELECT eno,ename,ejob,emanager,ehiredate,esalary,deptno,emp_img FROM emp
	// WHERE (esalary > ? OR eno < ? AND ename LIKE ? AND deptno=1) ORDER BY esalary DESC
@Test
public void query1(){
	QueryWrapper<Emp> queryWrapper = new QueryWrapper<>();
	queryWrapper.gt("esalary" , 3000.0); //gt是大于 第一个参数是列名,第二个参数是数据,
	queryWrapper.or().lt("eno" , 10);//lt小于
	queryWrapper.like("ename" , "%y%");// like.像.....
	queryWrapper.apply(" deptno=1");
	queryWrapper.orderByDesc("esalary");
	List<Emp> list = service.list(queryWrapper);
}
@Test // SELECT ename ,ejob FROM emp WHERE (esalary > ?)
public void query2(){
	QueryWrapper<Emp> queryWrapper = new QueryWrapper<>();
	queryWrapper.select("ename " , "ejob").gt("esalary" , 3000.0);
	List<Map<String, Object>> maps = service.listMaps(queryWrapper);
	System.out.println(maps);
}

8.7 mybatis plus中自定义方法

  • 自定义的方法

    @Select("select * from goods_info where gt_id=#{typeId}")
    public List<GoodsInfo> selectByTypeId(Integer typeId);
    
  • 自定义方法支持分页

    @Select("select * from goods_info")
    @ResultMap(value = "goodsinfomap")
    public IPage<GoodsInfo> selectAllPage(IPage<GoodsInfo> page);
    
  • 自定义的方法支持QueryWrapper

    // 自定义的方法,支持QueryWrapper(支持条件)
    // *** 如果自定义的方法要使用QueryWrapper , 建议sql语句中就不要自定义其他的查询条件了,
    // 所有的条件都通过QueryWrapper进行设置。
    // ${ew.sqlSegment} -- ew,就代表QueryWrapper ,拼sql语句段在select语句中。
    // *** ${ew.sqlSegment} ,没有 “where”
    // ${ew.customSqlSegment} , 有 “where ”
    @Select("select * from goods_info ${ew.customSqlSegment}")
    @ResultMap(value = "goodsinfomap")
    public List<GoodsInfo> selectAllQW(@Param(Constants.WRAPPER) QueryWrapper<GoodsInfo> wrapper);
    // 支持分页和支持自定义条件查询
    @Select("select * from goods_info where ${ew.sqlSegment}")
    @ResultMap(value = "goodsinfomap")
    public IPage<GoodsInfo> selectAllPageQW(IPage<GoodsInfo> page,@Param(Constants.WRAPPER) QueryWrapper<GoodsInfo> wrapper);
    

8.8 mybatis plus关联查询

  • 多对一

    实体类

    @TableName("goods_info") // 指定实体类对应的表名
    @ApiModel(value = "GoodsInfo对象", description = "")
    public class GoodsInfo implements Serializable {
    	private static final long serialVersionUID = 1L;
    	@TableId(value = "gi_id", type = IdType.AUTO) // 指定主键对应的列
    	private Integer giId;
    	//@TableField(value = "gt_id") -- 省略了普通列名的指定
    	private Integer gtId;
    	private String giName;
    	private Double giPrice;
    	private Integer giNum;
    	private String giNote;
    	@TableField(value = "`gi_img`")
    	private String giImg;
    	// 1.用注解,设置属性和table无关
    	//@TableField(exist = false) // 表示goodType属性不是table中列
    	//private GoodsType goodsType ; // 一个商品属余某个类型
    	// 2.用transient关键字,设置属性,该属性和table无关
    	private transient GoodsType goodsType ;
    	// 省略了get/set
    }
    

    mapper.java

    // 关联查询
    @Select("select * from goods_info")
    @Results(id="goodsinfomap" , value = {
    	@Result(column = "gt_id" , property = "gtId"),// 指定GoodsInfo中的gtId属性对应的列 ,避免gtId属性是null.
    	@Result(column = "gt_id" , property = "goodsType" , // 指定了关联属性和列对应
    		// GoodsTypeMapper中要有 这个方法(selectById)
    		one = @One(select = "com.hqyj.shopmp.mapper.GoodsTypeMapper.selectById")
    	)
    })
    public List<GoodsInfo> selectAll();
    
    @Select("select * from goods_info where gi_id=#{giId}")
    @Results( value = {
    	@Result(column = "gt_id" , property = "gtId"),// 指定GoodsInfo中的gtId属性对应的列 ,避免gtId属性是null.
    	@Result(column = "gt_id" , property = "goodsType" , // 指定了关联属性和列对应
    		one = @One(select = "com.hqyj.shopmp.mapper.GoodsTypeMapper.selectById")
    	)
    })
    public GoodsInfo selectById(Integer giId);
    //1 . 根据商品类型,查询该类型下的商品
    @Select("select * from goods_info where gt_id=#{typeId}")
    public List<GoodsInfo> selectByTypeId(Integer typeId);
    
  • 一对多

    实体类

    @TableName("goods_type")
    @ApiModel(value = "GoodsType对象", description = "")
    public class GoodsType extends Model<GoodsType> implements Serializable {
    	private static final long serialVersionUID = 1L;
    	@TableId(value = "gt_id", type = IdType.AUTO)
    	private Integer gtId;
    	private String gtName;
    	@TableField(exist = false) // 设置该属性和table无关
    	private List<GoodsInfo> goodsInfos;
    	// 省略了get/set
    }
    

    mapper.java

    // 关联查询
    @Select("select * from goods_type")
    @Results(id="goodstypemap" , value = {
    	@Result(column = "gt_id" , property = "gtId"), // 列和属性的对应关系: 避免goodsType中的gtId为null.
    	@Result(column = "gt_id" , property = "goodsInfos", // 指定关联属性的对应关系 ,GoodsInfoMapper中要有selectByTypeId方法
    		many = @Many(select = "com.hqyj.shopmp.mapper.GoodsInfoMapper.selectByTypeId")
        ) // 设置查询的语句对应的方法。
    })
    public List<GoodsType> selectAll();
    
    @Select("select * from goods_type where gt_id=#{gtId}")
    @ResultMap(value = "goodstypemap") // 引用一个存在的resultmap.
    public GoodsType selectByTypeId(Integer gtId);
    
    @Select("select * from goods_type where gt_id=#{gtId}")
    public GoodsType selectById(Integer gtId);
    

8.9 RESTful风格的接口设计

请求的url地址不用动词, 用名词,全小写

不同类型的操作,根据method的类型进行区分

和主键相关的操作主键值可以在url中提交

  • controller中的请求路径和请求方式

    package com.hqyj.shopmp.controller;
    import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    import com.hqyj.shopmp.entity.GoodsInfo;
    import com.hqyj.shopmp.entity.RestResult;
    import com.hqyj.shopmp.service.IGoodsInfoService;
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    import javax.lang.model.element.VariableElement;
    import java.util.List;
    
    @RestController
    @RequestMapping("/goods")
    @Api(tags = "商品信息管理" , value = "商品信息相关的请求")
    public class GoodsInfoController {
    	@Autowired
    	IGoodsInfoService service ;
    	@RequestMapping(value = "/goodsinfos" , method = RequestMethod.GET)
    	public RestResult<GoodsInfo> query(){
    		List<GoodsInfo> list = service.list();// 查询所有
    		return RestResult.ok(list);
    	}
    	@RequestMapping(value = "/goodsinfos/{page1}/{size}" , method = RequestMethod.POST)
    	public RestResult<GoodsInfo> queryPage(@PathVariable("page1") Integer page1,@PathVariable("size")Integer size){
    		Page<GoodsInfo> page = service.page(new Page<>(page1, size));
    		return RestResult.ok(page.getRecords()); // 查询所有
    	}
    	@GetMapping(value = "/goodsinfo/{giId}") // 主键值放在url地址中
    	public RestResult<GoodsInfo> queryOne(@PathVariable("giId")Integer giId){
    		return RestResult.ok(service.getById(giId));
    	}
    	@DeleteMapping(value = "/goodsinfo/{giId}")
    	public RestResult<GoodsInfo> deleteOne(@PathVariable("giId")Integer id){
    		boolean b = service.removeById(id);
    		if(b){
    			return RestResult.ok("success");
    		}else{
    			return RestResult.ok("fail");
    		}
    	}
    	@PutMapping(value="/goodsinfo")
    	public RestResult<GoodsInfo> updateGoodsInfo(GoodsInfo goodsInfo){
    		boolean b = service.updateById(goodsInfo);
    		if(b){
    			return RestResult.ok("success");
    		}else{
    			return RestResult.ok("fail");
    		}
    	}
    	@PostMapping(value = "/goodsinfo")
    	public Boolean addGoodsInfo(GoodsInfo goodsInfo){
    		boolean save = service.save(goodsInfo);
    		return save;
    	}
    }
    
  • 前后端分离项目后台返回对象

    package com.hqyj.shopmp.entity;
    import java.util.List;
    public class RestResult<T> {
    	private Integer code ; // 用于标志后台方法执行是否成功( 1表示成功, 0表示失败)
    	private String msg ; // 提示成功
    	private T obj; // 返回的单个对象
    	private List<T> list; // 返回的是一个集合
    	// 提供构造函数
    	public RestResult() {}
    	public RestResult(Integer code) {
    		this.code = code;
    	}
    	public RestResult(Integer code, String msg) {
    		this.code = code;
    		this.msg = msg;
    	}
    	public RestResult(Integer code, String msg, T obj) {
    		this.code = code;
    		this.msg = msg;
    		this.obj = obj;
    	}
    	public RestResult(Integer code, String msg,List<T> list) {
    		this.code = code;
    		this.msg = msg;
    		this.list = list;
    	}
    	public RestResult(Integer code, List<T> list) {
    		this.code = code;
    		this.list = list;
    	}
    	public RestResult(Integer code, T obj) {
    		this.code = code;
    		this.obj = obj;
    	}
    	// 提供静态方法
    	public static <T> RestResult<T> ok(){
    		return new RestResult<T>(1);
    	}
    	public static <T> RestResult<T> ok(String msg){
    		return new RestResult<T>(1 ,msg);
    	}
    	public static <T> RestResult<T> ok(T obj){
    		return new RestResult<T>(1 ,obj);
    	}
    	public static <T> RestResult<T> ok(List<T> list){
    		return new RestResult<T>(1 ,list);
    }
    	public RestResult(Integer code, String msg, T obj, List<T> list) {
    		this.code = code;
    		this.msg = msg;
    		this.obj = obj;
    		this.list = list;
    	}
    	public Integer getCode() {
    		return code;
    	}
    	public void setCode(Integer code) {
    		this.code = code;
    	}
    	public String getMsg() {
    		return msg;
    	}
    	public void setMsg(String msg) {
    		this.msg = msg;
    	}
    	public T getObj() {
    		return obj;
    	}
    	public void setObj(T obj) {
    		this.obj = obj;
    	}
    	public List<T> getList() {
    		return list;
    	}
    	public void setList(List<T> list) {
    		this.list = list;
    	}
    }
    

9.springboot + jpa

JPA:(java persistence api) , 是java提供的访问数据库的规范。Hibernate是完全实现了jpa . spring 对hibernate实现的jpa进行了封装, 提供了spring data jpa , 使用spring data jpa的包,实现数据库访问。

sql : 查询的数据库 , select * from emp

hql: 查询的对象 , from Emp

9.1 spring boot项目的创建

创建细节见第7节

9.2 spring data jpa包的导入

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<scope>runtime</scope>
</dependency>

9.3 数据库的连接

application.properties

#连接数据库的信息
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/shopdb?
serverTimezone=Asia/Shanghai
spring.datasource.username=root
#配置数据库
spring.jpa.database=mysql
#显示sql
spring.jpa.show-sql=true
spring.jpa.open-in-view=false

9.4 实体类 & table的关系

@Data
@ToString
@Entity // 设置为实体类 , 可以对这个实体进行crud的操作
@Table(name = "goods_info") // 设置表名
public class GoodsInfo {
    @Id // 表示主键
	//***根据table的主键的要求,确定是否要设置主键增长策略
	@GeneratedValue(strategy = GenerationType.IDENTITY) // IDENTITY-- mysql的自动增长
	@Column(name = "gi_id") // 设置列名
	private Integer giId;
	@Column(name = "gt_id")
	private Integer gtId;
	@Column(name = "gi_Name")
	private String giName;
	@Column(name = "gi_price")
	private Double giPrice;
	@Column(name = "gi_num")
	private Integer giNum;
	@Column(name = "gi_note")
	private String giNote;
	@Column(name = "gi_img")
	private String giImg;
}

如果提示注解是红色的,那么需要做相关设置

配置数据连接(数据库连接建好之后,使用 alt+ enter快捷键,在弹出菜单中选择数据库。)
在这里插入图片描述
选择数据源
在这里插入图片描述

9.5 持久层dao

继承JpaRepository , 父接口中定义了一些crud的方法。

// JpaRepository<GoodsInfo , Integer> , 第一个参数是实体类的类型, 第二个参数是实体类的
主键的类型
@Repository // 持久层的注解
public interface GoodsInfoDao extends JpaRepository<GoodsInfo , Integer> {
}

9.6 service

@Service
@Transactional
public class GoodsInfoServiceImpl implements IGoodsInfoService {
	@Autowired
	GoodsInfoDao dao ;
	@Override
	public GoodsInfo getOne(Integer giId) {
	// findById方法:如果传入的id, 找不到对应的数据,就会抛异常
	//opt.isPresent() ,进行判断,如果为true, 表示查询到了数据,可以get,
	// 否则, 就表示没有查到数据, 就返回null.
		Optional<GoodsInfo> opt = dao.findById(giId);
		if(opt.isPresent()){
			return opt.get();
		}
		return null;
	}
	@Override
	public List<GoodsInfo> getAll() {
		return dao.findAll();
	}
	@Override
	public boolean addGoodsInfo(GoodsInfo goodsInfo) {
		// 根据商品的名字查询,如果查不到这个商品,就添加
		GoodsInfo gi = new GoodsInfo() ;
		gi.setGiName(goodsInfo.getGiName());
		boolean exists = dao.exists(Example.of(gi));// 只根据名字是否相等进行查
		if(!exists){
			GoodsInfo save = dao.save(goodsInfo);// 保存。
			return true;
		}
		return false;
	}
	@Override
	public boolean updateGoodsInfo(GoodsInfo goodsInfo) {
		GoodsInfo save = dao.save(goodsInfo);
		return true;
	}
	@Override
	public boolean deleteGoodsInfo(Integer giId) {
		dao.deleteById(giId);
		return true;
	}
}

9.7 添加事务

@Transactional注解可以放在方法前或者类前,设置业务方法支持事务。

@Transactional // 设置事务: 业务方法中的数据库操作要么都成功,要么都失败
public boolean updateTest(Integer giId){
	GoodsInfo one = getOne(giId);
	one.setGiName("蜂蜜220");
	updateGoodsInfo(one);
	GoodsInfo two = new GoodsInfo() ;
	two.setGiId(giId); // 其他字段都是null, 所以更新不成功。
	dao.save(two);
	return true;
}
// 两个都会成功
@Transactional
public boolean updateTest1(Integer giId){
	GoodsInfo one = getOne(giId);
	one.setGiName("蜂蜜220");
	updateGoodsInfo(one);
	GoodsInfo two = one;
	two.setGiName("奶茶110");
	dao.save(two);
	return true;
}

9.8 jpa的复杂查询和自定义方法

  • 分页

    @Autowired
    GoodsInfoDao dao ;
    @Test // 分页的使用
    public void test1(){
    	int page = 1; // 注意page是从开始的。也就是0页表示第一页的数据, 1页表示的是第二页的数据
    	int size = 3;
    	PageRequest pageRequest = PageRequest.of(page , size);
    	Page<GoodsInfo> all = dao.findAll(pageRequest);
    	System.out.println(all.getContent());
    }
    
  • 排序

    @Test // 排序
    public void test2(){
    	//"giNum" ,"giPrice" 是属性名,不能使用列名。
    	// 1.多个排序字段,统一的升序或者降序
    	// List<GoodsInfo> all = dao.findAll(Sort.by(Sort.Direction.ASC,"giNum","giPrice"));
    	// 2.多个排序字段,排序方式不一致
    	List<GoodsInfo> all = dao.findAll(Sort.by(Sort.Order.asc("giNum"),
    	Sort.Order.desc("giPrice")));
    }
    
  • 分页和排序

    @Test // 分页和排序一起使用
    // 排序的设置,放在PageRequest对象中
    public void test3(){
    	PageRequest pageRequest = PageRequest.of(0,5 ,
    	Sort.by(Sort.Order.desc("giNum"),
    	Sort.Order.asc("giPrice")));
    	dao.findAll(pageRequest);
    }
    
  • example

    @Test //Example 查询:拼接的等于和and的查询
    public void test4(){
    	GoodsInfo goodsInfo =new GoodsInfo() ;
    	// goodsInfo.setGiName("牛奶");
    	goodsInfo.setGtId(2);
    	goodsInfo.setGiNum(100);
    	List<GoodsInfo> all = dao.findAll(Example.of(goodsInfo));
    }
    
  • 根据命名规则自定义方法

    // 按方法命名规则,自定义查询的方法
    // 1.查询价格大于多少
    List<GoodsInfo> findByGiPriceGreaterThan(Double giPrice);
    // 2.查询价格大于xx,或者数量小于等于xx
    List<GoodsInfo>
    findByGiPriceGreaterThanOrGiNumLessThanEqual(Double giPrice ,Integer giNum);
    

在这里插入图片描述

  • 自定义方法

    // hql: 从对象中查询
    // sql:从table中查询
    @Query("from GoodsInfo where giName like :giName and giPrice > :giPrice")
    List<GoodsInfo> selectByWhere(@Param("giName") String giName ,
    @Param("giPrice") Double giPrice);
    // -- nativeQuery=true ,设置为使用sql, 默认为false, 表示使用hql
    @Query(value= "select gt_name as `name`,sum(gi_num) as num " +
    		" from goods_info , goods_type \n" +
    		"where goods_info.gt_id = goods_type.gt_id " +
    		"GROUP BY goods_type.gt_id" , nativeQuery=true)
    List<Map<String ,Object>> selectByGourpGtId();
    

9.9 关联查询

  • 单向一对多

    一方可以找多方, 多方中不找一方。

    @Entity
    @Data
    @Table(name = "java_class")
    public class JavaClass {
    	@Id
    	@Column(name = "c_id")
    	private Integer cId;
    	@Column(name = "c_name")
    	private String cName;
    	@Column(name = "c_intro")
    	private String cIntro;
    	// 管理关系相关的属性
    	@OneToMany(fetch = FetchType.EAGER ,cascade = CascadeType.ALL)
    	@JoinColumn(name="c_id")// 外键相关的列
    	private List<Student> stus;
    }
    
  • 单向多对一

    多方可以找1方,反之不行

    @Entity
    @Data
    @Table(name = "student")
    public class Student {
    	@Id
    	@Column(name = "stu_id")
    	private Integer stuId;
    	@Column(name = "stu_name")
    	private String stuName;
    	@Column(name = "stu_sex")
    	private String stuSex;
    	@Column(name = "c_id" ,insertable = false , updatable = false)
    	private Integer cId; 
    	@ManyToOne(fetch = FetchType.EAGER )
    	@JoinColumn(name="c_id")
    	private JavaClass javaClass ;
    }
    
  • 双向多对一,一对多

    设置某一方维护关系,然后维护关系方使用@JoinColumn

    @Entity
    @Data
    @Table(name = "java_class")
    public class JavaClass {
    	@Id
    	@Column(name = "c_id")
    	private Integer cId;
    	@Column(name = "c_name")
    	private String cName;
    	@Column(name = "c_intro")
    	private String cIntro;
    	// 管理关系相关的属性
    	// mappedBy = "javaClass" : 由多方维护关系,
    	// ,"javaClass" 是多方中有关联关系的属性名。
    	// 设置了对方维护关系,那么就不需要@JoinColumn
    	@OneToMany(fetch = FetchType.EAGER ,cascade = CascadeType.ALL,mappedBy = "javaClass")
    	private List<Student> stus;
    }
    
    @Entity
    @Data
    @Table(name = "student")
    public class Student {
    	@Id
    	@Column(name = "stu_id")
    	private Integer stuId;
    	@Column(name = "stu_name")
    	private String stuName;
    	@Column(name = "stu_sex")
    	private String stuSex;
    	@Column(name = "c_id" ,insertable = false , updatable = false)
    	private Integer cId;
    	@ManyToOne(fetch = FetchType.EAGER )
    	@JoinColumn(name="c_id")
    	private JavaClass javaClass ;
    }
    
  • 单向多对多

    @JoinTable指定关系表

    @Entity
    @Data
    @Table(name = "course")
    public class Course {
    	@Id
    	@Column(name = "c_id")
    	private String cId;
    	@Column(name = "c_name")
    	private String cName;
    	@Column(name = "c_redit")
    	private Integer cRedit;
    	// 课程找到学生
    	@ManyToMany(fetch = FetchType.EAGER , cascade = CascadeType.ALL)
    	@JoinTable(name = "cou_stu", // 中间表
    		joinColumns = {@JoinColumn(name="c_id")}, // 中间表相关的列名
    		inverseJoinColumns = {@JoinColumn(name="stu_id")}// 中间表相关的列名
    	)
    	private List<Student> stus;
    }
    
  • 双向多对多

    Course.java

    // 课程找到学生
    @ManyToMany(fetch = FetchType.EAGER , cascade = CascadeType.ALL)
    @JoinTable(name = "cou_stu", // 中间表
    	joinColumns = {@JoinColumn(name="c_id")}, // 中间表相关的列名
    	inverseJoinColumns = {@JoinColumn(name="stu_id")}// 中间表相关的列名
    )
    private List<Student> stus;
    

    Student.java

    // 委托另一方维护关系。
    @ManyToMany(fetch = FetchType.EAGER ,mappedBy = "stus")
    private List<Course> courses;
    
  • 一对一

    同一张表中

    两张表,共用主键

    两张表,设置外键约束,外键唯一

    演示单向的一对一(通过外键约束的一对一)

    @OneToOne(fetch = FetchType.EAGER )
    @JoinColumn(name = "c_id" , unique = true)
    private Student stu ;
    

9.10 thymeleaf的使用

  • springboot项目静态资源的使用

    静态资源,放在resources文件夹中的static目录中,然后可以直接访问

    访问路径:http://localhost:8080/静态资源的路径

    可以修改静态资源的访问路径

    #修改静态资源的访问路径,默认值时/**, 表示resources/static的路径为根下的路径。
    spring.mvc.static-path-pattern=/a/**
    
  • 导入thymeleaf的包

    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-thymeleaf</artifactId>
    	<version>2.7.4</version>
    </dependency>
    
  • 创建模板文件

    模板文件放在resource/templates目录中

    在html标签中,添加:xmlns:th=“https://www.thymeleaf.org/”

    在模板中使用th:xx,设置对应的内容。

    <!DOCTYPE html>
    <html lang="en" xmlns:th="https://www.thymeleaf.org/">
    <head>
    	<meta charset="UTF-8">
    	<title>Title</title>
    	<link rel="stylesheet" th:href="@{/css/main.css}">
    	<script th:src="@{/js/jquery.js}"></script>
    </head>
    <body>
    	<!-- th:text="${word}" , ${word}从请求中传过来。 -->
    	<h2 th:text="${word}">欢迎xx!</h2>
    	<table>
    		<tr>
    			<td>班级名</td>
    			<td>班级简介</td>
    		</tr>
    		<tr th:each="jl: ${javaclass}">
    			<td th:text="${jl.cName}">名字</td>
    			<td th:text="${jl.cIntro}">介绍</td>
    		</tr>
    	</table>
    	<hr>
    	<img th:src="@{/img/one.jpg}">
    	<script>
    		$("tr").css("color","green");
    	</script>
    </body>
    </html>
    
  • Controller:方法的返回值是字符串,表示要找的模板文件(html)的路径

    @Controller
    public class TClassController {
    	Logger logger = LoggerFactory.getLogger(TClassController.class);
    	@RequestMapping("/list")
    	public String query(ModelMap map){
    		map.put("word" , "tom, 下午好,欢迎使用");
    		JavaClass jc = new JavaClass();
    		jc.setCName("java2002");
    		jc.setCIntro("企业级开发");
    		JavaClass jc1 = new JavaClass();
    		jc1.setCName("h52002");
    		jc1.setCIntro("web开发");
    		JavaClass jc2 = new JavaClass();
    		jc2.setCName("嵌入式2002");
    		jc2.setCIntro("软硬件集合开发");
    		List<JavaClass> list = new ArrayList<>();
    		list.add(jc1);list.add(jc);list.add(jc2);
    		map.put("javaclass" , list);
    		return "first" ;// 找html页面
    	}
    }
    

9.11 logback日志记录

logback是springboot项目集成的日志记录包,不需要单独导入jar包。日志记录的相关配置可以有单 独的xml文件,如果修改了日志记录的xml文件,不需要重启项目。

日志等级(由低到高): trace < debug < info < warn < error < fatal

springboot中默认已经设置好了日志记录的相关配置,可以在application.properties中调整日志的显 示级别

# 默认
logging.level.root=info

在自定义的类中如果要使用日志记录,就创建Logger对象,调用日志记录的方法

@Controller
public class TClassController {
	// 在需要记录日志的类中,创建Logger对象。
	// 创建logger对象, 在需要记录日志的地方,调用logger对象的方法,记录日志。
	Logger logger = LoggerFactory.getLogger(TClassController.class);
	@RequestMapping("/list")
	public String query(ModelMap map){
	/*
	logger.debug();
	logger.info();
	logger.warn();
	logger.error();
	*/
	logger.warn("执行了list请求。");
	map.put("word" , "tom, 下午好,欢迎使用");
	JavaClass jc = new JavaClass();
	jc.setCName("java2002");
	jc.setCIntro("企业级开发");
	List<JavaClass> list = new ArrayList<>();
	list.add(jc1);list.add(jc);list.add(jc2);
	map.put("javaclass" , list);
	logger.info("执行list请求结束了。。。。。。。");
	return "first" ;// 找html页面
	}
}

可以自定义logback-spring.xml文件,调整日志记录的设置

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
	<contextName>logback</contextName>
	<!--输出到控制台-->
	<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
		<encoder>
			<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
			<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
			<charset>UTF-8</charset>
		</encoder>
	</appender>
	<!--按天生成日志-->
	<appender name="logFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
		<Prudent>true</Prudent>
		<!-- 过滤器,只打印ERROR级别的日志 -->
		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
			<!--日志文件输出的文件名-->
			<FileNamePattern>d:/applog/%d{yyyy-MM-dd}/%d{yyyy-MM-dd}.log</FileNamePattern>
			<!--日志文件保留天数-->
			<MaxHistory>15</MaxHistory>
		</rollingPolicy>
		<layout class="ch.qos.logback.classic.PatternLayout">
			<Pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</Pattern>
		</layout>
	</appender>
	<!-- 设置第三方框架的日志级别。 name是类名或包名,level是级别
	****减少第三方的日志记录, 提高level的等级,避免过多的不必要的日志记录。
	-->
	<logger name="com.zaxxer.hikari" level="warn"/>
	<logger name="org.springframework" level="warn"/>
	<logger name="io.netty" level="warn"/>
	<logger name="org.apache.ibatis" level="warn"/>
	<logger name="io.lettuce" level="warn"/>
	<logger name="springfox" level="warn"/>
	<logger name="org.hibernate" level="warn"/>
	<!-- 开发环境下的日志配置 -->
	<root level="info">
		<appender-ref ref="console"/>
		<appender-ref ref="logFile"/>
	</root>
</configuration>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值