net start mysql #启动服务器
如果要卸载mysql可以运行一下命令
net stop mysql #停止服务器
Mybatis XML Mapper 用法
select
resultMap 用于配置 Java 对象的属性与查询结果列的对应关系
resultMap 包含的属性:
- id:必填且唯一
- type:必填,查询结果映射的对象类型
- extends:选填,表示该 resultMap 继承自其他的 resultMap
- autoMapping:选填,表示是否启用非映射字段(resultMap 中没有配置的字段)的自动映射功能
resultMap 包含的子标签:
- constructor:使用构造方法注入结果,包含两个子标签
- idArg:id 参数,标记结果作为 id(唯一值),可以帮助提高整体性能
- arg:注入到构造方法的一个普通结果
- id:一个 id 结果,标记结果作为 id(唯一值),可以帮助提高整体性能
- result:注入到 Java 对象属性的普通结果
- association:类型关联,可以将结果进行包装成这种类型
- collection:复杂类型的集合
- discriminator:根据结果值来决定使用哪个结果映射
- case:基于某些值得结果映射
子标签 id result 都包含的属性
- column:从数据库得到的列名,或者是列的别名
- property:映射到结果的属性,支持嵌套如:address.street.number
- javaType:一个 Java 类的完全限定名或一个类型别名(通过 typeAlias 配置或者默认的类型);若映射到普通的 Bean,MyBatis 会自动判断属性的类型,如果是映射到 HashMap,则需要明确地指定 javaType 属性
- jdbcType:列对应的数据库类型。指明插入、更新、删除操作时,对于为空的列的处理方式
- typeHandler:覆盖默认的类型处理器,值可以是类的完全限定名或类型别名
当 resultType = 某个 Bean 时,需要设置别名来应对列名和属性名不一致的情况;使用 resultMap 则使用配置好的映射方式。
MyBatis 映射是将属性名的映射是转换为大写之后再进行映射,即 property=”userName” 和 property=”username” 都可以匹配到对象的 userName 属性上面。
<resultMap id="userMap" type="tk.mybatis.simple.model.SysUser">
<id property="id" column="id"/>
<result property="userName" column="user_name"/>
<result property="userPassword" column="user_password"/>
<result property="userEmail" column="user_email"/>
<result property="userInfo" column="user_info"/>
<result property="headImg" column="head_img" jdbcType="BLOB"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
</resultMap>
<select id="selectById" resultMap="userMap">
select * from sys_user where id = #{id}
</select>
<select id="selectAll" resultType="tk.mybatis.simple.model.SysUser">
select id,
user_name userName,
user_password userPassword,
user_email userEmail,
user_info userInfo,
head_img headImg,
create_time createTime
from sys_user
</select>
关联查询结果嵌套
一次 SQL 查询根据表或指定的属性映射到不同的对象中,对象里面嵌套对象,使用 属性.属性 进行赋值,注意双引号
缺点:SQL 比较复杂,不容易写对;嵌套结果应用到不同的类上,增加服务器压力
<select id="selectRolesByUserId" resultType="tk.mybatis.simple.model.SysRole">
select
r.id,
r.role_name roleName,
r.enabled,
r.create_by createBy,
r.create_time createTime,
u.user_name as "user.userName",
u.user_email as "user.userEmail"
from sys_user u
inner join sys_user_role ur on u.id = ur.user_id
inner join sys_role r on ur.role_id = r.id
where u.id = #{userId}
</select>
- 使用 resultMap 继承
<resultMap id="userRoleMap" extends="userMap" type="tk.mybatis.simple.model.SysUser">
<result property="role.id" column="role_id"/>
<result property="role.roleName" column="role_name"/>
<result property="role.enabled" column="enabled"/>
<result property="role.createBy" column="create_by"/>
<result property="role.createTime" column="role_create_time" jdbcType="TIMESTAMP"/>
</resultMap>
- 使用 resultMap 继承
- 使用 resultMap association 标签配置一对一映射(使用 association 和 对上面进行改造)
association 属性:
- property:实体类的属性名
- javaType:属性对应的 Java 类型,下面的例子使用的是 javaType
- resultMap:使用现有的 resultMap,不用重新配置
columnPrefix:查询列前缀,配置前缀后,子标签配置 result 的 column 可以省略前缀
<resultMap id="userRoleMap" extends="userMap" type="tk.mybatis.simple.model.SysUser">
<association property="role" columnPrefix="role_" javaType="tk.mybatis.simple.model.SysRole"/>
<result property="id" column="role_id"/>
<result property="roleName" column="role_name"/>
<result property="enabled" column="enabled"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
</resultMap>
<select id="selectUserAndRoleById2" resultMap="userRoleMap">
select
u.id,
u.user_name,
u.user_password,
u.user_email,
u.user_info,
u.head_img,
u.create_time,
r.id role_id,
r.role_name role_role_name,
r.enabled role_enabled,
r.create_by role_create_by,
r.create_time role_create_time
from sys_user u
inner join sys_user_role ur on u.id = ur.user_id
inner join sys_role r on ur.role_id = r.id
where u.id = #{id}
</select>
- 进一步的将 association 中的 javaType 替换为 resultMap
```xml
- 进一步的将 association 中的 javaType 替换为 resultMap
```xml
<a name="TRQyc"></a>
### 关联的嵌套查询
不同于**关联结果嵌套**,**关联的嵌套查询**则是通过简单的进行**额外的查询**
association 标签嵌套查询常用属性:
- select:另一个映射查询的 id,Mybatis 会额外执行这个查询获取嵌套对象的结果
- column:列名(或别名),将主查询中列的结果作为嵌套查询的参数。例如 column={prop1=col1,prop2=col2},prop1 和 prop2 作为嵌套查询的参数
- fetchType:数据加载方式,可选值未 lazy(延迟加载) 和 eager(积极加载),该配置会覆盖全局的 lazyLoadingEnabled 配置
xml
// userMapper.xml
<select id="selectUserAndRoleByIdSelect" resultMap="userRoleMapSelect">
select
u.id,
u.user_name,
u.user_password,
u.user_email,
u.user_info,
u.head_img,
u.create_time,
ur.role_id
from sys_user u
inner join sys_user_role ur on u.id = ur.user_id
where u.id = #{id}
</select>
// roleMapper.xml
<select id="selectRoleById" resultMap="roleMap">
select * from sys_role where id = #{id}
</select>
- 全局延迟加载属性,注意,延迟加载通过动态代理实现,通过 SqlSession 执行 SQL,注意 SqlSession 的生命周期,避免 SqlSession 已经销毁,而仍然去调用查询方法,造成异常。
```xml
<setting name="aggressiveLazyLoading" value="false"/>
lazyLoadTriggerMethods 默认值为 equals,clone,hashCode,toString,当调用配置中的方法时,会自动触发对象的延迟加载
一对多的查询结果嵌套 collection
查询用户角色下面的权限,collection property 指定实体类属性,以及 ofType 对应的 JavaBean 类型 ```xml public class SysUser implements Serializable { private List
roleList; } // 进一步,使用 resultMap 代替
Mybatis 数据合并原理:查询 userMap 中的 id 标签对应的字段是否相同,相同就合并;若没有配置 id,则比对所有的字段,全部相同则合并;所以在 resultMap 中配置 id 子标签可以提高查询效率
<a name="SXTpL"></a>
## insert
<a name="PF0xZ"></a>
### insert 包含的属性
- id:唯一标志
- parameterType:传入语句参数的完全限定名或别名,由于 Mybatis 可以推断出传入语句的具体参数,因此不建议配置
- flushCache:默认值为 true,即任何时候语句被调用都会清空一级缓存和二级缓存
- timeout:等待数据库返回结果的超时时间,超过即抛出异常
- statementType:STATEMENT/PREPARED/CALLABLE,Mybatis 采用的预编译方式,默认为 PREPARED
- useGeneratedKeys:默认值为 false,若设为 true,MyBatis 会使用 JDBC 的 getGeneratedKeys 方法取出由数据库内部生成的主键
- keyProperty:将 useGeneratedKeys 获取的主键值赋值给 keyProperty 指定的属性名
- keyColumn:仅 INSERT 和 UPDATE 有效。通过 useGeneratedKeys 生成的键值设置表中的列名
- databaseId:若配置了 databaseIdProvider
注意:对于特殊的数据类型,建议指明 jdbcType 的值,防止类型错误(BLOB、TIMESTAMP)
<a name="S56Km"></a>
### 插入之后返回主键的方式
- 使用 useGeneratedKeys
```java
<insert id="insert2" useGeneratedKeys="true" keyProperty="id">
insert into sys_user(
user_name, user_password,
<if test="userEmail != null">
<if test="userEmail != ''">
user_email,
</if>
</if>
user_info, head_img, create_time)
values(
#{userName}, #{userPassword},
<if test="userEmail != null">
<if test="userEmail != ''">
#{userEmail},
</if>
</if>
#{userInfo}, #{headImg, jdbcType=BLOB}, #{createTime, jdbcType=TIMESTAMP})
</insert>
- 使用 selectKey
order 值在 MySQL 中是 AFTER ,在 Oracle 是 BEFORE,因为 Oracle 是先获取值,再插入,并且写插入语句的时候,必须指定 id,否则会抛出主键不为空的异常。
<insert id="insert3">
insert into sys_user(
user_name, user_password, user_email,
user_info, head_img, create_time)
values(
#{userName}, #{userPassword}, #{userEmail},
#{userInfo}, #{headImg, jdbcType=BLOB}, #{createTime, jdbcType=TIMESTAMP})
<selectKey keyColumn="id" resultType="long" keyProperty="id" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
</insert>
接口包含多个参数场景
建议使用 @Param ,给参数配置@Param注解后,MyBatis 就会自动将参数封装成 Map 类型,@Param 注解值
会作为 Map 中的 key,因此在 SQL 部分就可以通过配置的注解值来使用参数。
List
动态 SQL
where、if 标签配合使用实现动态查询
- 当 if 条件都不满足,SQL 中不会加入 where
- 当 if 条件有满足项,where 标签会自动去除语句第一个 and
<select id="selectByUser" resultType="tk.mybatis.simple.model.SysUser">
select id,
user_name userName,
user_password userPassword,
user_email userEmail,
user_info userInfo,
head_img headImg,
create_time createTime
from sys_user
<where>
<if test="@tk.mybatis.util.StringUtil@isNotEmpty(userName)">
and user_name like concat('%', #{userName}, '%')
</if>
<if test="userEmail != null and userEmail != ''">
and user_email = #{userEmail}
</if>
</where>
</select>
insert if 标签在列部分和values部分需要同时加上,保证上下可以互相对应 完全匹配
<insert id="insert2" useGeneratedKeys="true" keyProperty="id">
insert into sys_user(
user_name, user_password,
<if test="userEmail != null">
<if test="userEmail != ''">
user_email,
</if>
</if>
user_info, head_img, create_time)
values(
#{userName}, #{userPassword},
<if test="userEmail != null">
<if test="userEmail != ''">
#{userEmail},
</if>
</if>
#{userInfo}, #{headImg, jdbcType=BLOB}, #{createTime, jdbcType=TIMESTAMP})
</insert>
choose when otherwise 实现条件选择
<select id="selectByIdOrUserName" resultType="tk.mybatis.simple.model.SysUser">
select id,
user_name userName,
user_password userPassword,
user_email userEmail,
user_info userInfo,
head_img headImg,
create_time createTime
from sys_user
where 1 = 1
<choose>
<when test="id != null">
and id = #{id}
</when>
<when test="userName != null and userName != ''">
and user_name = #{userName}
</when>
<otherwise>
limit 0
</otherwise>
</choose>
</select>
set 标签
- set 标签会自动去除最后字符串结尾的 逗号
- 注意:若 if 条件都不满足,结尾还是要加上一个更新语句 id = #{id},避免 SQL 错误
<update id="updateByIdSelective">
update sys_user
<set>
<if test="userName != null and userName != ''">
user_name = #{userName},
</if>
<if test="userPassword != null and userPassword != ''">
user_password = #{userPassword},
</if>
<if test="userEmail != null and userEmail != ''">
user_email = #{userEmail},
</if>
<if test="userInfo != null and userInfo != ''">
user_info = #{userInfo},
</if>
<if test="headImg != null">
head_img = #{headImg, jdbcType=BLOB},
</if>
<if test="createTime != null">
create_time = #{createTime, jdbcType=TIMESTAMP},
</if>
id = #{id}
</set>
where id = #{id}
</update>
foreach 实现 in 集合
collection 必填,值为要迭代循环的属性名
- 当参数类型为 list 时,collection = list;当参数类型为 数组 时, collection = array;
```xml
List
selectByIdList(List idList);
```
- 当参数类型为 list 时,collection = list;当参数类型为 数组 时, collection = array;
```xml
List
foreach 批量插入 ```xml int insertList(List
userList);
#{user.userName}, #{user.userPassword},#{user.userEmail},
#{user.userInfo}, #{user.headImg, jdbcType=BLOB}, #{user.createTime, jdbcType=TIMESTAMP})
</foreach>
</insert>
- foreach 动态 update
```xml
int updateByMap(Map<String, Object> map);
<update id="updateByMap">
update sys_user
set
<foreach collection="_parameter" item="val" index="key" separator=",">
${key} = #{val}
</foreach>
where id = #{id}
</update>
- bind 标签创建一个变量,供上下文使用
<if test="userName != null and userName != ''">
<bind name="userNameLike" value = "'%' + userName + '%'"/>
and user_name = #{userNameLike}
</if>
MyBatis 注解方式的基本用法
MyBatis注解方式就是将SQL语句直接写在接口上。这种方式的优点是,对于需求比较简单的系统,效率较高。
缺点是,当SQL有变化时都需要重新编译代码,一般情况下不建议使用注解方式。
@Select
- 当列名和属性名不一致时,需要使用 @Results 进行映射
@Results 实现其实就是 resultMap,也可以根据 id 进行引用
@Results(id = "roleResultMap", value = {
@Result(property = "id", column = "id", id = true),
@Result(property = "roleName", column = "role_name"),
@Result(property = "enabled", column = "enabled"),
@Result(property = "createBy", column = "create_by"),
@Result(property = "createTime", column = "create_time")
})
@Select("select id,role_name, enabled, create_by, create_time from sys_role where id = #{id}")
SysRole selectById2(Long id);
@ResultMap("roleResultMap")
@Select("select * from sys_role")
List<SysRole> selectAll();
insert
使用 @Options(useGeneratedKeys = true, keyProperty = “id”) 返回自增主键
- 使用 @SelectKey(statement = “SELECT LAST_INSERT_ID()”, keyProperty = “id”, resultType = Long.class, before = false) 返回非自增主键
- before = false 等同于 XML 配置方式下面的 order=”AFTER”
```xml
@Insert({“insert into sys_role(id, role_name, enabled, create_by, create_time)”,
int insert(SysRole sysRole);"values(#{id}, #{roleName}, #{enabled}, #{createBy}, #{createTime, jdbcType=TIMESTAMP})"})
- before = false 等同于 XML 配置方式下面的 order=”AFTER”
```xml
@Insert({“insert into sys_role(id, role_name, enabled, create_by, create_time)”,
// 返回自增主键 @Insert({“insert into sys_role(role_name, enabled, create_by, create_time)”, “values(#{roleName}, #{enabled}, #{createBy}, #{createTime, jdbcType=TIMESTAMP})”}) @Options(useGeneratedKeys = true, keyProperty = “id”) int insert2(SysRole sysRole);
// 返回非自增主键 before = false 就是前面 XML 中 order = AFTER @Insert({“insert into sys_role(role_name, enabled, create_by, create_time)”, “values(#{roleName}, #{enabled}, #{createBy}, #{createTime, jdbcType=TIMESTAMP})”}) @SelectKey(statement = “SELECT LAST_INSERT_ID()”, keyProperty = “id”, resultType = Long.class, before = false) int insert3(SysRole sysRole);
<a name="MrNKd"></a>
## Provider 注解
自定义一个类,然后专门写 SQL。Mybatis 提供了 @SelectProvider、@InsertProvider、@UpdateProvider、@DeleteProvider 4 个 provider 实现查询、插入、更新、删除操作
注意:
provider 主键包含 type 和 method 两个必填属性;type 指定的类必须包含一个空的构造方法,method 指定的方法必须返回 String 类型
```xml
@SelectProvider(type = PrivilegeProvider.class, method = "selectById")
SysPrivilege selectById(Long id);
public class PrivilegeProvider {
// 返回 String 类型
public String selectById(final Long id){
return new SQL(){
{
SELECT("id, privilege_name, privilege_url");
FROM("sys_privilege");
WHERE("id = #{id}");
}
}.toString();
}
}
MyBatis 高级查询
高级结果映射
鉴别器映射
discriminator
存储过程
存储过程不支持 二级缓存,所以设置 useCache = false
mode:IN 入参、OUT 出参(必须指定 jdbcType)、INOUT 输入输出参数
javaType:BLOB 类型对应 byte[] 数组,而 MyBatis 映射 Java 类不推荐基本数据类型,
void selectUserById(SysUser user);
<select id="selectUserById" statementType="CALLABLE" useCache="false">
{call select_user_by_id(
#{id, mode=IN},
#{userName, mode=OUT, jdbcType=VARCHAR},
#{userPassword, mode=OUT, jdbcType=VARCHAR},
#{userEmail, mode=OUT, jdbcType=VARCHAR},
#{userInfo, mode=OUT, jdbcType=VARCHAR},
#{headImg, mode=OUT, jdbcType=BLOB, javaType=_byte[]},
#{createTime, mode=OUT, jdbcType=TIMESTAMP}
)}
</select>
枚举
MyBatis 缓存配置
一级缓存
MyBatis的一级缓存存在于sqlSession的生命周期中,在同一个sqlSession中查询时,MyBatis会把执行的方法和参数通过算法生成缓存的键值,将键值和查询结果存入一一个Map对象中。如果同一个sqlSession中执行的方法和参数完全- - 致,那么通过算法会生成相同的键值,当Map缓存对象中已经存在该键值时,则会返回缓存中的对象。
二级缓存
存在于 SqlSessionFactory 生命周期中,首先开启全局配置,默认是打开的,一般不用显示声明。
<configuration>
<settings>
<setting name="cacheEnabled" value="true"/> 默认值 true
</settings>
</configuration>
MyBatis 二级缓存与命名空间进行绑定,所以二级缓存需要配置到 Maper.xml 映射文件或者 Mapper.java 文件中。
- 在 mapper 文件中加入
元素开启二级缓存 <mapper namespace="tk.mybatis.simple.mapper.UserMapper">
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
</mapper>
cache 属性
- eviction 收回策略
- LRU ,默认值
- FIFO
- SOFT 软引用,移除基于垃圾回收器状态和软引用规则的对象
- WEAK 弱引用,更积极地移除基于垃圾回收器状态和软引用规则的对象
- flushInterval 刷新间隔,可以设置任意正整数,单位为 ms。默认情况下不设置,缓存仅调用语句时更新
- size 引用数目,要记住的缓存对象数目和运行环境可用内存资源数目,默认为 1024
readOnly,可以设置为 true or false,只读缓存只能读不能修改,默认为 false
接口中配置缓存
@CacheNamespace
public interface RoleMapper {
}
如果接口和XML同时使用,那么需要改为参照缓存,避免冲突
@CacheNamespaceRef(RoleMapper.class)
public interface RoleMapper {
}
二级缓存脏读
二级缓存事基于命名空间的,多表查询的时候,可能另外的操作更新了,查询到脏数据
可以使用参照缓存解决脏数据问题,但是这样做在表连接较多的时候就很麻烦
<mapper namespace="tk.mybatis.simple.mapper.UserMapper">
<cache-ref namespace="tk.mybatis.simple.mapper.RoleMapper"/>
</mapper>
二级缓存适用场景
- 查询为主的应用
- 绝大部分都是单表操作,若关联表比较少,可以使用参照缓存
MyBatis 插件开发
拦截器接口
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
Object plugin(Object target);
void setProperties(Properties properties);
}
拦截器实现
@Intercepts 注解的属性事一个 @Signature 数组,可以在同一个拦截器中拦截不同的接口和方法
@Signature 属性
- type:设置拦截的接口,可选值是 Executor 、ParameterHandler、ResultSetHandler、StatmentHandler
- method:接口中的方法名称,即 type 中接口中定义的方法
args:拦截方法的参数类型数组
@Intercepts(
@Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class,
RowBounds.class, ResultHandler.class}
)
)
public class PageInterceptor implements Interceptor {
拦截器接口触发时机
Executor ```java public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;
int update(MappedStatement ms, Object parameter) throws SQLException;
List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException; List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException; Cursor queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException; List
flushStatements() throws SQLException; void commit(boolean required) throws SQLException;
void rollback(boolean required) throws SQLException;
CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
boolean isCached(MappedStatement ms, CacheKey key);
void clearLocalCache();
void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
Transaction getTransaction();
void close(boolean forceRollback);
boolean isClosed();
void setExecutorWrapper(Executor executor);
}
- ResultSetHandler
```java
public interface ResultSetHandler {
//该方法会在除存储过程及返回值类型为
Cursor<T>(org.apache.ibatis.cursor.Cursor<T>)以外的查询方法中被调用
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
// 该方法是3.4.0版本中新增加的,只会在返回值类型为Cursor<T>的查询方法中被调用,
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
// 该方法只在使用存储过程处理出参时被调用
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
StatementHandler ```java public interface StatementHandler {
// 该方法会在数据库执行前被调用,优先于当前接口中的其他方法而被执行 Statement prepare(Connection connection, Integer transactionTimeout)
throws SQLException;
// 该方法在prepare方法之后执行,用于处理参数信息 void parameterize(Statement statement)
throws SQLException;
// 在全局设置配置defaultExecutorType=”BATCH”时,执行数据操作才会调用该方法, void batch(Statement statement)
throws SQLException;
int update(Statement statement)
throws SQLException;
// 执行SELECT方法时调用
List query(Statement statement, ResultHandler resultHandler) throws SQLException;
// 该方法是3.4.0版本中新增加的,只会在返回值类型为Cursor
的查询中被调用 Cursor queryCursor(Statement statement) throws SQLException;
BoundSql getBoundSql();
ParameterHandler getParameterHandler();
}
- ParameterHandler
```java
public interface ParameterHandler {
Object getParameterObject();
void setParameters(PreparedStatement ps)
throws SQLException;
}