【 通用 Mapper 】

1 引言

1.1 作用

替我们生成常用增删改查操作的 SQL 语句。

1.2 代码官方发布地址

https://gitee.com/free

在这里插入图片描述

文档地址:https://gitee.com/free/Mapper/wikis/1.1-java?parent=1.integration
在这里插入图片描述

1.3 前置知识

MyBatis
Spring

2 快速入门

2.1 创建测试数据

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');

2.2 导包

    <dependencies>
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper</artifactId>
            <version>4.0.4</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>
                <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.2</version>
        </dependency>
    </dependencies>

2.3 配置文件

jdbc.properties

jdbc.user=root
jdbc.password=123456
jdbc.url=jdbc:mysql://localhost:3306/common_mapper?useUnicode=true&characterEncoding=utf8
jdbc.driver=com.mysql.jdbc.Driver

log4j.properties

log4j.rootLogger=DEBUG,myConsole
log4j.appender.myConsole=org.apache.log4j.ConsoleAppender
log4j.appender.myConsole.ImmediateFlush=true
log4j.appender.myConsole.Target=System.out
log4j.appender.myConsole.layout=org.apache.log4j.PatternLayout
log4j.appender.myConsole.layout.ConversionPattern=[%-5p] %d(%r) --> [%t] %l: %m %x %n

log4j.logger.com.mchange.v2=ERROR

mybatis-config.xml

<?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>

</configuration>

spring-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
	
	<!-- 配置数据源 -->
	<context:property-placeholder location="classpath:jdbc.properties"/>

	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="user" value="${jdbc.user}"/>
		<property name="password" value="${jdbc.password}"/>
		<property name="jdbcUrl" value="${jdbc.url}"/>
		<property name="driverClass" value="${jdbc.driver}"/>
	</bean>

	<!-- 整合MyBatis -->
	<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="configLocation" value="classpath:mybatis-config.xml"/>
		<property name="dataSource" ref="dataSource"/>
	</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.ys.mapper.mappers"/>
	</bean>

	<!-- 配置Service自动扫描的包 -->
	<context:component-scan base-package="cn.ys.services"/>

	<!-- 配置声明式事务 -->
	<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"/>
	</bean>

	<aop:config>
		<aop:advisor advice-ref="txAdvice" pointcut="execution(* *..*Service.*(..))"/>
	</aop:config>

	<tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
		<tx:attributes>
			<tx:method name="get*" read-only="true"/>
			<tx:method name="save*" rollback-for="java.lang.Exception" propagation="REQUIRES_NEW"/>
			<tx:method name="remove*" rollback-for="java.lang.Exception" propagation="REQUIRES_NEW"/>
			<tx:method name="update*" rollback-for="java.lang.Exception" propagation="REQUIRES_NEW"/>
		</tx:attributes>
	</tx:advice>
</beans>

2.4 Java 实体类

考虑到基本数据类型在 Java 类中都有默认值,会导致 MyBatis 在执行相关操作时很难判断当前字段是否为 null,所以在 MyBatis 环境下使用 Java 实体类时尽量不要使用基本数据类型,都使用对应的包装类型

package cn.ys.entities;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Table(name="tabple_emp")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
	
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private Integer empId;//emp_id
	
	private String empName;//emp_name
	
	@Column(name="emp_salary_apple")
	private Double empSalary;//emp_salary_apple
	
	private Integer empAge;//emp_age

	@Override
	public String toString() {
		return "Employee [empId=" + empId + ", empName=" + empName + ", empSalary=" + empSalary + ", empAge=" + empAge
				+ "]";
	}

}

注解说明

  • @Table
    作用:建立实体类和数据库表之间的对应关系。
    默认规则:实体类类名首字母小写作为表名。Employee 类→employee 表。
    用法:在@Table 注解的 name 属性中指定目标数据库表的表名

  • @Column
    作用:建立实体类字段和数据库表字段之间的对应关系。
    默认规则:
    实体类字段:驼峰式命名
    数据库表字段:使用“_”区分各个单词
    用法:在@Column 注解的 name 属性中指定目标字段的字段名

  • @Id
    通用 Mapper 在执行 xxxByPrimaryKey(key)方法时,有两种情况。
    情况 1:没有使用@Id 注解明确指定主键字段

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 主键明确标记和数据库表中主键字段对应的实体类字段。

  • @GeneratedValue
    作用:让通用 Mapper 在执行 insert 操作之后将数据库自动生成的主键值回写到实体类对象中。
    自增主键用法:
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer empId;//emp_id

