1.介绍
1.1 什么是MyBatis?
- MyBatis 是一款优秀的持久层框架
- 它支持自定义 SQL、存储过程以及高级映射
- MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
1.2 如何获取MyBatis?
-
Maven仓库
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency>
-
GitHub:https://github.com/mybatis/mybatis-3
-
中文网文档:https://mybatis.org/mybatis-3/zh/index.html
1.3 数据持久化:程序的数据由瞬时状态转化为持久状态的过程
1.4 持久层:完成持久化工作的代码块
1.5 为什么需要MyBatis?
- 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
- 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
- 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
- 提供映射标签,支持对象与数据库的orm字段关系映射
- 提供对象关系映射标签,支持对象关系组建维护
- 提供xml标签,支持编写动态sql。
2.第一个MyBatis程序
思路:搭建环境->导入Mybatis->编写代码->测试
2.1搭建环境
-
创建数据库和表
CREATE DATABASE mybatis1; USE mybatis1 CREATE TABLE users( uid INT PRIMARY KEY, uname VARCHAR(32), uaccount VARCHAR(32), upwd VARCHAR(32) )
-
导入依赖
<!--导入依赖--> <dependencies> <!--msql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> </dependencies>
-
创建包
-
创建实体类
public class Users implements Serializable { private long uid; private String uname; private String uaccount; private String upwd; public Users() { } public Users(long uid, String uname, String uaccount, String upwd) { this.uid = uid; this.uname = uname; this.uaccount = uaccount; this.upwd = upwd; } public long getUid() { return uid; } public void setUid(long uid) { this.uid = uid; } public String getUname() { return uname; } public void setUname(String uname) { this.uname = uname; } public String getUaccount() { return uaccount; } public void setUaccount(String uaccount) { this.uaccount = uaccount; } public String getUpwd() { return upwd; } public void setUpwd(String upwd) { this.upwd = upwd; } public String toString() { return "Users{" + "uid=" + uid + ", uname='" + uname + '\'' + ", uaccount='" + uaccount + '\'' + ", upwd='" + upwd + '\'' + '}'; } }
-
创建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> <!--加载外部配置文件--> <properties resource="db.properties"/> <settings> <setting name="logImpl" value="LOG4J"/> </settings> <typeAliases> <package name="com.codewen.entity"/> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/codewen/mapper/UsersMapper.xml"/> </mappers> </configuration>
-
创建获取SQLSession的工具类
public class SqlSessionUtil { private static SqlSessionFactory sqlSessionFactory; static { String resource = "mybatis-config.xml"; try { InputStream is = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); } catch (IOException e) { e.printStackTrace(); } } public static SqlSession getSession() { return sqlSessionFactory.openSession(); } }
-
创建mapper
public interface UsersMapper { List<Users> queryAllUsers(); }
-
创建mapper对应的xml
<?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.codewen.mapper.UsersMapper"> <select id="UsersMapper" resultType="com.codewen.entity.Users"> SELECT * FROM users </select> </mapper>
-
创建测试类
public class Test01 { public static void main(String[] args) { SqlSession session = SqlSessionUtil.getSession(); UsersMapper mapper = session.getMapper(UsersMapper.class); List<Users> users = mapper.queryAllUsers(); for(Users user : users) { System.out.println(user); } } }
此时运行后会发现一个异常 这是没有在mybatis配置文件中注册mapper的原因
-
注册mapper(有三种resource、class、url)
<mappers> <mapper resource="com/codewen/mapper/UsersMapper.xml"/> <!--<mapper class="com.codewen.mapper.UsersMapper"/>--> </mappers>
2.3运行结果
2.4资源过滤问题
如果遇到写的配置文件无法导入或者不生效的问题 可能是资源过滤问题
出现原因:配置文件没有放到resources目录下
解决方法(在pom.xml中设置resources文件):
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
3.CRUD
3.1 namespace中的包名要和dao接口全限定名一致
步骤(注意增删改需要提交事务):
- 在dao层接口中先写方法
- 再到mapper.xml中写对应的sql
- 直接进行测试即可
3.2 select
- id:就是namespace对应接口中的方法
- resultType:SQL语句执行的返回值类型 只有引用类型(必须写全限定类名)和基本类型
- parameterType:参数类型
// 查询所有用户
List<Users> queryAllUsers();
// 查询一个用户
Users queryUser(int uid);
<select id="queryAllUsers" resultType="com.codewen.entity.Users">
SELECT * FROM users
</select>
<select id="queryUser" resultType="com.codewen.entity.Users">
select * from users where uid=#{uid};
</select>
3.3 insert
// 增加一个用户
int insertUser(Users user);
<insert id="insertUser" parameterType="com.codewen.entity.Users">
insert into users (uname,uaccount,upwd) values (#{uname},#{uaccount},#{upwd});
</insert>
3.4 delete
// 删除一个用户
int deleteUser(int uid);
<delete id="deleteUser" parameterType="int">
delete from users where uid = #{uid};
</delete>
3.5 update
// 修改一个用户
int updateUser(Users user);
<update id="updateUser" parameterType="com.codewen.entity.Users">
update users set uname=#{uname}, upwd=#{upwd} where uid=#{uid};
</update>
3.6关于传参
- map传递参数在sql中只需要通过key即可取出值 parameterType=“map”
- 对象传递参数在sql中必须对应对象属性才能取出值 parameterType=“object”
- 如果传递参数是一个基本数据类型如int,那么parameterType可以省略不写
- 但如果传递参数是多个,那么必定不能省略不写,可以用map或者注解@Param
4.配置文件
4.1核心配置文件mybatis-config.xml(标签的顺序有先后,必须按照下面顺序排列)
4.2 环境配置environments
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis1"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</dataSource>
</environment>
</environments>
- environments的default属性表示选择的默认环境
- mybatis中的事务类型有两种,一种是JDBC,另一种是managed
- mybatis中的数据源类型有三种 POOLED、UNPOOLED、JNDI
4.3 属性properties
没有引用外部properties文件之前
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis1"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
引入外部properties文件之后
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis1
username=root
password=123
<?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 resource="db.properties"/>
<!--设置日志-->
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
</configuration>
4.4 typeAliases
第一种:typeAlias type时指定实体类的全限定类名 alias是别名(这种方式适合实体类较少的情况,取别名方便)
<typeAlias type="com.codewen.entity.Users" alias="user"/>
第二种:package name是实体类所在的包名(这种方式适合实体类较多的情况)
这种方式的别名就是实体类名的小写,当然也可以用大写,建议用小写
<package name="com.codewen.entity"/>
但是这种方式不能为其它别名,如果想改其它别名,得用到注解
@Alias("uuu")
4.5 设置settings
常见的设置
- cacheEnabled 使用缓存 默认为true
- lazyLoadingEnabled 懒加载 默认为false
- mapUnderscoreToCamelCase 驼峰命名 默认为false
- logImpl 指定日志实现 如log4j 默认无
4.6 插件plugins
- mybatis-generator-core
- mybatis-plus (mybatis简化版)
- 通用mappers
4.7 映射器mappers
有四种 resources、url、class、name(url不要去用)
方式一(推荐使用):
<mapper resource="com/codewen/mapper/UsersMapper.xml"/>
方式二:
<mapper class="com.codewen.mapper.UsersMapper"/>
注意点
- 接口和mapper.xml必须同名
- 接口和mapper.xml必须在同一个包或者在resources中相同的包
方式三:
<package name="com.codewen.mapper"/>
注意点
- 接口和mapper.xml必须同名
- 接口和mapper.xml必须在同一个包或者在resources中相同的包
4.8 生命周期和作用域
- SqlSessionFactory 相当于连接池
- SqlSession 相当于一个会话(请求)
- mapper 相当于一个具体的业务
5.日志
5.1 日志工厂
曾经:sout、控制台
现在:LOG4J、STDOUT_LOGGING…
5.2 stdout_logging(注意name和value大小写别写错!)
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
5.3 log4j
第一步:导入log4j依赖或包
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
第二步:创建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/codewen.log
log4j.appender.file.MaxFileSize=5mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd HH:mm:ss}][%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
第三步:mybatis-config.xml中开启日志(注意name和value大小写别写错!)
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
5.4 log4j的一般使用方法
static Logger logger = Logger.getLogger(Test1.class);
@Test
public void test2() {
logger.info("这是info级别日志");
logger.debug("这是debug级别日志");
logger.error("这是error级别日志");
}
6.属性名和字段名不匹配
第一种:修改sql语句,将sql中列的字段名取别名为属性名
第二种:使用resultMap,只需要映射不同的
<resultMap id="allUsersMap" type="users">
<result column="uid" property="id"/>
</resultMap>
<select id="queryAllUsers" resultMap="allUsersMap">
SELECT * FROM users
</select>
7.分页
7.1 使用limit分页
语法:
-- 分页 select * from stu limit 开始索引=(当前页面-1)*页面大小,页面大小;
SELECT * FROM stu LIMIT 0,5;
SELECT * FROM stu LIMIT 5,5;
SELECT * FROM stu ORDER BY id LIMIT 0,5;-- 可以先进行排序然后再分页显示
7.2 使用Rowbounds分页
// 使用Rowbounds分页
List<Users> queryAllUsersByRB();
<select id="queryAllUsersByRB" resultType="users">
SELECT * FROM users;
</select>
List<Users> users = sqlSession.selectList("com.codewen.mapper.UsersMapper.queryAllUsersByRB", null, new RowBounds(0, 2));
7.3 mybatis分页插件 PageHelper(需要时直接看文档使用)
8.sql语句中获取参数值
第一种:${} 这种方法不推荐,有SQL注入的风险,原样输出,但是还是适合于动态排序(动态字段)
第二种:#{} 预编译,可以有效防止SQL注入 会自动给String类型加上’'
9.Lombok使用
使用步骤:
第一步:下载lombok插件
第二步:导入jar包/依赖
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
第三步:创建实体类,利用注解使用
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private int id;
private String account;
private String pwd;
}
@Data:getter、setter、toString、equals、hashCode
@NoArgsConstructor:无参构造器
@AllArgsConstructor:有参构造器
10.映射
10.1 一对一association(javaType是属性的类型)
比如一个学生对应一个老师
第一种:按照结果进行嵌套
首先搭建数据库环境
# 创建数据库
CREATE DATABASE mybatis2;
# 创建学生表
CREATE TABLE student (
sno INT PRIMARY KEY AUTO_INCREMENT, -- 主键
sname VARCHAR(32),
sage INT,
s_tname VARCHAR(32)
);
# 创建教师表
CREATE TABLE teacher (
tno INT PRIMARY KEY AUTO_INCREMENT, -- 主键
tname VARCHAR(32) UNIQUE
);
# 添加外键约束及级联更新
ALTER TABLE student ADD CONSTRAINT s_tno_fk FOREIGN KEY(s_tno) REFERENCES teacher(tno) ON UPDATE CASCADE ON DELETE CASCADE;
# 插入数据
INSERT INTO teacher (tname,ttype) VALUES ('uzi','生物');
INSERT INTO teacher (tname,ttype) VALUES ('zs','语文');
INSERT INTO teacher (tname,ttype) VALUES ('ls','数学');
INSERT INTO student (sname,sage,s_tno) VALUES ('小红',17,3);
INSERT INTO student (sname,sage,s_tno) VALUES ('小龙',19,3);
INSERT INTO student (sname,sage,s_tno) VALUES ('小刚',18,2);
INSERT INTO student (sname,sage,s_tno) VALUES ('小李',16,3);
INSERT INTO student (sname,sage,s_tno) VALUES ('小赵',20,2);
INSERT INTO student (sname,sage,s_tno) VALUES ('小陈',18,1);
INSERT INTO student (sname,sage,s_tno) VALUES ('小宋',18,1);
INSERT INTO student (sname,sage,s_tno) VALUES ('老王',21,2);
# 级联查询
SELECT * FROM student,teacher WHERE student.s_tno=teacher.tno
然后组合教师类
private Teacher teacher;
最后根据返回结果进行属性和列的一一映射(注意映射了的属性才能获取值)
<resultMap id="student_teacher_map" type="student">
<id property="sno" column="sno"/>
<result property="sname" column="sname"/>
<result property="sage" column="sage"/>
<association property="teacher" javaType="teacher">
<id property="tno" column="tno"/>
<result property="tname" column="tname"/>
<result property="ttype" column="ttype"/>
</association>
</resultMap>
<select id="queryAllStudent2" resultMap="student_teacher_map">
SELECT *
FROM student,teacher
WHERE student.s_tno=teacher.tno
</select>
第二种:子查询的方式
不同之处是mapper.xml处编写不同
<!--第二种一对一关联查询-->
<select id="queryTeacher" resultType="teacher">
select * from teacher where tno=#{tno}
</select>
<resultMap id="student_teacher_map2" type="student">
<id property="sno" column="sno"/>
<result property="sname" column="sname"/>
<result property="sage" column="sage"/>
<result property="s_tno" column="s_tno"/>
<association property="teacher" column="s_tno" javaType="teacher" select="queryTeacher"/>
</resultMap>
<select id="queryAllStudent3" resultMap="student_teacher_map2">
SELECT *
FROM student
</select>
10.2 一对多collection(注意ofType是属性中元素的类型)
比如一个老师对应一个学生
private List<Student> students;
sql语句
SELECT * FROM student,teacher WHERE student.s_tno=teacher.tno AND tno=1
mapper.xml文件
<select id="queryAllStudentByTno" resultMap="teacher_student_map">
SELECT *
FROM student,teacher
WHERE student.s_tno=teacher.tno AND tno=#{tno};
</select>
<resultMap id="teacher_student_map" type="teacher">
<id property="tno" column="tno"/>
<result property="tname" column="tname"/>
<result property="ttype" column="ttype"/>
<collection property="students" ofType="student">
<id property="sno" column="sno"/>
<result property="sname" column="sname"/>
<result property="sage" column="sage"/>
</collection>
</resultMap>
11.动态sql及标签
11.1 sql标签与include标签
使用sql标签可以实现复用sql,id为该sql语句的名称
<sql id="queryAllBooks">
select * from book;
</sql>
sql标签跟include标签进行sql复用
<include refid="queryAllBooks"/>
11.2 if标签
if标签用法和java中的if很相似,test属性中是判断的内容
<if test="btime != null">
btime=#{btime}
</if>
<if test="author_array != null and author_array[0] != null">
and bauthor=#{author_array[0]}
</if>
11.3 where标签
where标签用来替代sql中传统的where,一般配合if标签使用
where标签的作用:
- 如果where后面没有语句,那么最后的sql不会把where加上
- 如果where后面有语句,那么会把第一个语句的and去掉
SELECT * FROM book
<where>
<if test="btime != null">
btime=#{btime}
</if>
<if test="author_array != null and author_array[0] != null">
and bauthor=#{author_array[0]}
</if>
</where>
11.4 set标签
set标签用来替代sql中传统的set,一般配合if标签使用
set标签的作用:用于update的sql中,会把末尾多余的逗号去掉
update book
<set>
<if test="bname!=null and bname!=''">
bname=#{bname},
</if>
<if test="bauthor!=null and bauthor!=''">
bauthor=#{bauthor},
</if>
<if test="bintroduction!=null and bintroduction!=''">
bintroduction=#{bintroduction}
</if>
</set>
...
11.5 trim标签
<trim prefix="" prefixOverrides="" suffix="" suffixOverrides=""></trim>
prefix:为标签下的sql语句加一个前缀
prefixOverrides:标签下的sql语句前面多了可以去掉
suffix:为标签下的sql语句加一个后缀
suffixOverrides:标签下的sql语句后面多了可以去掉
<trim prefix="ORDER BY" suffixOverrides="," suffix="desc">
<!-- 时间排序 -->
<if test="conditionMap.byTime !=null and conditionMap.byTime !='' ">
btime,
</if>
<!-- 字数排序 -->
<if test="conditionMap.byWord !=null and conditionMap.byWord !='' ">
totalwords,
</if>
<!-- 阅读数排序 -->
<if test="conditionMap.byRead !=null and conditionMap.byRead !='' ">
readtimes,
</if>
</trim>
这个例子中**suffixOverrides=","**就是把suffixOverrides:标签下的sql语句后面多余的逗号去掉
11.6 bind标签
bind标签就相当于在value值中,把需要的字符拼接好,然后用name中的值去代替拼接好的参数。(一般用于与#{}取值拼接)
使用 bind 拼接字符串不仅可以避免因更换数据库而修改 SQL,也能预防 SQL 注入。
<bind name="bindfparam" value=" '%'+ parameterMap.fparam +'%' "/>
concat(bname,bauthor,bcategory) like #{bindfparam}
11.7 foreach标签
foreach标签可以迭代对象的属性、数组、集合、对象数组、对象集合,构建in条件语句或者批量操作语句
<foreach collection="" item="" open="" close="" separator="" index=""/>
collection:表示要迭代的对象的属性、数组、集合、对象数组、对象集合
item:collection中要迭代的单个元素
open:表示该段语句以什么开头
close:表示该语句以什么结尾
separator:表示该语句以什么划分
index:在list、Set和数组中,index表示当前迭代的位置,在map中,index代指是元素的key,一般不用
注意点:
- 如果要迭代的是数组(简单类型的或者对象类型的都一样) collection中必须是array
// 通过bnum的in条件查询书籍
List<Book> queryBookByBIN(int[] bnums);
<!--通过bnum的in条件查询书籍-->
<select id="queryBookByBIN" resultType="book">
select * from book
<where>
bnum
<foreach collection="array" item="bnum" open=" in(" close=");" separator=",">
#{bnum}
</foreach>
</where>
</select>
- 如果要迭代的是集合,collection必须是list
List<Book> queryBookByBIN(List bnums);
<!--通过bnum的in条件查询书籍-->
<select id="queryBookByBIN" resultType="book">
select * from book
<where>
bnum
<foreach collection="list" item="bnum" open=" in(" close=");" separator=",">
#{bnum}
</foreach>
</where>
</select>
- 如果要迭代的是对象,collection直接为对象中的属性即可
12.关于传参
12.1 如果传入的参数只有一个
parameterType可以不写(这里其实parameterType可以随意写,只要是可以识别的)
// 传入的是一个对象
int addBook(Book book);
// 那么parameterType要写的话就写该对象的类型
parameterType="book"
// 传入的是一个基本数据类型
List<Book> queryBookByBnum(int bnum);
// 要写的话就写对应的基本数据类型
parameterType="int"
12.2 如果传入的参数是两个及以上(要使用@Param来命名参数)
parameterType就没必要写了
// 通过btime和bauthor查询书籍
List<Book> queryBookByTA(String btime, String bauthor);
<!--通过btime和bauthor查询书籍-->
<select id="queryBookByTA" resultType="book">
SELECT * FROM book
<where>
<if test="btime != null">
btime=#{btime}
</if>
<if test="bauthor != null">
and bauthor=#{bauthor}
</if>
</where>
</select>
可以看到这样无法识别传入的参数,要使用@Param来命名参数,取的时候直接取命名的即可
List<Book> queryBookByTA(@Param("btime") String btime,@Param("bauthor") String bauthor);
12.3 关于其他类型的传参及取值
如果参数中有map(可以通过#{map.键}取值)
List<Book> queryBookByTA(@Param("btime") String btime,@Param("author_map") Map author_map);
<!--通过btime和bauthor查询书籍-->
<select id="queryBookByTA" resultType="book">
SELECT * FROM book
<where>
<if test="btime != null">
btime=#{btime}
</if>
<if test="author_map != null and author_map.bauthor != null">
and bauthor=#{author_map.bauthor}
</if>
</where>
</select>
如果传入的参数有list(可以通过#{list[i]}取值)
List<Book> queryBookByTA(@Param("btime") String btime,@Param("author_list") List author_list);
<!--通过btime和bauthor查询书籍-->
<select id="queryBookByTA" resultType="book">
SELECT * FROM book
<where>
<if test="btime != null">
btime=#{btime}
</if>
<if test="author_list.size>0 and author_list[0] != null">
and bauthor=#{author_list[0]}
</if>
</where>
</select>
如果传入的参数有数组(类似list取值)
List<Book> queryBookByTA(@Param("btime") String btime,@Param("author_array") String[] author_array);
<!--通过btime和bauthor查询书籍-->
<select id="queryBookByTA" resultType="book">
SELECT * FROM book
<where>
<if test="btime != null">
btime=#{btime}
</if>
<if test="author_array != null and author_array[0] != null">
and bauthor=#{author_array[0]}
</if>
</where>
</select>
13.关于返回类型
13.1 如果返回的不是复杂类型要使用resultType
不是复杂类型:基本数据类型、JavaBean、map、list、数组… resultType中是MyBatis内置Java类型对应的别名
13.2 如果返回值类型是复杂类型(要用resultMap来进行属性和字段的映射)
如:字段名与属性名不一致需要重新映射(即实体类的属性与数据库的字段不一致时) 主键用 其他字段用
<id property="tno" column="tno"/>
<result property="tname" column="tname"/>
<result property="ttype" column="ttype"/>
若存在一对一或者一对多的复杂关系映射 可见10.映射
14.缓存
14.1 一级缓存 sqlSession级别的缓存,默认是开启的
失效的情况:
- sqlSession不同
- sqlSession相同,查询不同
- 手动清除了一级缓存
- sqlSession相同,两次相同查询之间进行了增删改
14.2 二级缓存
开启二级缓存步骤:
第一步:显式开启全局配置
<setting name="cacheEnabled" value="true"/>
第二步:mapper.xml中增加cache标签
cache中的一些参数
第三步:实体类都要实现可序列化接口
注意点:
- 每个select标签都有useCache=“true” 跟二级缓存有关,一级缓存不受影响
- useCache=“true” 开启二级缓存,但如果全局配置没开启二级缓存(cacheEnabled=false),useCache就没用
14.3 缓存原理
首先,查询会先看二级缓存中有没有,如果没有再看一级缓存,一级缓存也没有就查数据库,查到了后把结果放到一级缓存,待当前会话结束后,一级缓存中的缓存数据会转移到二级缓存中
14.4 自定义缓存
创建一个类实现org.apache.ibatis.cache.Cache接口即可
14.5 第三方提供的二级缓存ehcache
第一步:导入依赖或包
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.0.0</version>
</dependency>
第二步:
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
第三步:创建ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<diskStore path="./tmpdir/Tmp_EhCache"/>
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"/>
<cache
name="cloud_user"
eternal="false"
maxElementsInMemory="5000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>