1、前言
Mybati虽然极大的提高了程序员对数据库的操作, 但还是存在以下痛点:
1、mapper.xml文件里有大量的sql,当数据库表字段变动,配置文件就要修改;
2、需要自己实现sql分页,select * from table where . . . limit 1,3;
3、数据库可移植性差:如果项目更换数据库,比如oracle–>mysql,mapper.xml中的sql要重新写,因为Oracle的PLSQL 和mysql 支持的函数是不同的;
4、生成的代码量过大;
5、批量操作,批量插入,批量更新,需要自写。
2、什么是通用mapper
通用mapper的作用就是自动生成我们常用的增删改查SQL语句。是中国程序员在 MBG 的基础上结合了部分 JPA 注解做出来的。
Github地址:https://gitee.com/free/Mapper.git
3、使用流程示例
先创建一个数据库用于案例测试:
CREATE TABLE `tabple_emp` (
`emp_id` int NOT NULL AUTO_INCREMENT ,
`emp_name` varchar(500) NULL ,
`emp_salary` double(15,5) NULL ,
`emp_age` int NULL ,
PRIMARY KEY (`emp_id`)
);
INSERT INTO `tabple_emp` (`emp_name`, `emp_salary`, `emp_age`) VALUES ('tom', '1254.37', '27');
INSERT INTO `tabple_emp` (`emp_name`, `emp_salary`, `emp_age`) VALUES ('jerry', '6635.42', '38');
INSERT INTO `tabple_emp` (`emp_name`, `emp_salary`, `emp_age`) VALUES ('bob', '5560.11', '40');
INSERT INTO `tabple_emp` (`emp_name`, `emp_salary`, `emp_age`) VALUES ('kate', '2209.11', '22');
INSERT INTO `tabple_emp` (`emp_name`, `emp_salary`, `emp_age`) VALUES ('justin', '4203.15', '30');
3.1 导入依赖
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>最新版本</version>
</dependency>
最新版本可以去maven中央仓库查看:
3.2 编写实体类
public class Employee {
private Integer empId;
private String empName;
private Double empSalary;
private Integer empAge;
// ..有参构造、无参构造、set、get、toString
}
3.3 创建Mapper
import tk.mybatis.mapper.common.Mapper;
public interface EmployeeMapper extends Mapper<Employee> {
}
这里继承了 tk.mybatis.mapper.common.Mapper
接口,在接口上指定了泛型类型 Employee。当你继承了 Mapper 接口后,此时就已经有了针对 Employee的大量方法,方法如下:
3.4 配置通用Mapper
为了让我们自己创建的Mapper成为通用Mapper,让项目在启动的时候,把上述方法都自动生成好,这样在运行时就可以使用上面所有的方法。
根据不同的开发环境,需要不同的配置方式,完整的内容可以 在https://github.com/abel533/Mapper/wiki/1.integration中查看文档,我们这里以最常见的 Spring 和 MyBatis 集成为例。
在集成 Spring 的环境中使用 MyBatis 接口方式时,需要配置 MapperScannerConfigurer,在这种情况下使用通用 Mapper,只需要修改spring配置文件如下:
<!-- 整合通用Mapper所需要做的配置修改: -->
<!-- 原始全类名:org.mybatis.spring.mapper.MapperScannerConfigurer -->
<!-- 通用Mapper使用:tk.mybatis.spring.mapper.MapperScannerConfigurer -->
<bean class="tk.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.klb.mapper.mappers"/>
</bean>
<property>
的value为我们创建的Mapper的所在包。
3.5 测试
编写测试方法来访问数据库:
service:
@Service
public class EmployeeService {
@Autowired
public EmployeeMapper employeeMapper;
public Employee getOne(Employee employeeQueryCondition) {
return employeeMapper.selectOne(employeeQueryCondition);
}
}
public class EmployeeMapperTest {
private ApplicationContext iocContainer = new ClassPathXmlApplicationContext("spring-context.xml");
private EmployeeService employeeService = iocContainer.getBean(EmployeeService.class);
@Test
public void testSelectOne() {
//1.创建封装查询条件的实体类对象
Employee employeeQueryCondition = new Employee(null, "bob", 5560.11, null);
//2.执行查询
Employee employeeQueryResult = employeeService.getOne(employeeQueryCondition);
//3.打印
System.out.println(employeeQueryResult);
}
}
4、常用注解
4.1 @Table
作用:建立实体类和数据库表之间的对应关系。
默认规则:实体类类名首字母小写作为表名。Employee 类→employee 表。
用法:在@Table 注解的 name 属性中指定目标数据库表的表名。
4.2 @Column
作用:建立实体类字段和数据库表字段之间的对应关系。
默认规则:
实体类字段:驼峰式命名
数据库表字段:使用“_”区分各个单词
用法:在@Column 注解的 name 属性中指定目标字段的字段名。
4.3 @Id
通用 Mapper 在执行 xxxByPrimaryKey(key)
方法时,有两种情况:
情况 1:没有使用@Id 注解明确指定主键字段,生成的sql语句如下:
SELECT emp_id,emp_name,emp_salary_apple,emp_age FROM tabple_emp WHERE emp_id = ? AND emp_name = ? AND emp_salary_apple = ? AND emp_age = ?
之所以会生成上面这样的 WHERE 子句是因为通用 Mapper 将实体类中的所有字段都拿来放在一起作为联合主键。
情况 2:使用@Id
主键明确标记和数据库表中主键字段对应的实体类字段。
4.4 @GeneratedValue
作用:让通用 Mapper 在执行 insert 操作之后将数据库自动生成的主键值回写到实体类对象中。
自增主键用法:
序列主键用法:
4.5 @Transient
用于标记不与数据库表字段对应的实体类字段。
@Transient
private String otherThings; //非数据库表中字段
5、常用方法
5.1 selectOne
通用 Mapper 替我们自动生成的 SQL 语句情况:
实体类封装查询条件生成 WHERE 子句的规则:
1、使用非空的值生成 WHERE 子句;
2、在条件表达式中使用“=”进行比较。
5.2 xxxByPrimaryKey
需要使用@Id
主键明确标记和数据库表主键字段对应的实体类字段,否则通用Mapper 会将所有实体类字段作为联合主键。
5.3 xxxSelective
非主键字段如果为 null 值,则不加入到 SQL 语句中。
6、QBC查询
QBC全称为Query By Criteria,Criteria 是 Criterion 的复数形式。意思是:规则、标准、准则。在 SQL 语句中相当于查询条件。
QBC 查询是将查询条件通过 Java 对象进行模块化封装。
示例代码:
@Test
public void testSelectByExample() {
//目标:WHERE (emp_salary>? AND emp_age<?) OR (emp_salary<? AND emp_age>?)
//1.创建Example对象
Example example = new Example(Employee.class);
//***********************
//i.设置排序信息
example.orderBy("empSalary").asc().orderBy("empAge").desc();
//ii.设置“去重”
example.setDistinct(true);
//iii.设置select字段
example.selectProperties("empName","empSalary");
//***********************
//2.通过Example对象创建Criteria对象
Example.Criteria criteria01 = example.createCriteria();
Example.Criteria criteria02 = example.createCriteria();
//3.在两个Criteria对象中分别设置查询条件
//property参数:实体类的属性名
//value参数:实体类的属性值
criteria01.andGreaterThan("empSalary", 3000)
.andLessThan("empAge", 25);
criteria02.andLessThan("empSalary", 5000)
.andGreaterThan("empAge", 30);
//4.使用OR关键词组装两个Criteria对象
example.or(criteria02);
//5.执行查询
List<Employee> empList = employeeService.getEmpListByExample(example);
for (Employee employee : empList) {
System.out.println(employee);
}
}
7、MBG
7.1 介绍
MBG全称为Mybatis Generator,用于根据数据表生成Mybatis所需的实体类、配置文件和Mapper接口。
通用 Mapper 专用代码生成器生成的 Model 会在原有基础上增加 @Table
,@Id
,@Column
等注解,方便自动会数据库字段进行映射。
参考文档:https://github.com/abel533/Mapper/wiki/4.1.mappergenerator
7.2 使用方法
7.2.1 导入依赖
<!--MBG核心-->
<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.6</version>
</dependency>
<!-- 通用 Mapper -->
<!-- https://mvnrepository.com/artifact/tk.mybatis/mapper -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>4.0.0</version>
</dependency>
<!-- 如果你只需要用到通用 Mapper 中的插件,可以只引入 mapper-generator -->
<!-- 注意,这个包不需要和上面的 mapper 同时引入,mapper 中包含 generator -->
<!-- https://mvnrepository.com/artifact/tk.mybatis/mapper-generator -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-generator</artifactId>
<version>1.0.0</version>
</dependency>
同时在pom.xml文件中配置一些属性:
<properties>
<!-- ${basedir}引用工程根目录 -->
<!-- targetJavaProject:声明存放源码的目录位置 -->
<targetJavaProject>${basedir}/src/main/java</targetJavaProject>
<!-- targetMapperPackage:声明MBG生成XxxMapper接口后存放的package位置 -->
<targetMapperPackage>cn.klb.shop.mappers</targetMapperPackage>
<!-- targetModelPackage:声明MBG生成实体类后存放的package位置 -->
<targetModelPackage>cn.klb.shop.entities</targetModelPackage>
<!-- targetResourcesProject:声明存放资源文件和XML配置文件的目录位置 -->
<targetResourcesProject>${basedir}/src/main/resources</targetResourcesProject>
<!-- targetXMLPackage:声明存放具体XxxMapper.xml文件的目录位置 -->
<targetXMLPackage>mappers</targetXMLPackage>
<!-- 通用Mapper的版本号 -->
<mapper.version>4.0.0-beta3</mapper.version>
<!-- MySQL驱动版本号 -->
<mysql.version>5.1.37</mysql.version>
</properties>
7.2.2 编写MBG配置文件
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<!--suppress MybatisGenerateCustomPluginInspection -->
<generatorConfiguration>
<context id="Mysql" targetRuntime="MyBatis3Simple" defaultModelType="flat">
<property name="beginningDelimiter" value="`" />
<property name="endingDelimiter" value="`" />
<plugin type="tk.mybatis.mapper.generator.MapperPlugin">
<property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
<property name="caseSensitive" value="true"/>
<property name="forceAnnotation" value="true"/>
<property name="beginningDelimiter" value="`"/>
<property name="endingDelimiter" value="`"/>
</plugin>
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/common_mapper?useUnicode=true&characterEncoding=utf8"
userId="root"
password="root">
</jdbcConnection>
<!-- 配置Java实体类存放位置 -->
<javaModelGenerator
targetPackage="${targetModelPackage}"
targetProject="${targetJavaProject}" />
<!-- 配置XxxMapper.xml存放位置 -->
<sqlMapGenerator
targetPackage="${targetXMLPackage}"
targetProject="${targetResourcesProject}" />
<!-- 配置XxxMapper.java存放位置 -->
<javaClientGenerator
targetPackage="${targetMapperPackage}"
targetProject="${targetJavaProject}"
type="XMLMAPPER" />
<table tableName="tabple_emp" domainObjectName="Employee">
<!-- 配置主键生成策略 -->
<generatedKey column="emp_id" sqlStatement="Mysql" identity="true" />
</table>
</context>
</generatorConfiguration>
7.3 配置Generator插件
配置插件很简单,就是配置maven插件,在pom.xml文件中添加:
<build>
<plugins>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.6</version>
<configuration>
<configurationFile>
${basedir}/src/main/resources/generator/generatorConfig.xml
</configurationFile>
<overwrite>true</overwrite>
<verbose>true</verbose>
</configuration>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.29</version>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>4.0.0</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
8、自定义Mapper
8.1 通用Mapper继承关系
我们可以根据开发的实际需要对 Mapper接口进行定制。
下图是tk.mybatis.mapper.common.Mapper
的继承图:
有时候我们的业务不需要那么多预提供的方法,所以我们可以继承相应的父接口。
8.2 自定义
创建一个目录专门存放自定义的Mapper:
package cn.klb.mapper.mine_mappers;
import tk.mybatis.mapper.common.base.select.SelectAllMapper;
import tk.mybatis.mapper.common.example.SelectByExampleMapper;
public interface MyMapper<T>
extends SelectAllMapper<T>,SelectByExampleMapper<T> {
}
使用自定义的Mapper:
package cn.klb.mapper.mappers;
import cn.klb.mapper.entities.Employee;
import cn.klb.mapper.mine_mappers.MyMapper;
public interface EmployeeMapper extends MyMapper<Employee> {
}
spring的配置文件中:
<!-- 整合通用Mapper所需要做的配置修改: -->
<!-- 原始全类名:org.mybatis.spring.mapper.MapperScannerConfigurer -->
<!-- 通用Mapper使用:tk.mybatis.spring.mapper.MapperScannerConfigurer -->
<bean class="tk.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.klb.mapper.mappers"/>
</bean>
修改为:
<!-- 整合通用Mapper所需要做的配置修改: -->
<!-- 原始全类名:org.mybatis.spring.mapper.MapperScannerConfigurer -->
<!-- 通用Mapper使用:tk.mybatis.spring.mapper.MapperScannerConfigurer -->
<bean class="tk.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.klb.mapper.mappers"/>
<property name="properties">
<value>
mappers=cn.klb.mapper.mine_mappers.MyMapper
</value>
</property>
</bean>
9、接口拓展
自定义Mapper是对已提供的方法进行重新组合了一下,不属于拓展。
下面来自定义一个Mapper实现批量更新操作。
拓展步骤如下:
9.1 导入依赖
<dependencies>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>4.0.0-beta3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
</dependencies>
9.2 编写MapperTemplate子类
自定义一个类,继承tk.mybatis.mapper.mapperhelper.MapperTemplate
:
package cn.klb.mapper.mine_mappers;
import java.util.Set;
import org.apache.ibatis.mapping.MappedStatement;
import tk.mybatis.mapper.entity.EntityColumn;
import tk.mybatis.mapper.mapperhelper.EntityHelper;
import tk.mybatis.mapper.mapperhelper.MapperHelper;
import tk.mybatis.mapper.mapperhelper.MapperTemplate;
import tk.mybatis.mapper.mapperhelper.SqlHelper;
public class MyBatchUpdateProvider extends MapperTemplate {
public MyBatchUpdateProvider(Class<?> mapperClass, MapperHelper mapperHelper) {
super(mapperClass, mapperHelper);
}
public String batchUpdate(MappedStatement statement) {
//1.创建StringBuilder用于拼接SQL语句的各个组成部分
StringBuilder builder = new StringBuilder();
//2.拼接foreach标签
builder.append("<foreach collection=\"list\" item=\"record\" separator=\";\" >");
//3.获取实体类对应的Class对象
Class<?> entityClass = super.getEntityClass(statement);
//4.获取实体类在数据库中对应的表名
String tableName = super.tableName(entityClass);
//5.生成update子句
String updateClause = SqlHelper.updateTable(entityClass, tableName);
builder.append(updateClause);
builder.append("<set>");
//6.获取所有字段信息
Set<EntityColumn> columns = EntityHelper.getColumns(entityClass);
String idColumn = null;
String idHolder = null;
for (EntityColumn entityColumn : columns) {
boolean isPrimaryKey = entityColumn.isId();
//7.判断当前字段是否为主键
if(isPrimaryKey) {
//8.缓存主键的字段名和字段值
idColumn = entityColumn.getColumn();
//※返回格式如:#{record.age,jdbcType=NUMERIC,typeHandler=MyTypeHandler}
idHolder = entityColumn.getColumnHolder("record");
}else {
//9.使用非主键字段拼接SET子句
String column = entityColumn.getColumn();
String columnHolder = entityColumn.getColumnHolder("record");
builder.append(column).append("=").append(columnHolder).append(",");
}
}
builder.append("</set>");
//10.使用前面缓存的主键名、主键值拼接where子句
builder.append("where ").append(idColumn).append("=").append(idHolder);
builder.append("</foreach>");
//11.将拼接好的字符串返回
return builder.toString();
}
}
其中最关键的就是batchUpdate
方法,里面编写了批量更新的逻辑,其实就是以前的mapper.xml中的sql语句拼接。
9.3 自定义Mapper
自定义一个Mapper,里面包含一个名字为batchUpdate
的方法,并使用@UpdateProvider
注解,属性type的值为第一步定义的类的类对象,method属性的值为固定值dynamicSQL
:
package cn.klb.mapper.mine_mappers;
import java.util.List;
import org.apache.ibatis.annotations.UpdateProvider;
public interface MyBatchUpdateMapper<T> {
@UpdateProvider(type=MyBatchUpdateProvider.class, method="dynamicSQL")
void batchUpdate(List<T> list);
}
9.4 使用
上面一步写好的自定义Mapper后,就可以按照通用Mapper的方式来使用,首先是继承这个自定义的Mapper:
package cn.klb.mapper.mine_mappers;
import tk.mybatis.mapper.common.base.select.SelectAllMapper;
import tk.mybatis.mapper.common.example.SelectByExampleMapper;
public interface MyMapper<T>
extends SelectAllMapper<T>,SelectByExampleMapper<T>,MyBatchUpdateMapper<T> {
}
然后使用MyMapper:
package cn.klb.mapper.mappers;
import cn.klb.mapper.entities.Employee;
import cn.klb.mapper.mine_mappers.MyMapper;
public interface EmployeeMapper extends MyMapper<Employee> {
}
然后修改spring的配置文件:
<!-- 整合通用Mapper所需要做的配置修改: -->
<!-- 原始全类名:org.mybatis.spring.mapper.MapperScannerConfigurer -->
<!-- 通用Mapper使用:tk.mybatis.spring.mapper.MapperScannerConfigurer -->
<bean class="tk.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.klb.mapper.mappers"/>
<property name="properties">
<value>
mappers=cn.klb.mapper.mine_mappers.MyMapper
</value>
</property>
</bean>
编写测试类:
@Test
public void testBatch(){
ClassPathXmlApplicationContext iocContainer = new ClassPathXmlApplicationContext("spring-context.xml");
EmployeeService employeeService = iocContainer.getBean(EmployeeService.class);
List<Employee> empList = new ArrayList<>();
empList.add(new Employee(9, "newName01", 111.11, 10));
empList.add(new Employee(10, "newName02", 222.22, 20));
empList.add(new Employee(11, "newName03", 333.33, 30));
employeeService.batchUpdateEmp(empList);
iocContainer.close();
}
9.5 小结
10、二级缓存
1、MyBatis 配置文件开启二级缓存功能
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
2、 在 XxxMapper 接口上使用@CacheNamespace
注解:
@CacheNamespace
public interface EmployeeMapper extends MyMapper<Employee> {
}
11、类型处理器
11.1 复杂类型
11.1.1 使用场景
如果我们的数据表对应的实体类是复杂类型:
@Table(name="table_user")
public class User {
@Id
private Integer userId;
private String userName;
private Address address;
private SeasonEnum season;
// set\get\toString...
}
其中Address
是复杂类型:
public class Address {
private String province;
private String city;
private String street;
// set\get\toString...
}
通用 Mapper 默认情况下会忽略复杂类型,对复杂类型不进行“从类到表”的映射。也就是说,如果实体类存在复杂类型,会赋值失败。
解决方法就是使用类型处理器。
11.1.2 定义类型处理器
自定义的类型处理器需要继承org.apache.ibatis.type.BaseTypeHandler
:
package cn.klb.mapper.handlers;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import cn.klb.mapper.entities.Address;
public class AddressTypeHandler extends BaseTypeHandler<Address> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Address address, JdbcType jdbcType) throws SQLException {
//1.对address对象进行验证
if(address == null) {
return ;
}
//2.从address对象中取出具体数据
String province = address.getProvince();
String city = address.getCity();
String street = address.getStreet();
//3.拼装成一个字符串
//规则:各个值之间使用“,”分开
StringBuilder builder = new StringBuilder();
builder.append(province).append(",").append(city).append(",").append(street);
String parameterValue = builder.toString();
//4.设置参数
ps.setString(i, parameterValue);
}
@Override
public Address getNullableResult(ResultSet rs, String columnName) throws SQLException {
//1.根据字段名从rs对象中获取字段值
String columnValue = rs.getString(columnName);
//2.验证columnValue是否有效
if(columnValue == null || columnValue.length() == 0 || !columnValue.contains(",")) {
return null;
}
//3.根据“,”对columnValue进行拆分
String[] split = columnValue.split(",");
//4.从拆分结果数组中获取Address需要的具体数据
String province = split[0];
String city = split[1];
String street = split[2];
//5.根据具体对象组装一个Address对象
Address address = new Address(province, city, street);
return address;
}
@Override
public Address getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
//1.根据字段名从rs对象中获取字段值
String columnValue = rs.getString(columnIndex);
//2.验证columnValue是否有效
if(columnValue == null || columnValue.length() == 0 || !columnValue.contains(",")) {
return null;
}
//3.根据“,”对columnValue进行拆分
String[] split = columnValue.split(",");
//4.从拆分结果数组中获取Address需要的具体数据
String province = split[0];
String city = split[1];
String street = split[2];
//5.根据具体对象组装一个Address对象
Address address = new Address(province, city, street);
return address;
}
@Override
public Address getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
//1.根据字段名从rs对象中获取字段值
String columnValue = cs.getString(columnIndex);
//2.验证columnValue是否有效
if(columnValue == null || columnValue.length() == 0 || !columnValue.contains(",")) {
return null;
}
//3.根据“,”对columnValue进行拆分
String[] split = columnValue.split(",");
//4.从拆分结果数组中获取Address需要的具体数据
String province = split[0];
String city = split[1];
String street = split[2];
//5.根据具体对象组装一个Address对象
Address address = new Address(province, city, street);
return address;
}
}
11.1.3 注册类型处理器
方法一(字段级别):在对应复杂属性上加@ColumnType
:
@ColumnType(typeHandler=AddressTypeHandler.class)
private Address address;
方法二(全局):在 MyBatis 配置文件中配置 typeHandlers:
<typeHandlers>
<!-- handler属性:指定自定义类型转换器全类名 -->
<!-- javaType属性:指定需要使用“自定义类型转换器”进行类型处理的实体类型 -->
<typeHandler
handler="cn.klb.mapper.handlers.AddressTypeHandler"
javaType="cn.klb.mapper.entities.Address"/>
</typeHandlers>
11.2 枚举类型
11.2.1 方法一
让通用 Mapper 把枚举类型作为简单类型处理,增加一个通用 Mapper 的配置项,在 Spring 配置文件中找到 MapperScannerConfigurer:
<!-- 整合通用Mapper所需要做的配置修改: -->
<!-- 原始全类名:org.mybatis.spring.mapper.MapperScannerConfigurer -->
<!-- 通用Mapper使用:tk.mybatis.spring.mapper.MapperScannerConfigurer -->
<bean class="tk.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.klb.mapper.mappers"/>
<property name="properties">
<value>
enumAsSimpleType=true
</value>
</property>
</bean>
本质上就是使用了org.apache.ibatis.type.EnumTypeHandler<E>
。
11.2.2 方法二
为枚举类型配置对应的类型处理器,和复杂类型一模一样操作。