https://mybatis.org/mybatis-3/zh/configuration.html
https://www.w3cschool.cn/mybatis/mybatis-dyr53b5w.html
https://mybatis.org/mybatis-3/zh/getting-started.html
Mybatis
构建项目
导入环境
在Maven中导入jar
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
Mybatis环境配置
- mybatis-config.xml
- mybatis全局配置文件
- XXXmapper.xml
- sql映射配置文件
MyBatis 可以配置成适应多种环境,但每个 SqlSessionFactory 实例只能选择一种环境。
<?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"/>
<!-- 映射实体类 -->
<typeAliases>
<package name="cn.landsall.entity"/>
</typeAliases>
<!-- 配置环境 -->
<environments default="development">
<!-- default指定默认环境 -->
<environment id="development">
<!-- 配置事务管理器 -->
<!-- 在Spring环境下会被覆盖 -->
<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>
<!-- 映射sql语句 -->
<mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<package name="cn.landsall.dao"/>
</mappers>
</configuration>
创建Mapper接口
映射sql
- 通过注解
- 通过xml
```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">
<a name="vRiW2"></a>
## 构建SqlSessionFactory
`SqlSessionFactory` 由 `SqlSessionFactoryBuilder` 获得,用于创建SqlSession对象,整个项目只需创建一次<br />`SqlSessionFactoryBuilder` 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例<br />`SqlSession` 相当于 Connection 每一次交互都应该创建一次
<a name="106a0"></a>
### 通过读取xml配置文件
![image.png](https://cdn.nlark.com/yuque/0/2020/png/1561124/1601983692845-354e75b1-c959-4747-9d68-38c8bb97558d.png#align=left&display=inline&height=475&margin=%5Bobject%20Object%5D&name=image.png&originHeight=949&originWidth=1533&size=1312216&status=done&style=none&width=766.5)
<a name="EU850"></a>
# XML配置
<a name="UWeim"></a>
## 属性(properties)
属性可以在外部进行配置,并可以进行**动态替换**。你既可以在典型的** Java 属性文件**中配置这些属性,也可以在 **properties 元素的子元素**中设置
```xml
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>
设置好的属性可以在整个配置文件中用来替换需要动态配置的属性值
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
也可以在 SqlSessionFactoryBuilder.build() 方法中传入属性值。
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, props);
// ... 或者 ...
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, props);
属性加载顺序
properties 元素体内指定的属性 -> resource 属性读取类路径下的属性文件 -> 方法参数传递的属性
占位符默认值
从 MyBatis 3.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"/>
<!-- 修改默认值的分隔符 默认为: -->
<property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="?:"/>
</properties>
设置(settings)
https://mybatis.org/mybatis-3/zh/configuration.html#settings
常用
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
mapUnderscoreToCamelCase | 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 | true | false | False |
logImpl | 指定MyBatis应该使用哪种日志记录实现。如果不存在此设置,则将自动发现日志记录实现。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 没有设置 |
类型别名(typeAliases)
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
<!-- mybatis-config.xml 中 -->
<typeAliases>
<!-- 指定类的别名 -->
<typeAlias alias="Author" type="domain.blog.Author"/>
<!-- 指定实体类的包 -->
<package name="domain.blog"/>
</typeAliases>
在没有注解的情况下,包中的类会使用 Bean 的首字母小写的非限定类名来作为它的别名
类型处理器(typeHandlers)
MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型
自定义类型处理器
// ExampleTypeHandler.java
@MappedJdbcTypes(JdbcType.VARCHAR)
public class ExampleTypeHandler extends BaseTypeHandler<String> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, parameter);
}
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
return rs.getString(columnName);
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return rs.getString(columnIndex);
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return cs.getString(columnIndex);
}
}
<!-- mybatis-config.xml -->
<typeHandlers>
<typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
</typeHandlers>
- 指定类型处理器处理的 Java 类型
- 添加
**javaType**
属性- javaType=”String”
- 添加
@MappedJdbcTypes
注解- JdbcType.VARCHAR
- 添加
- 指定关联的 JDBC 类型
- 添加
jdbcType
属性 - 添加
@MappedJdbcTypes
注解
- 添加
-
泛型类型处理器
//GenericTypeHandler.java
public class GenericTypeHandler<E extends MyObject> extends BaseTypeHandler<E> {
private Class<E> type;
public GenericTypeHandler(Class<E> type) {
if (type == null) throw new IllegalArgumentException("Type argument cannot be null");
this.type = type;
}
...
对象工厂(objectFactory)
https://mybatis.org/mybatis-3/zh/configuration.html#objectFactory
插件(plugins)
https://mybatis.org/mybatis-3/zh/configuration.html#plugins
映射器(mappers)
指定映射文件的位置
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
- 使用接口注册需要把mapper.xml文件放置于接口同一个包的路径下,且文件名相同,才能识别xml配置
resource文件夹下的路径名与包名一致,项目输出时会生成在对应包名下
XML映射
https://mybatis.org/mybatis-3/zh/sqlmap-xml.html
sql
这个元素可以用来定义可重用的 SQL 代码片段,以便在其它语句中使用。 参数可以静态地(在加载的时候)确定下来,并且可以在不同的 include 元素中定义不同的参数值
<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>
等于
<select id="selectUsers" resultType="map">
select
t1.id,t1.username,t1.password,
t2.id,t2.username,t2.password
from some_table t1
cross join some_table t2
</select>
参数
mybatis会自动映射基本数据类型,无需指定parameterType
<select id="selectUsers" resultType="User">
select id, username, password
from users
where id = #{id}
</select>
复杂Java对象,基于OGNL表达式插入对应属性
<insert id="insertUser" parameterType="User">
insert into users (id, username, password)
values (#{id}, #{username}, #{password})
</insert>
JDBC 要求,如果一个列允许使用 null 值,并且会使用值为 null 的参数,就必须要指定 JDBC 类型(jdbcType)
#{realname,jdbcType=VARCHAR}
完整的类型处理配置#{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}
字符串替换
#{}
会创建占位符?${}
插入不转义的字符串 — 常用于替换表名列名@Select("select * from user where ${column} = #{value}")
User findByColumn(@Param("column") String column, @Param("value") String value);
其中 ${column} 会被直接替换,而 #{value} 会使用 ? 预处理。
结果映射(resultMap)
隐式映射
<select id="selectUsers" resultType="com.someapp.model.User">
select id, username, hashedPassword
from some_table
where id = #{id}
</select>
隐式映射了如下resultMap
<resultMap id="userResultMap" type="User">
<id property="id" column="id" />
<result property="username" column="username"/>
<result property="password" column="password"/>
</resultMap>
显式映射
Java类中的property映射到数据库查询结果的column
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="user_name"/>
<result property="password" column="hashed_password"/>
</resultMap>
<select id="selectUsers" resultMap="userResultMap">
select user_id, user_name, hashed_password
from some_table
where id = #{id}
</select>
动态SQL
if
常用于模糊查询where语句
<select id="findActiveBlogWithTitleLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
</select>
choose、when、otherwise
<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>
where
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。
而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们清除。
<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>
set
去点 , 后缀
<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>
trim
prefix 和 suffix 会在有内容返回时添加前缀和后缀
prefixOverrides 和 suffixOverrides 会在语句前后把指定内容清除,多个内容用|
隔开
eg: 类似where的功能
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
eg:类似set
<trim prefix="SET" suffixOverrides=",">
...
</trim>
foreach
collection:集合
item:集合项
index:当前迭代的序号
open:开头的字符串
separator:间隔元素之间的字符串
end:结尾的字符串
- foreach可以迭代list,set,map和数组对象
- 当迭代Map时,index,item分别是键和值
<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>
bind
bind
元素允许你在 OGNL 表达式以外创建一个变量,并将其绑定到当前的上下文<select id="selectBlogsLike" resultType="Blog">
<bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
SELECT * FROM BLOG
WHERE title LIKE #{pattern}
</select>
MyBatis CRUD
查询
增删改
p
多行插入
<insert id="insertUserList" parameterType="list" useGeneratedKeys="true">
insert into user (username, password, realname, mobile, age) values
<foreach collection="list" item="item" separator=",">
(#{item.username},#{item.password},#{item.realname},#{item.mobile},#{item.age})
</foreach>
</insert>
模糊查询
- 在传参数时手动传入%% — 内部使用PreStatement
- 使用${value} — 内部使用Statement
SelectKey
在
- order = “BEFORE”
- 插入前(类似数据库自动生成主键)
- order = “AFTER”
- 插入后(获取数据库自动生成的值)
设置值
语句执行后,自动增长的id值会被赋值给user (使用代替useGeneratedKeys=”true” keyProperty=”id”)
分页
https://www.w3cschool.cn/mybatis/mybatis-8n4d3bpg.html
缓存
mybatis的缓存分为两级:一级缓存、二级缓存
https://www.w3cschool.cn/mybatis/mybatis-xlc73bt4.html
https://mybatis.org/mybatis-3/sqlmap-xml.html#cache
一级缓存
一级缓存为 sqlsesson 缓存,缓存的数据只在 SqlSession 内有效。在操作数据库的时候需要先创建 SqlSession 会话对象,在对象中有一个 HashMap 用于存储缓存数据,此 HashMap 是当前会话对象私有的,别的 SqlSession 会话对象无法访问。
-
一级缓存失效的情况
不同的SqlSession对应不同的一级缓存
- 同一个SqlSession但是查询条件不同
- 同一个SqlSession两次查询期间执行了任何一次增删改操作
- 同一个SqlSession两次查询期间手动清空了缓存
openSession.clearcache()
二级缓存:
二级缓存是 mapper 级别的缓存,也就是同一个 namespace 的 mappe.xml ,当多个 SqlSession 使用同一个 Mapper 操作数据库的时候,得到的数据会缓存在同一个二级缓存区域
- 在sqlSession关闭后才会产生二级缓存
- 二级缓存非只读对象需要实现序列化接口
-
开启二级缓存
mybatis-config.xml
<settings>
<setting name="cacheEnabled" value="true"/>
<settings>
Mapper.xml
<mapper>
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>
</mapper>
清除缓存的策略为LRU
当前mapper下所有语句开启二级缓存
每隔60秒刷新,最大存储512个对象,而返回的对象是只读的
只读:对象不需要实现序列化接口,且性能较好
非只读:对象需要实现序列化接口,性能较差,但安全如果要禁用某一个语句的二级缓存
<select id="getCountByName" useCache="false">
其他
mybastis 映射Java类属性不区分大小写