MyBatis学习与实践
- 一、MyBatis简介
- 三、MyBatis框架使用
- 四、单元测试
- 五、MyBatis的CRUD操作
- 六、工具类的封装
- 八、MyBatis主配置文件
- 8.4 plugins标签
- 8.5 environments标签
- 九、映射文件mapper
- 十、分页插件
- 十一、关联映射
- 十二、动态SQL
- 十四、MyBatis日志配置
- 十五、配置数据库连接池-整合Druid
- 十六、MyBatis缓存
- 十七、延迟加载机制(只在子查询中有效果)
一、MyBatis简介
== 千锋涛哥 039==
1.1 框架概念
框架,就是软件的半成品,完成了软件开发过程中的通用操作,程序员只需要很少或者不用进行加工就能够实现特定的功能,从而简化开发人员在软件开发中的步骤,提高开发效率。
1.2 常用的框架
前端框架 jQuery,React
服务端框架:servlet 框架 springMVC
- MVC框架,简化了Servlet的开发步骤。
– Sturts
– Struts2 不是Struts是升级,而是另一个框架
– SrpingMVC - 持久层框架:完成数据库操作的框架
– apache DBUtils
– Hibernate
– Spring JPA
– Mybatis - 胶水框架:Spring
SSM框架= Spring + SpringMVC + MyBatis三个字母的缩写。
==040 ==
1.3 MyBatis介绍
MyBatis是一个半自动的ORM框架,先出iBatis,后有Hibernate全自动的框架,但效率不高。随着互联网项目(高可用,高性能)的出现,逐渐暴露出一些缺点。大家又想到了MyBatis,还是半自动的还好,可以人为参与修改。
ORM Object Relational Mapping,对象关系映射,将java中的一个对象与数据库表中一行记录一一对应。ORM框架提供了实体类与数据表的映射关系,通过映射文件的配置,实现对象的持久化。
- MyBatis前身是iBatis,是Apache软件基金会提供的一个开源项目。
- 2010年迁移到Google code,正式更名为MyBatis.
- 2013年迁移到Github托管。
- MyBatis特点。
– 支持自定义SQL,存储过程。
– 对原有的JDBC进行了封闭,几乎消除了所有JDBC代码。
– 支持XML和注解配置,自动完成ROM操作,实现结果映射。
== 041 ==
二、MyBatis框架部署
框架部署就是将框架引入到我们的项目中。
2.1 创建maven项目
- Java工程和web工程均可
2.2 添加项目依赖
- 在项目pom文件中添加依赖
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
2.3 创建MyBatis配置文件
- 在resources文件夹下创建MyBatis配置文件
<!-- 在environments配置数据库连接信息-->
<!-- 在environments标签中可以定义多个environment,每个environment标签可以定义一套连接配置-->
<!-- default属性,用来指定使用哪个environment标签 -->
<environments default="mysql">
<environment id="mysql">
<!-- transactionManager 标签用于配置数据管理方式-->
<transactionManager type="JDBC"></transactionManager>
<!-- <dataSource用于配置数据库连接信息-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
== 042==
三、MyBatis框架使用
3.1 创建数据表
CREATE TABLE `student` (
`sid` int(10) NULL COMMENT 'ID' ,
`stu_num` varchar(30) NULL COMMENT '编号' ,
`stu_name` varchar(30) NULL COMMENT '姓名' ,
`stu_gender` varchar(30) NULL COMMENT '性别' ,
`stu_age` int(10) NULL COMMENT '年龄'
);
3.2 创建实体类
导入lombok依赖,以便通过注解实现实体类的,get,set,tostring。
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>
com.zhang.bean.Student.java
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student {
private int stuId;
private String stuNum;
private String stuName;
private String stuGender;
private int stuAge;
}
3.3 创建DAO,定义操作方法
- com.zhang.dao.StudentDAO.java
public interface StudentDAO {
public int insertStudent(Student student); // 添加
public int deleteStudent(String stuNum); // 删除
}
3.4 创建DAO接口的映射文件
- 在resources目录下,新建名为mappers文件夹
- 创建mappers中新建名为StudentMapper.xml的映射文件。(根据模板创建文件)
- 在映射文件中对DAO中定义的方法进行实现。
<mapper namespace="com.zhang.dao.StudentDAO">
<!-- 相关于DAO接口的实现类,namespace属性要的指定实现DAO接口的全限定名-->
<insert id="insertStudent" parameterType="com.zhang.bean.Student">
insert into student(sid,stu_num,stu_name,stu_gender,stu_age)
values(#{stuId},#{stuNum},#{stuName},#{stuGender},#{stuAge});
</insert>
<insert id="deleteStudent" parameterType="com.zhang.bean.Student">
delete from student where stu_num = #{stuName};
</insert>
</mapper>
- mapper文件相当于接口文件的实现类。
3.5 将映射文件添加到主配置文件
- resources/mybatis-config.xml中处理。
<mappers>
<mapper resource="mappers/StudentMapper"></mapper>
</mappers>
==043 ==
四、单元测试
4.1 添加依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
4.2 创建单元测试类
在被测试类右键选择generate --选择test
public class StudentDAOTest {
@org.junit.Test
public void insertStudent() {
try {
// 加载mybatis配置文件
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 连接工厂
SqlSessionFactory factory = builder.build(is);
// 会话(连接)
SqlSession sqlSession = factory.openSession();
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
System.out.println(studentDAO);
// 测试StudentDAO中的方法
int i = studentDAO.insertStudent(new Student(0, "001", "张恒超", "nan", 23));
// 此处需要手动提交
sqlSession.commit();
System.out.println(i);
} catch (IOException e) {
e.printStackTrace();
}
}
@org.junit.Test
public void deleteStudent() {
}
}
问题坑1
测试时报错,java.sql.SQLException: Incorrect string value: '\xE5\xB0‘
原因是数据库表和字段的字符集为latin1,改为UTF-8即可解决问题。参考链接
问题坑2
测试时报:You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true
原因是数据库连接时,得配置说明SSL属性。具体在mybatis-config.xml中添加 useSSL=false 即可解决问题,具体参见如下。
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
044
五、MyBatis的CRUD操作
5.1 添加操作
5.2 删除操作-根据学号删除
- 在StudentDAO中定义删除方法。
public interface StudentDAO {
public int insertStudent(Student student); // 添加
public int deleteStudent(String stuNum); // 删除
}
- 在StudentMapper.xml中对接口方法进行实现。
<detete id="deleteStudent" parameterType="com.zhang.bean.Student">
delete from student where stu_num = #{stuName};
</detete>
- 在测试类中创建测试方法
@org.junit.Test
public void eleteStudent() {
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//SqlSessionFactoryBuilder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// SqlSessionFactory表示MyBatis的会话工厂
SqlSessionFactory factory = builder.build(is);
// SqlSession表示MyBatis与数据库之间的会话,通过工厂方法设计模式。
SqlSession sqlSession = factory.openSession();
// 通过SqlSession对象调用 getMapper方法获取DAO接口对象
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
// 调用被测试方法
int i = studentDAO.deleteStudent("002");
System.out.println(i);
// 此处需要手动提交
sqlSession.commit();
} catch (IOException e) {
e.printStackTrace();
}
}
==45 ==
5.3 修改操作
根据学号修改其他字段信息,一般不修改学号,ID信息
– DAO中添加方法
public interface StudentDAO {
public int insertStudent(Student student); // 添加
public int deleteStudent(String stuNum); // 删除
public int updateStudent(Student student); // 修改
}
- mapper中添加实现
<update id="updateStudent">
update student
set stu_name = #{stuName},
stu_gender = #{stuGender},
stu_age = #{stuAge}
where stu_num = #{stuNum}
</update>
- 测试类中添加测试方法
@org.junit.Test
public void updateStudent(){
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
SqlSession sqlSession = factory.openSession();
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
int i = studentDAO.updateStudent(new Student(1, "001", "李文浩", "女", 32));
sqlSession.commit();
assertEquals(1,i);
} catch (IOException e) {
e.printStackTrace();
}
}
== 46 ==
5.4 查询所有
- DAO中定义方法
public List<Student> listStudents(); // 查询所有
- Mapper中实现方法
<!-- resultType指定查询结果封装的对象的实体类,不能省略-->
<!-- resultSets指定当前操作返回的集合类型,可省略-->
<!-- 方法一,指定数据库表字段与对象属性的对应关系
<select id="listStudents" resultType="com.zhang.bean.Student" resultSets="java.util.List">
select sid stuId,stu_num stuNum,stu_name stuName
,stu_gender stuGender,stu_age stuAge
from student;
</select>
-->
<!-- 方法二 resultMap 标签用于定义对象与数据表的映射关系-->
<resultMap id="studentMap" type="com.zhang.bean.Student">
<id column="sid" property="stuId"></id>
<result column="stu_num" property="stuNum"></result>
<result column="stu_name" property="stuName"></result>
<result column="stu_gender" property="stuGender"></result>
<result column="stu_age" property="stuAge"></result>
</resultMap>
<!-- resultMap用于引用一个实体的映射关系,有了上面resultMap定义及引用,resultType就可以省略了-->
<select id="listStudents" resultType="com.zhang.bean.Student" resultMap="studentMap">
select sid,stu_num,stu_name,stu_gender,stu_age
from student;
</select>
- 测试类中实现测试方法
@org.junit.Test
public void listStudents(){
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
SqlSession sqlSession = factory.openSession();
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
List<Student> students = studentDAO.listStudents();
sqlSession.commit();
for (Student stu:students){
System.out.println(stu);
}
// assertNotNull(students);
} catch (IOException e) {
e.printStackTrace();
}
}
== 047==
5.5 根据主键查询一条记录
- Dao中定义方法
public Student queryStudent(String stuNum); // 查询一条记录
- Mapper定义实现
<select id="queryStudent" resultMap="studentMap">
select sid,stu_num,stu_name,stu_gender,stu_age
from student
where stu_num = #{stuNum}
</select>
- 单元测试
@org.junit.Test
public void queryStudent(){
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
SqlSession sqlSession = factory.openSession();
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
Student stu = studentDAO.queryStudent("001");
sqlSession.commit();
System.out.println(stu);
// assertNotNull(students);
} catch (IOException e) {
e.printStackTrace();
}
}
== 048==
5.7 查询总计录数
- DAO方法
public int getCount(); // 查询记录数
- Mapper实现
<select id="getCount" resultType="int">
select count(1)
from student;
</select>
- 测试方法测试
@org.junit.Test
public void getCount(){
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
SqlSession sqlSession = factory.openSession();
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
int count = studentDAO.getCount();
sqlSession.commit();
System.out.println(count);
// assertNotNull(students);
} catch (IOException e) {
e.printStackTrace();
}
}
== 49==
5.6 查询操作-多参数查询
分页查询(需要多个参数的情况)
- DAO中定义操作方法,如果方法有多个参数,使用@Param注解声明参数的别名。
// 分页录数,多参数可以通过@Param-参数注解给参数别名的方式
public List<Student> listStudentsByPages(@Param("start") int start,@Param("pageSize") int pageSize); // 分页录数
- Mapper中定义实现
<!-- 多参数分页查询 -->
<select id="listStudents" resultMap="studentMap">
select sid,stu_num,stu_name,stu_gender,stu_age
from student
limit #{start},#{pageSize}
</select>
注意:如果DAORR操作方法没有通过@Param指定参数别名,在SQL中也可以通过arg0,arg1 或param1,param2…获取参数。
- 测试方法并测试
@org.junit.Test
public void listStudentsByPages(){
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
SqlSession sqlSession = factory.openSession();
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
List<Student> students = studentDAO.listStudentsByPages(0,1);
sqlSession.commit();
for (Student stu:students){
System.out.println(stu);
}
// assertNotNull(students);
} catch (IOException e) {
e.printStackTrace();
}
}在这里插入代码片
050
5.8 添加操作回填生成的主键id
- mapper.xml insert操作标签中,添加useGeneratedKeys,keyProperty
<!-- useGeneratedKeys表示添加操作是否需要回填生成的主键-->
<!-- keyProperty设置回填的主键值赋值到参数对象的哪个属性-->
<insert id="insertStudent" useGeneratedKeys="true" keyProperty="stuId" parameterType="com.zhang.bean.Student">
insert into student(sid,stu_num,stu_name,stu_gender,stu_age)
values(#{stuId},#{stuNum},#{stuName},#{stuGender},#{stuAge});
</insert>
- 测试验证
051
六、工具类的封装
- 建包 com.zhang.utils
- 建工具类 MyBatisUtil.java
public class MyBatisUtil {
private static SqlSessionFactory factory;
private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>();
static{
try {
// 加载mybatis配置文件
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 连接工厂
factory = builder.build(is);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
SqlSession sqlSession = local.get();
if(sqlSession == null){
sqlSession = factory.openSession();
local.set(sqlSession);
}
return sqlSession;
}
public static <T extends Object>T getMapper(Class<T> c){
SqlSession sqlSession = getSqlSession();
return sqlSession.getMapper(c);
}
}
- 调用工具类
需要事务管理的insert方法中
public void insertStudent() {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
System.out.println(studentDAO);
无需事务管理的select方法中
public void listStudents(){
StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);
List<Student> students = studentDAO.listStudents();
for (Student stu:students){
System.out.println(stu);
}
052
SqlSession对象作用
- getMapper(DAO.class)可以获取Mapper(Dao接口的实例).
- 事务管理。
当我们获取sqlSession对象时,就默认开启了事务。
操作完成之后中,需手动提交。
7.1 手动提交事务
操作完成后提交事务,异常失败时sqlSession commmit();,回滚事务sqlSession rollback().
7.2 自动提交事务
- 修改MyBatisUtil.xml
public class MyBatisUtil {
private static SqlSessionFactory factory;
private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>();
static{
try {
// 加载mybatis配置文件
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 连接工厂
factory = builder.build(is);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSessionFactory getFactory(){
return factory;
}
private static SqlSession getSqlSession(boolean isAutoCommit){
SqlSession sqlSession = local.get();
if(sqlSession == null){
// SqlSessionFactory调用openSession方法获取sqSession对象时,可以通过参数设置事务是否自动提交
// 默认为false,事务不自动提交,true则自动提交
sqlSession = factory.openSession(isAutoCommit);
local.set(sqlSession);
}
return sqlSession;
}
// 手动事务提交
public static SqlSession getSqlSession(){
return getSqlSession(false);
}
// 自动事务提交
public static <T extends Object>T getMapper(Class<T> c){
SqlSession sqlSession = getSqlSession(true);
return sqlSession.getMapper(c);
}
}
== 053==
八、MyBatis主配置文件
mybatis-config.xml是mybatis框架的主配置文件,主要用于配置mybaits数据源及属性信息。
8.1 properties标签
- properties标签,用于设置键值对,或引用属性文件,引用之后中,在配置时就可以获取属性文件中的键值对了。
1)在resources目录下创建 jdbc.properties文件,创建键值对。
mysql_driver=com.mysql.jdbc.Driver
mysql_url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=false
mysql_username=root
mysql_password=root
2)在mybatis-config.xml中通过propertits标签引用properties文件中的键值对
<!-- properties标签 1.可以定义键值对,2.可以引用属性文件-->
<properties resource="jdbc.properties">
3)修改environment
<dataSource type="POOLED">
<property name="driver" value="${mysql_driver}"/>
<property name="url" value="${mysql_url}"/>
<property name="username" value="${mysql_username}"/>
<property name="password" value="${mysql_password}"/>
</dataSource>
8.2 settings标签
<settings>
<!-- 设置启动二级缓存 -->
<setting name="cacheEnabled" value="true"/>
<!-- 启动延迟加载-->
<setting name="lazyLoadEnabled" value="true"/>
</settings>
8.3 typeAliases标签
类型别名
<!-- typeAliases用于给实体类取别名,在映射文件中可以直接使用别名来代替实体类的全限定名-->
<typeAliases>
<typeAlias type="com.zhang.bean.Student" alias="Student"></typeAlias>
</typeAliases>
8.4 plugins标签
<!-- plugins配置插件,用于配置mybatis插件,例如分页插件-->
<plugins>
<plugin interceptor=""></plugin>
</plugins>
8.5 environments标签
配置数据连接环境
<!-- 在environments配置数据库连接信息-->
<!-- 在environments标签中可以定义多个environment,每个environment标签可以定义一套连接配置-->
<!-- default属性,用来指定使用哪个environment标签 -->
<environments default="mysql">
<environment id="mysql">
<!-- transactionManager 标签用于配置数据管理方式-->
<!-- transactionManager type=JDBC可以进行事务的提交和回滚操作
transactionManager type=MANAGED 依赖于容器进行事务管理,本身不进行事务的提交和回滚操作-->
<transactionManager type="JDBC"></transactionManager>
<!-- <dataSource用于配置数据库连接信息-->
<dataSource type="POOLED">
<property name="driver" value="${mysql_driver}"/>
<property name="url" value="${mysql_url}"/>
<property name="username" value="${mysql_username}"/>
<property name="password" value="${mysql_password}"/>
</dataSource>
</environment>
</environments>
8.6 mapper标签
用于载入映射文件,项目中有多少个映射文件,就得在此标签配置多少行。
<mappers>
<mapper resource="mappers/StudentMapper.xml"></mapper>
</mappers>
==054 ==
九、映射文件mapper
mapper文件都得按mybatis的规范进行配置。
9.1 MyBatis初始化
== 055==
9.2 mapper根标签
mapper文件相当于DAO接口的实现类,namespace属性指定要实现DAO接口的全限定名。
9.3 insert标签
声明添加操作(sql:insert)
常用属性
id属性,绑定对应 DAO接口的方法。
parameterType属性,用以指定接口中对应方法的参数类型(可省略)。
useGeneratedKeys属性,设置添加操作是否需要回填生成的主键。
keyProperty属性,设置回填的id设置到参数对象中的哪个属性。
timeout属性,设置此操作的超时时间,如果不设置则一直等待。
主键回填的两种方式
方式一
<insert id="insertStudent" >
<selectKey keyProperty="stuId" resultType="java.lang.Integer">
select last_insert_id()
</selectKey>
insert into student(sid,stu_num,stu_name,stu_gender,stu_age)
values(#{stuId},#{stuNum},#{stuName},#{stuGender},#{stuAge});
</insert>
方式二
<!-- useGeneratedKeys表示添加操作是否需要回填生成的主键-->
<!-- keyProperty设置回填的主键值赋值到参数对象的哪个属性-->
<insert id="insertStudent" useGeneratedKeys="true" keyProperty="stuId" parameterType="com.zhang.bean.Student">
insert into student(sid,stu_num,stu_name,stu_gender,stu_age)
values(#{stuId},#{stuNum},#{stuName},#{stuGender},#{stuAge});
</insert>
9.4 delete标签
声明删除操作
9.5 updage标签
声明修改操作
9.6 select标签
声明查询操作
- id指定绑定方法的方法名
- parameterType属性,设置参数类型
- resultType 指定当前sql返回数据封装的对象类型
- resultMap 指定从数据表到实体类和属性的对应关系
- useCache 指定查询操作是否需要缓存
- timeout 设置超时时间
9.7 resultMap标签
用于声明实体属性与实体类表中的映射关系。
<!-- 方法二 resultMap 标签用于定义对象与数据表的映射关系-->
<resultMap id="studentMap" type="com.zhang.bean.Student">
<id column="sid" property="stuId"></id>
<result column="stu_num" property="stuNum"></result>
<result column="stu_name" property="stuName"></result>
<result column="stu_gender" property="stuGender"></result>
<result column="stu_age" property="stuAge"></result>
</resultMap>
9.8 cache标签
设置当前DAO进行数据操作时的缓存属性设置
<cache size="" readOnly="">
</cache>
9.9 sql和include标签
sql标签,定义sql片段,然后通过include引用。
- 定义sql标签
<sql id="stuitem">
sid,stu_num,stu_name,stu_gender,stu_age
</sql>
- include引用sql片段
<select id="listStudents" resultType="com.zhang.bean.Student" resultMap="studentMap">
select <include refid="stuitem"/>
from student;
</select>
056
十、分页插件
分页插件是一个独立于myBatis框架之外的第三方插件。
10.1 添加分页插件的依赖
PageHelper
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.0</version>
</dependency>
10.2 配置插件
在mybatis-config.xml中进行配置
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
10.3 分页实例
对学生信息进行分页查询
@Test
public void listStudentByPages(){
StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);
PageHelper.startPage(1,4);
List<Student> students = studentDAO.listStudents();
PageInfo<Student> pageInfo = new PageInfo<Student>(students);
// pageInfo中包含了数据及分页信息
List<Student> list = pageInfo.getList();
for(Student stu:list){
System.out.println(stu);
}
}
** 带条件分页**
实际上与不带条件的差异在于mapper.xml中的查询语句。
==057 ==
十一、关联映射
11.1 实体关系
实体–数据实体,实体关系指的就是数据与数据之间的关系。
** 一对一关联**
学生对学生证,人和身份证,用户基本信息和详情之间的关系。
- 主键关联,通过主键ID关联,例如用户表主键和详情表中主键相同时,表示是匹配的数据。
一对多关联 等于多对一关联
实例:班级对学生,学生对班级
数据表关系:在多的一端添加外键和一的一字段进行关联。
多对多关联
用户和角色
建立第三张关系表添加两个外键,分别与两张表主键进行关联。
058
11.2 创建项目,部署MyBatis项目
- 新建maven项目
- pom.xml中设置打包方式为war war
- 创建web.xml
- 添加web支持,两种方式 一是如下图的方式,二是添加jsp,servlet依赖的方式。
-
maven记得要刷新一下
-
添加服务器配置
- 部署MyBatis框架
– pom中添加MyBatis 依赖
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
– resources文件夹中新建jdbc.properties,mybatis-config.xml
– 创建com.zhang.utils.MyBatilUtil.java封装工具类
下面的代码要熟练掌握,为什么这么写。
package com.zhang.utils;
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>();
static{
try {
// 加载mybatis配置文件
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 连接工厂
factory = builder.build(is);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSessionFactory getFactory(){
return factory;
}
private static SqlSession getSqlSession(boolean isAutoCommit){
SqlSession sqlSession = local.get();
if(sqlSession == null){
// SqlSessionFactory调用openSession方法获取sqSession对象时,可以通过参数设置事务是否自动提交
// 默认为false,事务不自动提交,true则自动提交
sqlSession = factory.openSession(isAutoCommit);
local.set(sqlSession);
}
return sqlSession;
}
// 手动事务提交
public static SqlSession getSqlSession(){
return getSqlSession(false);
}
// 自动事务提交
public static <T extends Object>T getMapper(Class<T> c){
SqlSession sqlSession = getSqlSession(true);
return sqlSession.getMapper(c);
}
}
059
11.3 一对一关联
11.3.1创建表
-- 用户账号表
CREATE TABLE `user` (
`user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户id',
`user_name` varchar(20) COLLATE utf8_bin NOT NULL COMMENT '用户名',
`user_pwd` varchar(20) COLLATE utf8_bin NOT NULL COMMENT '用户密码',
`user_realname` varchar(20) COLLATE utf8_bin NOT NULL COMMENT '用户姓名',
`user_img` varchar(100) COLLATE utf8_bin NOT NULL COMMENT '用户图像',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-- 用户信息详情表
CREATE TABLE `user_info` (
`detail_id` int NOT NULL AUTO_INCREMENT COMMENT '用户信息id' ,
`user_addr` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '用户地址' ,
`user_tel` char(11) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '用户电话' ,
`user_desc` varchar(200) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '用户描述' ,
`user_id` int NOT NULL UNIQUE COMMENT '用户id' ,
PRIMARY KEY (`detail_id`)
)
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_bin;
11.3.2 创建实体类
com.zhang.pojo.User.java
com.zhang.pojo.UserInfo.java
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class User {
private int userId;
private String userName;
private String userPwd;
private String userRealname;
private String userImg;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class UserInfo {
private int detailId;
private String userAddr;
private String userTel;
private String userDesc;
private int userId;
}
11.3.3 创建DAO
public interface UserDao {
public int insertUser(User user);
public User queryUser(String userName);
}
060
@Test
public void testInsertUser() {
User user = new User(0,"lisi","123123","王五","01.jpg",null);
UserInfo userInfo = new UserInfo(0,"陕西西安","18710056688","有个性才能成长",0);
SqlSession sqlSession = MyBatisUtil.getSqlSession();
try {
UserDao userDao = sqlSession.getMapper(UserDao.class);
int i = userDao.insertUser(user);
System.out.println(i);
userInfo.setUserId(user.getUserId());
UserInfoDao userInfoDao = sqlSession.getMapper(UserInfoDao.class);
int j = userInfoDao.insertUserInfo(userInfo);
System.out.println(j);
sqlSession.commit();
}catch (Exception e){
e.printStackTrace();
sqlSession.rollback();
}
}
061
11.3.4 关联查询
在查询用户的同时关联查询出与之对应的详情
- 实体
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class User {
private int userId;
private String userName;
private String userPwd;
private String userRealname;
private String userImg;
private UserInfo userInfo; // 将userinfo 作为user的属性
}
- 映射文件
<resultMap id="userMap" 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"/>
<result column="detail_id" property="userInfo.detailId"/>
<result column="user_addr" property="userInfo.userAddr"/>
<result column="user_desc" property="userInfo.userDesc"/>
<result column="user_tel" property="userInfo.userTel"/>
</resultMap>
<select id="queryUser" resultMap="userMap">
select a1.user_id,a1.user_name,a1.user_pwd,a1.user_realname,a1.user_img
,a2.detail_id,a2.user_addr,a2.user_desc,a2.user_tel
from user a1 left join user_info a2 on a1.user_id = a2.user_id
where a1.user_name = #{userName}
</select>
11.4 一对多
案例:班级对学生
11.4.1 创建数据表
CREATE TABLE `classes` (
`cid` int(11) NOT NULL AUTO_INCREMENT COMMENT '班级id',
`cname` varchar(30) COLLATE utf8_bin NOT NULL COMMENT '班级名',
`cdesc` varchar(100) COLLATE utf8_bin NOT NULL COMMENT '班级描述',
PRIMARY KEY (`cid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE `students` (
`sid` int(11) NOT NULL AUTO_INCREMENT COMMENT '学生id',
`sname` varchar(30) COLLATE utf8_bin NOT NULL COMMENT '学生姓名',
`sage` varchar(100) COLLATE utf8_bin NOT NULL COMMENT '学生年龄',
`scid` int(11) NOT NULL COMMENT '学生所属班级id',
PRIMARY KEY (`sid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
11.4.2 创建实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Classes {
private int cId;
private String cName;
private String cDesc;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Students {
private int sId;
private String sName;
private int sAge;
private int sCid;
}
11.4.3 关联查询
关联查询出一个班级下所有的学生
改造 classes实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Classes {
private int cId;
private String cName;
private String cDesc;
private List<Students> stus; //存储班级下的所有学生信息
}
- 连接查询mapper
mybatis-config.xml中增加别名配置
<typeAlias type="com.zhang.pojo.Classes" alias="Classes"></typeAlias>
<typeAlias type="com.zhang.pojo.Students" alias="Students"></typeAlias>
增加映射文件配置
<mapper resource="mappers/ClassMapper.xml"></mapper>
ClassMapper.xml中完善查询逻辑
<resultMap id="classMap" type="Classes">
<id column="cid" property="cId"/>
<result column="cname" property="cName"/>
<result column="cdesc" property="cDesc"/>
<!-- classes对象的stus属性是一个List集合,需要使用collection标签-->
<!-- collection标签的ofType属性声明集合中元素的类型-->
<collection property="stus" ofType="Students">
<result column="sid" property="sId"/>
<result column="sname" property="sName"/>
<result column="sage" property="sAge"/>
</collection>
</resultMap>
<select id="queryClass" resultMap="classMap">
select a1.cid,a1.cname,a1.cdesc,a2.sid,a2.sname,a2.sage
from classes a1 left join students a2 on a1.cid = a2.scid
where a1.cid = #{cId}
</select>
- 测试类
public class ClassDaoTest {
@Test
public void testQueryClass() {
ClassDao classDao = MyBatisUtil.getMapper(ClassDao.class);
Classes classes = classDao.queryClass(1);
System.out.println(classes);
}
}在这里插入代码片
- 子查询
063
11.5 多对一关联
查询学生信息时,并查询学生所属班级信息
11.5.1 实体类信息修改
学生所在班级属性用实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Students {
private int sId;
private String sName;
private int sAge;
private Classes classes; // 学生所在班级属性用实体类
}
需要将班级类中的关联学生实体类信息注释掉
// private List<Students> stus; //存储班级下的所有学生信息
11.5.2 关联查询
- mybatis-config.xml
确保 <typeAlias <mapper中正确配置,如下
<typeAlias type="com.zhang.pojo.Classes" alias="Classes"></typeAlias>
<typeAlias type="com.zhang.pojo.Students" alias="Students"></typeAlias>
..................................
<mapper resource="mappers/StudentMapper.xml"></mapper>
- mapper
<resultMap id="StudentMap" type="Students">
<id column="sid" property="sId"/>
<result column="sname" property="sName"/>
<result column="sage" property="sAge"/>
<result column="cid" property="classes.cId"/>
<result column="cname" property="classes.cName"/>
<result column="cdesc" property="classes.cDesc"/>
</resultMap>
<select id="queryStudentById" resultMap="StudentMap">
select a1.sid,a1.sname,a1.sage,a1.scid,a2.cid,a2.cname,a2.cdesc
from students a1 left join classes a2 on a1.scid = a2.cid
where a1.sid = #{sId}
</select>
- test 类
@Test
public void queryStudentById() {
StudentDao studentDao = MyBatisUtil.getMapper(StudentDao.class);
Students students = studentDao.queryStudentById(1001);
System.out.println(students);
}
064
11.6 多对多关联
实例:学生与课程之间的关系
11.6.1 创建数据表
CREATE TABLE `courses` (
`course_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '课程id',
`course_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '课程名称',
PRIMARY KEY (`course_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='课程信息表';
CREATE TABLE `grades` (
`sid` int(11) NOT NULL COMMENT '学生id',
`course_id` int(11) NOT NULL COMMENT '课程id',
`score` int(11) NOT NULL COMMENT '成绩'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
11.6.2 创建实体类
省略部分内容,待做实践练习。
十二、动态SQL
筛选心仪的对象,性别,年龄,城市,
电商网站,筛选商品时,筛选条件可多可少。
12.1 什么是动态sql
根据查询条件动态完成SQL的拼接。
12.2 动态sql案例
复制项目,源文件复制后改名
只保留pom.xml和src文件夹。
更改pom文件中的 mybatis-demo3改成需要的名称
用IDEA打开新复制的项目
12.2.1 创建实体表
CREATE TABLE `members` (
`member_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '会员id',
`member_nick` varchar(30) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '呢称',
`member_gender` char(2) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '性别',
`member_age` int(11) NOT NULL COMMENT '年龄',
`member_city` varchar(30) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '城市',
PRIMARY KEY (`member_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员信息表';
12.2.2 创建实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Member {
private int memberId;
private String memeberNick;
private String memeberGender;
private int memberAge;
private String memberCity;
}
066
12.2.3 创建DAO接口
在DAO接口中创建多条件查询的接口
public interface MemberDAO {
public Member queryMemberByNick(String nick);
// 在多条件查询中,如果查询条件不确定,可以直接使用HashMap作为参数
// 优点:HashMap 无需单独定义传递查询条件的类
public List<Member> searchMember(HashMap<String,Object> params);
// 也可以专用定义用于查询条件的实体类,用于存放查询参数
// 缺点:需要单独定义一个实体类,用于封装参数
public List<Member> searchMember(MemberSearchConfition params);
}
-67
12.3 if标签用法
<resultMap id="memberMap" 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="memberMap">
Select a1.member_id,a1.member_nick,a1.member_gender,a1.member_age,a1.member_city
from members a1
<trim prefix="where" prefixOverrides="and | or" suffix="order by member_age">
<if test="gender != null">
and member_gender = #{gender}
</if>
<if test="minAge != null">
and member_age >= #{minAge}
</if>
<if test="maxAge != null">
and member_age <= #{maxAge}
</if>
<if test="city != null">
and member_city = #{city}
</if>
</trim>
</select>
- 动态sql中
12.4 where标签
<where></where> -- 标签会自动将第一个动态条件的and剔除掉
- trim标签如下使用,会自动将第一个and 或者 or 拿掉
<trim prefix="where" prefixOverrides="and | or" suffix="order by member_age">
12.5 foreach标签
test类
@Test
public void searchMemberByCity(){
ArrayList<String> cities = new ArrayList<>();
cities.add("武汉");
cities.add("西安");
MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
List<Member> members = memberDAO.searchMemberByCity(cities);
for (Member m:members){
System.out.println(m);
}
}
mapper实现
<select id="searchMemberByCity" resultMap="memberMap">
Select a1.member_id,a1.member_nick,a1.member_gender,a1.member_age,a1.member_city
from members a1
where member_city in
<foreach collection="list" item="cityName" separator="," open="(" close=")">
#{cityName}
</foreach>
</select>
== 069==
十三、模糊查询${}与#{}
通过昵称查询会员信息
// 根据呢称查询用户信息--模糊查询
// 模糊查询需要使用${key}取值,与sql进行拼接
// 在使用${key}时,即使只有一个参数也需要使用@Param注解声明参数key(非String对象参数时可以不用声明)
public List<Member> searchMemberByNick(@Param("keyword") String keyword);
13.1 通过HashMap
@Test
public void searchMemberByNick(){
MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
HashMap<String,Object> params = new HashMap<String,Object>();
params.put("keyword","花");
List<Member> members = memberDAO.searchMemberByNick(params);
for (Member m:members){
System.out.println(m);
}
}
<!-- ${key} 表示获取参数,先获取参数的值拼接到sql语句中,再编译执行SQL语句,可能引起SQL注入问题-->
<!-- #{key} 表示获取参数,先完成SQL编译(预编译),预编译之后再将获取的参数设置到SQL语句中,可以避免SQL注入问题-->
<select id="searchMemberByNick" resultMap="memberMap">
Select a1.member_id,a1.member_nick,a1.member_gender,a1.member_age,a1.member_city
from members a1
where member_nick like "%${keyword}%"
</select>
13.2 通过String字符串
@Test
public void searchMemberByNick(){
MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
List<Member> members = memberDAO.searchMemberByNick("花");
for (Member m:members){
System.out.println(m);
}
}
特别注意:需加parameterType="java.lang.String"
<select id="searchMemberByNick" parameterType="java.lang.String" resultMap="memberMap">
Select a1.member_id,a1.member_nick,a1.member_gender,a1.member_age,a1.member_city
from members a1
where member_nick like "%${keyword}%"
</select>
== 070==
十四、MyBatis日志配置
MyBatis作为一个封装好的ORM框架,其运行的过程没有办法跟踪,为了让开发者了解MyBatis执行流程及每个执行步骤所完成的工作,MyBatis框架本身支持log4j日志框架,对运行过程进行跟踪记录。我们只需对MyBatis进行相关的日志配置,就可以看到MyBatis运行过程中的日志信息。
14.1 添加日志框架依赖
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
14.2 添加日志配置文件
- resources目录下创建名为 log4j.properties文件
- 日志输出的方式
# Global logging configuration
# 声明日志的输出级别,及输出方式stdout 表示输入到控制台
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# 定义日志的打印格式 %t表示线程名称 %5p表示输出日志级别
log4j.appender.stdout.layout.ConversionPattern=[%t] %5p - %msg \:%n%m
14.3 日志信息的级别
级别 | 说明 |
---|---|
DEBUG | 输出调试信息 |
INFO | 输出提示信息 |
WARN | 输出警告信息 |
ERROR | 一般性错误信息 |
FATAL | 致命性错误信息 |
071
十五、配置数据库连接池-整合Druid
MyBatis作为一个ORM框架,在进行数据库操作时是需要和数据连接的,频繁创建和销毁数据库连接会消耗一定的资源。
当我们配置MyBatis数据源时,只要配置了datasource标签的type属性为POOLED时,就可以使用MyBatis内置的连接池管理连接。
如果我们想要使用第三方的数据库连接,则需进行自定义配置。
15.1 常见连接池
- DBCP
- C3P0 使用简单,效率比较低
- Druid 效率是比较高的,基于配置,企业中应用的比较多,提供了比较便捷的监控系统。
- Hikari 基于日语翻译过来的,是性能最高的,
15.2 添加druid依赖
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
15.3 创建druid连接池工厂
public class DruidDateSourceFactory extends PooledDataSourceFactory {
public DruidDateSourceFactory() {
this.dataSource = new DruidDataSource();
}
}
15.4 将DruidDateSourceFactory 配置给MyBatis数据源
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<!-- <dataSource用于配置数据库连接信息-->
<!-- POOLED使用MyBatis内置的连接池-->
<!-- MyBatis需要一个连接池工厂,这个工厂可以产生数据库连接池 PooledDataSourceFactory -->
<dataSource type="com.zhang.utils.DruidDateSourceFactory">
<property name="driverClass" value="${mysql_driver}"/>
<property name="jdbcUrl" value="${mysql_url}"/>
<property name="username" value="${mysql_username}"/>
<property name="password" value="${mysql_password}"/>
</dataSource>
</environment>
十六、MyBatis缓存
MyBatis是基于JDBC的封装,使数据库操作更加便捷,MyBatis除了对JDBC操作步骤进行封装之外,也对其性能进行了优化。
- 在MyBatis引入缓存机制,用于提升MyBatis的检索效率。
- 在MyBatis引入 延迟加载机制,用于减少对数据库不必要的访问。
16.1 缓存的工作原理
缓存实际就是存储数据的内存。
16.2 MyBatis缓存分一级缓存和二级缓存
16.2.1 一级缓存
一级缓存也叫SqlSession级缓存,为每个SqlSession单独分配的缓存内存,无需手动开户可直接使用,多个SqlSession的缓存是不共享的。
特性
1、如果多次查询使用的是同一个SqlSesson对象,则第一次查询之后数据会存放到缓存,后续的查询则直接访问缓存中的数据。
2、如果第一次查询之后中,如果对对象进行了修改,此修改会影响到缓存,第二次查询,则查询是的缓存中的数据,但此时数据还没有持久化到数据库。这样第一次查询的结果与第二次的不一致,与数据库中的实际值也不一致。
3、当我们再次查询时,如果想要路过缓存时,则可以通过sqlSession.clearCache()清除缓存。
4、如果第一查询之后第二次查询之前,使用当前的sqlsession执行了修改操作,此修改操作会使第一次查询并缓存的数据失效,因此,第二次查询会再次访问数据库,重新查询数据。
@Test
public void searchMemberById(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
MemberDAO memberDAO = sqlSession.getMapper(MemberDAO.class);
Member members1 = memberDAO.searchMemberById(1);
System.out.println(members1);
members1.setMemberAge(99);
//sqlSession.clearCache();
System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
Member members2 = memberDAO.searchMemberById(1);
System.out.println(members2);
}
– 第二查询之前,修改了数据的情况
@Test
public void searchMemberById(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
MemberDAO memberDAO = sqlSession.getMapper(MemberDAO.class);
Member members1 = memberDAO.searchMemberById(1);
System.out.println(members1);
members1.setMemberAge(99);
//sqlSession.clearCache();
memberDAO.updateMemberById(1,99);
sqlSession.commit(); //commit()后会导致缓存失败,第二次查询时重查数据库
System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
Member members2 = memberDAO.searchMemberById(1);
System.out.println(members2);
}
074
16.2.2 二次查询与数据库数据不一致问题
问题:
第一次查询之后,进行了修改操作,数据库数据已经被修改,但是第二次查询的时候依然显示的修改前的数据。
分析:
修改操作和查询操作不是同一个线程,因此使用不同的对(使用不同的sqlsession),因此修改操作不会导致查询操作的缓存失效,第二次查询时依然访问的是缓存而没有查数据库。
解决方案:
1、让修改操作和查询操作使用相同的sqlsession.(不合理)
2、让每次进行查询操作之后,清空缓存。让再次查询的时候直接查数据库。
75
MyBatis缓存存在一定的局限性,可以生效,但我们一般不用。
并发访问,一个线程,多线程时导致资源竞争的问题。每个线程用自己的session。
二级缓存,
16.2.3 二级缓存
二级缓存也称SqlSessionFactory级缓存,通过同一个factory对象获取的sqlsession可以共享二级缓存。在应用服务器中,SqlSessionFactory本身是单例的,因此二级缓存可以实现全局共享。
特性:
1、二级缓存默认没有开启,需要在mybatis-config.xml中的setting标签中开启。
2、二级缓存只能缓存实现了序列化接口的对象
3、
- 在mybatis-config.xml中的setting标签中开启。
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
- 在需要使用二级缓存的Mapper文件中配置cache标签使用。
<cache/>
- 被缓存的实体类,实现序列化接口。
public class Member implements Serializable {
private int memberId;
private String memberNick;
private String memberGender;
private int memberAge;
private String memberCity;
}
- 测试二级缓存
@Test
public void searchMemberById(){
SqlSessionFactory factory = MyBatisUtil.getSqlSessionFactory();
// 多个sqlSession对象必须来自于同一个sqlSessionFactory
SqlSession sqlSession1 = factory.openSession(true);
SqlSession sqlSession2 = factory.openSession(true);
MemberDAO memberDAO1 = sqlSession1.getMapper(MemberDAO.class);
Member members1 = memberDAO1.searchMemberById(1);
System.out.println(members1);
sqlSession1.commit(); //第一次查询之后执行commit,会将当前查询结果缓存到二级缓存。
System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
MemberDAO memberDAO2 = sqlSession1.getMapper(MemberDAO.class);
Member members2 = memberDAO2.searchMemberById(1);
System.out.println(members2);
}
16.3 查询操作的缓存标签
只有selects标签可以使用。
useCache=“false” 强制当前操作不使用缓存信息 true则强制使用缓存。
<select id="searchMemberById" resultMap="memberMap" useCache="false">
Select a1.member_id,a1.member_nick,a1.member_gender,a1.member_age,a1.member_city
from members a1
where member_id = #{mid}
</select>
76
十七、延迟加载机制(只在子查询中有效果)
延迟加载-- 如果在MyBatis开启了延迟加载,则在执行了子查询(至少查询二次以上),默认只执行第一次查询,当用到子查询的查询结果时,才会触发子查询的执行;如果无需使用子查询结果,则子查询不会执行。
– fetchType=“lazy” 延迟加载标签
<resultMap id="classMap" type="Classes">
<id column="cid" property="cId"/>
<result column="cname" property="cName"/>
<result column="cdesc" property="cDesc"/>
<collection property="stus" select="com.zhang.dao.StudentDAO.queryStudentsByCid" column="cid" fetchType="lazy"/>
</resultMap>
<select id="queryClassById" resultMap="classMap" >
select cid,cname,cdesc
from classes
where cid = #{cid}
</select>