最近要写接口,赶时间,看了下黑马视频,原理没有涉及到,后续再好好刷一次,具体掌握如何使用
参考文章: https://blog.youkuaiyun.com/hejingyuan6/article/details/36203505#
文章目录
Mybatis
什么是Mybatis?
持久层框架,底层是用java编写的
它封装了jdbc操作的很多细节,只用关注sql本身,无需关注注册驱动,创建链接等繁杂过程,使用了ORM(对象关系映射)思想实现了结果集的封装
技术解决方案
JDBC:
connection
PreparedStatement
ResultSet
Spring的JdbcTemplate
Spring对JDBC的简单封装
Apache的DBUtils
他和Spring的JDBCTemplate很像,也是对JDBC的简单封装
Mybatis环境搭建
-
创建maven工程导入坐标
-
创建实体类和dao层接口
-
创建mybatis的主配置文件
-
创建映射配置文件
注意事项
-
创建.xml文件是为了保持和之前文件目录结构保持一致。持久层操作接口名称和映射文件一一对应
-
mybatis映射文件位置必须和dao层接口的包结果相同
-
映射配置文件的mapper标签namespace属性的取值必须是dao接口的全限定类名
-
映射配置文件操作配置,select id 属性值必须是dao接口的方法名
搭建的不同方式
有两种方式,一种是XML文件的方式,一种是注解的方式,还可以实现你的接口来实现功能
XML文件的方式配置
使用XML方式,需要在主配置文件中指定mapper的resource
不需要使用注解,需要你生成一个XML文件来存放映射关系
使用XML方式,需要在主配置文件中指定mapper的resource
不需要使用注解,需要你生成一个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>
<!-- 配置properties-->
<properties resource="jdbcConfig.properties"></properties>
<!--使用typeAliases配置别名,它只能配置domain中类的别名 -->
<typeAliases>
<package name="com.itheima.domain"></package>
</typeAliases>
<!--配置环境-->
<environments default="mysql">
<!-- 配置mysql的环境-->
<environment id="mysql">
<!-- 配置事务 -->
<transactionManager type="JDBC"></transactionManager>
<!--配置连接池-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</dataSource>
</environment>
</environments>
<!-- 配置映射文件的位置 -->
<mappers>
<package name="com.itheima.dao"></package>
</mappers>
映射文件
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--指定在哪个类中-->
<mapper namespace="com.mybatisexercise.dao.UserDao">
<!--指定类中的方法和返回值类型-->
<select id="findAll" resultType="com.mybatisexercise.domain.User">
select * from user;
</select>
</mapper>
注解方式
使用注解的方式,需要在主配置文件中指定mapper的class
然后在你的接口上使用注解
读取配置文件两种方式
使用类加载器
它只能读取类路径的配置文件
使用servletContext对象的个体Real Path()
工厂模式的优势
降低类之间的依赖关系,生产SqlSession就使用了工厂模式
代理模式的优势
代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。
更通俗的说,代理解决的问题当两个类需要通信时,引入第三方代理类,将两个类的关系解耦,让我们只了解代理类即可,而且代理的出现还可以让我们完成与另一个类之间的关系的统一管理,但是切记,代理类和委托类要实现相同的接口,因为代理真正调用的还是委托类的方法。
1、静态代理
由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。
优点:
代理使客户端不需要知道实现类是什么,怎么做的,而客户端只需知道代理即可(解耦合),对于如上的客户端代码,newUserManagerImpl()可以应用工厂将它隐藏,如上只是举个例子而已。
缺点:
理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
2)代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了
2、动态代理
动态代理是在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象
在程序运行时运用反射机制动态创建而成。
在Java中要想实现动态代理机制,需要java.lang.reflect.InvocationHandler 接口和 java.lang.reflect.Proxy 类的支持
java.lang.reflect.InvocationHandler接口的定义如下:
//Object proxy:被代理的对象
//Method method:要调用的方法
//Object[] args:方法调用时所需要参数
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
java.lang.reflect.Proxy类的定义如下:
//CLassLoader loader:类的加载器
//Class<?> interfaces:得到全部的接口
//InvocationHandler h:得到InvocationHandler接口的子类的实例
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
具体实现:
//动态代理类只能代理接口(不支持抽象类),代理类都需要实现InvocationHandler类,实现invoke方法。该invoke方法就是调用被代理接口的所有方法时需要调用的,该invoke方法返回的值是被代理接口的一个实现类
public class LogHandler implements InvocationHandler {
// 目标对象
private Object targetObject;
//绑定关系,也就是关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法。
public Object newProxyInstance(Object targetObject){
this.targetObject=targetObject;
//该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
//第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
//第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口
//第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
//根据传入的目标返回一个代理对象
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(),this);
}
@Override
//关联的这个实现类的方法被调用时将被执行
/*InvocationHandler接口的方法,proxy表示代理,method表示原对象被调用的方法,args表示方法的参数*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("start-->>");
for(int i=0;i<args.length;i++){
System.out.println(args[i]);
}
Object ret=null;
try{
/*原对象方法调用前处理日志信息*/
System.out.println("satrt-->>");
//调用目标方法
ret=method.invoke(targetObject, args);
/*原对象方法调用后处理日志信息*/
System.out.println("success-->>");
}catch(Exception e){
e.printStackTrace();
System.out.println("error-->>");
throw e;
}
return ret;
}
}
优点:
我们可以通过LogHandler代理不同类型的对象,如果我们把对外的接口都通过动态代理来实现,那么所有的函数调用最终都会经过invoke函数的转发,因此我们就可以在这里做一些自己想做的操作,比如日志系统、事务、拦截器、权限控制等。这也就是AOP(面向切面编程)的基本原理。
静态代理和动态代理区别:
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。而且动态代理的应用使我们的类职责更加单一,复用性更强
Mybatis工作原理
- Mybatis在运行时,首先通过dom4j(还有其他XML解析技术)解析XML来解析你的SqlMapConfig文件,转换成input stream流。
- 使用input stream中的数据取出来生成一个Configuration配置对象,把Configuration中的参数取出来创建mysql connect对象,并构建SqlSessionFactory。
- SqlSessionFactory单例模式创建SqlSession对象
- SqlSession对象需要传入 .class 参数,并返回一个 .class接口的动态代理对象,供你调用
- 可以对这个对象进行操作,在执行时通过反射和动态代理来执行你的语句。
pojo对象
使用ognl对象解析对象字段的值,#{} 或 ${}括号中的值是pojo属性名称
Mysql配置文件
Mysql在Window中不区分大小写,在linx中区分大小写
mybatis实体类和数据库的字段不匹配两种解决方案:
一、更改xml配置文件sql语句和参数
<insert>
insert into table (username,Sex) values(#{username},#{UserSex});
</insert>
<select>
select username,sex as UserSex from user;
</select>
二、ResultMap
上面那种方法语句多的时候太过于繁琐
可以使用resultMap
<!--左边的是实体类字段,右边的是数据库字段,一一对应-->
<!-- 配置 查询结果的列名和实体类的属性名的对应关系 ,这样写不区分大小写-->
<resultMap id="userMap" type="uSeR">
<!-- 主键字段的对应 -->
<id property="userId" column="id"></id>
<!--非主键字段的对应-->
<result property="userName" column="username"></result>
<result property="userAddress" column="address"></result>
<result property="userSex" column="sex"></result>
<result property="userBirthday" column="birthday"></result>
</resultMap>
<!-- 根据queryVo的条件查询用户 -->
<select id="findUserByVo" parameterType="com.itheima.domain.QueryVo" resultMap="userMap">
select * from user where username like #{user.username}
</select>
注意事项
在使用ResuleMap时,在返回值定义要使用resultMap,而不是使用ResultType! 否则会找不到你这个类型而报错
创建自定义注解
创建一个接口
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Select {
/**
* 配置SQL语句的
* @return
*/
String value();
}
在代码上方使用注解
@Target
说明了 Annotation 所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、
类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。
在Annotation类型的声明中使用了target可更加明晰其修饰的目标。
作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
取值(ElementType)有:
1.CONSTRUCTOR:用于描述构造器
2.FIELD:用于描述域
3.LOCAL_VARIABLE:用于描述局部变量
4.METHOD:用于描述方法
5.PACKAGE:用于描述包
6.PARAMETER:用于描述参数
7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
@Retention:
@Retention定义了该 Annotation 被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。
作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)
取值(RetentionPoicy)有:
1.SOURCE:在源文件中有效(即源文件保留)
2.CLASS:在class文件中有效(即class保留)
3.RUNTIME:在运行时有效(即运行时保留)
@Documented:
@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。
@Inherited:
@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。
当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。
JNDI数据源
JNDI(java naming and directory interface) 模仿的window系统的注册表。
注册表是kv类型的。k是绝对路径+名称,v是存放的数据。
JNDI中存放的就是对象。
JNDI视频没看,后期要看可以看58.59这两个视频
XML标签导入外部配置文件
resource方式
resuource来指定classpath的下的配置文件
<!-- 配置properties-->
<properties resource="jdbcConfig.properties"></properties>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</dataSource>
url绝对路径的方式
<properties url="G:\BaiduNetdiskDownload\第三天代码\day03_eesy_01datasourceAndTx\src\main\resources\jdbcConfig.properties"></properties>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</dataSource>
实体类和实体类接口别名
TypeAliases
在使用标签的时候,返回值中写全路径,很繁琐
可以使用typeAliases来去进行取别名
虽然配置了,但一次一个类,太麻烦
<typeAliases>
<!--typeAlias用于配置别名。type属性指定的是实体类全限定类名。alias属性指定别名,当指定了别名就再区分大小写 -->
<typeAlias type="com.itheima.domain.User" alias="user"></typeAlias>
</typeAliases>
Package
使用上面的方式进行取别名,但是如果实体类太多,工作还是很繁琐,就可以使用标签来完成
直接配置一个包里面所有的类的别名
<typeAliases>
<!--可以指定一个包,里面的类别名全部都是是类的名称,不区分大小写-->
</typeAliases><package name="com.itheima.domain"></package>
</typeAliases>
<!--也可以写在mappers中,package是指定dao接口所在的包,指定了之后不需要写mapper和resource了或者class了-->
<mappers>
<!-- <mapper resource="com/mybatisexercise/dao/UserDao.xml"/>
<mapper class="com.mybatisexercise.dao.UserDao"/>-->
<package name="com.exercise.dao"></package>
</mappers>
注意事项
上面两个注解只能在SqlMapConfig.xml中使用,不能在和实体类对应的xml文件中使用
Mybatis连接池
什么是连接池?
连接池可以减少我们获取链接所消耗的时间和开销
线程池,就是一个线程安全的集合来存储着连接对象。还实现了队列的特性:先进先出
Mybatis连接池
三种类型的连接池
主配置文件SQLMapConfig.xml中的dataSource标签,type就是表示使用何种连接池的方式
type的取值:
POOLED 采用用了传统的javax.sql.DataSource规范的连接池 mybatis中有针对规范的实现
UNPOOLED 采用了传统获取连接的方式,虽然也实现了javax.sql.DataSource接口,但是没有使用池的思想。他每次使用都会创建一个新的连接
JNDI 采用了服务器提供的JNDI技术实现的,来获取DataSource对象,不同服务器能拿到的DataSource对象是不一样的。
注意:如果不是Web或者Maven的war工程是不能使用的,我们实际开发中使用的是Tomcat服务器,采用的连接池使用的是dbcp连接池
<dataSource type="POOLED">
<!-- 配置连接数据库的4个基本信息 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/eesy_mybatis"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</dataSource>
Mybatis事务
什么是事务?事物的四大特性
对数据库的事务而言,应该具有以下几点:创建(create)、提交(commit)、回滚(rollback)、关闭(close)。对应地,MyBatis将事务抽象成了Transaction接口:其接口定义如下:
不考虑隔离性会产生的问题
事务的四种隔离机制
原文章连接:https://www.cnblogs.com/ubuntu1/p/8999403.html
数据库事务的隔离级别有4种,由低到高分别为Read uncommitted 、Read committed 、Repeatable read 、Serializable 。而且,在事务的并发操作中可能会出现脏读,不可重复读,幻读。
Read uncommitted
读未提交,顾名思义,就是一个事务可以读取另一个未提交事务的数据。
事例:老板要给程序员发工资,程序员的工资是3.6万/月。但是发工资时老板不小心按错了数字,按成3.9万/月,该钱已经打到程序员的户口,但是事务还没有提交,就在这时,程序员去查看自己这个月的工资,发现比往常多了3千元,以为涨工资了非常高兴。但是老板及时发现了不对,马上回滚差点就提交了的事务,将数字改成3.6万再提交。
分析:实际程序员这个月的工资还是3.6万,但是程序员看到的是3.9万。他看到的是老板还没提交事务时的数据。这就是脏读
那怎么解决可能的不可重复读问题?Repeatable read !
Read committed
读提交,顾名思义,就是一个事务要等另一个事务提交后才能读取数据。
事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(程序员事务开启),收费系统事先检测到他的卡里有3.6万,就在这个时候!!程序员的妻子要把钱全部转出充当家用,并提交。当收费系统准备扣款时,再检测卡里的金额,发现已经没钱了(第二次检测金额当然要等待妻子转出金额事务提交完)。程序员就会很郁闷,明明卡里是有钱的…
分析:这就是读提交,若有事务对数据进行更新(UPDATE)操作时,读操作事务要等待这个更新操作事务提交后才能读取数据,可以解决脏读问题。但在这个事例中,出现了一个事务范围内两个相同的查询却返回了不同数据,这就是不可重复读。
Repeatable read
重复读,就是在开始读取数据(事务开启)时,不再允许修改操作
事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(事务开启,不允许其他事务的UPDATE修改操作),收费系统事先检测到他的卡里有3.6万。这个时候他的妻子不能转出金额了。接下来收费系统就可以扣款了。
分析:重复读可以解决不可重复读问题。写到这里,应该明白的一点就是,不可重复读对应的是修改,即UPDATE操作。但是可能还会有幻读问题。因为幻读问题对应的是插入INSERT操作,而不是UPDATE操作。
什么时候会出现幻读?
事例:程序员某一天去消费,花了2千元,然后他的妻子去查看他今天的消费记录(全表扫描FTS,妻子事务开启),看到确实是花了2千元,就在这个时候,程序员花了1万买了一部电脑,即新增INSERT了一条消费记录,并提交。当妻子打印程序员的消费记录清单时(妻子事务提交),发现花了1.2万元,似乎出现了幻觉,这就是幻读。
那怎么解决幻读问题?Serializable!
Serializable 序列化
Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
值得一提的是:大多数数据库默认的事务隔离级别是Read committed,比如Sql Server , Oracle。Mysql的默认隔离级别是Repeatable read。
Mybatis中基于XML配置的动态SQL语句的拼接
多条件查询
mappers配置文件中的几个标签
<if>
<where>
<foreach>
<sql>
下面介绍他们的使用:
where标签
不用标签正常是
<select id="findUserByCondition" resultMap="UserMap" parameterType="com.itheima.domain.User">
select * from user where 1=1
<if test="userName!=null">
and username=#{username}
</if>
<if test="userSex!=null">
and sex=#{userSex}
</if>
</select>
用了where标签就是
<select id="findUserByCondition" resultMap="UserMap" parameterType="com.itheima.domain.User">
select * from user
<where>
<if test="userName!=null">
and username=#{username}
</if>
<if test="userSex!=null">
and sex=#{userSex}
</if>
</where>
</select>
if标签
不用if标签,无法进行判断到底有没有传参数
在多条件查询时必须使用这个标签!
<select id="findUserByCondition" resultMap="UserMap" parameterType="com.itheima.domain.User">
select * from user where 1=1
<where>
<if test="userName!=null">
and username=#{username}
</if>
<if test="userSex!=null">
and sex=#{userSex}
</if>
</where>
</select>
foreach标签
遍历拼接字符的一个标签
<select id="findUserInIds" resultMap="userMap" parameterType="QueryVo">
select * from user
<where>
<if test="ids != null and ids.size()>0">
<foreach collection="ids" open="and id in (" close=")" item="uid" separator=",">
#{uid}
</foreach>
</if>
</where>
</select>
Mybatis多表查询
一对一
实体和实体是一对一的关系,一对一使用查询使用
<resultMap id="userMap" type="account">
<!--这个是主键-->
<id property="id" cloumn="aid"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<association property="user" column="uid" javaType="User">
<id property="id" cloumn="id"></id>
<result property="user" column="username"></result>
<result property="address" column="address"></result>
<result property="sex" column="sex"></result>
<result property="birthday" column="birthday"></result>
<result>
</association>
</resultMap>
一对多
一对多,多的那部分实体类使用集合的方式来装数据(list,array等)
<resultMap id="userMap" type="user">
<!--这个是主键-->
<id property="id" cloumn="id"></id>
<result property="username" column="username"></result>
<result property="address" column="address"></result>
<result property="sex" column="sex"></result>
<result property="birthday" column="birthday"></result>
<!--实体对象中定义的集合名字,oftype是集合中装填的类型,这里取别名了-->
<collection property="accounts" ofType="account">
<id column="aid" property="id"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
</collection>
</resultMap>
多对多
<!--这个其实和一对多是一样的,把其中一个拿出来就行了-->
<!--定义role表的ResultMap-->
<resultMap id="roleMap" type="role">
<id property="roleId" column="rid"></id>
<result property="roleName" column="role_name"></result>
<result property="roleDesc" column="role_desc"></result>
<collection property="users" ofType="user">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="address" property="address"></result>
<result column="sex" property="sex"></result>
<result column="birthday" property="birthday"></result>
</collection>
</resultMap>
Mybatis延迟加载
什么是延迟加载?
在真正使用数据是再发起查询,不用的时候不查。按需加载(懒加载)
<!--必须写column属性,用户根据id查询时,所需要的参数的值
select是表示调用的方法-->
<!-- 定义封装account和user的resultMap -->
<resultMap id="accountUserMap" type="account">
<id property="id" column="id"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<!-- 一对一的关系映射:配置封装user的内容
select属性指定的内容:查询用户的唯一标识:用什么去查
column属性指定的内容:用户根据id查询时,所需要的参数的值
-->
<association property="user" column="uid" javaType="user" select="com.itheima.dao.IUserDao.findById"></association>
</resultMap>
<!--同时还要开启延迟加载的开关,在sqlMapConfig中配置为true-->
<!--配置参数-->
<settings>
<!--开启Mybatis支持延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"></setting>
</settings>
什么是立即加载?
不管用不用,只要一调用就立马发起查询
对应四种表关系,一对一,多对一,一对一,多对多。
**一对多,多对多:**通常情况下采用延迟加载
**一对一,多对一:**通常情况下使用立即加载
Mybatis的缓存
什么是缓存?
存在于内存中的临时数据,下次查询时,不需要再次查询,从而快速的获取数据。
为什么使用缓存?缓存的配置
减少和数据库的交互次数,提高执行效率。
什么样数据适用缓存,什么样数据不能使用?
**适用缓存:**经常查询的,不经常改变的。
数据的正确与否队最终结果影响不大。
不适用缓存:
经常改变的数据
数据的正确与否队最终结果影响大
Mybatis的一级缓存和二级缓存
一级缓存:
它指的是Mybatis中SqlSession对象的缓存,默认是开启的
当我们执行查询后,查询的结果会同时存入到sql’session为我们提供一块区域
该区域的结构是一个map,当我们再次查询相同的数据时,mybatis会先去sqlsessin中查询是否有,有的话直接拿出来。
当sqlsession过期后,缓存也就消失了
清除一级缓存,下面两个都可以清除缓存
调用
sqlsession.clearCache()
sqlsession.close()
一级缓存在调用sqlsession的修改,添加,删除,commit(),close()等方法,就会清空一级缓存
缓存的同步
一级缓存在调用sqlsession的修改,添加,删除,commit(),close()等方法,就会清空一级缓存,再次查询时就会重新查询
二级缓存:
它指的是Mybatis中SqlSessionFactory对象的缓存。有同一个sqlSessionFactory创建的sqlsession共享缓存内容。二级缓存默认是不开启的。
二级缓存存放的是数据,不是对象!也就是对象会消除,数据不会,在查询二级缓存时,会新建对象指向数据,所以不同sqlsession使用同一个二级缓存时不是同一个对象
使用步骤:
第一步:让Mybatis框架支持二级缓存(在SQLMapConfig.xml中配置)
第二步:让当前映射文件支持二级缓存(在IUserDao.xml中配置)
<cache/>
第三步:让当前的操作支持二级缓存,(在select标签中配置)
<select id="findById" parameterType="INT" resultType="user" UserCache="true">
select * from user where id =#{id}
</select>
Mybatis注解开发
没看
从72开始看,看到75
注解开发只会用注解的方式省略掉实体类对应的.xml文件,sqlmapconfig.xml文件依然存在
单表CRUD代理Dao方式
crud中一共有四个注解 @Insert @Update @Delete @Select
使用时直接写在你的实体接口上面写上直接调用即可
public interface IUserDao {
/**
* 查询所有用户
* @return
*/
@Select("select * from user")
List<User> findAll();
/**
* 保存用户
* @param user
*/
@Insert("insert into user(username,address,sex,birthday)values(#{username},#{address},#{sex},#{birthday})")
void saveUser(User user);
/**
* 更新用户
* @param user
*/
@Update("update user set username=#{username},sex=#{sex},birthday=#{birthday},address=#{address} where id=#{id}")
void updateUser(User user);
/**
* 删除用户
* @param userId
*/
@Delete("delete from user where id=#{id} ")
void deleteUser(Integer userId);
/**
* 根据id查询用户
* @param userId
* @return
*/
@Select("select * from user where id=#{id} ")
User findById(Integer userId);
/**
* 根据用户名称模糊查询
* @param username
* @return
*/
// @Select("select * from user where username like #{username} ")
@Select("select * from user where username like '%${value}%' ")
List<User> findUserByName(String username);
/**
* 查询总用户数量
* @return
*/
@Select("select count(*) from user ")
int findTotalUser();
}
注意事项
如果注解和XML两种方式都存在,然后在sqlMapConfig.xml中配置选择其中一种,是会报错的。
Mybatis要求只能存在一种方式
注解开发实体类和数据库字段名称不匹配的情况
@Results注解
//这里的id是让别人来引用的,不用再写一遍了
@Results(id="userMap",value={
@Result(id=true,column = "id",property = "userId"),
@Result(column = "username",property = "userName"),
@Result(column = "address",property = "userAddress"),
@Result(column = "sex",property = "userSex"),
@Result(column = "birthday",property = "userBirthday"),
@Result(property = "accounts",column = "id",
many = @Many(select = "com.itheima.dao.IAccountDao.findAccountByUid",
fetchType = FetchType.LAZY))
})
/**
* 根据id查询用户
* @param userId
* @return
*/
//这里就引用了上面写好的Results
@Select("select * from user where id=#{id} ")
@ResultMap("userMap")
//@ResultMap("userMap")
//上面的整体的写法,但在只有一个参数的时候可以省略掉
User findById(Integer userId);
一对一
/**
* 查询所有用户
* @return
*/
@Select("select * from user")
@Results(id="userMap",value={
@Result(id=true,column = "id",property = "userId"),
@Result(column = "username",property = "userName"),
@Result(column = "address",property = "userAddress"),
@Result(column = "sex",property = "userSex"),
@Result(column = "birthday",property = "userBirthday"),
@Result(property = "accounts",column = "id",
many = @Many(select = "com.itheima.dao.IAccountDao.findAccountByUid",
fetchType = FetchType.LAZY))
})
List<User> findAll();
多表查询操作
mybatis
33 34中流程原理
preparedstatement