1,概念
MyBatis是一个数据持久层(ORM)框架,封装了jdbc。把实体类和SQL语句之间建立了映射关系,是一种半自动化的ORM实现。MyBATIS需要开发人员自己来写sql语句,这可以增加了程序的灵活性,在一定程度上可以作为ORM的一种补充。
是由Apache开源项目iBatis3迁移到Github,命名为MyBatis。
1)优点
- 支持定制化SQL、存储过程及高级映射;
sql写在xml中,解耦、并可重用。 - 避免了几乎所有的JDBC代码和手动设置参数以及获取结果集;
- 可以使用简单的XML或注解用于配置和原始映射,将接口和java的POJO映射成数据库种的记录;
- 能与各种数据库兼容;
mybatis使用jdbc来连接数据库,只要jdbc支持的数据库mybatis也都支持。
2)缺点
- sql编写工作量大,复杂查询要求有一定的sql功底;
- sql语句依赖于具体的数据库,移植性差。
3)和其他持久化层技术对比
ORM(Object Relation Mapping)
O :object 对象
R: relation 关系
M: mapping 映射
ORM的作用就是把类转成SQL语句、可以把SQL语句转成类
- JDBC
SQL夹杂在java代码中耦合度高,导致硬编码内伤;
维护困难不易修改;
代码冗长,开发效率低。 - Hibernate和JPA(全自动的ORM框架)
操作简单,开发效率高。
但过于复杂的sql需要绕过框架,完全由框架内部产生sql不易特殊化吹;
反射操作太多导致数据库性能下降。 - MyBatis(半自动的ORM框架)
性能出色(sql优化优势更大);开发效率逊色Hibernate和JPA。
sql和java编码分开,功能边界清晰。==》java做业务,sql做数据。
4)字符转义:<![CDATA[ ]]>
使用<![CDATA[]]>来包含不被xml解析器解析的内容:”<”和”&”。但要注意的是:
(1) 此部分不能再包含”]]>”;
(2) 不允许嵌套使用;
(3)”]]>”这部分不能包含空格或者换行。
比如<![CDATA[<]]> 表示文本内容“<”
5)类型映射
1>基本类型映射
JDBC Type | Java Type | 备注 |
---|---|---|
CHAR | String | |
VARCHAR | String | |
LONGVARCHAR | String | |
NUMERIC | java.math.BigDecimal | |
DECIMAL | java.math.BigDecimal | |
BIT | boolean | |
BOOLEAN | boolean | |
TINYINT | byte | |
SMALLINT | short | |
INTEGER | int | |
BIGINT | long | |
REAL | float | |
FLOAT | double | |
DOUBLE | double | |
BINARY | byte[] | |
VARBINARY | byte[] | |
LONGVARBINARY | byte[] | |
DATE | java.sql.Date | |
TIME | java.sql.Time | |
TIMESTAMP | java.sql.Timestamp | |
CLOB | Clob | |
BLOB | Blob | |
ARRAY | Array | |
DISTINCT | mapping of underlying type | |
STRUCT | Struct | |
REF | Ref | |
DATALINK | java.net.URL[color=red][/color] | |
OTHER | TypeHandler(类型处理器)来自定义 | 数组: org.apache.ibatis.type.ArrayTypeHandler 举例: <result column="ip_list" property="ipList" typeHandler="org.apache.ibatis.type.ArrayTypeHandler" /> |
2>自定义TypeHandler
自定义的方式有两种,一种是实现TypeHandler这个接口,另一个就是继承BaseTypeHandler这个便捷的抽象类。
举例:年龄的类型处理器。
- 定义类型处理器;
//指定与其关联的JDBC 类型列表。如果在jdbcType 属性中也同时指定,则注解上的配置将被忽略。
@MappedJdbcTypes(JdbcType.INTEGER)
//指定与其关联的 Java类型列表。如果在javaType 属性中也同时指定,则注解上的配置将被忽略。
@MappedTypes(String.class)
public class GenderTypeHandler extends BaseTypeHandler {
//设置参数,这里将Java的String类型转换为JDBC的Integer类型
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws
SQLException {
ps.setInt(i, StringUtils.equals(parameter.toString(),"男")?1:2);
}
//以下三个参数都是将查询的结果转换
@Override
public Object getNullableResult(ResultSet rs, String columnName) throws SQLException {
return rs.getInt(columnName)==1?"男":"女";
}
@Override
public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return rs.getInt(columnIndex)==1?"男":"女";
}
@Override
public Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return cs.getInt(columnIndex)==1?"男":"女";
}
}
- 在配置文件application.properties中添加一行配置:
将typeHandler注入到mybatis中。
## 设置自定义的Typehandler所在的包,启动的时候会自动扫描配置到Mybatis中
mybatis.type-handlers-package=cn.cb.demo.typehandler
- xml引用——更新
<insert id="insertUser">
insert into user_info(user_id, gender)
values(
#{userId,jdbcType=VARCHAR},
#{gender,jdbcType=INTEGER,typeHandler=cn.cb.demo.typehandler.GenderTypeHandler})
</insert>
- xml引用——查询
<resultMap id="userResultMap" type="cn.cb.demo.domain.UserInfo">
<result column="user_id" property="userId"/>
<!-- 指定typeHandler属性为全类名-->
<result column="gender" property="gender" typeHandler="cn.cb.demo.typehandler.GenderTypeHandler"/>
<!-- jsonb类型-->
<result column="search_content" jdbcType="OTHER" property="searchContent" typeHandler="com.common.JsonbMapTypeHandler"/>
</resultMap>
3>demo——JsonListTypeHandler
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.ibatis.executor.result.ResultMapException;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeException;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
//注意,此处用泛型,如果传值过小,会用List<Integer>接数据,用List<Long>会报错,可以用Number接数据,也可以重新写一个TypeHandler(继承BaseTypeHandler<List<Long>>,重写getNullableResult方法:result = value == null ? null : JSON.parseObject(value, new TypeReference<List<Long>>(){});)
public class JsonListTypeHandler extends BaseTypeHandler<List<?>> {
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, List<?> objects, JdbcType jdbcType) throws SQLException {
if (objects == null) {
try {
preparedStatement.setNull(i, JdbcType.OTHER.TYPE_CODE);
} catch (SQLException e) {
throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . "
+ "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. "
+ "Cause: " + e, e);
}
} else {
try {
preparedStatement.setObject(i, JSONObject.toJSONString(objects), JdbcType.OTHER.TYPE_CODE);
} catch (Exception e) {
throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType
+ " . "
+ "Try setting a different JdbcType for this parameter or a different configuration property. "
+ "Cause: " + e, e);
}
}
}
@Override
public List<?> getNullableResult(ResultSet resultSet, String s) throws SQLException {
List<?> result;
try {
String value = resultSet.getString(s);
result = value == null ? null : JSONObject.parseObject(value, List.class);
} catch (Exception e) {
throw new ResultMapException(
"Error attempting to get column '" + s + "' from result list. Cause: " + e, e);
}
if (resultSet.wasNull()) {
return new ArrayList<>();
} else {
return result;
}
}
@Override
public List<?> getNullableResult(ResultSet resultSet, int i) throws SQLException {
List<?> result;
try {
String value = resultSet.getString(i);
result = value == null ? null : JSONObject.parseObject(value, List.class);
} catch (Exception e) {
throw new ResultMapException(
"Error attempting to get column #" + i + " from result list. Cause: " + e, e);
}
if (resultSet.wasNull()) {
return new ArrayList<>();
} else {
return result;
}
}
@Override
public List<?> getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
List<?> result;
try {
String value = callableStatement.getString(i);
result = value == null ? null : JSONObject.parseObject(value, List.class);
} catch (Exception e) {
throw new ResultMapException(
"Error attempting to get column #" + i + " from callable statement. Cause: " + e, e);
}
if (callableStatement.wasNull()) {
return new ArrayList<>();
} else {
return result;
}
}
}
2,使用
mybatis的sql操作有三种:
- 注解;
- xml;
- QueryWrapper + xml (推荐)
1)引入依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>