MyBatis
MyBatis官方中文文档
MyBatis学习笔记
Hello World
- 创建全局测试文件
官方文档提供格式, 下载主配置文件中
1 |
|
SQL映射文件
EmployeeMapper.xml
1 |
|
将sql映射文件注册在全局配置文件中
1
2
3
4<!-- 将我们写好的sql映射文件(EmployeeMapper.xml)一定要注册到全局配置文件(mybatis-config.xml)中 -->
<mappers>
<mapper resource="EmployeeMapper.xml" />
</mappers>
测试使用代码:
1 | public SqlSessionFactory getSqlSessionFactory() throws IOException { |
接口式编程
namespace: 名称空间指定为接口的全类名
1 | <mapper namespace="com.atguigu.mybatis.dao.EmployeeMapper"> |
使用接口但是不写实现类
1 | public SqlSessionFactory getSqlSessionFactory() throws IOException { |
mybatis会自动生成代理对象执行EmployeeMapper.xml 中定义的增删改查方法,
只需要写一个接口:
1 | public interface EmployeeMapper { |
- 接口式编程
- 原生: Dao ====> DaoImpl
- mybatis: Mapper ====> xxMapper.xml
- SqlSession代表和数据库的一次会话;用完必须关闭;
- SqlSession和connection一样她都是非线程安全。每次使用都应该去获取新的对象。
- mapper接口没有实现类,但是mybatis会为这个接口生成一个代理对象。
- (将接口和xml进行绑定)
- EmployeeMapper empMapper = sqlSession.getMapper(EmployeeMapper.class);
- 两个重要的配置文件:
- mybatis的全局配置文件:包含数据库连接池信息,事务管理器信息等…系统运行环境信息
- sql映射文件:保存了每一个sql语句的映射信息:
- 将sql抽取出来。
全局配置文件
编写外部属性properties文件
1 | driver = com.mysql.jdbc.Driver |
更改原来的xml配置文件
1 | <environments default="development"> |
实际在和spring整合的时候, 这一块配置都交给spring来做了
settings标签
1 | <settings> |
设置驼峰命名法
这样数据库如果column名字是last_name, User类里的字段是lastName, 那么就依然可以匹配
1 | <setting name = "mapUnderscoreToCamelCase" value="true"/> |
typeAliases标签
可以为bean类起别名
别名不区分大小写
1 | <typeAliases> |
批量起别名
package: 为某个包下的所有类批量起别名
name: 指定包名(此包下和所有子包下所有类, 默认别名是类名小写)
1
<package name="com.atguigu.mybatis.bean"/>
使用@Alias注解为某个类
typeHandlers标签
时间类型处理器
自定义处理器
plugins标签
简单介绍:
可以允许插件拦截四大对象
四大对象:
- Executor
- ParameterHandler
- ResultSetHandler
- StatementHandler
environments标签
environments: 环境们, 配置多重环境, default指定使用某种环境, 可以达到快速切换的目的
environment: 配置具体环境信息
- 标签下必须含有两个标签
- transactionManager
- 事务管理器的类型, JDBC|MANAGED
- 自定义事务管理器: 实现TransactionFactory接口.type 指定为全类名
- dataSource:
- 数据源类型: [UNPOOLED|POOLED|JNDI]
1 | <environments default="test"> |
可以通过实现DataSourceFactory来使用第三方数据源
1 | public interface DataSourceFactory { |
databaseIdProvider标签
数据库厂商标识
1 | <databaseIdProvider type="DB_VENDOR" /> |
databaseIdProvider 对应的 DB_VENDOR 实现会将 databaseId 设置为 DatabaseMetaData#getDatabaseProductName()
返回的字符串。
由于通常情况下这些字符串都非常长,而且相同产品的不同版本会返回不同的值,你可能想通过设置属性别名来使其变短:
1 | <databaseIdProvider type="DB_VENDOR"> |
会将 databaseId 设置为数据库产品名与属性中的名称第一个相匹配的值,如果没有匹配的属性,将会设置为 “null”
可以通过DatabaseIdProvider接口实现自定义标识
1 | public interface DatabaseIdProvider { |
mappers标签
- resource
1 | <!-- 使用相对于类路径的资源引用 --> |
- url 加上file:///完全限定资源定位符
1 | <!-- 使用完全限定资源定位符(URL) --> |
- class: 引用接口
1 | <!-- 使用映射器接口实现类的完全限定类名 --> |
全类名和接口名需要一致
也可以直接使用@Select, @Update, @Delete ….直接注解在接口上,
1 |
|
- name: 批量注册
1 | <!-- 将包内的映射器接口实现全部注册为映射器 --> |
映射文件
增删改查
修改Mapper接口添加增删改查
1 | public Employee getEmpById(Integer id); |
修改xml映射配置文件编写sql语句
1 | <!-- public void addEmp(Employee employee); --> |
测试
1 | /** |
单个参数:mybatis不会做特殊处理,
- #{参数名/任意名}:取出参数值。
多个参数:mybatis会做特殊处理。
多个参数会被封装成 一个map,
- key:param1…paramN, 或者参数的索引也可以
- value:传入的参数值
- #{}就是从map中获取指定的key的值;
```
异常:org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [1, 0, param1, param2]
操作:
方法:public Employee getEmpByIdAndLastName(Integer id,String lastName); 取值:#{id},#{lastName}
1
2
3
4
5
6
7
【命名参数】:明确指定封装参数时map的key;@Param("id")
```JAVA
public Employee getEmpByIdAndLastName(@Param("id")Integer id,@Param("lastName")String lastName);
多个参数会被封装成 一个map,
- key:使用@Param注解指定的值
- value:参数值
- #{指定的key}取出对应的参数值
1 | <!-- public Employee getEmpByIdAndLastName(Integer id,String lastName);--> |
POJO:如果多个参数正好是我们业务逻辑的数据模型,我们就可以直接传入pojo;
- #{属性名}:取出传入的pojo的属性值
Map:如果多个参数不是业务模型中的数据,没有对应的pojo,不经常使用,为了方便,我们也可以传入map
```JAVA
public Employee getEmpByMap(Map<String, Object> map);1
2
3
4
5
6
- ```JAVA
Map<String, Object> map = new HashMap<>();
map.put("id", 2);
map.put("lastName", "Tom");
map.put("tableName", "tbl_employee");#{key}:取出map中对应的值
```XML
<!-- public Employee getEmpByMap(Map<String, Object> map); --> <select id="getEmpByMap" resultType="com.atguigu.mybatis.bean.Employee"> select * from ${tableName} where id=${id} and last_name=#{lastName} </select> <!-- public Employee getEmpByMap(Map<String, Object> map); --> <select id="getEmpByMap" resultType="com.atguigu.mybatis.bean.Employee"> select * from ${tableName} where id=${id} and last_name=#{lastName} </select>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- TO:如果多个参数不是业务模型中的数据,但是经常要使用,推荐来编写一个TO(Transfer Object)数据传输对象
Page{
int index;
int size;
}
--------------
## 思考
```java
public Employee getEmp(@Param("id")Integer id,String lastName);
取值:id==>#{id/param1} lastName==>#{param2}
1 | public Employee getEmp(Integer id, Employee emp); |
- 取值:id==>#{param1} lastName===>#{param2.lastName/e.lastName}
1 | public Employee getEmpById(List<Integer> ids); |
- 取值:取出第一个id的值: #{list[0]}
特别注意:如果是Collection(List、Set)类型或者是数组,也会特殊处理。也是把传入的list或者数组封装在map中。
- key:
- Collection(collection),
- 如果是List还可以使用这个key(list),
- 数组(array)
结合源码—mybatis怎么处理参数
总结:参数多时会封装map,为了不混乱,我们可以使用@Param来指定封装时使用的key;
#{key}就可以取出map中的值;
(@Param(“id”)Integer id,@Param(“lastName”)String lastName);
ParamNameResolver解析参数封装map的;
//1、names:{0=id, 1=lastName};构造器的时候就确定好了
确定流程:
1.获取每个标了param注解的参数的@Param的值:id,lastName; 赋值给name;
2.每次解析一个参数给map中保存信息:(key:参数索引,value:name的值)
name的值:
标注了param注解:注解的值
没有标注:
1.全局配置:useActualParamName(jdk1.8):name=参数名
2.name=map.size();相当于当前元素的索引
{0=id, 1=lastName,2=2}
args【1,”Tom”,’hello’】:
1 | public Object getNamedParams(Object[] args) { |
参数值的获取
#{}:可以获取map中的值或者pojo对象属性的值;
${}:可以获取map中的值或者pojo对象属性的值;
1 | select * from tbl_employee where id=${id} and last_name=#{lastName} |
1 | Preparing: select * from tbl_employee where id=2 and last_name=? |
- 区别:
- #{}:是以预编译的形式,将参数设置到sql语句中;PreparedStatement;防止sql注入
- ${}:取出的值直接拼装在sql语句中;会有安全问题;
大多情况下,我们去参数的值都应该去使用#{};
原生jdbc不支持占位符的地方我们就可以使用${}进行取值
比如分表、排序。。。;按照年份分表拆分
1 | select * from ${year}_salary where xxx; |
#{}:更丰富的用法:
规定参数的一些规则:
javaType、 jdbcType、 mode(存储过程)、 numericScale、
resultMap、 typeHandler、 jdbcTypeName、 expression(未来准备支持的功能);
- jdbcType通常需要在某种特定的条件下被设置:
- 在我们数据为null的时候,有些数据库可能不能识别mybatis对null的默认处理。比如Oracle(报错);
- JdbcType OTHER:无效的类型;因为mybatis对所有的null都映射的是原生Jdbc的OTHER类型,oracle不能正确处理;
由于全局配置中:jdbcTypeForNull=OTHER;
oracle不支持;两种办法:
- #{email,jdbcType=OTHER}; 赋值的时候指定值
- jdbcTypeForNull=NULL
1 | <setting name="jdbcTypeForNull" value="NULL"/> |
自动映射
全局setting设置
- autoMappingBehavior默认是PARTIAL,开启自动映射的功能。唯一的要求是列名和javaBean属性名一致
- 如果autoMappingBehavior设置为null则会取消自动映射
- 数据库字段命名规范,POJO属性符合驼峰命名法,如A_COLUMNaColumn,我们可以开启自动驼峰命名规则映射功能,mapUnderscoreToCamelCase=true。
自定义resultMap,实现高级结果集映射。
resultMap
和resultType只能二选一
可以使用rusultMap自定义映射规则:
1 | <resultMap type="com.atguigu.mybatis.bean.Employee" id="MySimpleEmp"> |
- constructor-类在实例化时, 用来注入结果到构造方法中
- idArg-ID 参数; 标记结果作为ID 可以帮助提高整体效能
- arg-注入到构造方法的一个普通结果
- id––一个ID 结果; 标记结果作为ID 可以帮助提高整体效能
- result–注入到字段或JavaBean 属性的普通结果
- association–一个复杂的类型关联;许多结果将包成这种类型
- 嵌入结果映射–结果映射自身的关联,或者参考一个
- collection–复杂类型的集
- 嵌入结果映射–结果映射自身的集,或者参考一个
- discriminator–使用结果值来决定使用哪个结果映射
- case–基于某些值的结果映射
- 嵌入结果映射–这种情形结果也映射它本身,因此可以包含很多相同的元素,或者它可以参照一个外部的结果映射。
场景一:
查询Employee的同时查询员工对应的部门
Employee===Department
一个员工有与之对应的部门信息;
1 | id last_name gender d_id did dept_name (private Department dept;) |
1 |
|
因为JavaBean Employee里面有个人dept属性, 所以级联查询直接引用属性的属性—-dept.id
1 |
|
Department:
1 | public class Department { |
association标签
联级查询
1 | <!-- |
使用association分步查询:
- 使用select指定的方法(传入column指定的这列参数的值)查出对象,并封装给property指定的属性
1 | <!-- 使用association进行分步查询: |
可以使用延迟加载(懒加载);(按需加载)
Employee==>Dept:
我们每次查询Employee对象的时候,都将一起查询出来。
部门信息在我们使用的时候再去查询;
分段查询的基础之上加上两个配置:
1 | <!--显示的指定每个我们需要更改的配置的值,即使他是默认的。防止版本更新带来的问题 --> |
场景二:Collection 标签
查询部门的时候将部门对应的所有员工信息也查询出来:注释在DepartmentMapper.xml中
Department:
1 | public class Department { |
需要的结果:
1 | did dept_name || eid last_name email gender |
1 | <!--嵌套结果集的方式,使用collection标签定义关联的集合类型的属性封装规则 --> |
使用collection的分段查询
在EmployeeMapper.xml中
1 | <select id="getEmpsByDeptId" resultType="com.atguigu.mybatis.bean.Employee"> |
然后再DepartmentMapper中使用EmployeeMapper中的getEmpsByDeptId
1 | <!-- collection:分段查询 --> |
如果想将多列的值传递过去:
将多列的值封装map传递;
1
column="{key1=column1,key2=column2}"
fetchType=”lazy”:用fetchType作为Collection标签属性表示使用延迟加载;
- lazy:延迟
- eager:立即
- 默认为延迟加载 lazy
discriminator鉴别器
1 | <discriminator javaType=""></discriminator> |
鉴别器:mybatis可以使用discriminator判断某列的值,然后根据某列的值改变封装行为
场景:
如果查出的是女生:就把部门信息查询出来,否则不查询;
如果是男生,把last_name这一列的值赋值给email;
1 | <resultMap type="com.atguigu.mybatis.bean.Employee" id="MyEmpDis"> |
id & result
- id 和result 映射一个单独列的值到简单数据类型(字符串,整型,双精度浮点数,日期等)的属性或字段。
动态SQL
四种标签
if:判断
choose (when, otherwise):分支选择;带了break的swtich-case
如果带了id就用id查,如果带了lastName就用lastName查;只会进入其中一个
trim 字符串截取(where(封装查询条件), set(封装修改条件))
foreach 遍历集合
if
创建一个新的映射
1 | public interface EmployeeMapperDynamic { |
1 | <!-- 查询员工,要求,携带了哪个字段查询条件就带上这个字段的值 --> |
查询的时候如果某些条件没带可能sql拼装会有问题
- 给where后面加上1=1,以后的条件都and xxx.
- mybatis使用where标签来将所有的查询条件包括在内。mybatis就会将where标签中拼装的sql,多出来的and或者or去掉
但是where只会去掉第一个多出来的and或者or。
这时候可以使用trim标签
1 | <!--public List<Employee> getEmpsByConditionTrim(Employee employee); --> |
choose
如果带了id就用id查,如果带了lastName就用lastName查;只会进入其中一个
1 | public List<Employee> getEmpsByConditionChoose(Employee employee); |
1 | //测试choose |
1 | <!-- public List<Employee> getEmpsByConditionChoose(Employee employee); --> |
set标签
可以取出多余的逗号
1 | <!--public void updateEmp(Employee employee); --> |
trim
可以做到跟set一样的效果, 去除多余的逗号
1 | <update id="updateEmp"> |
foreach
collection:指定要遍历的集合:
- list类型的参数会特殊处理封装在map中,map的key就叫list
item:将当前遍历出的元素赋值给指定的变量
separator:每个元素之间的分隔符
open:遍历出所有结果拼接一个开始的字符
close:遍历出所有结果拼接一个结束的字符
index:索引。遍历list的时候是index就是索引,item就是当前值
- 遍历map的时候index表示的就是map的key,item就是map的值
#{变量名}就能取出变量的值也就是当前遍历出的元素
1 | <!--public List<Employee> getEmpsByConditionForeach(List<Integer> ids); --> |
测试
1 | List<Employee> list = mapper.getEmpsByConditionForeach(Arrays.asList(1,2)); |
批量保存
1 | <!--public void addEmps(@Param("emps")List<Employee> emps); --> |
测试
1 |
|
第二种方式:
1 | <!-- 这种方式需要数据库连接属性allowMultiQueries=true; |
但是这种方式需要开启jdbc连接属性的分号隔开功能 — allowMultiQueries
1 | jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true = |
Oracle中的批量保存
Oracle不支持values(),(),()
Oracle支持的批量方式
多个insert放在begin - end里面
1
2
3
4
5
6begin
insert into employees(employee_id,last_name,email)
values(employees_seq.nextval,'test_001','test_001@atguigu.com');
insert into employees(employee_id,last_name,email)
values(employees_seq.nextval,'test_002','test_002@atguigu.com');
end;利用中间表:
1
2
3
4
5
6
7insert into employees(employee_id,last_name,email)
select employees_seq.nextval,lastName,email from(
union
select 'test_a_02' lastName,'test_a_e02' email from dual
union
select 'test_a_03' lastName,'test_a_e03' email from dual
)
1 | <insert id="addEmps" databaseId="oracle"> |
两个MyBatis内置参数
不只是方法传递过来的参数可以被用来判断,取值。。。
mybatis默认还有两个内置参数:
parameter:代表整个参数
- _单个参数:_parameter就是这个参数
- 多个参数:参数会被封装为一个map;_parameter就是代表这个map
databaseId:如果配置了databaseIdProvider标签。
- databaseId就是代表当前数据库的别名oracle
1 | <!--public List<Employee> getEmpsTestInnerParameter(Employee employee); --> |
bind
bind:可以将OGNL表达式的值绑定到一个变量中,方便后来引用这个变量的值
1 | <!--public List<Employee> getEmpsTestInnerParameter(Employee employee); --> |
sql标签
抽取可重用的sql片段。方便后面引用
sql抽取:经常将要查询的列名,或者插入用的列名抽取出来方便引用
include来引用已经抽取的sql:
include还可以自定义一些property,sql标签内部就能使用自定义的属性
- include-property:取值的正确方式${prop},
- #{不能使用这种方式}
1 | <insert id="addEmps"> |
引用抽取出来的insertColumn
1 | <sql id="insertColumn"> |
缓存
- MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。
- MyBatis系统中默认定义了两级缓存一级缓存
缓存的顺序:
- 二级缓存
- 一级缓存
一级缓存
sqlSession级别的缓存。一级缓存是一直开启的;SqlSession级别的一个Map
也称为本地缓存, 与数据库同一次会话查询到的数据会放在本地缓存中
以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库;
一级缓存失效情况(没有使用到当前一级缓存的情况,效果就是,还需要再向数据库发出查询):
sqlSession不同。
sqlSession相同,查询条件不同.(当前一级缓存中还没有这个数据)
sqlSession相同,两次查询之间执行了增删改操作(这次增删改可能对当前数据有影响)
sqlSession相同,手动清除了一级缓存(缓存清空)
1 |
|
二级缓存
(全局缓存):基于namespace级别的缓存:一个namespace对应一个二级缓存:
工作机制:
一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中;
如果会话关闭;一级缓存中的数据会被保存到二级缓存中;新的会话查询信息,就可以参照二级缓存中的内容;
不同namespace查出的数据会放在自己对应的缓存中(map)
- sqlSession===EmployeeMapper==>Employee
- DepartmentMapper===>Department
使用:
在全局配置文件中开启全局二级缓存配置:
1
<setting name="cacheEnabled" value="true"/>
去各个mapper.xml中配置使用二级缓存:不写标签属性就代表都是默认值
1 | <cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"></cache> |
- 我们的POJO需要实现序列化接口
和缓存有关的设置/属性:
- cacheEnabled=true:false:关闭缓存(二级缓存关闭)(一级缓存一直可用的)
- 每个select标签都有useCache=”true”:
- false:不使用缓存(一级缓存依然使用,二级缓存不使用)
- 每个增删改标签的:flushCache=”true”:(一级二级都会清除)
- 增删改执行完成后就会清楚缓存;
- 测试:flushCache=”true”:一级缓存就清空了;二级也会被清除;
- 查询标签:flushCache=”false”:
- 如果flushCache=true;每次查询之后都会清空缓存;缓存是没有被使用的;
- sqlSession.clearCache();只是清楚当前session的一级缓存;
- localCacheScope:本地缓存作用域:(一级缓存SESSION);当前会话的所有数据保存在会缓存中;
- STATEMENT:可以禁用一级缓存;
cache 标签
在那个命名空间下写, 哪个空间就有二级缓存
1 | <cache></cache> |
测试:
效果:数据会从二级缓存中获取
- 查出的数据都会被默认先放在一级缓存中。
- 只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中
1 |
|
第三方缓存整合:
提供了接口Cache
导入第三方缓存jar包或者依赖;
导入MyBatis提供的与第三方缓存整合的适配包;官方有;也可以用适配包依赖
mapper.xml中使用自定义缓存
1
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
ehcache.xml
- ```xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# 整合Spring
先引入各种依赖
```xml
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.4</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>org.bitbucket.swattu</groupId>
<artifactId>spring-mvc</artifactId>
<version>4.1.22</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
</dependencies>
- ```xml
然后在创建spring配置文件applicationContext.xml, 并在web.xml配置文件中配置contextConfigListener
1 | <context-param> |
在spring配置文件中, 配置component scan, 排除扫描Controller, 不与springMVC冲突
1 | <!-- spring配置文件, 管理所有业务逻辑组件--> |
然后WEB-INF下创建springMVC配置文件, 配置1component scan只扫描Controller
1 | <!-- spring配置文件, 管理所有业务逻辑组件--> |
并配置视图解析器, 和开启注解扫描
1 | <!-- 视图解析器--> |
在spring配置文件中配置数据库连接
1 | <!-- 映入配置文件--> |
然后在spring配置文件applicationContext中开始整合Mybatis
1 | <!-- spring事务管理器--> |
1 | <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"> |
最后写个Controller和前端jsp页面发送请求
1 |
|
MyBatis 逆向工程
MBG Code generator 官方文档
MyBatis Generator (MBG) generates code in different styles depending on how it is configured. This is controlled by specifying the
targetRuntime
attribute on a<context>
configuration element. The table below summarizes the different options.
先导入依赖 mybatis-generator-core
To get up and running quickly with MyBatis Generator (MBG), follow these steps:
Create and fill out a configuration file appropriately (see below for samples)
Save the file in some convenient location (like \temp\generatorConfig.xml)
Run MBG from the command line with a command like this:
1
2 java -jar mybatis-generator-core-x.x.x.jar -configfile \temp\generatorConfig.xml -overwrite
This will tell MBG to run using your configuration file. It will also tell MBG to overwrite any existing Java or Kotlin files with the same name. If you want to save any existing files, then omit the
-overwrite
parameter. If there is a conflict, MBG will save the newly generated file with a unique name (e.g. MyClass.java.1).After running MBG, you will need to create or modify the standard MyBatis configuration make use of your newly generated code. See the Tasks After Running MyBatis Generator page for more information.
加入 官方提供的配置模板
1 |
|
<jdbcConnection>
指定如何连接到数据库
<javaTypeResolver>
定义JavaBean的生成模型策略
<sqlMapGeneraotr>
定义sql映射生成策略
<javaClientGenerator>
指定mapper接口所在的位置
<table>
指定要逆向分析的表格
Running MyBatis Generator
MyBatis Generator (MBG) can be run in the following ways:
- …
- From another Java program with an XML configuration
- From another Java program with a Java based configuration
把一下代码放进测试类, 然后加上异常处理
1 |
|
运行即可, 系统会生成逆向工程文件
生成的xxxExample 都是封装查询条件
而Criteria就是拼装条件查询, 里面封装了很多方法可以拼接查询条件
MyBatis运行原理
- Executor
- StatementHandler
- ParameterHandler
- ResultSetHandler
MyBatis插件
在四大对象创建的时候, 每个对象返回之前都是通过interceptorChain.pluginAll(parameterHandler)
获取到所有Interceptor调用plugin方法然后返回包装后的target
这样可以为对象创建一个代理对象, AOP(面向切面)
代理对象就可以拦截四大对象的每一个执行方法
插件实现
写一个类实现拦截器
implements Interceptor
第一个方法
intercept
1
2
3
4
5
//执行target方法
Object proceed = invocation.proceed();
//然后返回
return proceed;plugin
方法包装对象, 为对象创建代理对象
1
2
3//可以借助wrap方法
Object wrap = Plugin.wrap(target, this);
return wrap;
setProperties
方法 – 讲插件注册时的property属性设置进来加上
@Interceptor
注解 – 完成插件签名, 告诉MyBatis当前类拦截那个对象的哪个方法1
2
3将写好的插件注册到mybatis全局配置文件中
1
2
3
4
5
6<plugins>
<plugin interceptors="全类名">
<property name="username" value="root"/>
</plugin>
</plugins>
MyBatis扩展
分页插件
pageHelper
- 导入依赖
- 在MyBatis的xml文件中添加pageintercepor拦截器
1 | <plugins> |
使用:
在调用mapper方法之前添加 PageHelpler.startPage(1, 5)
就代表第一页, 每页五条记录, 自动拦截了Mysql
1 | Page<Object> page = PageHelper.startPage(1,5); |
通过返回的page对象, 还可以获取很多其他参数:
1 | page.getpageNum(); //当前页码 |
也可以使用PageInfo
获取
1 | PageInfo<Employee> info = new PageInfo<>(emps); |
这样还能获取更多数据
1 | info.isFirstPage() |
PageInfo还可以传另一个参数,
1 | PageInfo<Employee> info = new PageInfo<>(emps, 5); |
这样可以连续显示5页数据, 第一页第二页显示1-5, 第三页3在中间, 第四页4显示在中间
1 | info.getNavigatepageNums();//给出连续显示的页码, 方便解决分页逻辑 |
批量操作
dafaultExecutorType — BATCH 默认是simple
原生使用:
非批量:
没一条都发一次sql, 预编译一次, 很慢
1 | SqlSession openSession = sqlSessionFactory.openSession(); |
批量:
只预编译一次, 只发送一次Sql
1 | SqlSession openSession = sqlSessionFactory.openSession(ExecutorType.BATCH); |
Spring整合配置:
配置一个可以批量执行的sqlSession
1 | <bean id="sqlSesion" class="org.mybatis.spring.SqlSessionTemplate"> |
在Service层里面使用的时候, 直接使用注解自动注入sqlSession就好了
1 |
|
自定义TyeHandler
测试枚举类型的映射.
默认会保存成为枚举的名字. 因为默认使用了, EnumTypeHandler
如果要改变使用EnumOrdinalTypeHandler
去到mybatis全局配置文件
1 | <typeHandlers> |
自定义
在枚举里面, 添加一个构造器
希望数据库里面保存的是我们给枚举对象的特定属性, 就需要一个自定义处理器
自己创建一个类, 然后实现TypeHandler 或者继承BaseTypeHandler
在setParameter(PreparedStatement ps,int i, EmpStatus empstatus)
里面自定义设置值:
1 | ps.setString(i, parameter.getCode().toString()); |
在getResult(ResultSet rs, String columnName)`里面自定义获取值:
1 | //根据在数据库中拿到的状态码, 返回枚举对象 |
最后在mybatis全局配置文件中配置自定义的处理器就可以了