MyBatis
MyBatis的核心配置文件
习惯上命名为mybatis-config.xml,这个文件名仅仅只是建议,并非强制要求。将来整合Spring之后,这个配置文件可以省略,所以大家操作时可以直接复制、粘贴。
核心配置文件主要用于配置连接数据库的环境以及MyBatis的全局配置信息
核心配置文件存放的位置是src/main/resources目录下
mapper接口和映射文件要保证两个一致:
1. mapper接口的全类名和映射文件的namespace一致
2、mapper接口中的方法的方法名要和映射文件中的sql的d保持一致
返回类型
resultType:设置结果类型,即查询的数据要转换为的java类型
resultMap:自定义映射,处理多对一或一对多的映射关系
properties标签
获取参数的两种条件
1、 若者mapper接口方法的参数为单个的字面量类型
#{}的本质是占位符赋值,${}的本质是字符串拼接
此时可以通过#{}和${}以任意的内容获取参数值,一定要注意${}的单 引号问题
4、若mapper接口方法的参数为实体类类型的参数
只需要通过#{}和${}访问实体类中的属性名,就可以获取相对应的属性值,(底层调用了get方法获取)
注解
5、可以在mapper接口方法的参数上设置@Param注解
此时MyBatis会将这些参数放在map中,以两种方式进行存储
a>以@Param注解的value属性值为键,以参数为值
a>以param1, param2...为键,以参数为值
查询功能
查询所有的用户信息为map集合
若查询的数据有多条时,并且要将每条数据转换为map集合
此时有两种解决方案:
1.将mapper接口方法的返回值设置为泛型是map的list集合
List<Map<String, object>> getAllUserToMap();
2、可以将每条数据转换的map集合放在一个大的map中, 但是必须要通过@MapKey注解
将查询的某个字段的值作为大的map的键
模糊查询
1.'%$ {mohu}%'
2.concat( '%', #{mohu}, '%')-->
3."%" #{ mohu }"%"
批量查询
delete from tb_brand where id in(${ids})
动态设置表名
只能用¥{}
MyBatis框架
映射
字段名和属性名不一致的情况,如何处理映射关系
1,为查询的字段设置别名,和属性名保持一致
2、当字段符合MySQL的要求使用,而属性符合java的要求使用驼峰
此时可以在MyBat is的核心配置文件中设置一个全局配置,可以自动将下划线映射为驼峰
<settings>
<!--将下划线映射为驼峰-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
3.自定义映射resultMap:设置目定义的映射关系
id:唯一标识
type:处理映射关系的实体类的类型
常用的标签:
id: 处理主键和实体类中属性的映射关系
result:处理普通字段和实体类中属性的映射关系
column:设置映射关系中的字段名,必须是sql 查询出的某个字段
property:设置映射关系中的属性的属性名,必须是处理的实体类类型中的属性名
多级映射
1.级联
2.association:处理多对一的映射关系(处理实体类类型)
property:设置需要处理映射关系的属性的属性名
javaType:设置要处理的属性的类型
3.延时加载
一对多
用集合存储
collection:处理一对多的映射关系(处理集合类型的属性)
ofType: 设置集合类型的属性中存储的数据的类型
动态sql
Mybatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能, 它存在的意义是为了解决拼接SQL语句字符串时的痛点问题。
1、if,通过test属性中的表达式判断标签中的内容是否有效(是否会拼接到sql中)
2, where
a.若where标签中有条件成立,会自动生成where关键字
b.会自动将where标签中内容前多余的and去掉,内容后的and无法去掉
c.若where标签中没有任何一个条件成立,则where没 有任何功能
3.trim
prefix. suffix: 在标签中内容前面或后面添加指定内容
prefixoverrides. suffixoverrides:在标签中内容前面或后面去掉指定内容
4.choose、when, otherwise
相当于java中的if...else if..else
when至少设置一 个,otherwise最多设置一个
5,foreach
collection:设置要循环的数组或集合
item:用- -个字符串表示数组或集合中的每一个数据
separator:设置每次循环的数据之间的分隔符
open:循环的所有内容以什么开始
close:循环的所有内容以什么结束
缓存
一级缓存
MyBatis的一级缓存是SqlSession 级别的,即通过同一个sqlSess ion查询的数据会被缓存
再次使用同一个sqlSession查询同一条数据,会从缓存中获取
使一级缓存失效的四种情况:
1)不同的SqlSession对应不同的一级缓存
2)同一个SqlSession但是查询条件不同
3)同一个SqISession两次查询期间执行了任何- -次增删改操作
4)同一个SqISession两次查询期间手动清空了缓存
二级缓存
MyBatis的_二级缓存:
MyBatis的二级缓存是SqlSessionFactory级别的,即通过同一个SqlSessionFactory所获取的SqlSession对象
查询的数据会被缓存,在通过同一个sqlSess ionFactory所获取的sqlSession查询相同的数据会从缓存中获取
MyBatis二级缓存开启的条件:
a>在核心配置文件中,设置全局配置属性cacheEnabled= "true",默认为true,不需要设置
b>在映射文件中设置标签cache/>
c>.二级缓存必须在SqlSession关闭或提交之后有效
d>.查询的数据所转换的实体类类型必须实现序列化的接口
使二级缓存失效的情况:
两次查询之间执行了任意的增删改, 会使一级和二级缓存同时失效
查询顺序
先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。
如果二级缓存没有命中,再查询- -级缓存
如果一级缓存也没有命中,则查询数据库
SqISession关闭之后,一级缓存中的数据会写入二级缓存
分页功能
spring
IOC
IOC: Inversion of Control,翻译过来是反转控制。
DI: Dependency Injection,翻译过来是依赖注入。
DI是IoC的另一种表述方式:即组件以一些预先定义好的方式(例如: setter 方法)接受来自于容器的资源注
入。相对于IOC而言,这种表述更直接。
结论: IoC就是一种反转控制的思想,而DI是对IoC的一种具体实现。
实现
ClassPathXmlApplicationContext
通过读取类路径下的XML格式的配置文件创建I0C容器对象
FileSystemXmlApplicationContext
通过文件系统路径读取XML格式的配置文件创建IOC容器对象
创建对象
//获取IOC容器
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
//获取bean
Student studentOne = (Student) ioc.getBean("studentOne");
System.out.println(studentOne);
获取bean的三种方式
获取bean的三种方式:
1、根据bean的id获取
2,根据bean的类型获取
注意:根据类型获取bean时,要求IOC容器中有且只有一个类型匹配的bean
若没有任何一个类型匹配的bean,此时抛出异常: NoSuchBeanDefinitionException
若有多个类型匹配的bean,此时抛出异常: NoUniqueBeanDefinit ionExcept ion
3.根据bean的id和类型获取
结论:
根据类型来获取bean时,在满足bean唯- -性的前提 下
其实只是看:「对象 * instancegf *指定的类型」的返回结果
只要返回的是true就可以认定为和类型匹配,能够获取到。
即通过bean的类型、bean所继承的类的类型、bean所实现的接口的类型都可以获取bean
依赖注入
set注入
property: 通过成员变量的set方法进行赋值
name:设置需要赋值的属性名(和set方法有关)
value:设置为属性所赋的值
<property name="sid" value="1001"></property>
<property name="sname" value="张三"></property>
<property name="age" value="23"></property>
<property name="gender" value="男"></property>
构造器注入
<bean id="studentThree" class="com.atguigu.spring.pojo.Student">
<constructor-arg value="1002"></constructor-arg>
<constructor-arg value="李四"></constructor-arg>
<constructor-arg value="女"></constructor-arg>
<constructor-arg value="24" name="age"></constructor-arg>
</bean>
null值
<property name="gender" >
<null />
</property>
<: <
>: >
CDATA节其中的内容会原样解析<![CDATA[...]]>
CDATA节是xml中一个特殊的标签,因此不能写在一个属性中
<property name="sname">
<value><![CDATA[<王五>]]></value>
</property>
为类类型赋值
内部been
<!--ref:引用IOC容器中的某个bean的id-->
<property name="clazz" ref="clazzOne"></property>
<bean id="clazzOne" class="com.atguigu.spring.pojo.Clazz">
<property name="cid" value="1111"></property>
<property name="cname" value="最强王者班"></property>
</bean>
级联
级联的方式,要保证提前为clazz属性赋值或者实例化
<property name="clazz.cid" value="2222"></property>
<property name="clazz.cname" value="远大前程班"></property>
list集合赋值
标签
<property name="students">
<list>
<ref bean="studentOne"></ref>
<ref bean="studentTwo"></ref>
<ref bean="studentThree"></ref>
</list>
</property>
<property name="students" ref="studentList"></property>
<!--配置一个集合类型的bean,需要使用util的约束-->
<util:list id="studentList">
<ref bean="studentOne"></ref>
<ref bean="studentTwo"></ref>
<ref bean="studentThree"></ref>
</util:list>
map集合也类似
value:为普通类型赋值
value-ref:为类类型赋值
bean生命周期
bean的后置处理器会在生命周期的初始化前后添加额外的操作,需要实现BeanPostProcessor接口, 且配置到IOC容器中,需要注意的是,bean后置处理器不是单独针对某一个bean生效, 而是针对IOC容器中所有bean都会执行
FactoryBean
FactoryBean是一个接口,
需要创建一个类实现该接口
其中有三个方法:
getobject():通过一个对象交给I0C容器管理
getobjectType():设置所提供对象的类型
issingleton():所提供的对象是否单例
当把FactoryBean的实现类配置为bean时,会将当前类中getobject()所返回的对象交给IOC容器管理
基于xml自动装配
自动装配:
根据指定的策略,在IOC 容器中匹配某个bean,自动为bean 中的类类型的属性或接口类型的属性赋值
可以通过bean.标签中的autowire属性设置自动装配的策略
自动装配的策略:
no,default: 表示不装配,即bean 中的属性不会自动匹配某个bean为属性赋值,此时属性使用默认值
byType:根据要赋值的属性的类型,在IOC 容器中匹配某个bean,为属性赋值
byName:将要赋值的属性的属性名作为bean的id在IOC容器中匹配某个bean, 为属性赋值
总结:当类型匹配的bean 有多个时,此时可以使用byName 实现自动装配
基于注解管理bean
注解
常用注解
@Component:将类标识为普通组件
@Controller:将类标识为控制层组件
@Service:将类标识为业务层组件
@Repository:将类标识为持久层组件
扫描
context:exclude-filter:排除扫描
type:设置排除扫描的方式
type="annotation|assignable"
annotation:根据注解的类型进行排除,expression需要设置排除的注解的全类名
assignable:根据类的类型进行排除,expression需要设置排除的类的全类名
context:include-filter:包含扫描
注意:需要在context:component-scan标签中设置use-default-filters="false"
use-default-filters="true"(默认),所设置的包下所有的类都需要扫描,此时可以使用排除扫描
use-default-filters="false",所设置的包下所有的类都不需要扫描,此时可以使用包含扫描
<context:component-scan base-package="com.atguigu.spring"></context:component-scan>
@Autowired注解的原理
a>.默认通过byType的方式, 在I0C容器中通过类型匹配某个bean为属性赋值
b>若有多个类型匹配的bean,此时会自动转换,为byName的方式实现自动装配的效果
即将要赋值的属性的属性名作为bean的id匹配某个bean为属性赋值
c>若byType和byName的方式都无妨实现自动装配,即I0C容器中有多个类型匹配的bean
且这些bean的d和要赋值的属性的属性名都不一致,此时抛异常: NoUniqueBeanDefinitionExcept ion
d>此时可以在要赋值的属性上, 添加一个注解@Qualifier
通过该注解的value属性值,指定某个bean的id,将这个bean为属性赋值
AOP
概念 底层用代理的方式实现
以通过预编译方式和运行期动态代理方式实现在不修改源代码的情况下
给程序动态统一添加额外功能的一种技术。
代理
动态代理有两种:
1、jdk动态代理,要求必须有接口,最终生成的代理类和目标类实现相同的接口
在com.sun.proxy包下,类名为$proxy2
2、cglib动态代理,最终生成的代理类会继承目标类,并且和目标类在相同的包下
生成代理的工厂类
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
public Object getProxy(){
/**
* ClassLoader loader:指定加载动态生成的代理类的类加载器
* Class[] interfaces:获取目标对象实现的所有接口的class对象的数组
* InvocationHandler h:设置代理类中的抽象方法如何重写
*/
ClassLoader classLoader = this.getClass().getClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
System.out.println("日志,方法:"+method.getName()+",参数:"+ Arrays.toString(args));
//proxy表示代理对象,method表示要执行的方法,args表示要执行的方法到的参数列表
result = method.invoke(target, args);
System.out.println("日志,方法:"+method.getName()+",结果:"+ result);
} catch (Exception e) {
e.printStackTrace();
System.out.println("日志,方法:"+method.getName()+",异常:"+ e);
} finally {
System.out.println("日志,方法:"+method.getName()+",方法执行完毕");
}
return result;
}
};
return Proxy.newProxyInstance(classLoader, interfaces, h);
}
接口实现类
public class CalculatorImpl implements Calculator {
@Override
public int add(int i, int j) {
int result = i + j;
System.out.println("方法内部,result:"+result);
return result;
}
@Override
public int sub(int i, int j) {
int result = i - j;
System.out.println("方法内部,result:"+result);
return result;
}
@Override
public int mul(int i, int j) {
int result = i * j;
System.out.println("方法内部,result:"+result);
return result;
}
@Override
public int div(int i, int j) {
int result = i / j;
System.out.println("方法内部,result:"+result);
return result;
}
}
测试类
/**
* 动态代理有两种:
* 1、jdk动态代理,要求必须有接口,最终生成的代理类和目标类实现相同的接口
* 在com.sun.proxy包下,类名为$proxy2
* 2、cglib动态代理,最终生成的代理类会继承目标类,并且和目标类在相同的包下
*/
@Test
public void testProxy(){
/*CalculatorStaticProxy proxy = new CalculatorStaticProxy(new CalculatorImpl());
proxy.add(1, 2);*/
ProxyFactory proxyFactory = new ProxyFactory(new CalculatorImpl());
Calculator proxy = (Calculator) proxyFactory.getProxy();
proxy.div(1,0);
}
相关术语
横切关注点
每一个横切关注点上要做的事情都需要写一个方法来实现, 这样的方法就叫通知方法。
基于注解的AOP
切面类必须通过@Aspect注解标识为一个切面
1、在切面中,需要通过指定的注解将方法标识为通知方法
* @Before:前置通知,在目标对象方法执行之前执行
* @After:后置通知,在目标对象方法的finally字句中执行
* @AfterReturning:返回通知,在目标对象方法返回值之后执行
* @AfterThrowing:异常通知,在目标对象方法的catch字句中执行
2、切入点表达式:设置在标识通知的注解的value属性中
* execution(public int com.atguigu.spring.aop.annotation.CalculatorImpl.add(int, int)
* execution(* com.atguigu.spring.aop.annotation.CalculatorImpl.*(..)
* 第一个*表示任意的访问修饰符和返回值类型
* 第二个*表示类中任意的方法
* ..表示任意的参数列表
* 类的地方也可以使用*,表示包下所有的类
3、重用切入点表达式
* //@Pointcut声明一个公共的切入点表达式
* @Pointcut("execution(* com.atguigu.spring.aop.annotation.CalculatorImpl.*(..))")
* public void pointCut(){}
* 使用方式:@Before("pointCut()")
4、获取连接点的信息
* 在通知方法的参数位置,设置JoinPoint类型的参数,就可以获取连接点所对应方法的信息
* //获取连接点所对应方法的签名信息
* Signature signature = joinPoint.getSignature();
* //获取连接点所对应方法的参数
* Object[] args = joinPoint.getArgs();
5、切面的优先级
* 可以通过@Order注解的value属性设置优先级,默认值Integer的最大值
* @Order注解的value属性值越小,优先级越高
声明事务
springMVC
三层架构分为表述层(或表示层)、业务逻辑层、数据访问层,表述层表示前台页面和后台
servlet
RequestMapping
1、@RequestMapping注解标识的位置
@RequestMapping标识一个类:设置映射请求的请求路径的初始信息
@RequestMapping标识一个方法:设置映射请求请求路径的具体信息
2、@RequestMapping注解value属性
作用:通过请求的请求路径匹配请求
value属性是数组类型,即当前浏览器所发送请求的请求路径匹配value属性中的任何一个值
则当前请求就会被注解所标识的方法进行处理
3、@RequestMapping注解的method属性
作用:通过请求的请求方式匹配请求
method属性是RequestMethod类型的数组,即当前浏览器所发送请求的请求方式匹配method属性中的任何一中请求方式
则当前请求就会被注解所标识的方法进行处理
若浏览器所发送的请求的请求路径和@RequestMapping注解value属性匹配,但是请求方式不匹配
此时页面报错:405 - Request method 'xxx' not supported
在@RequestMapping的基础上,结合请求方式的一些派生注解:
@GetMapping,@PostMapping,@DeleteMapping,@PutMapping
SpringMVC支持路径中的占位符
@RequestMapping("/test/rest/{username}/{id}")
public String testRest(@PathVariable("id") Integer id, @PathVariable("username") String username){
System.out.println("id:"+id+",username:"+username);
return "success";
}
<a th:href="@{/test/rest/admin/1}">测试@RequestMapping注解的value属性中的占位符</a><br>
@RequestMapping注解使用路径中的占位符
传统:/deleteUser?id=1
rest:/user/delete/1
需要在@RequestMapping注解的value属性中所设置的路径中,使用{xxx}的方式表示路径中的数据
在通过@PathVariable注解,将占位符所标识的值和控制器方法的形参进行绑定
通过控制器获取形参
@RequestParam:将请求参数和控制器方法的形参绑定
@RequestParam注解的三个属性:value、required、defaultValue
value:设置和形参绑定的请求参数的名字
required:设置是否必须传输value所对应的请求参数
默认值为true,表示value所对应的请求参数必须传输,否则页面报错:
400 - Required String parameter 'xxx' is not present
若设置为false,则表示value所对应的请求参数不是必须传输,若未传输,则形参值为null
defaultValue:设置当没有传输value所对应的请求参数时,为形参设置的默认值,此时和required属性值无关
@RequestMapping("/param")
public String getParam(
@RequestParam(value = "userName", required = true, defaultValue = "hello") String username,
String password,
<form th:action="@{/param/pojo}" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登录"><br>
</form>
域对象共享数据
@RequestMapping("/test/mav")
public ModelAndView testMAV(){
/**
* ModelAndView包含Model和View的功能
* Model:向请求域中共享数据
* View:设置逻辑视图实现页面跳转
*/
ModelAndView mav = new ModelAndView();
//向请求域中共享数据
mav.addObject("testRequestScope", "hello,ModelAndView");
//设置逻辑视图
mav.setViewName("success");
return mav;
}
@RequestMapping("/test/model")
public String testModel(Model model){
//org.springframework.validation.support.BindingAwareModelMap
System.out.println(model.getClass().getName());
model.addAttribute("testRequestScope", "hello,Model");
return "success";
}
@RequestMapping("/test/modelMap")
public String testModelMap(ModelMap modelMap){
//org.springframework.validation.support.BindingAwareModelMap
System.out.println(modelMap.getClass().getName());
modelMap.addAttribute("testRequestScope", "hello,ModelMap");
return "success";
}
@RequestMapping("/test/map")
public String testMap(Map<String, Object> map){
//org.springframework.validation.support.BindingAwareModelMap
System.out.println(map.getClass().getName());
map.put("testRequestScope", "hello,map");
return "success";
}
@RequestMapping("/test/session")
public String testSession(HttpSession session){
session.setAttribute("testSessionScope", "hello,session");
return "success";
}
@RequestMapping("/test/application")
public String testApplication(HttpSession session){
ServletContext servletContext = session.getServletContext();
servletContext.setAttribute("testApplicationScope", "hello,application");
return "success";
}
视图控制器
<!--开启mvc的注解驱动-->
<mvc:annotation-driven />
<!--
视图控制器:为当前的请求直接设置视图名称实现页面跳转
若设置视图控制器,则只有视图控制器所设置的请求会被处理,其他的请求将全部404
此时必须在配置一个标签:<mvc:annotation-driven />
-->
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>
RESTful
设置处理请求方式的过滤器
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<form th:action="@{/user}" method="post">
<input type="hidden" name="_method" value="put">
<input type="submit" value="修改用户信息">
</form
1.请求方式必须为post
2、当前请求必须传输请求参数_ method,_ method的值才是最终的请求方式
RequerBody注解
导入jackson的依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.1</version>
</dependency>
在SpringMVC的配置文件中设置<mvc:annotation-driven />
在处理请求的控制器方法的形参位置,直接设置json格式的请求参数要转换的java类型的形参,使用@RequestBody注解标识即可
@RequestMapping("/test/RequestBody/json")
public void testRequestBody(@RequestBody Map<String, Object> map, HttpServletResponse response) throws IOException {
System.out.println(map);
response.getWriter().write("hello,RequestBody");
}