<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.11version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.7version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.21version>
dependency>
习惯上命名为mybatis-configxml,这个文件名仅仅只是建议,并非强制要求。将来整合Spring之后,这个配置文件可以省略,所以大家操作时可以直接复制、粘贴。
核心配置文件主要用于配置连接数据库的环境以及MyBatis的全局配置信息
核心配置文件存放的位置是src/main/resources目录下
mapper接口和映射文件要保证两个一致:
1、mapper接口的全类名和映射文件的namespace一致
2、mapper接口中的方法的方法名要和映射文件中的sql的id保持一致
insert into t_user values('4','admin','123456',23,'男','[email protected]')
@Test
public void mybatisTest() {
try {
// 获取核心配置文件的输入流
InputStream stream = Resources.getResourceAsStream("mybatis_config.xml");
// 获取SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 获取SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(stream);
// 获取sql的会话对象SqlSession(不会自动提交事务),是Mybatis提供的操作数据的对象
// SqlSession sqlSession = sqlSessionFactory.openSession();
// 获取sql的会话对象SqlSession(会自动提交事务)
SqlSession sqlSession = sqlSessionFactory.openSession(true);
// 获取UserMapper的代理实现类,
// 通过getMapper方法,重写接口方法:通过UserMapper的全类名来找到当前对象的映射文件,再通过要调用的方法找到要调用的sql语句
/**
* mapper接口和映射文件要保证两个一致:
* 1,mapper接口的全类名和映射文件的namespace一致
* 2、mapper接口中的方法的方法名要和映射文件中的sqL的id保持一致
* */
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 执行sql方法
int result = mapper.insertUser();
// 接口重写的底层实现: 通过唯一标识找到sql并执行,唯一标识是namespace.sqlId
// int result = sqlSession.insert("com.fd.mybatis.mapper.UserMapper.insertUser");
System.out.println("结果:" + result);
// 需要手动提交事务
// sqlSession.commit();
// 关闭会话
sqlSession.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
1.添加依赖
log4j log4j 1.2.17
2. 加入log4j的配置文件
log4j的配置文件名为log4j.xml,存放的位置是src/main/resources目录下
日志的级别:从左到右打印的内容越来越详细
FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试)
实现效果:
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mydb?serverTimezone=UTC jdbc.username=root jdbc.password=mysql123.
package com.fd.mybatis.mapper;
import com.fd.mybatis.pojo.User;
import java.util.List;
/**
* @author 付东
*/
public interface UserMapper {
/**
* 新增用户
*
* @author lucky_fd
* @return int
**/
int insertUser();
/**
* 修改用户
*
* @author lucky_fd
* @return int
**/
int updateUser();
/**
* 删除用户
*
* @author lucky_fd
* @return int
**/
int deleteUser();
/**
* 查询用户
*
* @author lucky_fd
* @return com.fd.mybatis.pojo.User
**/
User getUserById();
/**
* 查询用户
*
* @author lucky_fd
* @return java.util.List
**/
List selectUser();
}
insert into t_user values('2','root','123456',23,'男','[email protected]')
update t_user set password = 'qwer' where id = '1';
delete from t_user where id = '1'
package com.fd.mybatis;
import static org.junit.Assert.assertTrue;
import com.fd.mybatis.mapper.UserMapper;
import com.fd.mybatis.pojo.User;
import com.fd.mybatis.utils.SqlSessionUtil;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* Unit test for simple App.
*/
public class AppTest
{
/**
* Rigorous Test :-)
*/
@Test
public void shouldAnswerWithTrue()
{
assertTrue( true );
}
@Test
public void mybatisAddTest() {
try {
// 获取核心配置文件的输入流
InputStream stream = Resources.getResourceAsStream("mybatis_config.xml");
// 获取SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 获取SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(stream);
// 获取sql的会话对象SqlSession(不会自动提交事务),是Mybatis提供的操作数据的对象
// SqlSession sqlSession = sqlSessionFactory.openSession();
// 获取sql的会话对象SqlSession(会自动提交事务)
SqlSession sqlSession = sqlSessionFactory.openSession(true);
// 获取UserMapper的代理实现类,
// 通过getMapper方法,重写接口方法:通过UserMapper的全类名来找到当前对象的映射文件,再通过要调用的方法找到要调用的sql语句
/**
* mapper接口和映射文件要保证两个一致:
* 1,mapper接口的全类名和映射文件的namespace一致
* 2、mapper接口中的方法的方法名要和映射文件中的sqL的d保持一致
* */
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 执行sql方法
int result = mapper.insertUser();
// 接口重写的底层实现: 通过唯一标识找到sql并执行,唯一标识是namespace.sqlId
// int result = sqlSession.insert("com.fd.mybatis.mapper.UserMapper.insertUser");
System.out.println("结果:" + result);
// 需要手动提交事务
// sqlSession.commit();
// 关闭会话
sqlSession.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Test
public void updateTest() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int result = mapper.updateUser();
System.out.println("结果:" + result);
}
@Test
public void deleteTest() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int result = mapper.deleteUser();
System.out.println("结果:" + result);
}
@Test
public void selectTest() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUserById();
System.out.println(user.toString());
}
@Test
public void SelectAllUserTest() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List userList = mapper.selectUser();
userList.forEach(user -> System.out.println(user.toString()));
}
}
此时可以通过#{}和${}以任意的内容获取参数值,一定要注意${}的单引号问题
此时MyBatis会将参数放在map集合中,以两种方式存储数据
a>以arg0,arg1...为键,以参数为值
b>以param1,param2..·为键,以参数为值
因此,只需要通过#{}和${}访map集合的键,就可以获取相对应的值,一定要注意${}的单引号问题
只需要通过#{}和${}访问map集合的键,就可以获取相对应的值,一定要注意${}的单引号问题
只需要通过#{}和${}访问实体类中的属性 就可以获取相对应的属性值,一定要注意${}的单引号问题
此时MyBatis会将这些参数放在map中,以两种方式进行存储
a>以@Param注解的value属性值为键,以参数为值
b>以param1,param2...为键,以参数为值
只需要通过#{}和${}访问map集合的键,就可以获取相对应的值,一定要注意${}的单引号问题
package com.fd.mybatis.mapper;
import com.fd.mybatis.pojo.User;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
public interface SelectMapper {
/*
* 若sql语句查询的结果为多条时,一定不能以实体类类型作为方法的返回值,
* 否则会抛出异常TooManyResultsException
* 若sql语句查询的结果为1条时,此时可以使用实体类类型List集合类型作为方法的返回值
*
* */
User getUserById(@Param("id") String id);
List getAllUser();
Integer getCount();
Map getUserByIdToMap(String id);
/**
* 查询所有的用户为map集合
*
* 若查询的数据有多条时,并且要将每条数据转换为map集台
* 此时有两种解决方案:
* 1、将mapper接口方法的返回值设置为泛型是map的List集合
* List
/**
* 新增用户
*
* */
int insertUser(User user);
insert into t_user values (null, #{name}, #{password}, #{age}, #{gender}, #{email})
/**
* 新增用户
* 并返回自增主键
* */
@Test
public void insertUserTest() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
User user = new User(null, "张三", "qesdf", 25, "男", "[email protected]");
mapper.insertUser(user);
// 返回自增ID
System.out.println(user);
}
测试结果:
package com.fd.mybatis.pojo;
/**
* SSM
*
* @author lucky_fd
* @since 2023-05-22
*/
public class Dept {
private String deptId;
private String deptName;
public Dept(String deptId, String deptName) {
this.deptId = deptId;
this.deptName = deptName;
}
public Dept() {
}
public String getDeptId() {
return deptId;
}
public void setDeptId(String deptId) {
this.deptId = deptId;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
@Override
public String toString() {
return "Dept{" +
"deptId='" + deptId + '\'' +
", deptName='" + deptName + '\'' +
'}';
}
}
package com.fd.mybatis.pojo;
/**
* SSM
*
* @author lucky_fd
* @since 2023-05-22
*/
public class Emp {
private String empId;
private String name;
private Integer age;
private String gender;
private Integer deptId;
private Dept dept;
public Emp() {
}
public Emp(String empId, String name, Integer age, String gender, Integer deptId) {
this.empId = empId;
this.name = name;
this.age = age;
this.gender = gender;
this.deptId = deptId;
}
public String getEmpId() {
return empId;
}
public void setEmpId(String empId) {
this.empId = empId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Integer getDeptId() {
return deptId;
}
public void setDeptId(Integer deptId) {
this.deptId = deptId;
}
@Override
public String toString() {
return "Emp{" +
"empId='" + empId + '\'' +
", name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
", deptId=" + deptId +
", dept=" + dept +
'}';
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
}
package com.fd.mybatis.mapper;
import com.fd.mybatis.pojo.Emp;
public interface EmpMapper {
Emp getEmpById(String id);
Emp getEmpAndDeptById(String id);
}
package com.fd.mybatis.mapper;
import com.fd.mybatis.pojo.Dept;
public interface DeptMapper {
Dept getDeptById(String id);
}
一个resultMap标签,就是对应着一个实体类。id就是他的名字,type就是他们的身体。实体类和对应表是一一对应的,实体类中每一个属性都对应着一个表中的字段。其中有个别属性上标明注解@TableField(exist = false),其含义就是该属性在表中不存在,大多为引用的属性,引用的集合,或者是为了满足某种业务需求而声明的某种标志(比如sql多表查询时把某个表中的某个字段数据给另一个实体中某个属性赋值而起的别名)等等。
分步查询的优点:可以实现延迟加载
但是必须在核心配置文件中设置全局配置信息:
lazyLoadingEnabled: 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载
aggressiveLazyLoading: 当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载
此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sl。此时可通过assoiation和 collection中的fetchType属性设置当前的分步查询是否使用延迟加载,fetchType="lazy(延迟加载) || eager(立即加载)'
Mybatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能,它存在的意义是为了解决 拼接SQL语句字符串时的痛点问题。
通过test属性中的表达式判断标签中的内容是否有效(是否会拼接到sql中)
and name = #{empName}
a.where标签中有条件成立,会自动生成where关健字
b.会自动将where标签中内容前多余的and去掉,但是其中内容后多余的and无法去掉
c.where标签中没有任何一个条件成立,则where没有任何功能
select * from t_emp
and name = #{empName}
and age = #{age}
and gender = #{gender}
prefix、suffix:在标签中内容前面或后面添加指定内容
prefixOverrides、suffixOverrides:在标签中内容前面或后面去掉指定内容
select * from t_emp
name = #{empName} and
age = #{age} and
gender = #{gender} and
相当于java中的if...else if...else,when至少设置一个,otherwise最多设置一个
select * from t_emp
name = #{empName}
age = #{age}
Mybatis中把list也是存在Map中,默认"list"是键名,list数据是值;Mybatis中数组也是存在Map中,默认"array"是键名,array数组数据是值。
collection:设置要循环的数组或集合
item:用一个字符串表示数组或集合中的每一个数据
separator:设置每次循环的数据之间的分隔符
open: 循环的所有内容以什么开始
close: 循环的所有内容以什么结束
insert into t_emp values
(null, #{emp.empName}, #{emp.age}, #(emp.gender}, null)
delete from t emp where
emp_id = #{empId]
可以记录一段sql,在需要用的地方使用include标签进行引用
emp_id, emp_name, age, gender, dept_id
select from t_emp
一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问。一级缓存默认是开启的
使一级缓存失效的四种情况:
1)不同的SqlSession对应不同的一级缓存
2)同一个SqlSession但是查询条件不同
3)同一个SqlSession两次查询期间执行了任何一次增删改操作
4)同一个SqlSession两次查询期间手动清空了缓存 sqlSession.clearCache();
案列演示:
@Test
public void getEmpByIdTest() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
CacheMapper mapper = sqlSession.getMapper(CacheMapper.class);
Emp emp = mapper.getEmpById("1");
System.out.println(emp);
Emp emp2 = mapper.getEmpById("1");
System.out.println(emp2);
}
案列结果:
二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取
二级缓存开启的条件:
a> 在核心配置文件中,设置全局配置属性cacheEnabled="true”,默认为true,不需要设置
b> 在映射文件中设置标签
c> 二级缓存必须在SqlSession关闭或提交之后有效
d> 查询的数据所转换的实体类类型必须实现序列化的接口
使二级缓存失效的情况:
两次查询之间执行了任意的增删改,会便一级和二级缓存同时失效
案列演示:
@Test
public void cacheTest() throws IOException {
InputStream io = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = builder.build(io);
SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
Emp emp = mapper1.getEmpById("1");
System.out.println(emp);
sqlSession1.close();
SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
Emp emp1 = mapper2.getEmpById("1");
System.out.println(emp1);
sqlSession2.close();
}
案列结果:
在mapper配置文件中添加的cache标签可以设置一些属性
1. eviction属性:缓存回收策略,默认的是 LRU。
2.flushInterval属性: 刷新间隔,单位毫秒
默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
3.size属性:引用数目,正整数
代表缓存最多可以存储多少个对象,太大容易导致内存溢出
4.readOnly属性: 只读, true/false
先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。
如果二级缓存没有命中,再查询一级缓存
如果一级缓存也没有命中,则查询数据库
SqlSession关闭之后,一级缓存中的数据会写入二级缓存
org.mybatis.caches
mybatis-ehcache
1.0.3
ch.qos.logback
logback-classic
1.2.11
存在SLF4j时,作为简易日志的log4j将失效,此时我们需要借助SLF4j的具体实现logback来打印日志。创建logback的配置文件logback.xml
UTF-8
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
UTF-8
${LOG_HOME}/${appName}.log
${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log
30
512MB
%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [%logger{50} : %line ] - %msg%n
@Test
public void cacheTest() throws IOException {
InputStream io = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = builder.build(io);
SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
Emp emp = mapper1.getEmpById("1");
System.out.println(emp);
sqlSession1.close();
SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
Emp emp1 = mapper2.getEmpById("1");
System.out.println(emp1);
sqlSession2.close();
}
(1) MyBatis 使用 RowBounds 对象进行分页,它是针对 ResultSet 结果集执行的内存分页,而非物理分页;
(2) 可以在 sql 内直接书写带有物理分页的参数来完成物理分页功能,
(3) 也可以使用分页插件来完成物理分页。
@Test
public void pageByRowsBoundsTest() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
RowBounds rowBounds = new RowBounds(1, 2);
List emps = sqlSession.selectList("com.fd.mybatis.mapper.EMPMapper.selectByExample", null, rowBounds);
emps.forEach(System.out::println);
sqlSession.close();
}
语法: select * from user limit startIndex,pageSize
select * from user limit 0,2;
具体实现:
com.github.pagehelper pagehelper 5.3.0
mybatis-config.xml配置文件添加分页插件
a.在查询功能之前使用PageHelper.startPage(int pageNum,int pageSize)开启分页功能
pageNum:当前页的页码
pageSize:每页显示的条数
b.在查询获取list集合之后,使用Pagelnfo pagelnfo = new Pagelnfo<>(List list, int navigatePages)获取分页相关数据。
list:分页之后的数据
navigatePages: 导航分页的页码数
c.分页相关数据
Pagelnfo{
pageNum=8,pageSize=4, size=2, startRow=29, endRow=30, total=30, pages=8,
list=Page{count=true, pageNum=8, pageSize=4, startRow=28, endRow=32, total=30,
pages=8,reasonable=false, pageSizeZero=false},
prePage=7, nextPage=0, isFirstPage=false, isLastPage=true, hasPreviousPage=true, hasNextPage=false, navigatePages=5, navigateFirstPage4, navigateLastPage8.navigatepageNums=[4, 5, 6, 7 8]
}
pageNum:当前页的页码
pageSize: 每页显示的条数
size:当前页显示的真实条数
total: 总记录数
pages:总页数
prePage:上一页的页码
nextPage:下一页的页码
isFirstPage/isLastPage: 是否为第一页/最后一页
hasPreviousPage/hasNextPage: 是否存在上一页/下一页
navigatePages:导航分页的页码数
navigatepageNums:导航分页的页码,[1,2,3,4,5]
@Test
public void pageTest() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
EMPMapper mapper = sqlSession.getMapper(EMPMapper.class);
// 开启分页
Page
错误回答:
Dao 接口里的方法,是不能重载的,因为是全限名+方法名的保存和寻找策略。正确回答:
通常一个 xml 映射文件,都会写一个 Dao 接口与之对应。Dao 接口就是人们常说的
Mapper
接口,接口的全限名,就是映射文件中的 namespace 的值,接口的方法名,就是映射文件中MappedStatement
的 id 值,接口方法内的参数,就是传递给 sql 的参数。Mapper
接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为 key 值,可唯一定位一个MappedStatement。
Mybatis 的 Dao 接口可以有多个重载方法,但是多个接口对应的映射必须只有一个,否则启动会报错。
Dao 接口的工作原理是 JDK 动态代理,MyBatis 运行时会使用 JDK 动态代理为 Dao 接口生成代理 proxy 对象,代理对象 proxy 会拦截接口方法,转而执行
MappedStatement
所代表的 sql,然后将 sql 执行结果返回。补充:
Dao 接口方法可以重载,但是需要满足以下条件:
- 仅有一个无参方法和一个有参方法
- 多个有参方法时,参数数量必须一致。且使用相同的
@Param
,或者使用param1
这种
MyBatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载,association 指的就是一对一,collection 指的就是一对多查询。在 MyBatis 配置文件中,可以配置是否启用延迟加载
lazyLoadingEnabled=true|false。
它的原理是,使用
CGLIB
创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName()
,拦截器invoke()
方法发现a.getB()
是 null 值,那么就会单独发送事先保存好的查询关联 B 对象的 sql,把 B 查询上来,然后调用 a.setB(b),于是 a 的对象 b 属性就有值了,接着完成a.getB().getName()
方法的调用。这就是延迟加载的基本原理。当然了,不光是 MyBatis,几乎所有的包括 Hibernate,支持延迟加载的原理都是一样的。
能,MyBatis 不仅可以执行一对一、一对多的关联查询,还可以执行多对一,多对多的关联查询,多对一查询,其实就是一对一查询,只需要把
selectOne()
修改为selectList()
即可;多对多查询,其实就是一对多查询,只需要把selectOne()
修改为selectList()
即可。关联对象查询,有两种实现方式,一种是单独发送一个 sql 去查询关联对象,赋给主对象,然后返回主对象。另一种是使用嵌套查询,嵌套查询的含义为使用 join 查询,一部分列是 A 对象的属性值,另外一部分列是关联对象 B 的属性值,好处是只发一个 sql 查询,就可以把主对象和其关联对象查出来。
那么问题来了,join 查询出来 100 条记录,如何确定主对象是 5 个,而不是 100 个?其去重复的原理是
标签内的
子标签,指定了唯一确定一条记录的 id 列,MyBatis 根据
列值来完成 100 条记录的去重复功能,
可以有多个,代表了联合主键的语意。
同样主对象的关联对象,也是根据这个原理去重复的,尽管一般情况下,只有主对象会有重复记录,关联对象一般不会重复。
举例:下面 join 查询出来 6 条记录,一、二列是 Teacher 对象列,第三列为 Student 对象列,MyBatis 去重复处理后,结果为 1 个老师 6 个学生,而不是 6 个老师 6 个学生。
MyBatis 可以映射枚举类,不单可以映射枚举类,MyBatis 可以映射任何对象到表的一列上。映射方式为自定义一个 TypeHandler
,实现 TypeHandler
的 setParameter()
和 getResult()
接口方法。 TypeHandler
有两个作用:
setParameter()
和 getResult()
两个方法,分别代表设置 sql 问号占位符参数和获取列查询结果。