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/beanshttp://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.PhoneSystem.out.println(ApplicationContext.class);// interfaceorg.springframework.context.ApplicationContextSystem.out.println(p1.getClass());//class com.hqyj.entity.Phone// --- 编译的时候 ac看引用的类型,是ApplicationContext接口。// --- 程序运行的时候, ac看实际指向的对象的类型, 是ClassPathXmlApplicationContext类System.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();}@Testpublic 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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://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 ;@AutowiredStudent1 stu1;@AutowiredList<String> list;
      }
      
  • 测试方法

    @RunWith(SpringJUnit4ClassRunner.class) // 测试相关类的引入
    @ContextConfiguration("classpath:springday3.xml") // 告知spring的容器对象,需要加载的spring配置文件的路径
    public class TestStu {@Autowired // 自动装配Student stu ;@AutowiredStudent1 stu1;@AutowiredList<String> list;@Testpublic void test1(){System.out.println(list);Assert.assertNotNull(list);}@Testpublic 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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttps://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;}}@Overridepublic 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 {@AutowiredResourceBundleMessageSource 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 {@ExceptionHandlerpublic 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 configurationPUBLIC "-//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 mapperPUBLIC "-//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)@Beforepublic 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();}@Afterpublic void closeAndCommit(){sqlSession.commit(); // 提交 (更新必须提交 ,不然数据库并不会写入)sqlSession.close();}@Testpublic 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_numfrom 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_numfrom 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_typewhere goods_info.gt_id = goods_type.gt_idGROUP 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_typewhere goods_info.gt_id = goods_type.gt_idGROUP 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 setgi_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 mapperPUBLIC "-//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 mapperPUBLIC "-//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 GeneratorConfiguration 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.一次可以设置多个table2. 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, 并且工资3000EmpExample 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 configurationPUBLIC "-//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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttps://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 {@AutowiredEmpService 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. 生成tokenpublic 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. 解析tokenpublic Claims parseToken(String token){JwtParser parser = Jwts.parser();parser.setSigningKey(secret);// 设置密钥(加密的密钥和解密的密钥相同)Jws<Claims> claimsJws = parser.parseClaimsJws(token); // 解析tokenClaims 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 {@AutowiredJWTUtil jwtUtil ;@Overridepublic 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 ;@Testvoid contextLoads() {//EmpService empService = new EmpService() ; // 自己创建的对象,对象内部的注解,无效。empService.queryByPage(1, 5);}
    }
    
  • service中的使用

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

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

    @Configuration
    public class InterceptorConfig implements WebMvcConfigurer {@AutowiredLoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor).addPathPatterns("/emp/**");}
    }
    
    @Configuration
    public class BeanConfig {// bean的创建@Beanpublic 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 {@AutowiredIEmpService service ;@Testpublic 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 {@AutowiredIEmpService 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;@Beanpublic 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 {@AutowiredIGoodsInfoService 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 {@AutowiredGoodsInfoDao dao ;@Overridepublic 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;}@Overridepublic List<GoodsInfo> getAll() {return dao.findAll();}@Overridepublic 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;}@Overridepublic boolean updateGoodsInfo(GoodsInfo goodsInfo) {GoodsInfo save = dao.save(goodsInfo);return true;}@Overridepublic 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>

框架阶段总结(华清远见)相关推荐

  1. 华清远见-重庆中心-框架阶段技术总结/知识点梳理

    文章目录 华清远见-重庆中心-框架阶段技术总结/知识点梳理/个人总结 框架 Java主流框架 Spring 概念 组成 名词解释 IOC DI Spring控制台应用 1.创建一个普通的Maven项目 ...

  2. 华清远见—重庆中心——JAVA高级阶段知识点梳理

    华清远见-重庆中心--JAVA高级阶段知识点梳理 String字符串 String是一个类,属于数据类型中的引用类型.Java中所有使用""引起来的内容都是属于这个类的实例,称为字 ...

  3. 华清远见-重庆中心-前端阶段技术总结

    华清远见-重庆中心-前端阶段技术总结 HTML Hyper Text Markup Language 超文本标记语言 超文本:超级文本/超链接文本,超越了文本的限制,如多媒体文件.超链接等. 标记:也 ...

  4. 阶段总结:华清远见毕业总结

    在毕业之际,苦于自身水平不足,不够完善的技能不足以在就业形势严峻的今天找到一份适合的工作,由于我是一个电子信息工程专业出身的学生,在学校里通过专业选修等课程接触到了单片机和嵌入式等课程,从而产生了一点 ...

  5. 华清远见嵌入式开发学习的6大阶段

    分享一下华清远见嵌入式开发学习的6大阶段.19年嵌入式培训经验,值得一看. 第一阶段:嵌入式系统开发基础理论 阶段目标 掌握Linux操作系统的使用,精通使用C语言编程,熟练掌握嵌入式Linux的开发 ...

  6. 华清远见-重庆中心-JAVA基础阶段技术总结

    系列文章目录 第一章 华清远见--重庆中心-JAVA基础阶段技术总结 第二章 文章目录 系列文章目录 文章目录 前言 一.关于java 1.发展历程 2.编程开发 3.java架构 4.java的特点 ...

  7. 华清远见-重庆中心-JavaWeb后端阶段技术总结

    华清远见-重庆中心-JavaWeb后端阶段技术总结 JavaWeb 使用Java开发Web服务的技术,统称为JavaWeb. B/S与C/S模式 B/S:Browser/Server 浏览器/服务器模 ...

  8. 华清远见-重庆中心-JAVA面向对象阶段技术总结

    华清远见-重庆中心-JAVA面向对象阶段技术总结 面向对象和面向过程的编程思想 面向对象(OOP) 通过创建(new)对象,赋予对象对应的行为和特征,让这些对象相互配合来解决问题 面向过程(POP) ...

  9. 华清远见嵌入式毕业总结

    自五月入学华清以来,系统性地学习嵌入式课程已四月有余,在整体的学习过程中,有一些感受想要记录和分享.        一开始对嵌入式的整体体系,并不清楚,最开始只了解应用层和底层这两个大方向.随着课程的 ...

  10. 在华清远见培训的感受(实习总结)

    文章声明 首先声明一下,我不想卷入所谓的商业斗争中去,我只对我的培训进行一些自己的感受.不要问我选择哪家培训感受好. 文章前言 我是在重庆的华清远见培训公司进行培训的,培训内容是JAVA EE.我是一 ...

最新文章

  1. 被拒稿、被否定:读博五年间都没有发 paper 是一种怎样的体验?
  2. pandas进行数据处理常用方法与属性
  3. 蓝宝石显卡bios_这操作竟能让显卡性能暴涨?原来不是黑科技,小白都会
  4. 华为2019上半年业绩发布:华为手机海外销量已恢复禁令前80%的水平
  5. python3 value counts函数_如何将value_counts()输出转换为数据帧?
  6. Java中9大内置基本数据类型Class实例和数组的Class实例(转载)
  7. extract进程 oracle,OracleGoldenGate系列:Extract进程的恢复原理
  8. 简单的excel上传解析和错误反馈信息下载
  9. Windows自带利器:Rundll.exe高级应用
  10. ArcGIS10.2安装的详细步骤、注意事项及许可变永久操作
  11. 使用并解析 OPML 格式的订阅列表来转移自己的 RSS 订阅(概念篇)
  12. b和kb的换算_b和kb的换算(b换算成kb)
  13. 美育在计算机教育中应用,浅谈在小学信息技术课堂中有效实施美育.
  14. 开关稳压电源的晶体管
  15. 如何删除顽固的 AVI 格式的影音文件
  16. 智能网联汽车封闭测试场建设内容简介​
  17. CS61-仅使用正则表达式匹配罗马数字|Python一对一学员答疑贴
  18. 明解C语言第九章部分答案和头歌部分答案
  19. 怎样安装西门子PLC
  20. python uiautomation_蜗牛笔记-文章-UIAutomation使用(一)

热门文章

  1. 跨学科知识和实战经验#学习小组招募
  2. linux那个适合于旧电脑,最好的轻量级Linux发行版旧计算机 | MOS86
  3. 什么是 WAL?Write-ahead logging
  4. 【木友会专用】节目录制助手V2.0 (更新时间:2009-09-25)
  5. Linux查看CPU核心数
  6. 51job(前程无忧官网)1万条招聘信息的爬取
  7. vue启动成功,无法访问localhost:8080,以及用ip进行访问———无法显示此页面
  8. Meteor with的使用
  9. 没做过几个项目,简历怎么写啊
  10. 使用Git上传代码到Gitee