目录
1.MyBatis CRUD工具类封装
package com.qfedu.util;
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 java.io.IOException;
import java.io.InputStream;
public class MyBatisUtil {
private static SqlSessionFactory factory;
//声明锁
private static final ThreadLocal<SqlSession>local=new ThreadLocal<SqlSession>();
//初始化factory对象
static{
//获取流
try {
InputStream is = Resources.getResourceAsStream("MyBatis_config.xml");
factory = new SqlSessionFactoryBuilder().build(is);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
return getSqlSession(false);
}
//获取sqlSession对象的方法
public static SqlSession getSqlSession(boolean isAutoCommit){
SqlSession sqlSession = local.get();
if (sqlSession==null){
//创建sqlSession
sqlSession = factory.openSession(isAutoCommit);
local.set(sqlSession);
}
return sqlSession;
}
public static <T extends Object> T getMapper(Class<T>c){
SqlSession sqlSession = getSqlSession(true);
return sqlSession.getMapper(c);
}
}
2.MyBatis关联查询 添加操作
2.1.一对一添加操作(以用户信息表为例)
2.2一对一查询操作
2.2.1 studentMapper映射文件
<?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">
<mapper namespace="com.qfedu.dao.UserDao">
<sql id="select">
user_id,user_name,user_pwd,user_realname,user_img
</sql>
<insert id="insertUser" useGeneratedKeys="true" keyProperty="userId">
insert into users (<include refid="select"/>)
values(#{userId},#{userName},#{userPwd},#{userRealName},#{userImg})
</insert>
<resultMap id="queryUser" type="user">
<id column="user_id" property="userId"/>
<result column="user_name" property="userName"/>
<result column="user_pwd" property="userPwd"/>
<result column="user_realname" property="userRealName"/>
<result column="user_img" property="userImg"/>
<association property="detail" select="com.qfedu.dao.DetailDao.queryDetailByUid" column="user_id"/>
</resultMap>
<select id="queryUser" resultMap="queryUser">
SELECT user_id,user_name,user_pwd,user_realname,user_img FROM users WHERE user_name=#{userName}
</select>
</mapper>
2.2.2主配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbc.properties">
</properties>
<typeAliases>
<typeAlias type="com.qfedu.dto.Detail" alias="detail"/>
<typeAlias type="com.qfedu.dto.User" alias="user"/>
<typeAlias type="com.qfedu.dto.Clazz" alias="class"/>
<typeAlias type="com.qfedu.dto.Students" alias="Students"/>
</typeAliases>
<!--配置连接数据库的环境-->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver_name}"/>
<property name="url" value="${driver_url}"/>
<property name="username" value="${driver_username}"/>
<property name="password" value="${driver_password}"/>
</dataSource>
</environment>
</environments>
<!--引入mybatis的映射文件-->
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
<mapper resource="mappers/DetailMapper.xml"/>
<mapper resource="mappers/ClazzMapper.xml"/>
<mapper resource="mappers/StudentMapper.xml"/>
</mappers>
</configuration>
2.3.一对多查询操作
2.3.1连接查询
映射文件(classMapper)
<?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">
<mapper namespace="com.qfedu.dao.ClazzDao">
<resultMap id="queryClazz" type="class">
<id column="cid" property="classId"/>
<result column="cname" property="className"/>
<result column="cdesc" property="className"/>
<collection property="students" ofType="Students">
<result column="sid" property="studentId"/>
<result column="sname" property="studentName"/>
<result column="sage" property="studentAge"/>
<result column="scid" property="studentClassId"/>
</collection>
</resultMap>
<select id="queryClazz" resultMap="queryClazz">
SELECT cid,cname,cdesc,sid,sname,sage
from classes c inner join students s on c.cid=s.scid WHERE cid=#{classId};
</select>
</mapper>
2.3.2子查询
2.3.2.1映射文件(classMapper)
<?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">
<mapper namespace="com.qfedu.dao.ClazzDao">
<resultMap id="queryClazz" type="class">
<id column="cid" property="classId"/>
<result column="cname" property="className"/>
<result column="cdesc" property="className"/>
<collection property="students" select="com.qfedu.dao.StudentsDao.queryStudentByClassId" column="cid"/>
</resultMap>
<select id="queryClazz" resultMap="queryClazz">
SELECT cid,cname,cdesc from classes WHERE cid=#{classId};
</select>
</mapper>
3.2.2.2映射文件(studentMapper)
<?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">
<mapper namespace="com.qfedu.dao.ClazzDao">
<resultMap id="queryClazz" type="class">
<id column="cid" property="classId"/>
<result column="cname" property="className"/>
<result column="cdesc" property="className"/>
<collection property="students" select="com.qfedu.dao.StudentsDao.queryStudentByClassId" column="cid"/>
</resultMap>
<select id="queryClazz" resultMap="queryClazz">
SELECT cid,cname,cdesc from classes WHERE cid=#{classId};
</select>
</mapper>
2.3多对一查询操作
2.3.1关联查询
<?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">
<mapper namespace="com.qfedu.dao.StudentsDao">
<resultMap id="queryStudentByStudentId" type="Students">
<id column="sid" property="studentId"/>
<result column="sname" property="studentName"/>
<result column="sage" property="studentAge"/>
<result column="scid" property="studentClassId"/>
<result column="cname" property="clazz.className"/>
<result column="cdesc" property="clazz.classDesc"/>
</resultMap>
<select id="queryStudentByStudentId" resultMap="queryStudentByStudentId">
select sid,sname,sage,scid,cname,cdesc
from students s inner join classes c
on s.scid=c.cid
WHERE s.sid=#{studentId};
</select>
</mapper>
2.3.2子查询
classMapper映射文件
<?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">
<mapper namespace="com.qfedu.dao.ClazzDao">
<resultMap id="queryClazz" type="class">
<id column="cid" property="classId"/>
<result column="cname" property="className"/>
<result column="cdesc" property="classDesc"/>
</resultMap>
<select id="queryClazz" resultMap="queryClazz">
SELECT cid,cname,cdesc from classes WHERE cid=#{classId}
</select>
</mapper>
studentMapper映射文件
<?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">
<mapper namespace="com.qfedu.dao.StudentsDao">
<resultMap id="queryStudentByStudentId" type="Students">
<id column="sid" property="studentId"/>
<result column="sname" property="studentName"/>
<result column="sage" property="studentAge"/>
<result column="scid" property="studentClassId"/>
<association property="clazz" select="com.qfedu.dao.StudentsDao.queryStudentByStudentId" column="scid"/>
</resultMap>
<select id="queryStudentByStudentId" resultMap="queryStudentByStudentId">
select sid,sname,sage,scid
from students WHERE sid=#{studentId};
</select>
</mapper>
3.动态sql
3.1案例(条件查询)动态拼接sql语句
3.1.1 if where 标签
<?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">
<mapper namespace="com.qfedu.dao.MemberDao">
<resultMap id="searchMember" type="Member">
<id column="member_id" property="memberId"/>
<result column="member_nick" property="memberNick"/>
<result column="member_gender" property="memberGender"/>
<result column="member_age" property="memberAge"/>
<result column="member_city" property="memberCity"/>
</resultMap>
<select id="searchMember" resultMap="searchMember">
select member_id,member_nick,member_gender,member_age,member_city from members
<where>
<if test="memberGender!=null">
and member_gender=#{memberGender}
</if>
<if test="minAge!=0">
and member_age >=#{minAge}
</if>
<if test="maxAge!=0">
and member_age <=#{maxAge}
</if>
</where>
order by member_age
</select>
</mapper>
<!--test是进行条件判断的判断语句--> test="memberGender!=null"
进行拼接的过程,如果说第一个条件不满足条件where标签会自动去掉其中的“and”
3.1.2 trim标签
<?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">
<mapper namespace="com.qfedu.dao.MemberDao">
<resultMap id="searchMember" type="Member">
<id column="member_id" property="memberId"/>
<result column="member_nick" property="memberNick"/>
<result column="member_gender" property="memberGender"/>
<result column="member_age" property="memberAge"/>
<result column="member_city" property="memberCity"/>
</resultMap>
<select id="searchMember" resultMap="searchMember">
select member_id,member_nick,member_gender,member_age,member_city from members
<trim prefix="where" prefixOverrides="and||or" suffix="order by member_age">
<if test="memberGender!=null">
and member_gender=#{memberGender}
</if>
<if test="minAge!=0">
and member_age >=#{minAge}
</if>
<if test="maxAge!=0">
and member_age <=#{maxAge}
</if>
</trim>
</select>
</mapper>
trim标签prefix属性表示的是前缀 prefixoverrides表示的是消除前缀的字符 suffix表示的是后缀
3.1.3 foreach标签
<?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">
<mapper namespace="com.qfedu.dao.MemberDao">
<resultMap id="searchMember" type="Member">
<id column="member_id" property="memberId"/>
<result column="member_nick" property="memberNick"/>
<result column="member_gender" property="memberGender"/>
<result column="member_age" property="memberAge"/>
<result column="member_city" property="memberCity"/>
</resultMap>
<select id="searchMemberByCity" resultMap="searchMember">
select member_id,member_nick,member_gender,member_age,member_city from members
where member_city in
<foreach collection="list" separator="," item="cityName" open="(" close=")">
#{cityName}
</foreach>
</select>
</mapper>
4.模糊查询
4.1代码部分
4.1.1Dao层
public List<Member> searchMemberByKeyWord(HashMap<String,Object>params);
4.1.2xml层
<?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">
<mapper namespace="com.qfedu.dao.MemberDao">
<resultMap id="searchMember" type="Member">
<id column="member_id" property="memberId"/>
<result column="member_nick" property="memberNick"/>
<result column="member_gender" property="memberGender"/>
<result column="member_age" property="memberAge"/>
<result column="member_city" property="memberCity"/>
</resultMap>
<select id="searchMemberByKeyWord" parameterType="java.lang.String" resultMap="searchMember">
select member_id,member_nick,member_gender,member_age,member_city from members where
member_nick like '%${keyWord}%'
</select>
</mapper>
4.1.3测试类
@Test
public void searchMemberByKeyWord() {
//创建哈希表
HashMap<String,Object>params = new HashMap<String,Object>();
params.put("keyWord","花");
MemberDao memberDao = getSqlSession().getMapper(MemberDao.class);
List<Member> members = memberDao.searchMemberByKeyWord(params);
for (Member member : members) {
System.out.println(member);
}
}
4.2${}和#{}的区别对比
1.${key}表示的是获取参数,先获取参数的值拼接到sql语句中,再编译执行sql语句;可能引起sql注入问题
2.#{key}表示的是获取参数时,先完成sql编译(预编译),预编译之后再将获取的参数设置到sql语句当中,可以避免sql注入问题
5.日志的配置
5.1日志记录级别
日志级别 | 日志详情 |
---|---|
fatal | 出现非常严重的错误事件,这些事件可能导致程序异常终止 |
error | 虽有错误,但允许应用程序继续运行 |
warn | 运行环境潜藏着危害 |
info | 报告信息 |
debug | 细粒度的信息事件,对应于程序的调试 |
5.2日志的配置流程
5.2.1添加Maven依赖
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
5.2.2配置properties文件模板
log4j.rootLogger=debug,stdout log4j.logger.org.mybatis.example.BlogMapper=trace log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=[%t]%5p-%n%m
6.Druid数据库连接池整合
6.1常见连接池性能比较
性能方面 hikariCP>druid>tomcat-jdbc>dbcp>c3p0 。
1:hikariCP的高性能得益于最大限度的避免锁竞争。
2:druid功能最为全面,sql拦截等功能,统计数据较为全面,具有良好的扩展性。
3:综合性能,扩展性等方面,可考虑使用druid或者hikariCP连接池。
可开启prepareStatement缓存,对性能会有大概20%的提升。
6.2.Druid数据库连接池整合步骤
6.2.1 添加Druid依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
6.2.2 创建DruidDataSource工厂
package com.qfedu.util;
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.datasource.pooled.PooledDataSourceFactory;
public class DruidDataSourceFactory extends PooledDataSourceFactory {
public DruidDataSourceFactory(){
this.dataSource=new DruidDataSource();
}
}
6.2.3 修改主配置文件
修改dataSource标签的type属性值以及property的name属性值即可
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbc.properties">
</properties>
<typeAliases>
<typeAlias type="com.qfedu.dto.Member" alias="Member"/>
</typeAliases>
<!--配置连接数据库的环境-->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"/>
<dataSource type="com.qfedu.util.DruidDataSourceFactory">
<property name="driverClass" value="${driver_name}"/>
<property name="jdbcUrl" value="${driver_url}"/>
<property name="username" value="${driver_username}"/>
<property name="password" value="${driver_password}"/>
</dataSource>
</environment>
</environments>
<!--引入mybatis的映射文件-->
<mappers>
<mapper resource="mappers/MemberMapper.xml"/>
</mappers>
</configuration>
7.MyBatis缓存
7.1缓存工作原理
缓存,就是存储数据的内存分为一级缓存和二级缓存
1.检查缓存中是否存在要查询的数据
2.如果存在,直接从缓存中获取数据并返回(不会查询数据库)
3.如果不存在,就从数据库中获取数据,并且将获取到的数据保存到缓存中,以供下次查询使用
7.2一级缓存
一级缓存也叫SqlSession级缓存,为每个Sqlsession单独分配缓存内存,无需手动开启直接使用,多个不同的Sqlsession的缓存是不共享的。
特性:
1.如果多次查询使用的是同一个SqlSession对象,则第一次查询之后的数据会放到缓存,后续的查询则直接访问缓存中的缓存数据
代码
@Test
public void searchMemberById() {
int memberId =1;
MemberDao memberDao = getSqlSession().getMapper(MemberDao.class);
Member member1 = memberDao.SearchMemberById(memberId);
System.out.println(member1);
System.out.println("-------");
Member member2=memberDao.SearchMemberById(memberId);
System.out.println(member2);
}
日志信息
[main]DEBUG-
Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.[main]DEBUG-
Opening JDBC Connection[main] INFO-
{dataSource-1} inited[Finalizer]DEBUG-
PooledDataSource forcefully closed/removed all connections.[main]DEBUG-
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@62bd765][main]DEBUG-
==> Preparing: select member_id,member_nick,member_gender,member_age,member_city from members where member_id =? [main]DEBUG-
==> Parameters: 1(Integer)[main]DEBUG-
<== Total: 1Member(memberId=1, memberNick=张三, memberGender=男, memberAge=21, memberCity=武汉)
-------
Member(memberId=1, memberNick=张三, memberGender=男, memberAge=21, memberCity=武汉)
2.如果第一次查询完成之后,对查询出的对象属性进行修改(此修改会影响到缓存),那么第二次查询会直接访问缓存,造成第二次查询的结果与数据库不一致
代码
@Test
public void searchMemberById() {
//SqlSession SqlSession1 =MyBatisUtil.getSqlSessionFactory().openSession();
//SqlSession SqlSession2 =MyBatisUtil.getSqlSessionFactory().openSession();
SqlSession SqlSession1 = getSqlSession();
MemberDao memberDao1 = SqlSession1.getMapper(MemberDao.class);
Member member1 = memberDao1.SearchMemberById(1);
System.out.println(member1);
member1.setMemberAge(39);
//SqlSession1.clearCache();
System.out.println("-------");
MemberDao memberDao2 = SqlSession1.getMapper(MemberDao.class);
Member member2 = memberDao2.SearchMemberById(1);
System.out.println(member2);
}
日志结果
[main]DEBUG-
Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.[main]DEBUG-
Opening JDBC Connection[main] INFO-
{dataSource-1} inited[Finalizer]DEBUG-
PooledDataSource forcefully closed/removed all connections.[main]DEBUG-
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@62bd765][main]DEBUG-
==> Preparing: select member_id,member_nick,member_gender,member_age,member_city from members where member_id =? [main]DEBUG-
==> Parameters: 1(Integer)[main]DEBUG-
<== Total: 1Member(memberId=1, memberNick=张三, memberGender=男, memberAge=21, memberCity=武汉)
-------
Member(memberId=1, memberNick=张三, memberGender=男, memberAge=39, memberCity=武汉)
原因:
由于两次操作同属于一个数据库会话(SqlSession对象)因此他们的缓存数据是共享的,从日志中可以看出当第一次查询数据库时,将查询到的数据存入到缓存中,此时修改属性时,修改了缓存中的属性,当第二次查询的时候,先查询缓存,发现缓存中存在memberId为1的数据,就会直接将缓存中的数据取出导致数据库与查询出来的值不一致的现象
解决方案:
清除该数据库会话的缓存数据,第二次查询直接查询数据库,从而保证数据的一致性
代码
@Test
public void searchMemberById() {
//SqlSession SqlSession1 =MyBatisUtil.getSqlSessionFactory().openSession();
//SqlSession SqlSession2 =MyBatisUtil.getSqlSessionFactory().openSession();
SqlSession SqlSession1 = getSqlSession();
MemberDao memberDao1 = SqlSession1.getMapper(MemberDao.class);
Member member1 = memberDao1.SearchMemberById(1);
System.out.println(member1);
member1.setMemberAge(39);
SqlSession1.clearCache();
System.out.println("-------");
MemberDao memberDao2 = SqlSession1.getMapper(MemberDao.class);
Member member2 = memberDao2.SearchMemberById(1);
System.out.println(member2);
}
日志信息
[main]DEBUG-
Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.[main]DEBUG-
Opening JDBC Connection[main] INFO-
{dataSource-1} inited[Finalizer]DEBUG-
PooledDataSource forcefully closed/removed all connections.[main]DEBUG-
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@62bd765][main]DEBUG-
==> Preparing: select member_id,member_nick,member_gender,member_age,member_city from members where member_id =? [main]DEBUG-
==> Parameters: 1(Integer)[main]DEBUG-
<== Total: 1Member(memberId=1, memberNick=张三, memberGender=男, memberAge=21, memberCity=武汉)
-------
[main]DEBUG-
==> Preparing: select member_id,member_nick,member_gender,member_age,member_city from members where member_id =? [main]DEBUG-
==> Parameters: 1(Integer)[main]DEBUG-
<== Total: 1Member(memberId=1, memberNick=张三, memberGender=男, memberAge=21, memberCity=武汉)
3.如果第一次查询之后第二次查询之前,使用当前的SqlSession对象执行了修改操作,此修改操作会使第一次查询到的数据并且缓存的数据失效,因此第二次查询会再次访问数据库
@Test
public void searchMemberById() {
//SqlSession SqlSession1 =MyBatisUtil.getSqlSessionFactory().openSession();
//SqlSession SqlSession2 =MyBatisUtil.getSqlSessionFactory().openSession();
SqlSession SqlSession1 = getSqlSession();
MemberDao memberDao1 = SqlSession1.getMapper(MemberDao.class);
Member member1 = memberDao1.SearchMemberById(1);
System.out.println(member1);
System.out.println("-------");
memberDao1.UpdateMemberById(1, 39);
SqlSession1.commit();
MemberDao memberDao2 = SqlSession1.getMapper(MemberDao.class);
Member member2 = memberDao2.SearchMemberById(1);
System.out.println(member2);
}
日志记录
[main]DEBUG-
Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.[main]DEBUG-
Opening JDBC Connection[main] INFO-
{dataSource-1} inited[Finalizer]DEBUG-
PooledDataSource forcefully closed/removed all connections.[main]DEBUG-
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@62bd765][main]DEBUG-
==> Preparing: select member_id,member_nick,member_gender,member_age,member_city from members where member_id =? [main]DEBUG-
==> Parameters: 1(Integer)[main]DEBUG-
<== Total: 1Member(memberId=1, memberNick=张三, memberGender=男, memberAge=21, memberCity=武汉)
-------
[main]DEBUG-
==> Preparing: update members set member_age=? where member_id=? [main]DEBUG-
==> Parameters: 39(Integer), 1(Integer)[main]DEBUG-
<== Updates: 1[main]DEBUG-
Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@62bd765][main]DEBUG-
==> Preparing: select member_id,member_nick,member_gender,member_age,member_city from members where member_id =? [main]DEBUG-
==> Parameters: 1(Integer)[main]DEBUG-
<== Total: 1Member(memberId=1, memberNick=张三, memberGender=男, memberAge=39, memberCity=武汉)
7.2.1两次查询数据库问题
两次查询数据库问题 |
---|
问题:
第一次查询之后,进行了修改操作,数据库已经被修改,但是第二次查询的时候依然显示的是修改前的数据
分析:
修改操作和查询操作不属于同一个线程,因此使用的是不同的studentDao对象(使用不同的SqlSession对象)因此修改操作不会导致查询操作的缓存失效,第二次查询的时候依然访问的是缓存而没有查数据库
解决方案:
1.让修改操作和查询操作使用相同的SqlSession对象(操作复杂,不合理)
2.每次查询操作之后,清空缓存,让再次查询的时候绕过缓存直接访问数据库即可
7.3二级缓存
二级缓存也被称之为SqlSessionFactory级缓存,通过同一个factory对象获取的SqlSession可以共享二级缓存;在应用服务中SqlSessionFactory是单例的,因此我们的二级缓存可以实现全局共享
7.3.1特性
1.二级缓存默认没有开启,需要在mybatis-config.xml中的setting标签中开启
2.二级缓存只能缓存实现序列化接口的对象
-
在Mybatis-config.xml开启二级缓存
<settings> <setting name="cacheEnabled" value="true"/> </settings>
-
在需要使用二级缓存的Mapper文件配置cache标签使用功能二级缓存
<cache/>
-
被缓存的实体类实现序列化接口
package com.qfedu.dto; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; import java.io.Serializable; @Data @NoArgsConstructor @AllArgsConstructor @ToString public class Member implements Serializable { private int memberId; private String memberNick; private String memberGender; private int memberAge; private String memberCity; }
实例代码:
@Test
public void testSearchMemberById2() {
//获取工厂
SqlSessionFactory factory = MyBatisUtil.getSqlSessionFactory();
//1.多个sqlSession对象必须来自于同一个sqlSessionFactory
SqlSession sqlSession1 = factory.openSession();
SqlSession sqlSession2 = factory.openSession();
MemberDao memberDao1 = sqlSession1.getMapper(MemberDao.class);
Member member1 = memberDao1.SearchMemberById(1);
System.out.println(member1);
//2.第一次查询之后执行sqlSession.commit()会将当前sqlSession的查询结果存到二级缓存
sqlSession1.commit();
System.out.println("------");
MemberDao memberDao2 = sqlSession2.getMapper(MemberDao.class);
Member member2 = memberDao2.SearchMemberById(1);
System.out.println(member2);
}
日志:
[main]DEBUG-
Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.[main]DEBUG-
Cache Hit Ratio [com.qfedu.dao.MemberDao]: 0.0[main]DEBUG-
Opening JDBC Connection[main] INFO-
{dataSource-1} inited[Finalizer]DEBUG-
PooledDataSource forcefully closed/removed all connections.[main]DEBUG-
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@279ad2e3][main]DEBUG-
==> Preparing: select member_id,member_nick,member_gender,member_age,member_city from members where member_id =? [main]DEBUG-
==> Parameters: 1(Integer)[main]DEBUG-
<== Total: 1Member(memberId=1, memberNick=张三, memberGender=男, memberAge=39, memberCity=武汉)
------
[main]DEBUG-
Cache Hit Ratio [com.qfedu.dao.MemberDao]: 0.5Member(memberId=1, memberNick=张三, memberGender=男, memberAge=39, memberCity=武汉)
7.3.2查询操作的缓存开关
<select id="SearchMemberById" resultMap="searchMember" useCache="true">
select member_id,member_nick,member_gender,member_age,member_city from members where
member_id =#{memberId}
</select>
8.延迟加载
8.1延迟加载概念特性
就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载。
优点: 先从单表查询,需要时再从关联表去关联查询,⼤⼤提⾼数据库性能,因为查询单表要比关联查询多张表速度要快。 缺点: 因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成⽤户等待时间变长,造成用户体验下降。 在多表中: 一对多,多对多:通常情况下采用延迟加载 一对一(多对一):通常情况下采用立即加载 注意: 延迟加载是基于嵌套查询(子查询)来实现的
8.2实现
8.2.1局部延迟加载
在association和collection标签中都有⼀个fetchType属性,通过修改它的值,可以修改局部的加载策略。
<!-- 开启⼀对多 延迟加载 -->
<resultMap id="userMap" type="user">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>
<!--
fetchType="lazy" 懒加载策略
fetchType="eager" ⽴即加载策略
-->
<collection property="orderList" ofType="order" column="id"
select="com.lagou.dao.OrderMapper.findByUid" fetchType="lazy">
</collection>
</resultMap>
<select id="findAll" resultMap="userMap">
SELECT * FROM `user`
</select>
8.2.2全局延迟加载
在Mybatis的核⼼配置⽂件中可以使⽤setting标签修改全局的加载策略。
<settings>
<!--开启全局延迟加载功能-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
局部的加载策略的优先级高于全局的加载策略
<!-- 关闭⼀对⼀ 延迟加载 -->
<resultMap id="orderMap" type="order">
<id column="id" property="id"></id>
<result column="ordertime" property="ordertime"></result>
<result column="total" property="total"></result>
<!--
fetchType="lazy" 懒加载策略
fetchType="eager" ⽴即加载策略
-->
<association property="user" column="uid" javaType="user"
select="com.lagou.dao.UserMapper.findById" fetchType="eager">
</association>
</resultMap>
<select id="findAll" resultMap="orderMap">
SELECT * from orders
</select>