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 createTimefrom sys_user</select>
关联查询结果嵌套
一次 SQL 查询根据表或指定的属性映射到不同的对象中,对象里面嵌套对象,使用 属性.属性 进行赋值,注意双引号
缺点:SQL 比较复杂,不容易写对;嵌套结果应用到不同的类上,增加服务器压力
<select id="selectRolesByUserId" resultType="tk.mybatis.simple.model.SysRole">selectr.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 uinner join sys_user_role ur on u.id = ur.user_idinner join sys_role r on ur.role_id = r.idwhere 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">selectu.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_timefrom sys_user uinner join sys_user_role ur on u.id = ur.user_idinner join sys_role r on ur.role_id = r.idwhere 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">selectu.id,u.user_name,u.user_password,u.user_email,u.user_info,u.head_img,u.create_time,ur.role_idfrom sys_user uinner join sys_user_role ur on u.id = ur.user_idwhere 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 createTimefrom 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 createTimefrom sys_userwhere 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```xmlint updateByMap(Map<String, Object> map);<update id="updateByMap">update sys_userset<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"><cacheeviction="FIFO"flushInterval="60000"size="512"readOnly="true"/></mapper>
cache 属性
- eviction 收回策略
- LRU ,默认值
- FIFO
- SOFT 软引用,移除基于垃圾回收器状态和软引用规则的对象
- WEAK 弱引用,更积极地移除基于垃圾回收器状态和软引用规则的对象
- flushInterval 刷新间隔,可以设置任意正整数,单位为 ms。默认情况下不设置,缓存仅调用语句时更新
- size 引用数目,要记住的缓存对象数目和运行环境可用内存资源数目,默认为 1024
readOnly,可以设置为 true or false,只读缓存只能读不能修改,默认为 false
接口中配置缓存
@CacheNamespacepublic 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```javapublic 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```javapublic interface ParameterHandler {Object getParameterObject();void setParameters(PreparedStatement ps)throws SQLException;}
