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看实际指向的对象的类型, 是
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();
}
@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 < #{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>