一、MyBatis简介
1、什么是mybatis?
mybatis 是一个优秀的基于 java 的持久层框架,它内部封装了 jdbc,使开发者只需要关注 sql 语句本身,而不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。
mybatis 通过 xml 或注解的方式将要执行的各种 statement 配置起来,并通过 java 对象和 statement 中
sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并返回。
采用 ORM 思想解决了实体和数据库映射的问题,对 jdbc 进行了封装,屏蔽了 jdbc api 底层访问细节,使我们不用与 jdbc api 打交道,就可以完成对数据库的持久化操作。
- MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。
- MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
- MyBatis可以用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录.
总之就是两个字
2、MyBatis历史 ���
- 原是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation 迁移到了Google Code,随着开发团队转投Google Code旗下, iBatis3.x正式更名为MyBatis ,代码于2013年11月迁移到Github(下载地址见后)。
- iBatis一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。 iBatis提供的持久层框架包括SQL Maps和Data Access Objects(DAO)
3、为什么要使用MyBatis?❔
- MyBatis是一个半自动化的持久化层框架。
- JDBC
- 夹在Java代码块里,耦合度高导致硬编码内伤
- 维护不易且实际开发需求中sql是有变化,频繁修改的情况多见
- Hibernate和JPA
- 长难复杂SQL,对于Hibernate而言处理也不容易
- 内部自动生产的SQL,不容易做特殊优化。
- 基于全映射的全自动框架,大量字段的POJO进行部分映射时比较困难。导致数据库性能下降。
- sql和java编码分开,功能边界清晰,一个专注业务、一个专注数据。
4、去哪里找MyBatis?
GitHub - mybatis/mybatis-3: MyBatis SQL mapper framework for Java
二、MyBatis_(XML式编程)
1.创建Maven工程
2.在pom.xml引入相关坐标
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<mybatis.version>3.5.3</mybatis.version>
<mysql.version>5.1.38</mysql.version>
<lombok.version>1.18.6</lombok.version>
<slf4j.version>1.6.4</slf4j.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- Mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- MySql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--java链接Oracle驱动-->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.1.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
</dependencies>
3.创建学生表和班级表
CREATE TABLE `class` (
`cid` int(11) NOT NULL AUTO_INCREMENT,
`cname` varchar(64) DEFAULT NULL,
PRIMARY KEY (`cid`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
CREATE TABLE `student` (
`sid` int(11) NOT NULL AUTO_INCREMENT,
`s_name` varchar(32) DEFAULT NULL,
`sgender` varchar(16) DEFAULT NULL,
`age` int(16) DEFAULT NULL,
`classid` int(16) DEFAULT NULL,
PRIMARY KEY (`sid`),
KEY `fk_s_c` (`classid`),
CONSTRAINT `fk_s_c` FOREIGN KEY (`classid`) REFERENCES `class` (`cid`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8
4.定义与表对应的实体类
public class Class {
private Integer cid;
private String cname;
public Integer getCid() {
return cid;
}
public void setCid(Integer cid) {
this.cid = cid;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
@Override
public String toString() {
return "Class{" +
"cid=" + cid +
", cname='" + cname + '\'' +
'}';
}
}
public class Student {
private Integer sid; //列名sid
private String sName; //列名s_name
private String sGender; //列名sgender
private Integer sage; //列名age
@Override
public String toString() {
return "Student{" +
"sid=" + sid +
", sName='" + sName + '\'' +
", sGender='" + sGender + '\'' +
", sage=" + sage +
'}';
}
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public String getsName() {
return sName;
}
public void setsName(String sName) {
this.sName = sName;
}
public String getsGender() {
return sGender;
}
public void setsGender(String sGender) {
this.sGender = sGender;
}
public Integer getSage() {
return sage;
}
public void setSage(Integer sage) {
this.sage = sage;
}
public Student() {}
public Student(Integer sid, String sName, String sGender, Integer sage) {
this.sid = sid;
this.sName = sName;
this.sGender = sGender;
this.sage = sage;
}
}
5.创建SQL映射文件
这是我们使用MyBatis时编写的最多的文件,学生类对应StudentMapper,CRUD的SQL语句就定义在这里
<?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.etc.mapper.StudentMapper">
<!--id是这个select语句的唯一标识-->
<!--resultType表示把查询到的结果封装给哪个实体类-->
<!--parameterType表示参数的类型 如果参数只有一个可以省略该属性-->
<!--#{sid}可以接受到执行该SQL时传递进来的参数-->
<!--<select id="selectOne" resultType="com.etc.domain.Student" parameterType="integer">-->
<select id="selectOne" resultType="com.etc.domain.Student">
select * from student where sid = #{sid}
</select>
</mapper>
6.创建MyBatis全局配置文件
MyBatis 的全局配置文件包含了影响 MyBatis 行为的设置(settings)和属性(properties)信息、如数据库连接池信息等。指导着MyBatis进行工作。我们可以先参照官方文件的配置示例。
<?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="mysql">
<environment id="mysql">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--驱动-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--URL-->
<property name="url" value="jdbc:mysql://localhost:3306/tx?serverTimezone=GMT"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--
将写好的SQL映射文件注册到全局配置文件中
在类路径下 直接写文件名-->
<mapper resource="StudentMapper"></mapper>
</mappers>
</configuration>
7.创建打印日志文件
为了方便调试,查看SQL的执行过程,可以引入log4j.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration debug="true">
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<param name="Encoding" value="UTF-8"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n"/>
</layout>
</appender>
<logger name="java.sql">
<level value="debug"/>
</logger>
<logger name="org.apache.ibatis">
<level value="info"/>
</logger>
<root>
<level value="debug"/>
<appender-ref ref="STDOUT"/>
</root>
</log4j:configuration>
8.编写测试代码
从 XML 中构建 SqlSessionFactory
- resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
从 SqlSessionFactory 中获取 SqlSession
- session = sqlSessionFactory.openSession();
通过 SqlSession 实例来直接执行已映射的 SQL 语句
- 执行com.etc.mapper.StudentMapper这个namespace下的id叫作selectOne的语句,参数是1
Object o = session.selectOne("com.etc.mapper.StudentMapper.selectOne", 1);
System.out.println(o);
session.close();
打印结果
name和age属性没有值,是因为实体类中的姓名和年龄属性跟表中的字段名不匹配
- select id="selectOne" resultType="com.etc.domain.Student" parameterType="integer">
<!-- 查询具体的字段并起别名 别名和类的属性名保持一致 后面会有其他处理方案-->
select sid,s_name sName,sgender,age sage from student where sid = #{sid}
</select>
再次打印结果
9.总结
- 根据全局配置文件创建一个sqlSessionFactory对象
- 全局配置文件中有一些运行环境信息
- 定义SQL映射文件 配置了SQL语句 以及SQL的封装规则
- 将SQL映射注册在全局配置文件中
- 使用sqlSessionFactory创建session对象
- 使用session对象执行增删改查操作,使用映射文件中SQL的唯一标识告诉mybatis要 执行谁
- 如果属性无法赋值,就修改SQL语句,查询每一个字段 并给无法映射的列起别名,别名要和类的属性名一致
三、MyBatis_(接口式编程)
1:创建Maven工程
2:在pom.xml引入相关坐标
3:创建学生表
4:定义与表对应的实体类
5:定义操作学生表的接口
public interface StudentMapper {
/**
* @param sid 要查询的学生id
* @return 返回学生实体对象
* selectOne是方法名 将来作为mapper文件中SQL的id
*/
public Student selectOne(Integer sid);
}
6:修改SQL映射文件
<?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">
<!--SQL映射文件要和接口动态绑定 所以namespace必须是接口的全限定名-->
<mapper namespace="com.etc.mappers.StudentMapper">
<!--
id 是接口中定义的方法名
resultType 返回值结果的全限定名
-->
<select id="selectOne" resultType="com.etc.domain.Student">
select sid,s_name sName,sgender,age sage from student where sid = #{sid}
</select>
</mapper>
7:编写测试代码
//从 XML 中构建 SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//从 SqlSessionFactory 中获取 SqlSession
SqlSession session = sqlSessionFactory.openSession();
//获取接口的实现类对象(底层是基于JDK动态代理技术)
StudentMapper m = session.getMapper(StudentMapper.class);
//调用方法 传递参数 接收结果
Student o = m.selectOne(1);
System.out.println(o);
//释放资源
session.close();
8:总结
- 根据全局配置文件创建一个sqlSessionFactory对象
- 全局配置文件中有一些运行环境信息
- 定义操作学生的接口,接口中定义抽象的CRUD方法
- 定义SQL映射文件 配置SQL语句 ,namespace是接口的全限定名 id是接口中方法的名称
- 将SQL映射注册在全局配置文件中
- 使用sqlSessionFactory创建session对象
- 使用session对象获取接口的代理实现类对象
- 调用实现类对象的方法 传参数 接收结果
- 有明确的返回值 有更强的类型检查,把DAO层的规范和实现分离了出来有利于扩展和维护
- 原生:Dao ---> Dao的实现类
- mybatis:xxxMapper ---> xxxMapper.XML文件
- SqlSession代表和数据库的一次会话 用完必须关闭
- SqlSession和connection一样都是线程不安全的 不能声明为成员变量,每次使用时都应该获取新的对象
- Mapper接口没有实现类 但是mybatis会将xml文件和接口绑定,为这个接口生成一个代理对象
- 全局配置文件mybatis-config.xml和SQL映射文件xxxMapper.xml
- SqlSession可以直接调用方法的id进行数据库操作,但是我们一般还是推荐使用SqlSession获取到Dao接口的代理类,执行代理对象的方法,可以更安全的进行类型检查操作
四、MyBatis-全局配置文件
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下
- configuration(配置) 根标签
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)(暂)
- objectFactory(对象工厂)(暂)
- plugins(插件)
- environments(环境配置)
- databaseIdProvider(数据库厂商标识)(暂)
- mappers(映射器)
- 需要定义标签的时候必须按照这个顺序来编写 不能打乱位置
1.properties(属性)
<!--可以使用字标签定义数据源相关属性 在下面的DataSource中使用el表达式获取定义的值 -->
<properties>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</properties>
<environments default="mysql">
<environment id="mysql">
<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>
<!--也可以用来引入外部properties配置文件的内容 db.properties放在类路径下或者resources目录下-->
<properties resource="db.properties"/>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--后期会把数据源的信息交个spring管理-->
#db.properties配置文件的内容 包括MySQL的和Oracle的配置信息
#数据库驱动
jdbc.driverClassName=com.mysql.jdbc.Driver
#数据库
jdbc.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT&allowMultiQueries=true
#jdbc.url=jdbc:mysql://localhost:3306/tx?serverTimezone=GMT
#用户名
jdbc.username=root
#密码
jdbc.password=123
#别名方式,扩展插件,监控统计用的filter:stat,日志用的filter:log4j,防御sql注入的filter:wall
jdbc.filters=stat
#最大连接数
jdbc.maxActive=300
#初始化连接数
jdbc.initialSize=2
#获取连接最大等待时间
jdbc.maxWait=60000
#最小连接数
jdbc.minIdle=1
#检测连接有效性的时间间隔
jdbc.timeBetweenEvictionRunsMillis=60000
#连接保持空闲而不被驱逐的最长时间
jdbc.minEvictableIdleTimeMillis=300000
#连接有效性,检测sql
jdbc.validationQuery=SELECT 'x'
#定时检测空闲连接有效性
jdbc.testWhileIdle=true
#检测获取的连接的有效性
jdbc.testOnBorrow=false
#检测要归还的连接的有效性
jdbc.testOnReturn=false
#是否缓存preparedStatement,即PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
jdbc.poolPreparedStatements=false
jdbc.maxPoolPreparedStatementPerConnectionSize=50
orcl.driver=oracle.jdbc.driver.OracleDriver
orcl.url=jdbc:oracle:thin:@localhost:1521:orcl
orcl.username=scott
orcl.password=tiger
2.settings(设置)
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。加粗斜体配置是常用配置
设置名 | 描述 | 有效值 | 默认值 |
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true | false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 | true | false | false |
aggressiveLazyLoading | 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)。 | true | false | false (在 3.4.1 及之前的版本中默认为 true) |
multipleResultSetsEnabled | 是否允许单个语句返回多结果集(需要数据库驱动支持)。 | true | false | true |
useColumnLabel | 使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察。 | true | false | true |
useGeneratedKeys | 允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。 | true | false | False |
autoMappingBehavior | 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。 | NONE, PARTIAL, FULL | PARTIAL |
autoMappingUnknownColumnBehavior | 指定发现自动映射目标未知列(或未知属性类型)的行为。NONE: 不做任何反应WARNING: 输出警告日志('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARN)FAILING: 映射失败 (抛出 SqlSessionException) | NONE, WARNING, FAILING | NONE |
defaultExecutorType | 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(PreparedStatement); BATCH 执行器不仅重用语句还会执行批量更新。 | SIMPLE REUSE BATCH | SIMPLE |
defaultStatementTimeout | 设置超时时间,它决定数据库驱动等待数据库响应的秒数。 | 任意正整数 | 未设置 (null) |
defaultFetchSize | 为驱动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖。 | 任意正整数 | 未设置 (null) |
defaultResultSetType | 指定语句默认的滚动策略。(新增于 3.5.2) | FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同于未设置) | 未设置 (null) |
safeRowBoundsEnabled | 是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。 | true | false | False |
safeResultHandlerEnabled | 是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false。 | true | false | True |
mapUnderscoreToCamelCase | N 映射到经典 Java 属性名 aColumn。 | true | false | False |
localCacheScope | MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。 | SESSION | STATEMENT | SESSION |
jdbcTypeForNull | 当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。 某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。 | JdbcType 常量,常用值:NULL、VARCHAR 或 OTHER。 | OTHER |
lazyLoadTriggerMethods | 指定对象的哪些方法触发一次延迟加载。 | 用逗号分隔的方法列表。 | equals,clone,hashCode,toString |
defaultScriptingLanguage | 指定动态 SQL 生成使用的默认脚本语言。 | 一个类型别名或全限定类名。 | org.apache.ibatis.scripting.xmltags.XMLLanguageDriver |
defaultEnumTypeHandler | 指定 Enum 使用的默认 TypeHandler 。(新增于 3.4.5) | 一个类型别名或全限定类名。 | org.apache.ibatis.type.EnumTypeHandler |
callSettersOnNulls | 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这在依赖于 Map.keySet() 或 null 值进行初始化时比较有用。注意基本类型(int、boolean 等)是不能设置成 null 的。 | true | false | false |
returnInstanceForEmptyRow | 当返回行的所有列都是空时,MyBatis默认返回 null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集(如集合或关联)。(新增于 3.4.2) | true | false | false |
logPrefix | 指定 MyBatis 增加到日志名称的前缀。 | 任何字符串 | 未设置 |
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未设置 |
proxyFactory | 指定 Mybatis 创建可延迟加载对象所用到的代理工具。 | CGLIB | JAVASSIST | JAVASSIST (MyBatis 3.3 以上) |
vfsImpl | 指定 VFS 的实现 | 自定义 VFS 的实现的类全限定名,以逗号分隔。 | 未设置 |
useActualParamName | 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1) | true | false | true |
configurationFactory | 指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3) | 一个类型别名或完全限定类名。 | 未设置 |
shrinkWhitespacesInSql | Removes extra whitespace characters from the SQL. Note that this also affects literal strings in SQL. (Since 3.5.5) | true | false | false |
3.typeAliases(类型别名)
<!--别名处理器 类型别名可为 Java 类型设置一个缩写名字。 目的在于简化全限定名的书写-->
<typeAliases>
<!--
type 要起别名的类的全限定名
alias 是自定义别名
-->
<!--
<typeAlias type="com.etc.domain.Student" alias="student"/>
-->
<!--
package 批量起别名
name 给哪个包下的类起别名 默认就是类名小写
如果不想使用默认别名 想自定义 可以在类的声明上使用@Alias注解
如果同一包下有两个类名相同的类,也可以使用@Alias注解加以区分
@Alias("stu")
public class Student {
}
别名不区分大小写
-->
<package name="com.etc.domain"/>
</typeAliases>
下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。
4.typeHandlers(类型处理器)了解
MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型。从 3.4.5 开始,MyBatis 默认支持 JSR-310(日期和时间 API) 。负责对java的类型和MySQL的类型进行转换.比如把java的String 转换映射成MySQL数据库的char/varchar类型,我们无需配置.mybatis会自动配置
5.plugins(插件)
MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 发行包中的源代码。 如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。 因为在试图修改或重写已有方法的行为时,很可能会破坏 MyBatis 的核心模块。 这些都是更底层的类和方法,所以使用插件的时候要特别当心。
6.environments(环境配置)
<!--
environments 可以配置多种数据库环境
default 指定使用默认环境 可以在开发/测试/mysql/Oracle之间切换
environment 配置具体的数据库环境信息
id 代表当前环境的唯一标识
transactionManager 事务管理器
type 事务管理器的类 当前环境使用何种事务管理方式
JDBC JdbcTransactionFactory
直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
MANAGED ManagedTransactionFactory
这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期, 默认情况下它会关闭连接
dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
type 指定数据源类型,有三种内建的数据源类型
UNPOOLED
这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,
是一个很好的选择。 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形
POOLED
这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间
JNDI
这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。
实际开发中我们使用Spring管理数据源,并进行事务控制的配置来覆盖上述配置
-->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
<environment id="oracle">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
7.databaseIdProvider(数据库厂商标识)(了解)
<!--
MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。
MyBatis 会加载带有匹配当前数据库 databaseId 属性和所有不带 databaseId 属性的语句。
DB_VENDOR VendorDatabaseIdProvider
得到数据库厂商的标识 mybatis根据数据库厂商的标识来执行不同的SQL
databased是用于在多种数据库分同一条SQL对应的数据库。
可以这样认为,在Mybatis中SqL的id和 databased组合才是一条SQL的唯一标识
-->
<databaseIdProvider type="DB_VENDOR">
<!--给不同的数据库起别名-->
<property name="MySQL" value="mysql" />
<property name="SQL Server" value="sqlserver"/>
<property name="DB2" value="db2"/>
<property name="Oracle" value="oracle" />
</databaseIdProvider>
<!--通过指定databaseId告诉mybatis这条SQL由什么数据库执行-->
<select id="selectOne" resultType="stu" databaseId="mysql">
select sid,s_name sName,sgender,age sage from student where sid = #{sid}
</select>
<select id="selectOne" resultType="stu" databaseId="oracle">
select * from student where sid = #{sid}
</select>
测试代码如下:
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
Connection connection = session.getConnection();
String dbName = connection.getMetaData().getDatabaseProductName();
String dbVersion = connection.getMetaData().getDatabaseProductVersion();
System.out.println("数据库名称是:" + dbName + ";版本是:" + dbVersion);
StudentMapper m = session.getMapper(StudentMapper.class);
Student o = m.selectOne(1);
System.out.println(o);
session.close();
注意:在测试时要执行两条一模一样的SQL语句,这样 在切换数据库环境时才能感受到效果
当是
当时
8.mappers(映射器)
<!-- 将SQL映射文件注册到全局配置中-->
<mappers>
<!-- resource
在类路径下 直接写文件名
如果在多级目录下 要带上目录名称
-->
<!-- <mapper resource="mappers/StudentMapper.xml"></mapper>-->
<!-- <mapper resource="StudentMapper.xml"></mapper>-->
<!--
DEMO:
也可以使用class属性 定义接口的全限定名
接口名和配置文件名必须相同并且
需要让接口和mapper文件路径相同
这是在由mapper.xml文件和mapper接口同时存在情况下使用
如果只有mapper接口 没有xml文件 SQL语句全部以注解形式定义在接口方法上,那么直接定义接口的全限定名即可
也可以使用全局注册 <package name="com.etc.mappers"/> 如果存在xml和接口绑定的情况 那么xml文件依然遵循DEMO规则
-->
<package name="com.etc.mappers"/>
<mapper class="com.etc.mappers.StudentMapper"></mapper>
</mappers>
<mappers>
<!-- 使用相对于类路径的资源引用 -->
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<!-- 使用完全限定资源定位符(URL) -->
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<!-- 使用映射器接口实现类的完全限定类名 -->
<mapper class="org.mybatis.builder.AuthorMapper"/>
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<package name="org.mybatis.builder"/>
</mappers>
9.objectFactory(对象工厂)了解
每次 MyBatis 创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成实例化工作。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认无参构造方法,要么通过存在的参数映射来调用带有参数的构造方法。
五、MyBatis-SQL映射文件
MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。
SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出):
- cache – 该命名空间的缓存配置。
- cache-ref – 引用其它命名空间的缓存配置。
- resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
- parameterMap – 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。
- sql – 可被其它语句引用的可重用语句块。
- insert – 映射插入语句。
- update – 映射更新语句。
- delete – 映射删除语句。
- select – 映射查询语句。
1.基本的CRUD
a.XML和接口绑定
接口
public Student selectOne(Integer sid);
public List<Student> selectAll();
public void insertOne(Student s);
public void updateOne(Student s);
// 1.mybatis允许我们在做增删改时直接定义返回值类型为Integer/Long/Boolean 基本或者包装类型都可以
// public boolean deleteOne(Integer sid);
public Integer deleteOne(Integer sid);
SQL映射文件
<!--
<select id="selectOne" resultType="student" parameterType="integer">
select sid,s_name,sgender,age sage from student
</select>-->
<select id="selectOne" resultType="student">
select sid,s_name,sgender,age sage from student where sid = #{sid}
</select>
<select id="selectAll" resultType="student">
select sid,s_name,sgender,age sage from student
</select>
<!--
<insert id="insertOne">
insert into student values(null,#{sName},#{sGender},#{sage})
</insert>
-->
<insert id="insertOne" parameterType="student">
insert into student values(null,#{sName},#{sGender},#{sage})
</insert>
<!--
<update id="updateOne">
update student set s_name=#{sName},sgender=#{sGender},age=#{sage} where sid=#{sid}
</update>
-->
<update id="updateOne" parameterType="student">
update student set s_name=#{sName},sgender=#{sGender},age=#{sage} where sid=#{sid}
</update>
<!--
<delete id="deleteOne">
delete from student where sid = #{sid}
</delete>-->
<delete id="deleteOne" parameterType="integer">
delete from student where sid = #{sid}
</delete>
b.注解Mapper接口
@Select("select sid,s_name sName,sgender,age sage from student where sid = #{sid}")
@ResultType(Student.class)//@ResultType 对应xml中ResultType的属性
public Student selectOne(Integer sid);
@Select("select sid,s_name,sgender,age sage from student")
@ResultType(Student.class)
public List<Student> selectAll();
@Insert("insert into student values(null,#{sName},#{sGender},#{sage})")
public void insertOne(Student s);
@Update("update student set s_name=#{sName},sgender=#{sGender},age=#{sage} where sid=#{sid}")
public void updateOne(Student s);
@Delete("delete from student where sid = #{sid}")
public Integer deleteOne(Integer sid);
注意:
1.DML方法允许定义布尔类型或者数值类型返回值
2.openSession() 获取的session执行完毕后需要手动调用commit()方法提交事务
3.openSession(true) 获取的session可以自动提交事务
2.Insert插入获取自增主键
a.MySQL
<!--mysql 支持主键自增 statement中有getGeneratedKeys() 可以获取到插入记录后生成的id
useGeneratedKeys= true 使用获取自增主键
keyProperty 获取到主键值后把值封装给javabean的哪个属性
-->
<insert id="insertOne" parameterType="student" useGeneratedKeys="true" keyProperty="sid">
insert into student values(null,#{sName},#{sGender},#{sage})
</insert>
<!--
select last_insert_id();获取最后一次插入记录的id 与表无关
order="AFTER" 表示在插入语句后执行
keyProperty 表示把查到的id封装给javabean的哪个属性
resultType 表示查到结果的类型
-->
<insert id="insertOne" parameterType="student">
<selectKey order="AFTER" keyProperty="sid" resultType="integer">
select last_insert_id()
</selectKey>
insert into student values(null,#{sName},#{sGender},#{sage})
</insert>
b.Oracle
<!--
如果是Oracle需要自己去创建序列
create sequence s_seq increment by 1 start with 1 nominvalue nomaxvalue nocycle nocache;
删除序列
drop sequence s_seq;
查询
select s_seq.currval from dual;
select s_seq.nextval from dual;
比方说我现在查出来值是10,那么我要把当前值改成8,那么可以这么改:
alter sequence 序列名 increment by -2;
如果我需要把当前值改成15,那么可以这么改:
alter sequence 序列名 increment by 5;
上述是通过修改当前序列增量长度间隔值,用于修改当前序列值,增加1或-1或n或-n,
修改了之后,要执行一下nextval
当修改好当前值之后,记得一定要把序列增量改回来,改为1:
alter sequence 序列名 increment by 1;
-->
<insert id="insertOne" parameterType="student" databaseId="oracle">
<selectKey order="BEFORE" keyProperty="sid" resultType="integer">
<!--
先执行 select s_seq.nextval from dual 得到序列值
把序列值封装对象id属性
再执行插入学生记录的SQL语句 通过#{sid}把id从javabean中取出来
-->
select s_seq.nextval from dual
</selectKey>
insert into student values(#{sid},#{sName},#{sGender},#{sage})
</insert>
<!--
先执行插入语句 s_seq.nextval 获取到序列值
再执行 select s_seq.currval from dual 封装给javabean对象的id属性
如果插入多条 select s_seq.currval就是最后一个id的值 了解 不建议使用
-->
<insert id="insertOne" parameterType="student" databaseId="oracle">
<selectKey order="AFTER" keyProperty="sid" resultType="integer">
select s_seq.currval from dual
</selectKey>
insert into student values(s_seq.nextval,#{sName},#{sGender},#{sage})
</insert>
3.params参数处理
1.单个参数
mybatis不会特殊处理 名字也可以随便起
2.多个参数
- mybatis会把多个参数封装成一个map
- select * from student where name=#{param1} and age=#{param2}
- select(string name,int age)
value:方法传递过来的参数
select sid,s_name,sgender,age sage from student where s_name = #{param1} and sgender=#{param2}
select sid,s_name,sgender,age sage from student where s_name = #{arg0} and sgender=#{arg1}
这两种方式都不推荐 可以使用注解给参数命名
多个参数还是封装到map集合中
key:@Param指定的名称
value:是方法传递的参数值
public Student select(@Param("sname") String sname, @Param("sgender") String sgender);
select sid,s_name,sgender,age sage from student where s_name = #{sname} and sgender=#{sgender}
- 可以把参数封装到map中, #{key} 就是获取map中的value,调用方法时传递Map集合
- Student select(HashMap<String, Object> map);
select sid,s_name,sgender,age sage from student where s_name = #{sname} and sgender=#{sgender}
- 如果多个参数正好是业务逻辑中的数据模型 可以直接传入POJO
- 属性名} 取出pojo对应的属性值
- 如果参数是数组或集合
- list 取值语法:list[index]
select sid,s_name,sgender,age sage from student where sid=#{list[1]}
如果方法的参数是数组 取值语法:#{array[index]}
select sid,s_name,sgender,age sage from student where sid=#{array[0]}
- 获取参数的语法总结
- sid,s_name,sgender,age sage from student where s_name = #{sname} and sgender=#{sgender}
sid,s_name,sgender,age sage from student where s_name = #{sname} and sgender=#{stu.sGender}
sid,s_name,sgender,age sage from student where s_name = #{param1} and sgender=#{param2.sGender}
sid,s_name,sgender,age sage from student where s_name = #{0} and sgender=#{1.sGender} ×
sid,s_name,sgender,age sage from student where sid=#{collection[1]} set集合不行 ×
3.#{} 和 ${} 区别
都可以获取map或者pojo中的属性值
区别在于 :
#{} 是以预编译的形式 将参数设置到SQL语句中 可以防止SQL注入
select sid,s_name,sgender,age sage from student where s_name = ? and sgender = ?
${} 是把取出来的值拼接到SQL上 注意取出来的值是字符串要用单引号引起来 大部分使用#{}
select sid,s_name,sgender,age sage from student where s_name = '${sname}' and sgender='${sgender}'
jdbcType
对于插入null值 mysql默认是支持的 如果表列没有指定非空约束,那么空值可以插入到表中
对于Oracle来说 在执行SQL时接收到的参数是null,那么插入时会报错,因此可以配置jdbcType让Oracle也支持插入null值
局部配置
insert into STUDENT values (stu_id.nextval,#{sName},#{sage},#{sGender,jdbcType=NULL})
全局配置
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="jdbcTypeForNull" value="NULL"/>
</settings>
4.select 返回结果
https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#select
1.返回一个或多个对象
public Student selectOne(Integer sid);
<!-- resultType是被封装的类型名称 -->
<select id="selectOne" resultType="student">
select sid,s_name,sgender,age sage from student where sid=#{sid}
</select>
public List<Student> selectAll();
<select id="selectAll" resultType="student">
<!--mybatis会自动把多条记录封装到多个对象 并放入集合中-->
select sid,s_name,sgender,age sage from student
</select>
2.返回一个Map集合
public Map<String,Object> selectMap(Integer sid);
<!-- key是属性名/列名 value是表记录值 resultType是map类型 -->
<select id="selectMap" resultType="map">
<!-- select sid,s_name,sgender,age sage from student where sid=#{sid} -->
select * from student where sid=#{sid}
</select>
<!--
结果是Map集合,key是指定列名,value是bean对象
@MapKey 指定返回的map集合中用哪一列做key 注意定义Map的key类型要和列的类型一致
-->
@MapKey("sid")
public Map<Integer,Student> selectMapKey(Integer sid);
<select id="selectMapKey" resultType="student">
select sid,s_name,sgender,age sage from student
</select>
3.自定义封装(单表)
<!--
resultMap 自定义封装规则
id 唯一标识方便引用
type 结果封装成何种类型
id主键列 property属性名 column列名
result普通列 property 属性名 column列名
-->
<resultMap id="mystudent" type="student">
<id property="sid" column="sid"/>
<result property="sName" column="s_name"/>
<result property="sGender" column="sgender"/>
<result property="sage" column="age"/>
</resultMap>
<!--resultMap引用定好的resultMap的id -->
<select id="selectOne" resultMap="mystudent">
select * from student where sid=#{sid}
</select>
4.自定义封装(多表一对一)
学生表和班级表是多对一的关系
一个学生 -> 对应一个班级
一个班级 -> 对应多个学生
a. 级联属性封装结果集
需求:查询学生,并将其班级信息一并查出
<resultMap id="mystudent2" type="student">
<id property="sid" column="sid"/>
<result property="sName" column="s_name"/>
<result property="sGender" column="sgender"/>
<result property="sage" column="age"/>
<!--
property 级联属性的属性
column 联合查询出来的字段
-->
<result property="c.cid" column="cid"/>
<result property="c.cname" column="cname"/>
</resultMap>
<select id="getStudentAndClass" resultMap="mystudent2">
select * from class c,student s where c.cid = s.classid and sid=#{sid} order by cid;
</select>
b. association 定义关联的单个对象的封装规则
需求:查询学生,并将其班级信息一并查出
<!-- 嵌套结果集-->
<resultMap id="mystudent3" type="student">
<id property="sid" column="sid"/>
<result property="sName" column="s_name"/>
<result property="sGender" column="sgender"/>
<result property="sage" column="age"/>
<!--
association 指定联合的javabean对象
property 指定哪个属性是联合的对象
javaType 指定这个属性对象的类型
-->
<association property="c" javaType="com.etc.domain.Class">
<id column="cid" property="cid"></id>
<result column="cname" property="cname"></result>
</association>
</resultMap>
<select id="getStudentAndClass" resultMap="mystudent3">
select * from class c,student s where c.cid = s.classid and sid=#{sid} order by cid;
</select>
c. 使用association 分步查询
需求:查询学生,并将其班级信息一并查出
1 定义ClassMapper
public interface ClassMapper {
public Class getClassByid(Integer cid);
}
2 定义ClassMapper配置文件
<mapper namespace="com.etc.mappers.ClassMapper">
<select id="getClassByid" resultType="class">
select * from class where cid=#{cid}
</select>
</mapper>
3 <!--
association分步查询
1 按照学生的id查询学生信息
2 根据学生的外键id查询对应的班级信息
3 把班级信息设置到学生的班级属性中
select 表示当前属性是调用select指定的方法查出的结果
column 指定将哪列的值传递select这个方法
-->
<resultMap id="mystudent4" type="student">
<id property="sid" column="sid"/>
<result property="sName" column="s_name"/>
<result property="sGender" column="sgender"/>
<result property="sage" column="age"/>
<association property="c" select="com.etc.mappers.ClassMapper.getClassByid" column="classid"></association>
</resultMap>
<select id="getStudentAndClassByStep" resultMap="mystudent4">
select * from student where sid=#{sid}
</select>
d. 使用association 分步查询并延迟加载
现在每次查询学生对象时都把对应的班级信息给查了出来.也可以在使用到班级信息的时候(比如输出)再去执行查询
<!--开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
<resultMap id="mystudent4" type="student">
<id property="sid" column="sid"/>
<result property="sName" column="s_name"/>
<result property="sGender" column="sgender"/>
<result property="sage" column="age"/>
<association property="c" select="com.etc.mappers.ClassMapper.getClassByid"
column="classid" fetchType="lazy"></association>
</resultMap>
<!-- fetchType 可以对每个SQL语句进行精准控制 lazy 就是延迟加载 eager 就是立即加载-->
<select id="getStudentAndClassByStep" resultMap="mystudent4">
select * from student where sid=#{sid}
</select>
5.自定义封装(多表一对多)
a.使用collection定义多个结果的封装规则
需求:查询班级,并将其学生信息一并查出
定义接口方法
public Class getClassAndStudentByid(Integer cid);
<resultMap id="myclass1" type="class">
<id column="cid" property="cid"></id>
<result column="cname" property="cname"></result>
<!--
collection 表示封装的结果是一个集合
property 指定给哪个属性封装多个结果
ofType 集合中的类型
-->
<collection property="students" ofType="com.etc.domain.Student">
<id property="sid" column="sid"></id>
<result property="sName" column="s_name"></result>
<result property="sGender" column="sgender"></result>
<result property="sage" column="age"></result>
</collection>
</resultMap>
<select id="getClassAndStudentByid" resultMap="myclass1">
select * from class c cross join student s on c.cid = s.classid and c.cid = #{cid}
</select>
b.使用collection分步查询
需求:查询班级,并将其学生信息一并查出
根据班级id查出该班级的信息
public Class getClassAndStudentByidAndStep(Integer cid);
根据班级id查询出该班中所有的学生
public List<Student> selectAllByID(@Param("classid") Integer id);
<mapper namespace="com.etc.mappers.StudentMapper">
<select id="selectAllByID" resultType="student">
select sid,s_name sName,sgender,age sage from student where classid=#{classid}
</select>
</mapper>
<resultMap id="myclass2" type="class">
<id property="cid" column="cid"></id>
<result property="cname" column="cname"></result>
<!--
select 根据那个SQL映射方法执行查询
column 把查询出来的哪一列作为参数传给select
fetchType 控制是否延迟加载
-->
<collection property="students" select="com.etc.mappers.StudentMapper.selectAllByID"
column="cid" fetchType="eager"></collection>
</resultMap>
<select id="getClassAndStudentByidAndStep" resultMap="myclass2">
select * from class where cid=#{cid}
</select>
6.鉴别器discriminator
<resultMap id="mystudent4" type="student">
<id property="sid" column="sid"/>
<result property="sName" column="s_name"/>
<result property="sGender" column="sgender"/>
<result property="sage" column="age"/>
<!--
discriminator 鉴别器 类似于switch case的思想
javaType 鉴别的列类型
column 根据哪一列鉴别
case 匹配
value 值
resultType 匹配之后封装的结果类型
-->
<discriminator javaType="string" column="sgender">
<case value="男" resultType="student">
<association property="c" select="com.etc.mappers.ClassMapper.getClassByid" column="classid" fetchType="eager"></association>
</case>
<case value="女" resultType="student">
<id property="sid" column="sid"/>
<result property="sName" column="sgender"/>
<result property="sGender" column="sgender"/>
<result property="sage" column="age"/>
</case>
</discriminator>
</resultMap>
<select id="getStudentAndClassByStep" resultMap="mystudent4">
select * from student where sid=#{sid}
</select>
7.内置参数
mybatis有两个默认的内置参数: _parameter 和 _databaseId
<!--
_databaseId 判断不同的dbid,通过切换数据库运行环境,动态执行不同的SQL
_parameter 通常用来判断传进来的参数是否为null
-->
<select id="findBydbId" resultMap="mystu1">
<if test="_databaseId=='mysql'">
select * from student where
<if test="_parameter!=null">
s_name like #{_parameter.sName}
</if>
</if>
<if test="_databaseId=='oracle'">
select * from student
</if>
</select>
8.模糊查询的几种写法
方式1:
<select id="findBlurry" resultMap="mystu1">
select * from student where s_name like #{sName}
</select>
StudentMapper m = session.getMapper(StudentMapper.class);
Student st = new Student();
st.setsName("%李%");
List<Student> list = m.findBlurry(st);
方式2: 不安全 不推荐
<select id="findBlurry" resultMap="mystu1">
select * from student where s_name like '%${sName}%'
</select>
StudentMapper m = session.getMapper(StudentMapper.class);
Student st = new Student();
st.setsName("李");
List<Student> list = m.findBlurry(st);
5.定义SQL片段(暂)
<sql id="st">
select * from student
</sql>
<!--SQL表示定义一个片段 id方便引用-->
<select id="findBydbId" resultMap="mystu1">
<if test="_databaseId=='mysql'">
<include refid="st"/> where
<if test="_parameter!=null">
s_name like #{_parameter.sName}
</if>
</if>
<if test="_databaseId=='oracle'">
<include refid="st"/> student
</if>
</select>
<!--在需要引入SQL片段的地方使用
include标签
refid 使用要引入的SQL片段的id
-->
<select id="findBlurry" resultMap="mystu1">
<bind name="name" value="'%'+sName+'%'"/>
<include refid="st"/> where s_name like #{name}
</select>
六、MyBatis-动态SQL
1.if判断
<mapper namespace="com.etc.mappers.StudentMapper">
<resultMap id="mystu1" type="student">
<id property="sid" column="sid"></id>
<result property="sName" column="s_name"></result>
<result property="sGender" column="sgender"></result>
<result property="sage" column="age"></result>
</resultMap>
<!--
1 select * from tb where 1=1 后面所有的条件前都加and
2 使用where标签 它可以自动去除条件前面的and
3 if可以判断传进来的条件是否不为空 长度是否不为零
4 如果把and 或者or 放在条件后面 where就失效了因此可以使用trim标签
select * from student where sid=#{sid} and s_name like #{sName} and sgender=#{sGender} and age=#{sage}
-->
<select id="selectBycondition" resultMap="mystu1">
select * from student
<where>
<if test="sid != null">
and sid=#{sid}
</if>
<if test="sName != null and sName != ''">
and s_name like #{sName}
</if>
<if test="sGender != null and sGender != ''">
and sgender=#{sGender}
</if>
<if test="sage != null">
and age=#{sage}
</if>
</where>
</select>
</mapper>
2.trim(where set)
原始的where
<!--and 也可以放在条件的后面,这种写法在全条件下没有问题 但是如果是有中间的条件那么会多and-->
<select id="selectBycondition" resultMap="mystu1">
select * from student
<where>
<if test="sid != null">
sid=#{sid} and
</if>
<if test="sName != null and sName != ''">
s_name like #{sName} and
</if>
<if test="sGender != null and sGender != ''">
sgender=#{sGender} and
</if>
<if test="sage != null">
age=#{sage}
</if>
</where>
</select>
使用trim标签拼接where和去除and
<!--使用trim标签-->
<select id="selectBycondition" resultMap="mystu1">
select * from student
<trim prefix="where" suffixOverrides="and">
<!--
prefix 给拼好的SQL语句前加前缀
suffix 加后缀
prefixOverrides 去除前缀
suffixOverrides 去除后缀
-->
<if test="sid != null">
sid=#{sid} and
</if>
<if test="sName != null and sName != ''">
s_name like #{sName} and
</if>
<if test="sGender != null and sGender != ''">
sgender=#{sGender} and
</if>
<if test="sage != null">
age=#{sage}
</if>
</trim>
</select>
原始的set
<update id="update">
<!--update student set s_name=#{sName},sgender=#{sGender},age=#{sage} where sid=#{sid} -->
<!--
如果少条件 最终SQL会多逗号
因此需要使用set标签
-->
update student
<set>
<if test="sName != null and sName != ''">
s_name= #{sName} ,
</if>
<if test="sGender != null and sGender != ''">
sgender=#{sGender} ,
</if>
<if test="sage != null">
age=#{sage}
</if>
</set>
where sid=#{sid}
</update>
使用trim标签拼接set和去除逗号
<update id="update">
update student
<!--拼接set前缀 去除逗号后缀-->
<trim prefix="set" suffixOverrides=",">
<if test="sName != null and sName != ''">
s_name= #{sName} ,
</if>
<if test="sGender != null and sGender != ''">
sgender=#{sGender} ,
</if>
<if test="sage != null">
age=#{sage}
</if>
</trim>
where sid=#{sid}
</update>
3.choose (when otherwise)
分支选择:带了id 只拼id 带了name 只拼name 只会进入其中一个
<select id="selectByconditionAndChoose" resultMap="mystu1">
select * from student
<where>
<choose>
<when test="sage != null">
age=#{sage}
</when>
<when test="sName != null and sName != ''">
s_name like #{sName}
</when>
<when test="sGender != null">
sgender=#{sGender}
</when>
<when test="sid != null">
sid=#{sid}
</when>
<otherwise>
</otherwise>
</choose>
</where>
</select>
4.foreach
a:遍历列表
<select id="selectByids" resultMap="mystu1">
<!--select * from student where sid in(1,2,3);-->
select * from student where sid in
<!--<foreach collection="list" item="id" separator="," open="(" close=")">-->
<foreach collection="list" item="id" separator="," open=" where sid in(" close=")">
#{id}
</foreach>
<!--
collection 指定要遍历的集合 list类型会特殊处理放在map中,key就叫list 此处无需#{list}
item 将遍历出来的元素赋值给指定变量
separator 每个元素之间的分隔符 如果是最后一个则不加
open 遍历出所有结果拼接一个开始的字符串
close 遍历出所有结果后拼接结束的字符串
index 遍历list时index就是当前元素索引
-->
</select>
b:批量插入_MySQL
<insert id="insert">
<!--
insert into student values(null,'aaa','男',12),(null,'bbb','女',22);
这是执行一次SQL
-->
insert into student values
<foreach collection="list" item="stu" separator=",">
(null,#{stu.sName},#{stu.sGender},#{stu.sage},null)
</foreach>
</insert>
<!--
这是执行多次SQL 需要在url上开启允许多次query
jdbc.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT&allowMultiQueries=true
可以用于批量删除 批量修改
-->
<insert id="insert">
<foreach collection="list" item="stu" separator=";">
insert into student values(null,#{stu.sName},#{stu.sGender},#{stu.sage},null)
</foreach>
</insert>
c:批量插入_Oracle
insert into STUDENT values (s_seq.nextval,'菅直人','男',55),(s_seq.nextval,'菅直人','男',55);这种语法Oracle是不支持的 ×
begin
insert into STUDENT values (s_seq.nextval,'菅直人','男',55);
insert into STUDENT values (s_seq.nextval,'小泉','男',44); √
end;
<!--end;后面分号不能少
此处不需要使用separator属性
-->
<insert id="insert" databaseId="oracle">
<foreach collection="list" item="stu" open="begin" close="end;">
insert into student values(s_seq.nextval,#{stu.sName},#{stu.sGender},#{stu.sage});
</foreach>
</insert>
七、MyBatis-缓存机制(暂)
MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存。一级缓存和二级缓存。
1、默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启。
2、二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
3、为了提高扩展性。MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
A:一级缓存:本地缓存
与数据库同一次会话期间查询到的数据会放在本地缓存中 以后使用到相同的数据 直接从缓存中获取