序列主键用法:
在这里插入图片描述
应用场景:购物车结账
 增加商品销量…
 减少商品库存…
 生成订单数据→封装到 Order 对象中→保存 Order 对象→数据库自动生成主键值→回写到实体类对象 Order 中
 生成一系列订单详情数据→List<OrderItem>→在每一个 OrderItem 中设置
Order 对象的主键值作为外键→批量保存 List<OrderItem> ……

  • @Transient 主键
    用于标记不与数据库表字段对应的实体类字段。
@Transient
private String otherThings; //非数据库表中字段

2.5 创建接口

package cn.ys.mappers;

import cn.ys.entities.Employee;
import tk.mybatis.mapper.common.Mapper;

/**
 * 具体操作数据库的Mapper接口,需要继承通用Mapper提供的核心接口:Mapper<Employee>
 * 泛型类型就是实体类的类型
 * @author Lenovo
 *
 */
public interface EmployeeMapper extends Mapper<Employee> {
	
}

2.6 创建 service

常用方法

  • selectOne 方法
    要求必须返回一个实体类结果,如果有多个,则会抛出异常

  • xxxByPrimaryKey 方法
    需要使用@Id 主键明确标记和数据库表主键字段对应的实体类字段,否则通用Mapper 会将所有实体类字段作为联合主键。

  • xxxSelective 方法
    非主键字段如果为 null 值,则不加入到 SQL 语句中。

package cn.ys.services;

import java.util.List;

import cn.ys.entities.Employee;
import cn.ys.mappers.EmployeeMapper;
import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import tk.mybatis.mapper.entity.Example;

@Service
public class EmployeeService {
	
	@Autowired
	private EmployeeMapper employeeMapper;

	public Employee getOne(Employee employeeQueryCondition) {
		return employeeMapper.selectOne(employeeQueryCondition);
	}

	public Employee getEmployeeById(Integer empId) {
		return employeeMapper.selectByPrimaryKey(empId);
	}

	public boolean isExists(Integer empId) {
		return employeeMapper.existsWithPrimaryKey(empId);
	}

	public void saveEmployee(Employee employee) {
		employeeMapper.insert(employee);
	}

	public void saveEmployeeSelective(Employee employee) {
		employeeMapper.insertSelective(employee);
	}

	public void updateEmployeeSelective(Employee employee) {
		employeeMapper.updateByPrimaryKeySelective(employee);
	}

	public void removeEmployee(Employee employee) {
		employeeMapper.delete(employee);
	}

	public void removeEmployeeById(Integer empId) {
		employeeMapper.deleteByPrimaryKey(empId);
	}

	public List<Employee> getEmpListByExample(Example example) {
		return employeeMapper.selectByExample(example);
	}

	public List<Employee> getEmpListByRowBounds(RowBounds rowBounds) {
		return employeeMapper.selectByRowBounds(null, rowBounds);
	}

}

2.7 测试

package cn.ys;

import java.util.List;

