MyBatis文档
MyBatis 学习笔记
简介
什么是Mybatis
MyBatis 是一款优秀的持久层框架,是Apache的一个Java开源项目 ,它支持自定义 SQL、存储过程以及高级映射, 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。 MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO为数据库中的记录。
- 原名 iBatis,在由 Apache 迁移到了谷歌,更名为 MyBatis。因此,MyBatis 指的就是 iBatis 3.x 后的版本
- 采用 ORM(Object Relational Mapping,对象关系映射)思想实现的持久化层框架。而之前学习的 JDBC 只是一种规范,JdbcTemplate 和 DBUtil 都是简单封装了 JDBC 的工具类
Mybatis和Hibernate的关联
- Hibernate是全自动全映射的ORM框架,使用HQL来定制SQL;而Mybatis是半自动的
- Hibernate会自动映射对象属性和数据库字段,自动生产SQL语句,供直接调用,但同样因为此,Hibernate难以优化SQL,且学习HQL的成本较高
- Mybatis以配置文件或注解的方式编写SQL,仅封装了预编译、设置参数、执行SQL、封装结果等操作, 只需要掌握 SQL 和 MyBatis 相关配置就可以使用,学习成本相对更低
入门
安装
目前Mybatis能够在githup上直接下载
要使用Mybatis只需要将 mybatis-xxx.jar 文件置于类路径(classpath)中即可。
maven依赖如下(除了mybatis外,还需要导入和数据库版本适配的驱动包)
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- database connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
构建全局配置文件
创建 mybatis-config.xml 配置文件,一般直接置于resource目录下。
该文件包含了对Mybatis系统的核心设置,包括获取数据库连接实例的数据源(DataSource)、决定事务作用域和控制方式的事务管理器(TransactionManager)。
首先简单配置如下:
<?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>
<!-- 和spring整合后 environments配置将废除 -->
<!--环境配置,可以配置多个,根据id选择默认环境 -->
<environments default="development">
<environment id="development">
<!-- 使用JDBC事务管理 -->
<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>
<!-- 配置文件映射,指定配置文件的位置,每个mapper都需要在核心配置文件中注册后才可使用 -->
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
- environment 元素体中包含了事务管理和连接池的配置
- mappers元素则包含了一组映射器(mapper),这些映射器的 XML文件包含了SQL代码和映射定义信息
通过配置文件构建Utils类
每个基于Mybatis的应用都是以一个SqlSessionFactory的实例为核心的。
而SqlSessionFactory的实例则可以通过SqlSessionFactoryBuilder获得。
而SqlSessionFactoryBuilder则可以从XML配置文件中构建
示例代码如下:
public class MybatisUtils {
// 声明静态属性
private static SqlSessionFactory sqlSessionFactory;
static {
try {
// 获取核心配置文件路径
String resource = "mybatis-config.xml";
// 封装为字节流
InputStream inputStream = Resources.getResourceAsStream(resource);
// 创建根据核心文件创建对象
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取SqlSession对象
*/
public static SqlSession getSqlSession() {
// 根据工厂类创建SqlSession对象并返回
return sqlSessionFactory.openSession();
}
创建测试类
创建POJO对象
public class User {
private int id;
private String name;
private String pwd;
/**
* 加上对应的构造方法、getters&setters、toString()
*/
}
创建持久层接口
public interface UserMapper {
User getUserById(int id);
}
创建对应的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">
<!-- namespace 命名空间,绑定一个对应的 Mapper 接口,且为全限定类名 -->
<mapper namespace="com.kuang.dao.UserMapper">
<!-- id 为指定接口的方法,parameterType为传入参数类型,resultType为返回值类型 -->
<select id="getUserById" parameterType="int" resultType="com.kuang.pojo.User">
select * from user user where id = #{id}
</select>
</mapper>
在核心配置文件中注册
<mappers>
<mapper resource="com/kuang/dao/UserMapper.xml"/>
</mappers>
测试
使用Junit进行测试
@Test
public void test3() {
// 调用自己编写的工具类获取SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 传入接口的字节码文件,创建代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 执行方法,并传入参数
User u = userMapper.getUserById(1);
// 打印
System.out.println(u);
// 释放资源
sqlSession.close();
}
详解
具体步骤如下:
- 通过字节流加载 核心配置文件(即 mybatis-config.xml)
- 使用SqlSessionFactoryBuilder读取输入流,并创建处SqlSessionFactory
- 使用SqlSessionFactory创建SqlSession
- 使用SqlSession创建代理对象(即 mapper)
- 通过代理对象调用方法并获取查询结果
- 释放资源
和传统JDBC写法比较
MyBatis | JDBC |
---|---|
连接池技术管理 | 手动地、频繁地创建和释放连接,浪费系统资源 |
SQL和代码分离,易于维护 | SQL经常变动,字符串拼接繁琐且容易出错 |
自动将java对象映射到sql语句,自动实现结果封装 | SQL语句的set内容、where条件都不一定,参数个数不明确,十分麻烦,且需要手动遍历封装 |
作用域和生命周期
SqlSessionFactoryBuilder
- 这个类可以被实例化、使用和丢弃,一旦创建了SqlSessionFactory后就不需要它了
- 最佳作用域是方法作用域(局部方法作用域)
- 没必要长期保留该对象,以保证所有的XML解析资源可以被释放
SqlSessionFactory
- 一旦被创建就应该在应用运行期间一直存在,不应该重复创建
- 最佳作用域是应用作用域,可使用单例模式或静态单例模式创造
SqlSession
- SqlSession的实例不是线程安全的,因此不能共享,即每个线程都应该有一个它自己的SqlSession实例
- 它的最佳作用域是请求或方法作用域,绝不能将SqlSession实例的引用放在一个类的静态域或实例变量
- 对SqlSession的关闭很重要,应该把关闭操作放到 finally块中,或者使用 try-with-source 方法获取
映射器实例
映射器是一些绑定映射语句的接口,接口实例从SqlSession中获得
合适的作用域是方法作用域,即映射器应该在调用它们的方法中被获取,使用完毕后即丢弃
配置
MyBatis的配置文件十分重要!
结构
- configuration(配置)
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- environment(环境变量)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器)
Properties(属性)
外部文件
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="password" value="zhao"/>
</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>
其中,username和password都会被替换
属性加载顺序
- 首先读取在 properties 元素体内指定的属性
- 然后根据 properties 元素中的 resource属性或url属性读取外部文件,并覆盖掉之前读取过的同名属性
- 最后读取作为方法参数传递的属性,覆盖掉之前读取过的同名属性
综上,优先级排名:方法参数 > 外部文件 > 大于核心配置文件
设置默认值
从MyBatis3.4.2开始,可以为占位符指定一个默认的值,如:
<dataSource type="POOLED">
<property name="username" value="${username:ut_user}"/> <!-- 如果属性 'username' 没有被配置,'username' 属性的值将为 'ut_user' -->
</dataSource>
该特性默认关闭,若要使用,需先配置,如:
<properties resource="org/mybatis/example/config.properties">
<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/> <!-- 启用默认值特性 -->
</properties>
但开启后,如果在属性命名中用到了 “:”,它就会被识别为默认值分隔符,鉴于 “:”,在三元运算等诸多场合都会被用到,所以需要修改默认值分隔符,也就是说,令找一种表示方式来代替 “:”,如
<properties resource="org/mybatis/example/config.properties">
<property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="?:"/> <!-- 修改默认值的分隔符 -->
</properties>
<!-- 此时在属性获取内容处,应表达如下-->
<dataSource type="POOLED">
<property name="username" value="${db:username?:ut_user}"/>
</dataSource>
Settings(设置)
这是MyBatis中极为重要的调整,会改变MyBatis的运行时行为。
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 全局性地开启或关闭缓存 | true | false | true |
lazyLoadingEnabled | 设置延迟加载,关联关系可通过设置 fetchType 属性来覆盖 | true | false | false |
aggressiveLazyLoading | 任何方法调用都会加载该对象的所有延迟加载属性,否则延迟加载属性按需加载 | true | false | false |
multipleResultSetsEnabled | 单个语句返回多个结果集 | true | false | true |
useColumnLabel | 使用列标签代替列名 | true | false | true |
useGeneratedKeys | 允许JDBC支持自动生成主键 | true | false | false |
autoMappingBehavior | 自动将列映射到字段或属性 NONE:关闭自动映射 PARTIAL:自动映射没有定义嵌套结果映射的字段 FULL:自动映射任何复杂的结果集 |
NONE, PARTIAL, FULL | PARTIAL |
autoMappingUnknownColumnBehavior | 指定发现自动映射目标未知列的行为 NONE:不做任何反应 WARNING:输出警告日志 FAILING:映射失败 |
NONE, WARNING, FAILING | NONE |
defaultExecutorType | 配置默认执行器 SIMPLE:普通执行器 REUSE:重用预处理语句(PreparedStatement) BATCH:既重用语句又批量更新 |
SIMPLE, REUSE, BATCH | SIMPLE |
defaultStatementTimeout | 超时时间 | 任意正整数 | null |
defaultFetchSize | 为驱动的结果集获取数量(fetchSize)设定一个建议值 | 任意正整数 | null |
defaultResultSetType | 指定语句默认的滚动策略 | FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(即未设置) | null |
safeRowBoundsEnabled | 是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false | true | false | false |
sageResultHandlerEnabled | 是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false | true | false | false |
mapUnderscoreToCamelCase | 是否开启驼峰命名自动映射 | true | false | false |
localCacheScope | 本地缓存机制的类型 SESSION:缓存一个会话中执行的所有查询 STATEMENT:仅对单个语句执行进行缓存,但对相同SqlSession的不同查询不会缓存 |
SESSION | STATEMENT | SESSION |
jdbcTypeForNull | 指定列的JDBC类型 | JDBC常量,如:NULL, VARCHAR, OTHER | OTHER |
lazyloadTriggerMethods | 指定对象触发延迟加载的条件 | 用逗号分隔的方法列表 | equals, clone, hashCode, toString |
defaultScriptingLanguage | 指定动态SQL生成使用的默认脚步语言 | 类型别名或全类名 | org.apache.ibatis.scripting.xmltags.XMLLanguageDriver |
defaultEnumTypeHandler | 指定 Enum使用的默认 TyperHandlder | 类型别名或全类名 | org.apache.ibatis.type.EnumTypeHandler |
callSettersOnNulls | 指定当结果集中值为null时是否调用映射对象的 setter 方法(基本类型不能设置为null) | true | false | false |
returnInstanceForEmptyRow | 当返回行的所有列都是空时,MyBatis默认返回空实例对象 | true | false | false |
logPrefix | 指定MyBatis增加到日志名称的前缀 | 任何字符串 | null |
logImpl | 指定MyBatis所用日志的具体实现,未指定时自动查找 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | null |
proxyFactory | 指定MyBatis创建可延迟加载对象多用到的代理工具 | CGLIB | JAVASSIST | JAVASSIST |
vfsImpl | 指定 VFS 的实现 | 自定义 VFS 的实现类全类名,以逗号分隔 | null |
useActualParamName | 允许使用方法签名中的名称作为语句参数名称 | true | false | true |
configurationFactory | 指定一个提供 Configuration 实例的类,用来加载被反序列化对象的延迟加载属性值(该类必须包含一个 static Configuration getConfiguration()方法) | 类的别名或全类名 | null |
shrinkWhitespacesInSql | 从SQL中删除多余的空格(会影响SQL中的文字字符串) | true | false | false |
defaultSqlProviderType | 指定一个类作为SQL语句提供类,需使用 @SelectProvider注解 | 类的别名或全类名 | null |
<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>
typeAliases(类型别名)
给Java类型设置一个缩写的名字,仅用于XML配置,可减少重复,例:
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
<typeAlias alias="Comment" type="domain.blog.Comment"/>
<typeAlias alias="Post" type="domain.blog.Post"/>
<typeAlias alias="Section" type="domain.blog.Section"/>
<typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>
也可以指定一个包名,MyBatis会在包名下面自动搜索需要的Java类型,此时会使用类的首字母小写的类名作为别名。例:
<typeAliases>
<package name="domain.blog"/>
</typeAliases>
此时也可以单独给类设置别名,使用 @Alias注解
@Alias("author")
public class Author {
...
}
常见的Java类型别名(不区分大小写):
别名 | 映射类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | _integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
typeHandlers(类型处理器)
类型处理器用于将获取到的值转换为Java类型
类型处理器 | Java类型 | JDBC类型 |
---|---|---|
BooleanTypeHandler | Boolean | boolean | BOOLEAN |
ByteTypeHandler | Byte | byte | BYTE | NUMERIC |
ShortTypeHandler | Short | short | SMALLINT | NUMERIC |
IntegerTypeHandler | Integer | int | INTEGER | NUMERIC |
LongTypeHandler | Long | long | BIGINT | NUMERIC |
FloatTypeHandler | Float | float | FLOAT | NUMERIC |
DoubleTypeHandler | Double | double | DOUBLE | NUMERIC |
BigDecimalTypeHandler | BigDecimal | DECIMAL | NUMERIC |
StringTypeHandler | String | CHAR | VARCHAR |
ClobReaderTypeHandler | Reader | - |
ClobTypeHandler | String | CLOB | LONGVARCHAR |
NStringTypeHandler | String | NVARCHAR | NCHAR |
NClobTypeHandler | String | NCLOB |
BlobInputStreamTypeHandler | InputStream | - |
ByteArrayTypeHandler | byte[ ] | 数据库兼容的字节流类型 |
BlobTypeHandler | byte[ ] | BLOB | LONGVARBINARY |
DateTypeHandler | Date | TIMESTAMP |
DateOnlyTypeHandler | Date | DATE |
TimeOnlyTypeHandler | Date | TIME |
SqlTimestampTypeHandler | Timestamp | TIMESTAMP |
SqlDateTypeHandler | Date | DATE |
SqlTimeTypeHandler | Time | TIME |
ObjectTypeHandler | Any | - |
EnumTypeHandler | Enumeration Type | VARCHAR或任何能兼容的字符串类型,用来存储枚举的名称 |
EnumOrdinalTypeHandler | Enumeration Type | 兼容 NUMERIC 或 DOUBLE 类型,用来存储枚举的序数值 |
SqlxmlTypeHandler | String | SQLXML |
InstantTypeHandler | Instant | TIMESTAMP |
LocalDateTimeTypeHandler | LocalDateTime | TIMESTAMP |
LoalDateTypeHandler | LocalDate | DATE |
LocalTimeTypeHandler | LocalTime | TIME |
OffsetDateTimeTypeHandler | OffsetDateTime | TIMESTAMP |
OffsetTimeTypeHandler | OffsetTime | TIME |
ZonedDateTimeTypeHandler | ZonedDateTime | TIMESTAMP |
YearTypeHandler | Year | INTEGER |
MonthTypeHandler | Month | INTEGER |
YearMonthTypeHandler | YearMonth | VARCHAR | LONGVARCHAR |
JapaneseDateTypeHandler | JapaneseDate | DATE |
objectFactory(对象工厂)
每次MyBatis创建结果对象的新实例时,都会使用一个对象工厂实例来完成实例化工作。
如果想要覆盖对象工厂的默认行为,可以通过创建自己的对象工厂来实现,
- 创建实例的方法 create(),包括无参构造和有参构造
- 配置工厂的方法 setProperties(),在初始化ObjectFactory后,元素体中定义的属性会作为参数传递过来
public class ExampleObjectFactory extends DefaultObjectFactory {
public Object create(Class type) {
return super.create(type);
}
public Object create(Class type, List<Class> constructorArgTypes, List<Object> constructorArgs) {
return super.create(type, constructorArgTypes, constructorArgs);
}
public void setProperties(Properties properties) {
super.setProperties(properties);
}
public <T> boolean isCollection(Class<T> type) {
return Collection.class.isAssignableFrom(type);
}
}
<!-- mybatis-config.xml -->
<objectFactory type="org.mybatis.example.ExampleObjectFactory">
<property name="someProperty" value="100"/>
</objectFactory>
plugins(插件)
MyBatis可使用插件在映射语句执行的过程中的某一点处进行拦截调用,方法包括:
- Executor(update, query, flushStatements, commit, rollback, getTransaction, close, isClosed);
- ParameterHandler(getParameterObject, setParameters);
- ResultSetHandler(handleResultSets, handleOutputParameters);
- StatementHandler(prepare, parameterize, batch, update, query);
自定义插件时,要特别小心,有可能会破坏MyBatis的核心模块
// ExamplePlugin.java
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
private Properties properties = new Properties();
public Object intercept(Invocation invocation) throws Throwable {
// implement pre processing if need
Object returnObject = invocation.proceed();
// implement post processing if need
return returnObject;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
<!-- mybatis-config.xml -->
<plugins>
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<property name="someProperty" value="100"/>
</plugin>
</plugins>
environments(环境配置)
MyBatis内部可配置多种环境,便于SQL映射应用于多个数据库中。但是,对于每个SqlSessionFactory实例,只能选择一种环境。
因此,如果想要连接多个数据库进行操作,就要创建多个SqlSessionFactory实例,每个数据库对应一个。
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties);
environments元素内部定义:
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<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>
其中
- default=“development”表示默认使用的环境ID
- id=“development”表示该environment元素定义的环境ID
- type=“JDBC”表示事务管理器的配置
- type=“POOLED”表示数据源的配置
事务管理器配置(transactionManager)
在Mybatis中有两种类型的事务管理器:
- JDBC
- MANAGED
JDBC:直接使用JDBC的提交和回滚设置,依赖从数据源获得的连接来管理事务作用域
MANAGER:这个配置几乎什么也不做
这两种事务管理器都不需要设置任何属性,其实都是类型别名而已。
数据源(dataSource)
在MyBatis中有三种内建的数据源类型:
- UNPOOLED
- POOLED
- JNDI
UNPOOLED:会在每次请求时打开和关闭连接,速度慢,性能表现依赖于选用的数据库
POOLED:使用数据库连接池组织JDBC连接对象,避免了创建新连接实例必须的初始化和认证时间,便于并发Web应用快速响应。
JNDI:能在如EJB或应用服务器等容器中使用
databaseIdProvider(数据库厂商标识)
MyBatis可以通过设置databaseId属性,实现针对不同的数据库厂商执行不同语句的效果。
MyBatis在实际工作中,回加载匹配databaseId和不带databaseId的所有语句,若同时找到了相同的语句,就会将不带databaseId的语句舍弃。
例:
<databaseIdProvider type="DB_VENDOR">
<property name="SQL Server" value="sqlserver"/>
<property name="DB2" value="db2"/>
<property name="Oracle" value="oracle" />
</databaseIdProvider>
mappers(映射器)
需要在核心配置文件中注册每一个映射器文件,注册方法有四种:
- 使用相对于类路径的资源引用
- 使用完全限定资源定位符
- 使用映射器接口类的全类名
- 使用包名
<mappers>
<!-- 使用相对于类路径的资源引用 -->
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
<!-- 使用完全限定资源定位符(URL) -->
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
<!-- 使用映射器接口实现类的完全限定类名 -->
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<package name="org.mybatis.builder"/>
</mappers>
映射器
映射器是MyBatis中最重要的部分!
结构
- cache:为该命名空间进行缓存配置
- cache-ref:引用其他命名空间的缓存配置
- resultMap:描述从数据库结果集加载数据并封装成对象的规则(很重要!!!)
- sql:可复用的SQL语句块
- insert:插入语句
- update:更新语句
- delete:删除语句
- select:查询语句
select
可从数据库中查询结果,并根据具体配置实现自动的对象封装返回。例:
<select id="selectPerson" parameterType="int" resultType="hashmap">
SELECT * FROM PERSON WHERE ID = #{id}
</select>
参数详解:
属性 | 描述 |
---|---|
id | 命名空间中唯一的标识符,可以被用来引用整条语句 |
parameterType | 方法的传入参数的属性类(别名或全类名),实际上MyBatis会通过TypeHandler自动推断出类型,因此默认为 unset |
resultType | 方法返回参数的类型,如果返回的是集合,就写泛型的类型(别名或全类名) |
resultMap | 对外部resultMap的命名引用(和resultType二者只能选择一个使用) |
flushCache | 设置为true后,会导致每次调用该语句都刷新一级、二级缓存(默认为 false) |
useCache | 设置为true后,会导致调用该语句后,结果会被保存到二级缓存中(默认为true) |
timeout | 驱动程序等待数据库返回结果的秒数 |
fetchSize | 驱动程序每次批量返回结果的行数 |
statementType | 可选STATEMENT, PREPARED, CALLABLE(默认为 PREPARED) |
resultSetType | 可选FORWARD_ONLY, SCROLL_SENSITIVE, SCROLL_INSENSITIVE, DEFAULT(即unset) |
databaseId | 数据库厂商标识 |
resultOrdered | 设置为true后,会导致嵌套查询的内部结果不会产生对之前结果的引用,能节省内存(默认为false) |
resultSets | 为返回的结果集赋予名称,多个名称间用逗号隔开 |
<select
id="selectPerson"
parameterType="int"
parameterMap="deprecated"
resultType="hashmap"
resultMap="personResultMap"
flushCache="false"
useCache="true"
timeout="10"
fetchSize="256"
statementType="PREPARED"
resultSetType="FORWARD_ONLY">
insert, update 和 delete
数据变更语句,例:
<insert id="insertAuthor">
insert into Author (id,username,password,email,bio)
values (#{id},#{username},#{password},#{email},#{bio})
</insert>
<update id="updateAuthor">
update Author set
username = #{username},
password = #{password},
email = #{email},
bio = #{bio}
where id = #{id}
</update>
<delete id="deleteAuthor">
delete from Author where id = #{id}
</delete>
相比于select特殊的属性为:
属性 | 描述 |
---|---|
useGeneratedKeys | 仅适用于 insert 和 update,会调用getGeneratedKey()方法取出由数据库内部生成的主键(默认值为 false) |
keyProperty | 仅适用于 insert 和 update,指定能够唯一识别对象的属性,如果生成列不止一个就用逗号隔开 |
keyColumn | 仅使用于 insert 和 update,设置生成键值在表中的列明,如果生成列不止一个就用逗号隔开 |
具体用法:
<insert id="insertAuthor" useGeneratedKeys="true"
keyProperty="id">
insert into Author (username,password,email,bio)
values (#{username},#{password},#{email},#{bio})
</insert>
sql
该元素可用来定义可重用的代码片段,例:
<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>
<select id="selectUsers" resultType="map">
select
<include refid="userColumns"><property name="alias" value="t1"/></include>,
<include refid="userColumns"><property name="alias" value="t2"/></include>
from some_table t1
cross join some_table t2
</select>
也可以在sql内部调用sql,例:
<sql id="sometable">
${prefix}Table
</sql>
<sql id="someinclude">
from
<include refid="${include_target}"/>
</sql>
<select id="select" resultType="map">
select
field1, field2, field3
<include refid="someinclude">
<property name="prefix" value="Some"/>
<property name="include_target" value="sometable"/>
</include>
</select>
字符串替换
占位符:
{ }:MyBatis会通过预处理的手段创建PreparedStatement参数占位符,安全且迅速
${ }:MyBatis进行字符串拼接,会有潜在的SQL注入风险
混合搭配小技巧:
@Select("select * from user where ${column} = #{value}")
User findByColumn(@Param("column") String column, @Param("value") String value);
结果映射
resultMap可以将数据从resultSet中提取出来,并且支持类似自定义一样的封装。非常重要!!!
例:
<!-- 配置resultMap,并取名为userResultMap -->
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="user_name"/>
<result property="password" column="hashed_password"/>
</resultMap>
<!-- 调用resultMap,并以此为依据进行数据封装 -->
<select id="selectUsers" resultMap="userResultMap">
select user_id, user_name, hashed_password
from some_table
where id = #{id}
</select>
但是,很多时候,我们会写复杂的查询语句,此时 resultMap的配置也会变得较为繁琐。一个对象可能关联多个其他对象,或关联一个集合,等等。
<!-- 非常复杂的结果映射 -->
<resultMap id="detailedBlogResultMap" type="Blog">
<constructor>
<idArg column="blog_id" javaType="int"/>
</constructor>
<result property="title" column="blog_title"/>
<association property="author" javaType="Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
<result property="password" column="author_password"/>
<result property="email" column="author_email"/>
<result property="bio" column="author_bio"/>
<result property="favouriteSection" column="author_favourite_section"/>
</association>
<collection property="posts" ofType="Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<association property="author" javaType="Author"/>
<collection property="comments" ofType="Comment">
<id property="id" column="comment_id"/>
</collection>
<collection property="tags" ofType="Tag" >
<id property="id" column="tag_id"/>
</collection>
<discriminator javaType="int" column="draft">
<case value="1" resultType="DraftPost"/>
</discriminator>
</collection>
</resultMap>
<!-- 非常复杂的语句 -->
<select id="selectBlogDetails" resultMap="detailedBlogResultMap">
select
B.id as blog_id,
B.title as blog_title,
B.author_id as blog_author_id,
A.id as author_id,
A.username as author_username,
A.password as author_password,
A.email as author_email,
A.bio as author_bio,
A.favourite_section as author_favourite_section,
P.id as post_id,
P.blog_id as post_blog_id,
P.author_id as post_author_id,
P.created_on as post_created_on,
P.section as post_section,
P.subject as post_subject,
P.draft as draft,
P.body as post_body,
C.id as comment_id,
C.post_id as comment_post_id,
C.name as comment_name,
C.comment as comment_text,
T.id as tag_id,
T.name as tag_name
from Blog B
left outer join Author A on B.author_id = A.id
left outer join Post P on B.id = P.blog_id
left outer join Comment C on P.id = C.post_id
left outer join Post_Tag PT on PT.post_id = P.id
left outer join Tag T on PT.tag_id = T.id
where B.id = #{id}
</select>
结构
- constructor:用于在实例化类时,调用构造方法
- idArg:ID参数,标记出来可以提高整体的性能
- arg:普通参数
- id:一个ID结果,标记出来可以提高整体的性能
- result:注入到字段或 JavaBean属性的普通结果
- association:复杂的关联类型,内部可嵌套 resultMap
- collection:复杂的集合类型,内部可以嵌套 resultMap
- discriminator:根据结果值判断具体使用哪个resultMap
- case:基于某些值的结果映射
resultMap中可配置的属性
属性 | 描述 |
---|---|
id | 当前命名空间中的一个唯一标识,用于标识一个结果映射 |
type | 类的别名或全类名 |
autoMapping | 若设置为 true,则会开启自动映射(默认为 unset) |
元素详解
id & result
这两个元素是映射的基础
- 二者都能将一个列的值映射到简单数据类型中
- id元素对应的属性会被标记为对象的标识符,在进行缓存和嵌套映射的时候能提高性能
id 和 result 的一些属性
属性 | 描述 |
---|---|
property | Java类中的属性名 |
column | 数据库中的列名或是查询语句中列的别名 |
javaType | Java类的类别名或全类名(MyBatis通常能够自动推断出来) |
jdbcType | JDBC类型,如果对象列是可修改的可为空的列,就必须给出 |
typeHandler | 类型处理器 |
constructor
针对一些很少改变或者基本不变类或表,建议通过 constructor 进行创建。
MyBatis能通过某种方式定位到 Java类中相应的构造方法,并将结果注入其中。如果存在名称和类型相同的属性则可以省略 javaType
<constructor>
<idArg column="id" javaType="int" name="id" />
<arg column="age" javaType="_int" name="age" />
<arg column="username" javaType="String" name="username" />
</constructor>
constructor 的一些属性
属性 | 描述 |
---|---|
column | 数据库中的列名或是查询语句中列的别名 |
javaType | Java类的类别名或全类名(MyBatis通常能够自动推断出来) |
jdbcType | JDBC类型,如果对象列是可修改的可为空的列,就必须给出 |
typeHandler | 类型处理器 |
select | 加载复杂类型属性的映射语句 |
resultMap | 映射嵌套的结果集 |
name | 构造方法形参的名字 |
association
关联元素能处理 “ has a ”的关系,如:一个用户对应一个博客,两个Java类内部存在关联。
MyBatis有两种加载关联的形式:
- 嵌套 select 查询:通过执行另外一个 SQL 映射语句来加载期望的复杂类型
- 嵌套结果映射:使用嵌套的结果映射来处理连接结果的重复子集
<!-- 嵌套结果映射 -->
<association property="author" column="blog_author_id" javaType="Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
</association>
<!-- 嵌套select查询 -->
<resultMap id="blogResult" type="Blog">
<association property="author" column="author_id" javaType="Author" select="selectAuthor"/>
</resultMap>
<select id="selectBlog" resultMap="blogResult">
SELECT * FROM BLOG WHERE ID = #{id}
</select>
<select id="selectAuthor" resultType="Author">
SELECT * FROM AUTHOR WHERE ID = #{id}
</select>
association 的属性
属性 | 描述 |
---|---|
property | Java类中的属性名 |
javaType | Java类的类别名或全类名(MyBatis通常能够自动推断出来) |
jdbcType | JDBC类型,如果对象列是可修改的可为空的列,就必须给出 |
typeHandler | 类型处理器 |
关联嵌套的Select查询
单独设置多个 select 查询语句,一个用来查询 A,一个用来查询 B ……
其他的所有属性都会被自动加载,只要它们的列名和属性名想匹配
但是,存在 ”N+1查询问题“:每一次执行一条单独的 SQL 语句获取一个列表,针对列表中的每一个记录,都会执行一个 select查询语句来加载详细信息。效率低!
嵌套select查询的属性
属性 | 描述 |
---|---|
column | 数据库中的列名或别名 |
select | 加载复杂类型属性的语句ID,会从 column属性指定的列中检索数据,并传递给目标 select语句 |
fetchType | 可选 lazy 或 eager |
<resultMap id="blogResult" type="Blog">
<association property="author" column="author_id" javaType="Author" select="selectAuthor"/>
</resultMap>
<select id="selectBlog" resultMap="blogResult">
SELECT * FROM BLOG WHERE ID = #{id}
</select>
<select id="selectAuthor" resultType="Author">
SELECT * FROM AUTHOR WHERE ID = #{id}
</select>
关联的嵌套结果映射
针对复杂的嵌套关联语句,我们可以选择连接查询。
关联嵌套的属性:
属性 | 描述 |
---|---|
resultMap | 引用的嵌套结果集,可以实现 resultMap的复用 |
columnPrefix | 给表中的每列设置相同的额外的前缀,来避免重复 |
notNullColumn | 指定非空列(指定后,MyBaits只有在该列非空情况下才会创建对象) |
autoMapping | 自动映射 |
<resultMap id="blogResult" type="Blog">
<id property="id" column="blog_id" />
<result property="title" column="blog_title"/>
<association property="author" column="blog_author_id" javaType="Author" resultMap="authorResult"/>
</resultMap>
<resultMap id="authorResult" type="Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
<result property="password" column="author_password"/>
<result property="email" column="author_email"/>
<result property="bio" column="author_bio"/>
</resultMap>
<select id="selectBlog" resultMap="blogResult">
select
B.id as blog_id,
B.title as blog_title,
B.author_id as blog_author_id,
A.id as author_id,
A.username as author_username,
A.password as author_password,
A.email as author_email,
A.bio as author_bio
from Blog B left outer join Author A on B.author_id = A.id
where B.id = #{id}
</select>
注意:在嵌套查询中,id 元素非常重要,设置与否会严重影响性能!
此外,针对结果列名和映射列名不同,设置 columnPrefix:
<resultMap id="blogResult" type="Blog">
<id property="id" column="blog_id" />
<result property="title" column="blog_title"/>
<association property="author"
resultMap="authorResult" />
<association property="coAuthor"
resultMap="authorResult"
columnPrefix="co_" />
</resultMap>
collection
集合元素类似管理元素,针对 一个博客有多篇文章类型的问题。
同样,集合中也可以进行 嵌套 select查询和嵌套结果查询。
嵌套select查询
<resultMap id="blogResult" type="Blog">
<collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectPostsForBlog"/>
</resultMap>
<select id="selectBlog" resultMap="blogResult">
SELECT * FROM BLOG WHERE ID = #{id}
</select>
<select id="selectPostsForBlog" resultType="Post">
SELECT * FROM POST WHERE BLOG_ID = #{id}
</select>
ofType属性:指定泛型类型,即集合存储元素的类型
其余属性含义和 association中相同
嵌套结果查询
<resultMap id="blogResult" type="Blog">
<id property="id" column="blog_id" />
<result property="title" column="blog_title"/>
<collection property="posts" ofType="Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<result property="body" column="post_body"/>
</collection>
</resultMap>
<select id="selectBlog" resultMap="blogResult">
select
B.id as blog_id,
B.title as blog_title,
B.author_id as blog_author_id,
P.id as post_id,
P.subject as post_subject,
P.body as post_body,
from Blog B
left outer join Post P on B.id = P.blog_id
where B.id = #{id}
</select>
再次强调 id元素对性能的重要性!
discriminator
有时候一个数据库拆线呢可能会返回多个不同的结果集,因此可以使用鉴别器来实现针对不同的值采用不同的resultMap承接。
<resultMap id="vehicleResult" type="Vehicle">
<id property="id" column="id" />
<result property="vin" column="vin"/>
<result property="year" column="year"/>
<result property="make" column="make"/>
<result property="model" column="model"/>
<result property="color" column="color"/>
<discriminator javaType="int" column="vehicle_type">
<case value="1" resultType="carResult">
<result property="doorCount" column="door_count" />
</case>
<case value="2" resultType="truckResult">
<result property="boxSize" column="box_size" />
<result property="extendedCab" column="extended_cab" />
</case>
<case value="3" resultType="vanResult">
<result property="powerSlidingDoor" column="power_sliding_door" />
</case>
<case value="4" resultType="suvResult">
<result property="allWheelDrive" column="all_wheel_drive" />
</case>
</discriminator>
</resultMap>
discriminator的属性:
属性 | 描述 |
---|---|
column | 数据库中的列名或列别名 |
javaType | Java类的类别名或全类名(MyBatis通常能够自动推断出来) |
value | 查出来待判断的值 |
resultMap | 针对不同值适用的不同结果集 |
cache
默认情况下MyBatis之开启本地缓存,即一级缓存,仅针对一个sqlSession中的数据进行缓存
要开启二级缓存需要手动在映射器中设置:
<cache/>
二级缓存开启后,
- 所有的select语句都会被缓存
- 只有发生insert, update, delete语句时才会刷新缓存
- 会使用LRU算法(最少使用算法)清除不必要的缓存
- 最多缓存1024个对象
- 对缓存的操作被视为 读/写操作,因此获取到的对象是不共享的
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
cache的属性:
属性 | 描述 |
---|---|
eviction | 配置清理缓存的算法,可选项有:LRU、FIFO、SOFT、WEAK |
flushInterbal | 设置刷新间隔 |
size | 设置最大缓存数目 |
readOnly | 设置只读属性。如果只读,则调用缓存时返回同一个对象,高效;如果可读可写,返回缓存对象的拷贝,安全但低效。 |
JAVA API
使用MyBatis最主要的接口就是SqlSession,我们可以通过这个接口来执行命令,获取映射器和管理事务。
SqlSession由SqlSessionFactory实例创建;SqlSessionFactory则由SqlSessionFactoryBuilder读取配置文件或注解创建。
String resource = "org/mybatis/builder/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(inputStream);
这里我们调用Resources工具类,从类路径、文件系统或者一个 web URL中加载资源文件。内部API 如下:
URL getResourceURL(String resource)
URL getResourceURL(ClassLoader loader, String resource)
InputStream getResourceAsStream(String resource)
InputStream getResourceAsStream(ClassLoader loader, String resource)
Properties getResourceAsProperties(String resource)
Properties getResourceAsProperties(ClassLoader loader, String resource)
Reader getResourceAsReader(String resource)
Reader getResourceAsReader(ClassLoader loader, String resource)
File getResourceAsFile(String resource)
File getResourceAsFile(ClassLoader loader, String resource)
InputStream getUrlAsStream(String urlString)
Reader getUrlAsReader(String urlString)
Properties getUrlAsProperties(String urlString)
Class classForName(String className)
SqlSessionFactoryBuilder
SqlSessionFactoryBuilder 有五个 build() 方法
SqlSessionFactory build(InputStream inputStream)
SqlSessionFactory build(InputStream inputStream, String environment)
SqlSessionFactory build(InputStream inputStream, Properties properties)
SqlSessionFactory build(InputStream inputStream, String env, Properties props)
SqlSessionFactory build(Configuration config)
其中environment决定加载哪种环境,properties决定加载哪些属性,优先级最高。
Configuration类中包含了对所有配置的设置,并以 Java API 的形式展示出来:
DataSource dataSource = BaseDataTest.createBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.setLazyLoadingEnabled(true);
configuration.setEnhancementEnabled(true);
configuration.getTypeAliasRegistry().registerAlias(Blog.class);
configuration.getTypeAliasRegistry().registerAlias(Post.class);
configuration.getTypeAliasRegistry().registerAlias(Author.class);
configuration.addMapper(BoundBlogMapper.class);
configuration.addMapper(BoundAuthorMapper.class);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(configuration);
SqlSessionFactory
SqlSessionFactory 中有六个方法创建 SqlSession 实例。
SqlSession openSession()
SqlSession openSession(boolean autoCommit) // 传 true将开启自动提交事务
SqlSession openSession(Connection connection) // 使用自己的 Connection 实例,MyBaits会根据 Connection来自动判断是否启用 autoCommit
SqlSession openSession(TransactionIsolationLevel level) // 设置事务隔离级别(NONE, READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE)
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level)
SqlSession openSession(ExecutorType execType) // 设置执行器类别(SIMPLE, REUSE, BATCH)
SqlSession openSession(ExecutorType execType, boolean autoCommit)
SqlSession openSession(ExecutorType execType, Connection connection)
Configuration getConfiguration();
通过不同方法的选择,可以实现对事务处理、数据库连接、语句执行的设置。
默认的空参方法会创建具有如下特性的 SqlSession:
- 开启事务作用域,但不自动提交
- 从当前环境配置的 DataSource 实例中获取 Connection对象
- 事务隔离级别将会使用驱动或数据源的默认设置
- 采用 SimpleExecutor,预处理语句不会复用也不会批量处理更新
SqlSession
SqlSession包含所有执行语句、提交或回滚事务以及获取映射器的方法。
直接执行语句(老式的用法)
这些方法用来执行定义在SQL映射文件中的语句,具体参数可以是原始类型、JavaBean、POJO或Map
<T> T selectOne(String statement, Object parameter) // 若返回多个对象就会报错,若未查到则返回 null
<E> List<E> selectList(String statement, Object parameter) // 返回列表,可承接多个对象
<T> Cursor<T> selectCursor(String statement, Object parameter) // 返回游标,借助迭代器实现懒加载
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey) // 返回字典,对象的一个属性作为key,对象作为value
int insert(String statement, Object parameter) // 返回影响行数
int update(String statement, Object parameter)
int delete(String statement, Object parameter)
分页查询方法:
<E> List<E> selectList (String statement, Object parameter, RowBounds rowBounds)
<T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds)
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowbounds)
void select (String statement, Object parameter, ResultHandler<T> handler)
void select (String statement, Object parameter, RowBounds rowBounds, ResultHandler<T> handler)
关于 RowBounds类,它是不可变类:
int offset = 100;
int limit = 25;
RowBounds rowBounds = new RowBounds(offset, limit);
当我们将 MyBatis的执行器类型设置为 BATCH时,可以使用以下方法执行缓存在 JDBC驱动类中的批量更新语句。
List<BatchResult> flushStatements()
使用映射器
在MyBatis中定义一个接口和同名的映射文件,通过反射的方法来操作数据库。
public interface AuthorMapper {
Author selectAuthor(int id);
List<Author> selectAuthors();
@MapKey("id")
Map<Integer, Author> selectAuthors();
int insertAuthor(Author author);
int updateAuthor(Author author);
int deleteAuthor(int id);
}
映射器接口不需要实现任何方法或继承自任何类,且不能在两个具有继承关系的接口中拥有相同的方法签名
可以选择给接口中的方法配置注解来替代或者方便映射文件的设置
注解 | 使用对象 | XML等价形式 | 描述 |
---|---|---|---|
@CacheNamespace | 类 | 为给定的命名空间配置缓存,属性同标签类似 | |
@Property | N/A | 指定参数值或占位符,属性同标签 | |
@CacheNamespaceRef | 类 | 引用另一个命名空间的缓存配置 | |
@ConstructorArgs | 方法 | 收集一组结果来传给构造方法,属性:Value数组 | |
@Arg | N/A | | | 表示构造方法的参数,是一个布尔值,表示该属性是否用于唯一标识 |
@TypeDiscriminator | 方法 | 根据不同取值决定使用何种映射集 | |
@Case | N/A | 表示某个值和对应情况下的映射 | |
@Results | 方法 | 定义一组映射结果集 | |
@Result | N/A | | | 在属性和字段之间设置单个结果映射 |
@One | N/A | 复杂类型的单个属性映射 | |
@Many | N/A | 复杂类型的集合属性映射 | |
@MapKey | 方法 | 无 | 供返回值为Map的方法使用,使得注释的属性成为key,将对象转化为Map |
@Options | 方法 | 映射语句的属性 | 可设置大部分的开关和配置项 |
@Insert | @Update | @Delete | @Select | 方法 | | | | | 分别代表将被执行的SQL语句 |
@InsertProvider | @UpdateProvider | @DeleteProvider | @SelectProvider | 方法 | | | | | 分别代表构建的动态SQL语句 |
@Param | 参数 | 无 | 自定义每个参数的名字 |
@SelectKey | 方法 | 查询主键 | |
@Flush | 方法 | 无 | 使用该注解后就能调用flushStatement()方法 |
示例:
@Insert("insert into table3 (id, name) values(#{nameId}, #{name})")
@SelectKey(statement="call next value for TestSequence", keyProperty="nameId", before=true, resultType=int.class)
int insertTable3(Name name);
@Insert("insert into table2 (name) values(#{name})")
@SelectKey(statement="call identity()", keyProperty="nameId", before=false, resultType=int.class)
int insertTable2(Name name);
@Flush
List<BatchResult> flush();
@Results(id = "userResult", value = {
@Result(property = "id", column = "uid", id = true),
@Result(property = "firstName", column = "first_name"),
@Result(property = "lastName", column = "last_name")
})
@Select("select * from users where id = #{id}")
User getUserById(Integer id);
@Results(id = "companyResults")
@ConstructorArgs({
@Arg(column = "cid", javaType = Integer.class, id = true),
@Arg(column = "name", javaType = String.class)
})
@Select("select * from company where id = #{id}")
Company getCompanyById(Integer id);
@SelectProvider(type = UserSqlBuilder.class, method = "buildGetUsersByName")
List<User> getUsersByName(String name);
class UserSqlBuilder {
public static String buildGetUsersByName(final String name) {
return new SQL(){{
SELECT("*");
FROM("users");
if (name != null) {
WHERE("name like #{value} || '%'");
}
ORDER_BY("id");
}}.toString();
}
}
@SelectProvider(type = UserSqlBuilder.class, method = "buildGetUsersByName")
List<User> getUsersByName(
@Param("name") String name, @Param("orderByColumn") String orderByColumn);
class UserSqlBuilder {
// 如果不使用 @Param,就应该定义与 mapper 方法相同的参数
public static String buildGetUsersByName(
final String name, final String orderByColumn) {
return new SQL(){{
SELECT("*");
FROM("users");
WHERE("name like #{name} || '%'");
ORDER_BY(orderByColumn);
}}.toString();
}
// 如果使用 @Param,就可以只定义需要使用的参数
public static String buildGetUsersByName(@Param("orderByColumn") final String orderByColumn) {
return new SQL(){{
SELECT("*");
FROM("users");
WHERE("name like #{name} || '%'");
ORDER_BY(orderByColumn);
}}.toString();
}
}
动态SQL
动态SQL是MyBatis的强大特性,能够方便SQL语句的拼接
主要标签:
- if
- choose(when, otherwise)
- trim(where, set)
- foreach
if
提供可选的查找功能,如果传入了某个参数,就将该参数作为查找标准之一。
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
choose, when, otherwise
类似Java中的 switch, case, default
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
trim, where, set
解决 where语句中 and拼接 和 update语句中 逗号拼接的问题
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
元素会在子元素返回内容的情况下才插入 WHERE子句,并且若子句以 AND或OR开头也会根据情况删除或保留
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}
</update>
元素会动态地在首行插入SET关键字,并且会根据情况删掉或保留逗号
此外,也可以自定义类似的元素标签
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
<trim prefix="SET" suffixOverrides=",">
...
</trim>
foreach
可以动态的遍历集合,在构造条件或插入语句时特别有用
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
- collection:集合
- item:集合项
- index:索引
- separator:分隔符
- open:开头
- close:结尾
MyBatis文档的更多相关文章
- MyBatis 文档 完整版
框架技术 1.框架技术 01.是一个应用程序的半成品 02.提供可重用的公共结构 03.按一定规则组织的一组组件 2.分析优势 01.不用再考虑公共问题 02.专心的业务实现上 03.结构统一,易于学 ...
- MYBATIS 文档
http://www.mybatis.org/mybatis-3/zh/index.html
- Mybatis文档阅读笔记(明日继续更新...)
今天在编写mybatis的mapper.xml时,发现对sql的配置还不是很熟,有很多一坨一坨的东西,其实是可以抽取成服用的.不过良好的组织代码,还是更重要的.
- Mybatis笔记三:MyBatis的API文档
mybatis文档:http://www.mybatis.org/mybatis-3/zh/getting-started.html mybatis-spring文档:http://www.mybat ...
- spring整合mybatis错误:Caused by: org.xml.sax.SAXParseException; lineNumber: 5; columnNumber: 62; 文档根元素 "mapper" 必须匹配 DOCTYPE 根 "configuration"。
运行环境:jdk1.7.0_17+tomcat 7 + spring:3.2.0 +mybatis:3.2.7+ eclipse 错误:Caused by: org.xml.sax.SAXParseE ...
- SpringBoot+rest接口+swagger2生成API文档+validator+mybatis+aop+国际化
代码地址:JillWen_SpringBootDemo mybatis 1. 添加依赖: <dependency> <groupId>org.mybatis.spring.bo ...
- Mybatis 框架文档 超具体笔记
1 Mybatis入门 1.1 单独使用jdbc编程问题总结 1.1.1 jdbc程序 Public static void main(String[] args) { Connec ...
- 基于Mybatis的Mysql数据库文档生成工具,支持生成docx(原创)
今天不写android--也写写数据库相关的东西 -------------------- 今日老夫闲来无事,设计了一款数据库文档生成工具 眼下仅仅支持mysql 主要是生成docx的 下载链接:下载 ...
- mybatis官网文档mybatis_doc
在平时的学习中,我们可以去参考官网的文档来学习,这个文档有中文的,方便我们去阅读,而且这里的分类很详细. 官网文档链接:http://www.mybatis.org/mybatis-3/zh/inde ...
随机推荐
- Cyclic Nacklace HDU - 3746
CC这个月底总是很郁闷,昨天他查了他的信用卡,没有任何意外,只剩下99.9元了.他很苦恼,想着如何度过这最后的几天.受"HDU CakeMan"企业家精神的启发,他想卖一些小东西来 ...
- Codeforces Round #650 (Div. 3) D. Task On The Board (构造,贪心)
题意:有一个字符串和一组数,可以对字符串删去任意字符后为数组的长度,且可以随意排序,要求修改后的字符串的每个位置上的字符满足:其余大于它的字符的位置减去当前位置绝对值之和等于对应序列位置上的数. 题解 ...
- Leetcode(877)-石子游戏
亚历克斯和李用几堆石子在做游戏.偶数堆石子排成一行,每堆都有正整数颗石子 piles[i] . 游戏以谁手中的石子最多来决出胜负.石子的总数是奇数,所以没有平局. 亚历克斯和李轮流进行,亚历克斯先开始 ...
- Web 实时通信方案 All In One
Web 实时通信方案 All In One HTTP 轮询, 单向通信,开销大 HTTP 长轮询, 单向通信,开销较小 WebSocket,双向通信,开销小 (TCP 高延迟,保证数据完整性) Ser ...
- Generator function vs Async/Await
Generator function vs Async/Await generator async /await refs xgqfrms 2012-2020 www.cnblogs.com 发布文章 ...
- 树莓派 4B 入门教程
树莓派 4B 入门教程 Raspberry Pi, Raspberry Pi 3B, Raspberry Pi 4B 树莓派 4B 入门手册 PDF Raspberry Pi Beginners Gu ...
- vscode & typescript & optional-chaining bug
vscode & typescript & optional-chaining bug https://www.cnblogs.com/xgqfrms/p/11745541.html ...
- Dart: 请求graphql数据
import 'package:http/http.dart' as http; const url = "http://127.0.0.1:4000/graphql"; main ...
- 高倍币VAST,如何破局NGK算力市场?
2020年,全球经济危机的爆发,无疑是给全球经济蒙上了一层阴影.而世界主要经济体也开启了无节制的放水,通过一轮又一轮的宽松货币政策,以刺激经济的发展.然而宽松的货币政策也加速了以美元为首的货币贬值,同 ...
- JavaScript中判断对象是否属于Array类型的4种方法及其背后的原理与局限性
前言 毫无疑问,Array.isArray是现如今JavaScript中判断对象是否属于Array类型的首选,但是我认为了解本文其余的方法及其背后的原理与局限性也是很有必要的,因为在JavaScrip ...