MyBatis
0、环境配置
- jdk: 1.8
- maven: 3.6.3
- mysql: 8.0.24
- mybatis: 3.5.7
##1、简介
1. 1、什么是mybatis
- MyBatis 是一款优秀的***持久层框架***,它支持自定义 SQL、存储过程以及高级映射。
- MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
- MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
- MyBatis本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了[google code](https://baike.baidu.com/item/google code/2346604),并且改名为MyBatis。
- 2013年11月迁移到Github。
获取MyBatis
-
maven仓库:https://mvnrepository.com/search?q=mybatis
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.7</version> </dependency>
-
github: https://github.com/mybatis/mybatis-3
-
官方文档: https://mybatis.org/mybatis-3/zh/index.html
###1. 2、持久化
- 持久化就是将程序的数据在持久状态和瞬时状态转换的过程
- 内存:断电即失
- 数据库(jdbc):io文件持久化
为什么需要持久化
- 有一些对象,不能让他丢掉
- 内存太贵
1. 3、持久层
Dao层
- 完成持久化工作的代码块
- 层界限十分明显
1. 4、为什么需要mybatis?
- 帮助程序员把数据存储到数据库中
- 传统的jdbc代码太复杂。简化,框架,自动化
2、搭建一个Mybatis程序
思路: 搭建环境 --> 导入mybatis --> =编写代码 --> 测试
2.1、环境搭建
-
创建数据库,表
-
创建项目
-
建一个普通maven项目
-
删除src目录(将该项目作为当前项目的父工程,通过引用到模块,不用重复导入依赖)
-
导入maven依赖
<!--导入依赖--> <dependencies> <!--mybatis--> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.7</version> </dependency> <!--mysql驱动--> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.24</version> </dependency> <!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.1</version> <scope>test</scope> </dependency> </dependencies>
-
新建maven模块
-
2. 2、创建一个模块
-
编写mybatis的核心配置文件(resources/mybatis-config.xml)
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <!--核心配置文件--> <configuration> <environments default="development"> <environment id="development"> <!--事务管理--> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="1507298022"/> </dataSource> </environment> </environments> <!--每一个Mapper.xml 都需要在mybatis核心配置文件中注册--> <mappers> <mapper resource="com/feng/dao/UserMapper.xml"/> </mappers> </configuration>
-
编写mybatis工具类
public class MybatisUtil { public static SqlSessionFactory sqlSessionFactory; static { try { // 获取SqlSessionFactory对象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } // 既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。 // SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。 // 你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句 public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(); } }
2. 3、编写代码
-
实体类
public class User { private Integer id; private String name; private String paw; }
-
Dao接口
public interface UserDao { List<User> getUserList(); }
-
接口实现类(UserMapper.xml/UserDao.xml 代替了原来的UserDaoImpl)
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace=绑定一个对应的Dao/Mapper接口--> <mapper namespace="com.feng.dao.UserDao"> <!--查询语句 id=需要重写的方法 resultType=将结果封装到一个对应的类中--> <select id="getUserList" resultType="com.feng.pojo.User"> select * from mybatis.user </select> </mapper>
2. 4、测试
可能会遇到的错误类型:Error parsing SQL Mapper Configuration. Cause: java.io.IOException: Could not find resource org/mybatis/example/BlogMapper.xml (配置文件没有注册)
-
测试代码
public class UserDaoTest { @Test public void test(){ // 得到SqlSession对象 SqlSession sqlSession = MybatisUtil.getSqlSession(); try { // 获取接口对象 UserDao mapper = sqlSession.getMapper(UserDao.class); // 调用方法 List<User> userList = mapper.getUserList(); for (User user : userList){ System.out.println(user); } } catch (Exception e) { e.printStackTrace(); } finally { // 关闭资源 sqlSession.close(); } } }
-
防止资源导出失败问题
<!--在build中配置resources, 防止资源导出失败问题--> <build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> </resources> </build>
-
还可能出现的问题
- 配置文件没注册
- 绑定接口错误
- 方法名不对
- 返回类型不对
- maven导出资源问题
3、CRUD
CRUD: Create Read Updata Delete (增删改查)
<mapper namespace="com.feng.dao.UserMapper">
<select id="getUserById" resultType="com.feng.pojo.User" parameterType="Integer">
select * from mybatis.user where id = #{id}
</select>
</mapper>
3. 1、参数信息:
- namespace:命名空间,接口的全类名
- id:当前接口的对应方法
- resultType:返回结果的类型(实体类),增删改没有
- parameterType:返回值类型,基本数据类型可以省略
3. 2、select
<select id="getUserById" resultType="com.feng.pojo.User" parameterType="Integer">
select * from mybatis.user where id = #{id}
</select>
3. 3、insert
<!--parameterType 的参数必须全类名-->
<insert id="insertUser" parameterType="com.feng.pojo.User">
insert into mybatis.user(id, name, paw) values (#{id}, #{name}, #{paw})
</insert>
3. 4、updata
<!--int 的参数类型可以不写-->
<update id="updateUser" parameterType="com.feng.pojo.User">
update mybatis.user set name = #{name}, paw = #{paw} where id = #{id}
</update>
3. 5、delete
<delete id="deleteUser" parameterType="Integer">
delete from mybatis.user where id = #{id}
</delete>
3. 6、测试
@Test
public void insertUser(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.insertUser(new User(1004, "丰", "1231223"));
// 增删改需要提交事务
sqlSession.commit();
sqlSession.close();
注意点:增删改需要提交事务
3. 7、万能的map
在我们的实体类或数据库中的表的字段过多时,可以使用Map, 使用该方法可以不用到实体类,将对应的参数封装到Map中
// 对应的接口
int insertUser2(Map<String, Object> map);
<!--参数类型使用的是map, 字段名不用于数据库中的字段名一致, 对应添加的字段名为对应map的key-->
<insert id="insertUser2" parameterType="map">
insert into mybatis.user(id, name, paw) values (#{userId}, #{UserName}, #{UserPaw})
</insert>
@Test
public void insertUser2(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<>();
map.put("userId", 1005);
map.put("UserName", "02");
map.put("UserPaw", "123456");
mapper.insertUser2(map);
// 增删改需要提交事务
sqlSession.commit();
sqlSession.close();
}
- Map传递参数,直接在sql中取出key即可: parameterType=“map”
- 对象传递参数,直接在对象中取对应的属性即可: parameterType=“Object”
- 只有一个基本数据类型传递参数,可以直接在sql中取得
- 多个参数,需要用到Map 或 注解
3. 8、模糊匹配
-
java执行代码时,传递通配符
List<User> userLike = mapper.getUserLike("%五%");
-
在sql中拼接通配符
select * from mybatis.user where name like "%"#{value}"%"
4、配置文件
4. 1、configuration(配置)
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- environment(环境变量)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器
4. 2、环境配置(environments)
-
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中
-
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
-
MyBatis 的默认事务管理器(transactionManager):JDBC
-
数据库连接池(dataSource):POOLED
4. 3、属性(properties)
- 属性可以在外部进行配置,并可以进行动态替换。
- 你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置
-
编写一个配置文件(jdbc.properties)
driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8 username=root password=1507298022
-
在核心配置(mtbatis-config.xml)中引入
<!--引入外部文件--> <properties resource="jdbc.properties"> <!--设置属性,优先级低于文件中读取出来的优先级--> <property name="username" value="dev_user"/> <property name="password" value="F2Fa3!33TYyg"/> </properties>
-
动态配置属性值
<dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource>
4. 4、类型别名(typeAliases)
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。后面使用到该类名时,可以直接通过别名引入。
<typeAliases>
<!--方式一,通过引用指定类-->
<typeAlias alias="User" type="com.feng.pojo.User"/>
<!--方式二,通过引用包,直接使用类名调用,若实体类有注解@Alias("xxx"),则使用xxx调用-->
<package name="com.feng.pojo"/>
</typeAliases>
4. 5、设置(settings)
官网说明: https://mybatis.org/mybatis-3/zh/configuration.html#settings
配置方法:
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
4. 6、映射器(mappers)
SQL 映射语句。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。
-
方式一: 常用
<!-- 使用相对于类路径的资源引用 --> <mappers> <mapper resource="com/feng/dao/UserMapper.xml"/> </mappers>
-
方法二
<!-- 使用映射器接口实现类的完全限定类名 --> <mappers> <mapper class="com.feng.dao.UserMapper"/> </mappers>
-
方式三
<!-- 将包内的映射器接口实现全部注册为映射器 --> <mappers> <package name="org.mybatis.builder"/> </mappers>
注意:方式二和方式三要求,类名与对应的xml文件名相同,并且需要在同一个包下
4. 7、生命周期和作用域
不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder
- 一旦创建了 SqlSessionFactory,就不再需要它了。
- 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)
SqlSessionFactory
- SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
- SqlSessionFactory 的最佳作用域是应用作用域。
- 最简单的就是使用单例模式或者静态单例模式。
- 可以想象为数据库连接池。
SqlSession
- 链接到链接池的一个请求。
- 每个线程都应该有它自己的 SqlSession 实例。
- SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
- 用完之后,马上关闭,防止资源被占用。
5、结果映射
resultMap
元素是 MyBatis 中最重要最强大的元素。- ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
解决属性名和字段名不一致问题(resultMap)
<!--resultMap映射,将属性和对应的字段联系起来(解决属性名与字段名不一致的问题)-->
<resultMap id="UserMap" type="User">
<result property="password" column="paw"/>
</resultMap>
<!--查询语句 id=需要重写的方法-->
<select id="getUserList" resultMap="UserMap">
select * from mybatis.user
</select>
- resultMap标签: 将 type 中的实体类映射到 id 属性值中,需要与select标签中的resultMap属性值对应
- property:对象的属性名
- column:数据库表的字段名
6、日志
在 settings 标签中设置 logImpl 属性的值
- SLF4J
- LOG4J(deprecated since 3.5.9) (需要掌握)
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING (需要掌握)
- NO_LOGGING
6. 1、STDOUT_LOGGING
idea自带的日志信息
<settings>
<!--配置日志信息-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
6. 2、LOG4J(常用)
- Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件。
- 我们也可以控制每一条日志的输出格式。
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
- 可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
-
导入Log4j包
<!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
-
配置log4j
<setting name="logImpl" value="LOG4J"/>
-
建立 log4j.properties 配置文件
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码 log4j.rootLogger=DEBUG,console,file #控制台输出的相关设置 log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.Threshold=DEBUG log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=[%c]-%m%n #文件输出的相关设置 log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/shun.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n #日志输出级别 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG
常用的Log4j 的日志信息
static Logger logger = Logger.getLogger(UserDaoTest.class); @Test public void log4j(){ logger.info("info进入了log4jTest"); logger.debug("debug进入了log4jTest"); logger.error("error进入了log4jTest"); }
7、分页
7. 1、使用 limit 分页(sql层面)
-
编写接口方法
// 需要两个参数(StartIndex, pageSize),多个参数可以用map封装 List<User> getLimitUser(Map<String, Integer> map);
-
在对应的配置文件中编写sql语句
<!--分页--> <select id="getLimitUser" parameterType="map" resultType="User"> select * from mybatis.user limit #{startIndex}, #{pageSize} </select>
-
测试
@Test public void getLimitUser(){ SqlSession sqlSession = MybatisUtil.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); Map<String, Integer> map = new HashMap<>(); map.put("startIndex", 1); map.put("pageSize", 3); List<User> limitUser = mapper.getLimitUser(map); System.out.println(limitUser); sqlSession.close(); }
7. 2、RowBounds分页(java层面)
-
编写接口方法
// RowBounds分页 List<User> getRowBoundsUserList();
-
配置文件
<!--使用RowBounds分页--> <select id="getRowBoundsUserList" resultMap="UserMap"> select * from mybatis.user </select>
-
测试
@Test public void getRowBoundsUserList(){ SqlSession sqlSession = MybatisUtil.getSqlSession(); RowBounds rowBounds = new RowBounds(1, 3); List<User> User = sqlSession.selectList("com.feng.dao.UserMapper.getRowBoundsUserList", null, rowBounds); System.out.println(User); sqlSession.close(); }
7. 3、分页插件(PageHelper)
需要时,查看文档: https://pagehelper.github.io/
8、使用注解开发
- 它们映射的语句可以不用 XML 来配置,而可以使用 Java 注解来配置。
- 使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心。
- 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。
-
注解在接口上实现
@Select("select * from user") // 在注解中写sql语句 List<User> getUserList();
-
在核心配置文件中绑定接口
<mappers> <!--<mapper class="com.feng.dao.UserMapper"/>--> <package name="com.feng.dao"/> </mappers>
本质:反射机制
底层:动态代理
8. 1、CRUD
-
接口
// 方法存在多个参数时,所有的参数前面都要加上 @Param("xx") 注解 @Select("select * from user where id = #{id}") User getUserById(@Param("id") Integer id); @Insert("insert into user(id, name, paw) values (#{id}, #{name}, #{password})") int insertUser(User user); @Update("update user set name = #{name}, paw = #{password} where id = #{id}") int updateUser(User user); // 参数为一个时,可以省略@Param("xx") @Delete("delete from user where id = #{id}") int deleteUser(Integer id);
关于@Param() 注解
- 基本数据类型与
String
类型需要加上 - 引用数据类型不用加,字段名需要与属性名一致
- 如果只有一个基本数据类型,可以不用加,建议加上
- 在sql中引用的是@Param()中设置的属性名
8. 2、Lombok
作用:使用注解的方式自动生成实体类的get,set,toString 等方法,使得代码不那么臃肿
使用:1.安装Lombok插件 2. 导入Lombok包(使用maven)
9、多表处理
创建student
和teacher
表
CREATE TABLE `teacher` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老师');
CREATE TABLE `student` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`tid` INT(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fktid` (`tid`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');
- 多个学生,对应一个老师
- 对于学生而言,多个学生,关联一个老师(多对一) 关联
- 对于老师而言,一个老师,有很多学生(一对多) 集合
9. 1、多对一处理
测试环境搭建
- 创建实体类 Teacher, Student
- 创建对应的Mapper接口,每一个实体类对应一个Mapper接口
- 创建对应的Mapper.xml配置文件
- 在核心配置文件中绑定注册我们的Mapper接口或xml文件
-
sql语句
select s.id, s.name, s.tid, t.name from mybatis.student s join mybatis.teacher t on t.id = s.tid
-
按照查询嵌套处理
<!--思路: 1、查询出所有的学生信息 2、根据查询出来的学生的tid,查出对应的老师 --> <!--按照查询嵌套处理--> <select id="getStudent" resultMap="StudentTeacher"> select * from mybatis.student </select> <resultMap id="StudentTeacher" type="Student"> <result property="id" column="id"/> <result property="name" column="name"/> <!--复杂的类型,需要单独处理 对象:association 集合:collection--> <!--select 嵌套查询,与下面一个select的id对应(映射)--> <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/> </resultMap> <select id="getTeacher" resultType="Teacher"> select * from mybatis.teacher </select>
-
按照结果嵌套处理
<!--按照结果嵌套处理--> <select id="getStudent2" resultMap="StudentTeacher2"> select s.id sid, s.name sname, t.id tid, t.name tname from mybatis.student s join mybatis.teacher t on s.tid = t.id; </select> <resultMap id="StudentTeacher2" type="Student"> <result property="id" column="sid"/> <result property="name" column="sname"/> <association property="teacher" javaType="Teacher"> <result property="id" column="tid"/> <result property="name" column="tname"/> </association> </resultMap>
9. 2、一对多处理
-
sql
select t.id, t.name, s.id, s.name from mybatis.student s join mybatis.teacher t on t.id = s.tid where t.id = 1;
-
接口方法
Teacher getTeacher(@Param("tid") Integer id);
-
按查询嵌套处理
<select id="getTeacher" resultMap="TeacherStudent"> select * from mybatis.teacher where id = #{tid} </select> <resultMap id="TeacherStudent" type="Teacher"> <result property="id" column="id"/> <collection property="students" column="id" javaType="ArrayList" ofType="Student" select="getStudent"/> </resultMap> <select id="getStudent" resultType="Student"> select * from mybatis.student </select>
-
按照结果嵌套处理
<select id="getTeacher2" resultMap="TeacherStudent2"> select t.id tid, t.name tname, s.id sid, s.name sname from mybatis.student s join mybatis.teacher t on t.id = s.tid where t.id = #{tid} </select> <resultMap id="TeacherStudent2" type="Teacher"> <result property="id" column="tid"/> <result property="name" column="tname"/> <collection property="students" ofType="Student"> <result property="id" column="sid"/> <result property="name" column="sname"/> <result property="tid" column="tid"/> </collection> </resultMap>
9. 3、小结
- 关联:association (多对一)
- 集合:collection (一对多)
- javaType:用来指定实体类的中的属性类型
- ofType:用来指定映射到集合中的pojo类型,泛型中的约束类型
10、动态sql
根据条件动态调整sql语句(sql拼接)
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
10. 1、环境搭建
-
创建数据表
CREATE TABLE blog( id INT(10) NOT NULL COMMENT '博客id', title VARCHAR(50) NOT NULL COMMENT '博客标题', author VARCHAR(30) NOT NULL COMMENT '博客作者', create_time datetime NOT NULL COMMENT '创建时间', `view` int(30) NOT NULL COMMENT '浏览量' );
-
创建实体类
public class Blog { private int id; private String title; private String author; private Date createTime; private int view; }
-
编写对应的Mapper接口和Mapper.xml文件
10. 2、if, where
<select id="queryBlogIf" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<if test="id != null">
id = #{id}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
</select>
10. 3、choose、when、otherwise
<select id="queryBlogChoose" resultType="Blog" parameterType="map">
select * from mybatis.blog
<where>
<choose>
<when test="title != null">
title = #{title}
</when>
<when test="author != null">
author = #{author}
</when>
<otherwise>
id = #{id}
</otherwise>
</choose>
</where>
</select>
10. 4、trim (where, set)
<!--自定义分割-->
<trim prefix="SET" suffixOverrides=",">
<!--这里的代码与where/set相同-->
</trim>
<update id="updateBlog" parameterType="map">
update mybatis.blog
<set>
<if test="title != null">
title = #{title},
</if>
<if test="author != null">
author = #{author}
</if>
</set>
where id = #{id}
</update>
所谓的动态sql,本质还是SQL语句,只是在sql层面去执行了逻辑代码
10. 5、foreach
<select id="queryBlogFor" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<if test="ids != null">
<foreach collection="ids" item="id" open="id in (" close=")" separator=",">
#{id}
</foreach>
</if>
</where>
</select>
万能的map集合中放有一个集合/数组,遍历出来的即为这个集合/数组中的值
- cllection:当前存放集合/数组的map对应的key
- item:遍历出来的值放入item中
- open/close:拼接以什么开头,以什么结尾
- separator:以什么分割
10. 6、SQL片段
如果有需要多次使用的sql标签,可以将其抽取出来,方便复用
<!--抽取出sql标签的公共部分-->
<sql id="if_id_author">
<if test="id != null">
id = #{id}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
<select id="queryBlogIf" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<!--include表签引用-->
<include refid="if_id_author"></include>
</where>
</select>
11、mybatais缓存
-
MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。
-
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。
-
要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行()。
-
映射语句文件中的所有 select 语句的结果将会被缓存。
-
映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
-
缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
-
缓存不会定时进行刷新(也就是说,没有刷新间隔)。
-
缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
-
以后如果需要获取相同的数据,直接在缓存中拿,就不用再去查数据库了。
11. 1、一级缓存(本地缓存)
作用域:数据库同一次会话期间,当前SqlSession内有效,sqlSession.close()后,清空当前缓存
一级缓存是默认开启的,sqlSession.clearCache() 手动清除缓存
11. 2、二级缓存(全局缓存)
- 因为二级缓存的作用域太低了,所以有了二级缓存
- 基于namespace级别的缓存
- 工作机制
- 一个会话查询出一条数据,这个数据不会马上保存到二级缓存中,而是在一级缓存中
- 当当前会话关闭,这个会话缓存就没了,此时,保存在当前一级缓存的内容才会加载到二级缓存中
- 新的会话查询信息,会先从二级缓存中取相关内容,如果没有,再去一级缓存中去拿,再没有,就去查数据库
- 不同的mapper查出来的数据会放在自己的缓存中(map)
开启二级缓存,可以自定义参数
<cache
eviction="FIFO" // 输入输出缓存
flushInterval="60000" // 60秒刷新一次缓存
size="512" // 最多可以存储结果对象或列表的 512 个引用
readOnly="true"/> // 返回的对象被认为是只读的
若没有配置 eviction=“FIFO” ,还需要将实体类序列化
11. 3、自定义缓存
ehcache: 需要的时候查文档 https://www.ehcache.org/