import cn.ys.entities.Employee;
import cn.ys.services.EmployeeService;
import org.apache.ibatis.session.RowBounds;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.entity.Example.Criteria;

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);
	}

	@Test
	public void testSelect() {
		
	}

	@Test
	public void testSelectAll() {
		
	}

	@Test
	public void testSelectCount() {
		
	}

	@Test
	public void testSelectByPrimaryKey() {
		
		//1.提供id值
		Integer empId = 3;
		
		//2.执行根据主键进行的查询
		Employee employee = employeeService.getEmployeeById(empId);
		
		//3.打印结果
		System.out.println(employee);
		
	}

	@Test
	public void testExistsWithPrimaryKey() {
		
		//1.提供主键值
		Integer empId = 33;
		
		//2.执行查询
		boolean exists = employeeService.isExists(empId);
		
		//3.打印结果
		System.out.println(exists);
		
	}

	@Test
	public void testInsert() {
		
		//1.创建实体类对象封装要保存到数据库的数据
		Employee employee = new Employee(null, "emp03", 3000.00, 23);
		
		//2.执行插入操作
		employeeService.saveEmployee(employee);
		
		//3.获取employee对象的主键字段值
		Integer empId = employee.getEmpId();
		System.out.println("empId="+empId);
		
	}

	@Test
	public void testInsertSelective() {
		
		//1.创建实体类对象封装要保存到数据库的数据
		Employee employee = new Employee(null, "emp04", null, 23);
		
		//2.执行插入操作
		employeeService.saveEmployeeSelective(employee);
		
	}

	@Test
	public void testUpdateByPrimaryKey() {
		
	}

	@Test
	public void testUpdateByPrimaryKeySelective() {
		
		//1.创建用于测试的实体类
		Employee employee = new Employee(7, "empNewName", null, null);
		
		//2.执行更新
		employeeService.updateEmployeeSelective(employee);
		
	}

	@Test
	public void testDelete() {
		
		//1.声明实体类变量作为查询条件
		Employee employee = null;
		
		//2.执行删除
		employeeService.removeEmployee(employee);
		
	}

	@Test
	public void testDeleteByPrimaryKey() {
		
		//1.提供主键值
		Integer empId = 13;
		
		//2.执行删除
		employeeService.removeEmployeeById(empId);
		
	}

	@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对象
		Criteria criteria01 = example.createCriteria();
		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);
		}
	}

	@Test
	public void testSelectOneByExample() {
		
	}

	@Test
	public void testSelectCountByExample() {
		
	}

	@Test
	public void testDeleteByExample() {
		
	}

	@Test
	public void testUpdateByExample() {
		
	}

	@Test
	public void testUpdateByExampleSelective() {
		
	}

	@Test
	public void testSelectByExampleAndRowBounds() {
		
	}

	@Test
	public void testSelectByRowBounds() {
		
		int pageNo = 3;
		int pageSize = 5;
		
		int index = (pageNo - 1) * pageSize;
		
		RowBounds rowBounds = new RowBounds(index, pageSize);
		
		List<Employee> empList = employeeService.getEmpListByRowBounds(rowBounds);
		for (Employee employee : empList) {
			System.out.println(employee);
		}
		
	}

}

3 QBC 查询

概念

Query By Criteria

Criteria 是 Criterion 的复数形式。意思是:规则、标准、准则。在 SQL 语句中相当于查询条件。

QBC 查询是将查询条件通过 Java 对象进行模块化封装。

// 目标: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 对象
Criteria criteria01 = example.createCriteria();
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);
}

4 逆向工程

4.1 原生 MyBatis 逆向工程和通用 Mapper 逆向工程对比

在这里插入图片描述

4.2 参考文档地址

https://github.com/abel533/Mapper/wiki/4.1.mappergenerator

5 自定义 Mapper<T> 接口

在这里插入图片描述

5.1用途

让我们可以根据开发的实际需要对 Mapper<T>接口进行定制。

5.2 创建自定义 Mapper<T>接口

在这里插入图片描述
在这里插入图片描述

注意:不能与原生的 Mapper 接口放同一个目录下

5.3 配置 MapperScannerConfigurer 注册 MyMapper<T>

<bean class="tk.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="cn.ys.mappers"/>
    <property name="properties">
        <value>
            mappers=cn.ys.mine_mappers.MyMapper
        </value>
    </property>
</bean>

6 通用 Mapper 接口扩展

这里的扩展是指增加通用 Mapper 没有提供的功能。

通用 Mapper 官方文档中使用一个批量 insert 作为扩展功能的例子:

  • tk.mybatis.mapper.additional.insert.InsertListMapper<T>
  • tk.mybatis.mapper.additional.insert.InsertListProvider

我们来仿照写一个批量 update。假设我们想生成下面这样的 SQL 语句:

UPDATE tabple_emp SET emp_name=?,emp_age=?,emp_salary=? where emp_id=? ;
UPDATE tabple_emp SET emp_name=?,emp_age=?,emp_salary=? where emp_id=? ;
UPDATE tabple_emp SET emp_name=?,emp_age=?,emp_salary=? where emp_id=? ; ……

为了生成上面那样的 SQL 语句,我们需要使用到 MyBatis 的 foreach 标签。

<foreach collection="list" item="record" separator=";" >
    UPDATE tabple_emp
    SET emp_name=#{record.empName}, 
    emp_age=#{record.empAge}, 
    emp_salary=#{record.empSalary}
    where emp_id=#{record.empId}
</foreach>

我们需要提供的接口和实现类
在这里插入图片描述

参考代码

package cn.ys.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);

}
package cn.ys.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);
	}
	
	/*
		<foreach collection="list" item="record" separator=";" >
			UPDATE tabple_emp
			<set>
				emp_name=#{record.empName},
				emp_age=#{record.empAge},
				emp_salary=#{record.empSalary},
			</set>
			where emp_id=#{record.empId}
		</foreach>
	 */
	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();
	}

}

7 二级缓存

MyBatis 配置文件开启二级缓存功能

<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>

在 XxxMapper 接口上使用@CacheNamespace 注解

@CacheNamespace
public interface EmployeeMapper extends MyMapper<Employee> {
}

8 类型处理器:TypeHandler

简单类型和复杂类型

基本数据类型 byte short int long double float char boolean
引用数据类型 类、接口、数组、枚举……
简单类型 只有一个值的类型
复杂类型 有多个值的类型

通用 Mapper 默认情况下会忽略复杂类型,对复杂类型不进行“从类到表”的映射。

Address 处理

自定义类型转换器
在这里插入图片描述
TypeHandler 接口

public interface TypeHandler<T> {
    //将 parameter 设置到 ps 对象中,位置是 i
    //在这个方法中将 parameter 转换为字符串
    void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
    //根据列名从 ResultSet 中获取数据,通常是字符串形式
    //将字符串还原为 Java 对象,以 T 类型返回
    T getResult(ResultSet rs, String columnName) throws SQLException;
    T getResult(ResultSet rs, int columnIndex) throws SQLException;
    T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}

继承树
在这里插入图片描述

BaseTypeHandler 类中的抽象方法说明

//将 parameter 对象转换为字符串存入到 ps 对象的 i 位置
public abstract void setNonNullParameter(
    PreparedStatement ps,
    int i, T parameter, JdbcType jdbcType) throws SQLException;
    //从结果集中获取数据库对应查询结果
    //将字符串还原为原始的 T 类型对象
    public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;
    public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;
    public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;

自定义类型转换器类
方法一 字段级别:@ColumnType 注解
在这里插入图片描述

方法二 全局级别:在 MyBatis 配置文件中配置 typeHandlers
在这里插入图片描述
在这里插入图片描述

枚举类型

办法一:让通用 Mapper 把枚举类型作为简单类型处理
 增加一个通用 Mapper 的配置项
在 Spring 配置文件中找到 MapperScannerConfigurer
在这里插入图片描述
本质:使用了 org.apache.ibatis.type.EnumTypeHandler

办法二:为枚举类型配置对应的类型处理器
在这里插入图片描述
类型处理器

  • 内置
    • org.apache.ibatis.type.EnumTypeHandler
      • 在数据库中存储枚举值本身
    • org.apache.ibatis.type.EnumOrdinalTypeHandler
      • 在数据库中仅仅存储枚举值的索引
  • 自定义

内置枚举类型处理器注册
 不能使用@ColumnType 注解
在这里插入图片描述
 需要在 MyBatis 配置文件中配置专门的类型处理器并在字段上使用@Column 注解
在这里插入图片描述
注意:加@Column 注解的作用是让通用 Mapper 不忽略枚举类型。

猜你喜欢

转载自blog.csdn.net/weixin_42112635/article/details/88849483